7

だから私は次のように画像をグレースケールに変換できることを理解しました:

public static Bitmap GrayScale(this Image img)
{
    var bmp = new Bitmap(img.Width, img.Height);
    using(var g = Graphics.FromImage(bmp))
    {
        var colorMatrix = new ColorMatrix(
            new[]
                {
                    new[] {.30f, .30f, .30f, 0, 0},
                    new[] {.59f, .59f, .59f, 0, 0},
                    new[] {.11f, .11f, .11f, 0, 0},
                    new[] {0, 0, 0, 1.0f, 0},
                    new[] {0, 0, 0, 0, 1.0f}
                });

        using(var attrs = new ImageAttributes())
        {
            attrs.SetColorMatrix(colorMatrix);
            g.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height),
                0, 0, img.Width, img.Height, GraphicsUnit.Pixel, attrs);
        }
    }
    return bmp;
}

次に、ピクセルの平均「方向」を計算します。

つまり、3x3の領域を見て、左側が右側よりも暗い場合、方向は右側になり、下部が上部よりも暗い場合、方向は上向きになり、左下が右上よりも暗い場合、方向は上向きになります。(3x3の領域ごとに小さなベクトル矢印を考えてください)。おそらくより良い例は、フォトショップでグレースケールグラデーションを描画し、それらが描画した角度を計算したい場合です。

私はこのMatLabのようなことをしましたが、それは何年も前のことです。これを計算するのに似た行列を使用できると思いますがColorMatrix、その方法はよくわかりません。この関数は私が望むものかもしれないようです。(上記のように)それをグレースケールに変換してから、これらの方向を計算するためにグレースケールマトリックスで何かを行うことができますか?

IIRC、私が欲しいのはエッジ検出と非常に似ています。

これらの方向ベクトルを計算した後、それらをループして画像の平均方向を計算します。

最終的な目標は、平均的な方向が常に上になるように画像を回転させたいということです。このように、1つが回転(90、180、または270度)されていることを除いて2つの同一の画像がある場合、それらは同じ方向に向けられます(人が逆さまになってしまうかどうかは関係ありません)。


*snip*一部のスパムを削除します。あなたは私の試みの残りを読みたいあなたの改訂を見ることができます。

4

4 に答える 4

9

角度の平均を計算することは一般的に悪い考えです:

...
        sum += Math.Atan2(yi, xi);
    }
}
double avg = sum / (img.Width * img.Height);

一連の角度の平均には明確な意味はありません。たとえば、上向きの 1 つの角度と下向きの 1 つの角度の平均は、右向きの角度です。それはあなたが望むものですか?「上」が+PIであると仮定すると、一方の角度がPI-[いくつかの小さな値]であり、もう一方が-PI+[いくつかの小さな値]である場合、ほぼ上向きの2つの角度間の平均は下向きの角度になります。それはおそらくあなたが望むものではありません。また、エッジの強度を完全に無視しています。実際の画像のほとんどのピクセルはまったくエッジではないため、グラデーションの方向はほとんどノイズです。

「平均方向」のようなものを計算したい場合は、角度の代わりにベクトルを加算し、ループの後に Atan2 を計算する必要があります。問題は次のとおりです。反対方向を指すグラデーションが互いに打ち消し合うため、そのベクトル和は画像内のオブジェクトについて何も教えてくれません。画像の最初/最後の行と最初/最後の列の明るさの違いについてのみ説明します。それはおそらくあなたが望むものではありません。

画像を方向付ける最も簡単な方法は、角度ヒストグラムを作成することだと思います。360°のグラデーション方向に対して (たとえば) 360 個のビンで配列を作成します。次に、各ピクセルの勾配角度と大きさを計算します。各勾配の大きさを直角ビンに追加します。これは単一の角度ではなく、単純な巡回相関を使用して 2 つの画像を互いに方向付けるために使用できる角度ヒストグラムを提供します。

これが機能するかどうかを確認するために一緒に投げた概念実証の Mathematica 実装を次に示します。

