2012-04-15 201 views
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區域的小矢量箭頭)。也許一個更好的例子是,如果你在photoshop中繪製灰度漸變,並且你想計算它們繪製的角度。

我已經做了像MatLab這樣的東西,但那是幾年前。我想我可以使用類似於ColorMatrix的矩陣來計算這個,但我不太確定如何。它看起來像this function可能是我想要的;我可以將它轉換爲灰度(如上),然後用灰度矩陣做些什麼來計算這些方向?

IIRC,我想要的與edge detection非常相似。

在我計算出這些方向向量之後,我只是要循環它們並計算圖像的平均方向。

最終目標是我想旋轉圖像,使它們的平均方向始終向上;這種方式如果我有兩個相同的圖像,除了一個被旋轉(90,180或270度),它們將以同樣的方式結束(我不關心一個人是否顛倒過來)。


*剪斷*刪除一些垃圾郵件。您可以查看您想要閱讀其餘嘗試的修訂。

+0

我想如果你計算每個圖像瓷磚與座標(例如'meshgrid')的相關性,你應該得到你的「方向」。此外,我懷疑平鋪方向的平均值會給出相同的答案,就好像您將整個圖像關聯爲單個平鋪一樣。如果您還有MatLab,我會用它來測試您的算法,然後將最終版本移植到C#中。 – 2012-04-15 23:53:11

+0

@BenVoigt:什麼是網格?如果我知道如何,我會高興地計算整個圖像的方向。我沒有MatLab的副本了......我只用了幾個學期的單元;我不確定我會記得如何使用它。需要不同的思考方式。 – mpen 2012-04-16 00:12:23

+0

所有你想要的聲音都是漸變的。你試過了嗎? – dranxo 2012-04-16 00:42:25

回答

9

計算角度的平均值通常是一個壞主意:

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

一組角度的平均無明確的意義:例如,一個角均朝上,一個角度朝下是指向右方的角度。那是你要的嗎?假設「向上」是+ PI,那麼朝上的兩個角度10幾乎的平均值將是朝下的角度,如果一個角度是PI- [某個小值],則另一個-PI + [某個小值]。這可能不是你想要的。此外,您完全忽略了邊緣的強度 - 實際圖像中的大部分像素根本不是邊緣,所以梯度方向主要是噪聲。

如果你想計算類似「平均方向」的東西,你需要加上向量而不是角度,然後計算循環後的Atan2。問題是:這個向量和不會告訴你圖像內部的物體,因爲指向相反方向的梯度相互抵消。它只會告訴你關於圖像的第一行/最後一行和第一/最後一列之間的亮度差異。這可能不是你想要的。

我認爲定向圖像最簡單的方法是創建一個角度直方圖:爲360度梯度方向創建一個包含(例如)360個分檔的數組。然後計算每個像素的梯度角度和幅度。將每個梯度大小添加到正確的角度箱。這不會給你一個單一的角度,而是一個角度直方圖,然後可以使用角度直方圖來使用簡單的循環相關來將兩個圖像定向到彼此。

這裏有一個驗證的概念,數學實現我扔在一起,看看是否這會工作:

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]"] 
) 

結果與樣品圖片:

enter image description here

的角度也直方圖說明平均角度不起作用的原因:直方圖本質上是一個尖銳的峯值,其他角度大致均勻。這個直方圖的平均值將始終由統一的「背景噪聲」支配。這就是爲什麼你用當前的算法爲每個「真實」圖像獲得幾乎相同的角度(大約180°)。

樹形圖像具有單一的主角(水平線),所以在這種情況下,您可以使用直方圖的模式(最常見的角度)。但是,這不會爲每個圖像的工作:

enter image description here

這裏有兩個峯。循環相關仍然應該將兩個圖像相互對齊,但僅僅使用該模式可能是不夠的。

另請注意,角度直方圖中的峯值不是「上」:在上面的樹形圖中,角度直方圖中的峯值可能是地平線。所以它是指向上。在Lena圖像中,它是背景中的垂直白條 - 所以它指向右側。使用最頻繁的角度簡單地定位圖像將使而不是以右側朝上的方式轉動每張圖像。

enter image description here

此圖片更是把峯:使用模式(或者,也許,任何一個角度)將是不可靠的定向這一形象。但角度直方圖作爲一個整體仍應該給你一個可靠的方向。

