gfranq.comフォトサービスでの地平線の配置

gfranq.comフォトサービスには、写真を任意の角度に揃える機能があります! この角度は自動的に計算されますが、必要に応じて手動で簡単に変更できます。 マウスの右ボタンで水平線を描くことができ、処理された写真は、Instagramとは異なり、長方形にすることができます。 さらに、元の画像サイズを維持したり、回転した画像の最大領域をカバーするオプションが提供されます。







自動整列方法がどのように機能するか、どのアルゴリズムが使用されたかを知りたいと思っている人は、猫の下で歓迎します。



自動水平調整方法



自動水平調整がほとんどの写真で許容可能なレベルで機能するために、このタスクは次の段階に分割されることが決定されました。



  1. 境界の定義。
  2. 直線の定義。
  3. 最も強い線の計算。
  4. 見つかった線と画像の中心の間の角度の計算。
  5. 計算された角度で画像を回転させます。
  6. 回転した画像に内接する最大の長方形の計算。


さらに、これらの手順を詳細に検討します。



境界定義(キャニー演算子)。


境界を決定するために、主観的および客観的な考慮事項( Wikipediaで読むことができます)に基づいて、Canny境界検出器を使用することが決定されました。

Cannyのアルゴリズムは、次の手順で構成されています。

  1. 画像を白黒に変換します。
  2. ガウス画像のぼかし。
  3. グラデーションを検索します。
  4. 非最大抑制とあいまいさの追跡。


直線の定義(ハフ変換)。


画像の境界線が見つかった後(明るさやその他の不均一性の急激な変化)、水平線は通常、直線またはほぼ直線のように見えるため(ノイズの可能性があるため)、アルゴリズムを適用して直線を抽出することができます。 Hough変換が、指定されたアルゴリズムとして選択されました。 ただし、この変換は非常に多くの行を返すことができ、さらに、行の角度が水平方向および垂直方向に測定される画像(行列)を返します-中心から行までの距離は、次のステップには不便です。 これらの問題を解決するために、線を極座標から長方形に変換する機能が記述されました。



極座標のラインを直交座標のラインセグメントに変換する
private static WeightedLine HoughLineToTwoPointLine(double theta, short radius, double intensity, int width, int height) { int r = radius; double t = theta; if (r < 0) { t += 180; r = -r; } t = (t / 180) * Math.PI; int w2 = width / 2; int h2 = height / 2; double x0 = 0, x1 = 0, y0 = 0, y1 = 0; if (theta != 0) { x0 = -w2; x1 = w2; double sint = Math.Sin(t); double cost = Math.Cos(t); y0 = (-cost * x0 + r) / sint; y1 = (-cost * x1 + r) / sint; } else { x0 = radius; x1 = radius; y0 = h2; y1 = -h2; } return new WeightedLine(x0 + w2, h2 - y0, x1 + w2, h2 - y1, intensity); }
      
      







結果の角度の計算


幅と高さの寸法で画像の中心を通る垂線と座標x1、y1、x2、y2および水平線の線(つまり、水平線がx1、y1、x2、y2になるように画像を回転する必要がある角度)の間の角度を計算する関数水平方向に整列):



結果角度の計算方法コード
 public static double CalculateAngle(int width, int height, double x1, double y1, double x2, double y2) { double dx = x2 - x1; double dy = y2 - y1; double x3 = width / 2; double y3 = height / 2; double r = dx * dx + dy * dy; double nx = (dx * (x3 * dx - dy * y1) + dy * (dx * y3 + x1 * dy)) / r - x3; double ny = (dx * (y1 * dx - x1 * dy) + dy * (dx * x3 + dy * y3)) / r - y3; double result = Math.Atan2(ny, nx) + Math.PI / 2; if (result > Math.PI) result = result - Math.PI * 2; return result; }
      
      







計算された角度が特定の設定回転値(この場合は45°)を超える場合、自動回転は実行されないことに注意してください。



結果の内接長方形の計算


画像を回転させる角度を計算した後、回転した画像に内接する最大の長方形の寸法を計算する必要があります。 この長方形は比例(この場合、元の画像サイズが保持される、つまり、内接画像の一部が元のサイズに引き伸ばされる)、または回転画像の最大領域全体を覆う不均衡になる可能性があることに注意してください。



この問題を解決するために、対応する質問がstackoverflowで見つかり、回答の1つが次のコードに変更されました: link to stackoverflow



上記の手順をよりよく理解するために、プロセスの図解を用意しました。

画像



技術的な詳細



このプロジェクトは、特定のモジュールのコードがC#で記述されるように設計されています。 これは、 以前の記事の 1つで説明されているように、.NETとJavaScriptの両方でコンパイルされます。 したがって、このモジュールのコードもC#で記述されています。 これを行うには、2次元配列ではなく1次元配列を使用する必要があり、他のスクリプト#の制限事項も考慮する必要がありました。



Google Chromeで画像を回転させる


残念ながら、Google Chromeには、変換(たとえば、回転)中の境界での画像の平滑化がないバグがあります。これは、左の図に明確に示されていますが、画像自体は正しく補間されます。 他の最新バージョンのブラウザー(IE、Firefox、Safari、Opera)では、このバグは私たちに気づかれました。 したがって、これを回避する方法を考え出しました。次のコードを使用して、画像の周囲に透明な境界線を描画するだけです(つまり、画像は2ピクセル小さくなります)。



 context.draw(image, 1, 1, decImageWidth - 2, decImageHeight - 2);
      
      





したがって、すべてのブラウザーでスムージング効果を実現することができました(右図を参照)。







おわりに



境界とハフ変換を決定するためのアルゴリズムの係数を変えることにより、許容可能な品質と自動アライメントの速度を達成することが可能で、画像のいくつかの例でテストされました。 メソッドが正しく機能しない画像では、新しい角度を追加するか既存の写真を編集することでgfranq.comフォトサービスで確認できるように、2つの方法で角度を手動で簡単に修正できます。



モバイルプラットフォーム( iOSおよびAndroid )では、近い将来、写真の位置合わせ機能が表示されます。



All Articles