angleHistogram[src_] :=
 (
  Lx = GaussianFilter[ImageData[src], 2, {0, 1}];
  Ly = GaussianFilter[ImageData[src], 2, {1, 0}];
  angleAndOrientation = 
   MapThread[{Round[ArcTan[#1, #2]*180/\[Pi]], 
      Sqrt[#1^2 + #2^2]} &, {Lx, Ly}, 2];
  angleAndOrientationFlat = Flatten[angleAndOrientation, 1];
  bins = BinLists[angleAndOrientationFlat , 1, 5];
  histogram = 
   Total /@ Flatten[bins[[All, All, All, 2]], {{1}, {2, 3}}];
  maxIndex = Position[histogram, Max[histogram]][[1, 1]];
  Labeled[
   Show[
    ListLinePlot[histogram, PlotRange -> All],
    Graphics[{Red, Point[{maxIndex, histogram[[maxIndex]]}]}]
    ], "Maximum at " <> ToString[maxIndex] <> "\[Degree]"]
  )

サンプル画像での結果:

ここに画像の説明を入力

角度ヒストグラムは、平均角度が機能しない理由も示しています。ヒストグラムは本質的に単一の鋭いピークであり、他の角度はほぼ均一です。このヒストグラムの平均は、常に均一な「バックグラウンド ノイズ」によって支配されます。そのため、現在のアルゴリズムでは、「実際のライブ」画像のそれぞれに対してほぼ同じ角度 (約 180°) が得られます。

木の画像には 1 つの優勢な角度 (水平線) があるため、この場合、ヒストグラムのモード (最も頻繁な角度) を使用できます。しかし、それはすべての画像でうまくいくわけではありません:

ここに画像の説明を入力

ここに 2 つのピークがあります。巡回相関では 2 つの画像が互いに向き合う必要がありますが、このモードを使用するだけではおそらく十分ではありません。

また、角度ヒストグラムのピークは「上」ではないことに注意してください。上の木の画像では、角度ヒストグラムのピークはおそらく水平線です。だから上向きです。Lena の画像では、背景にある垂直の白いバーなので、右を指しています。最も頻繁に使用される角度を使用して画像の向きを変えるだけでは、すべての画像の右側が上を向くわけではありません。

ここに画像の説明を入力

この画像にはさらに多くのピークがあります。モード (または、おそらく任意の角度) を使用すると、この画像の向きを合わせるのに信頼性がなくなります。しかし、全体としての角度ヒストグラムは、依然として信頼できる向きを提供するはずです。

注:画像を前処理したり、さまざまなスケールで勾配演算子を試したり、結果のヒストグラムを後処理したりしませんでした。実際のアプリケーションでは、これらすべてを微調整して、大量のテスト画像セットに最適なアルゴリズムを取得します。これは、アイデアがまったく機能するかどうかを確認するための簡単なテストです。

追加:このヒストグラムを使用して 2 つの画像を方向付けるには、次のようにします。

  1. すべてのヒストグラムを正規化して、ヒストグラムの下の領域が各画像で同じになるようにします (一部の画像が明るく、暗く、またはぼやけていても)
  2. 画像のヒストグラムを取得し、関心のある回転ごとに比較します。

たとえば、C# では次のようになります。

for (int rotationAngle = 0; rotationAngle < 360; rotationAngle++)
{
   int difference = 0;
   for (int i = 0; i < 360; i++)
      difference += Math.Abs(histogram1[i] - histogram2[(i+rotationAngle) % 360]);
   if (difference < bestDifferenceSoFar)
   {
      bestDifferenceSoFar = difference;
      foundRotation = rotationAngle;
   }
}

(ヒストグラムの長さが 2 のべき乗である場合は、FFT を使用してこれを高速化できます。ただし、コードははるかに複雑になり、256 ビンの場合はそれほど問題にならない可能性があります)

于 2012-04-16T16:28:53.627 に答える
1

さて、私はあなたにそれをする別の方法を与えることができます。きれいではありませんが、それがあなたのために働くことを願っています。

おそらくあなたの計算は大丈夫です。勾配が一度平均すると、期待したものとは異なる平均値になってしまうだけです。ですから、画像を見ると、平均角度が違うに違いないと思います。したがって;

  • 画像をバイナリに変換します。
  • ハフ変換を使用して線を検索する
  • 最長の線を取り、その角度を計算します。これにより、最も目立つ角度が得られます。
  • 行を正しくするために、前処理/後処理が必要になる場合があります。

そしてもう1つのアプローチの時点で。GISTを試すこれは基本的に、シーン認識で最も広く使用されている実装です。あなたの画像は実際のシーンであることがわかったので、このアプローチを取ることをお勧めします。この方法では、同じ画像の異なる方向ベクトルと比較するベクトルが得られます。これは非常によく知られている手法であり、あなたのケースに間違いなく適用できるはずです。

于 2012-04-16T07:31:55.110 に答える
0

画像のグラデーションを使用して、必要な方向を計算することを検討してください: en.wikipedia.org/wiki/Image_gradient

于 2012-04-16T06:24:59.870 に答える
0

2 つのガウス微分カーネル (X に 1 つ、Y に 1 つ) を使用してイメージを畳み込む必要があります。これは、実際には上記の回答の Lx と Ly です。

スライディング ウィンドウ (元のイメージのサブイメージ) と 1 次ガウス微分関数の間の合計積を計算する前に、平均ピクセル強度を事前に減算します。

たとえば、このチュートリアルを参照してください

最適な平滑化係数 sigma >= 1 を選択します。

ガウス カーネルを計算するには、1 次元変数 '(x-0)^2' を (x^2 + y^2) に置き換えて、2D ガウス関数 (正規分布から既知) を 1 回微分します。MS Excel などの 2D で描画できます。

幸運を!

マイケル

于 2012-05-23T21:45:58.400 に答える