注意:我沒有預處理圖像,我沒有嘗試不同比例的梯度算子,我沒有後處理結果直方圖。在現實世界的應用程序中,您可以調整所有這些東西,以獲得大量測試圖像的最佳算法。這只是一個快速測試,看看這個想法是否可以工作。

地址:要使用這個柱狀圖,你會

  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個bin,這可能並不重要)

+0

優秀的答案;感謝您花時間編寫和測試這個。我怎樣才能將直方圖作爲一個整體來確定方向?你的最後一個例子看起來很漂亮。最大值*正好相差90度,儘管我可以看到如果最大值出現的方式稍有不同,它可能會選擇不同的峯值。 – mpen 2012-04-17 01:46:13

+0

@Mark:最大值相差90度,因爲它是完全相同的圖像,旋轉90度,所以梯度具有完全相同的大小,並精確旋轉90度。如果您要調整圖像大小或使用有損壓縮進行壓縮,則該模式可能會是另一個高峯。 – Niki 2012-04-17 06:13:51

+0

是的,那是我的觀點 - 他們出來的*確切*正確,甚至沒有一點關閉。我已經用旋轉90度的圖像進行了一些實驗,答案總是正確的。我會嘗試使用有損JPEG壓縮,並希望今天晚些時候調整大小。 – mpen 2012-04-17 15:29:52

0

考慮使用圖像的梯度來計算你想要的方向:en.wikipedia.org/wiki/Image_gradient

+2

這很棒,但你基本上只給了我一個我所描述的名字。仍然遇到實施麻煩。 – mpen 2012-04-16 06:40:15

+0

當你將所有角度平均在一起時,會出現一個實現難題。漸變需要一個圖像,然後輸出一個矢量場。在某些地方(例如,圖像中的黑色區域),漸變將爲0,並且平均會輸出意想不到的東西。考慮使用模式或中位數。順便說一句,這是爲了什麼?這聽起來像你的最終目標是圖像註冊這是非常無辜cf http://l3.lcarrasco.com/2010/04/image-registration/ – dranxo 2012-04-16 07:15:29

+0

不,我不會那麼瘋狂。我只想在相同的90度方向上定向相同的圖像。例如,用數碼相機拍攝的照片通常會橫向出現,有時也會以這種方式上傳到網上。給定2個其他相同的圖像,我想以相同的方式定位它們,以便我可以確定它們是否是相同的圖像。我知道我可以旋轉圖像幾次,並進行多次比較,但這太慢了。我想預先處理圖像並將它們對齊到一致的方向,以加快未來的比較速度。 – mpen 2012-04-16 07:26:23

1

好吧,我可以給你做的另一種方式。雖然不會很漂亮,但希望它適合你。

它可能你的計算是可以的。只是一次平均的梯度最終會以不同於你期望的平均值。所以我懷疑看着你覺得的圖像必須有一個不同的平均角度。因此;

  • 將圖像轉換爲二進制。
  • 使用霍夫變換查找行
  • 取最長的一行並計算其角度。這應該給你最突出的角度。
  • 您可能需要進行一些前/後處理才能獲得正確的行。

而且還有一種方法。嘗試GIST這基本上是場景識別中最廣泛使用的一種實現。我發現你的圖片是真實的scenes,因此我建議採取這種方法。該方法將爲您提供一個向量,可以與同一圖像的不同方向向量進行比較。這是一個非常瞭解的技術,應該適用於您的情況。

+0

我認爲這將會產生與nikie相同的問題:如果沒有主線,該怎麼辦? – mpen 2012-04-17 01:44:31

+0

然後將沒有明確的方向。這就是爲什麼你需要一個參考圖像來找到方向。 – 2012-04-17 01:48:50

+0

@Mark - 試試Gist吧。 – 2012-04-17 01:55:35

0

您需要使用兩個高斯導數內核(一個在X中,另一個在Y中)來卷積圖像。 這實際上是上面答案中的Lx和Ly。

在計算滑動窗口(原始圖像的子圖像)和一階高斯導數函數之間的相加積之前,預先減去平均像素強度。

例如參見本教程: http://bmia.bmt.tue.nl/people/bromeny/MICCAI2008/Materials/05%20Gaussian%20derivatives%20MMA6.pdf

選擇最佳平滑因子西格瑪> = 1

爲了計算高斯核,分化一旦2D高斯函數(從正態分佈已知的)用(x^2 + y^2)代替1d變量'(x-0)^ 2'。你可以用2D繪製它,例如在MS Excel中。

祝你好運!

邁克爾

相關問題