2013-03-12 108 views
2

這些矩陣如何工作?我需要多個每個像素嗎?在沒有周圍像素的情況下,左上角,右上角,左下角和左下角的像素如何?矩陣是從左到右,從上到下,還是從上到下,然後從左到右?卷積矩陣如何工作?

爲什麼這個內核(邊緣增強):http://i.stack.imgur.com/d755G.png 變成這個形象:http://i.stack.imgur.com/NRdkK.jpg

+1

抄粘貼您的標題,以谷歌:http://www.roborealm.com/help/Convolution.php – Maroun 2013-03-12 08:15:32

+0

仍然沒有解釋如何從upperleft轉到BOTTOMLEFT ......這也說:「注意3x3」窗口「向右移一位,並且新像素值不被使用,但作爲第二個新圖像存儲。」這並不能說明你是否在每個像素上使用它! – user1095340 2013-03-12 08:20:33

+0

有很多鏈接,我只發佈其中的一個,在谷歌上查找它,你會發現很多。 – Maroun 2013-03-12 08:21:36

回答

1

卷積濾鏡應用到每一個像素。

在邊有幾件事情可以做(全部離開邊框類型或縮小圖像):

  1. 從圖像的邊緣跳過邊緣和作物1像素
  2. 用0或255替代圖像邊界外的任何像素
  3. 使用0(或255)與圖像邊緣像素值之間的三次樣條(或其他插值方法)來創建替代。

您應用卷積的順序並不重要(右上角到左下角是最常見的),無論順序如何,您都應該得到相同的結果。

但是,應用卷積矩陣時常見的錯誤是用新值覆蓋正在檢查的當前像素。這會影響您爲當前像素旁邊的像素提出的值。更好的方法是創建一個緩衝區來保存計算值,這樣卷積濾波器的以前的應用程序不會影響矩陣的當前應用。

從您的示例圖像中,很難判斷爲什麼應用的濾鏡創建黑白版本而未看到原始圖像。

+0

在方法2和方法3之間可以選擇哪種副作用並克隆邊緣的最近像素值?那麼,這些方法對於圖像的最終結果最大的區別是什麼? PS:很奇怪......一個月前你是如何找到這個問題的? – user1095340 2013-05-02 21:01:55

+0

這真的取決於你的過濾器和你正在使用的圖像,結果會是什麼。例如,如果您有全黑圖像,那麼選擇0會非常接近您的圖像,並且像索貝爾濾鏡不會檢測到這種變化。全白影像的類似說法。如果使用選項3,那麼添加的幻影邊界像素將非常接近現有的邊緣像素。這對於非常鮮豔的圖像有幫助,其中全背面或白色幻影像素會導致問題。 – 2013-05-03 05:58:07

+0

在實踐中,大多數人選擇選項1,因爲它更容易,一個像素的損失通常是不相關的。 我正在拖動未解答的問題(我的代表低度添加評論,所以我試圖改進它)。看起來圖像處理選項卡沒有得到太多的愛。 – 2013-05-03 05:58:46

0

下面是將卷積核應用於圖像的一步一步示例(爲了簡單起見,1D)。

Step by step convolution example

至於你的後邊緣增強內核,注意旁邊-1 +1。想想那會做什麼。如果該區域恆定,則+/- 1下的兩個像素將增加爲零(黑色)。如果兩個像素不同,它們將具有非零值。因此,您所看到的是彼此相鄰的不同像素會突出顯示,而相同的像素會被設置爲黑色。濾波圖像中的像素越亮(更白),差異就越大。

0

是的,你乘以每個像素,與矩陣。傳統的方法是找到相關的像素相關的像素被卷積,多個因素,並將其平均。所以一個3x3模糊:

1, 1, 1, 
1, 1, 1, 
1, 1, 1 

這個矩陣,意味着你把各種組件的相關值,並乘以他們。然後除以元素的數量。所以你會得到3乘3的方框,將所有的紅色值相加,然後除以9.你會得到3乘3的方框,將所有的綠色值相加,然後除以9.你會得到3乘3中,添加了所有的藍色值,然後除以9

http://intellabs.github.io/RiverTrail/tutorial/ convolution image.

這意味着兩件事情。首先,您需要第二大塊內存來執行此操作。你可以做每個像素。

但是,這只是針對傳統方法,傳統方法實際上存在嚴重缺陷。正確完成你永遠不需要任何額外的內存,並始終在你開始的內存佔用內完成整個操作。

public static void convolve(int[] pixels, int offset, int stride, int x, int y, int width, int height, int[][] matrix, int parts) { 
    int index = offset + x + (y*stride); 
    for (int j = 0; j < height; j++, index += stride) { 
     for (int k = 0; k < width; k++) { 
      int pos = index + k; 
      pixels[pos] = convolve(pixels,stride,pos, matrix, parts); 
     } 
    } 
} 

private static int crimp(int color) { 
    return (color >= 0xFF) ? 0xFF : (color < 0) ? 0 : color; 
} 

private static int convolve(int[] pixels, int stride, int index, int[][] matrix, int parts) { 
    int redSum = 0; 
    int greenSum = 0; 
    int blueSum = 0; 

    int pixel, factor; 

    for (int j = 0, m = matrix.length; j < m; j++, index+=stride) { 
     for (int k = 0, n = matrix[j].length; k < n; k++) { 
      pixel = pixels[index + k]; 
      factor = matrix[j][k]; 

      redSum += factor * ((pixel >> 16) & 0xFF); 
      greenSum += factor * ((pixel >> 8) & 0xFF); 
      blueSum += factor * ((pixel) & 0xFF); 
     } 
    } 
    return 0xFF000000 | ((crimp(redSum/parts) << 16) | (crimp(greenSum/parts) << 8) | (crimp(blueSum/parts))); 
} 

錯誤是內核傳統上返回到最中心的像素。這允許圖像在邊緣附近模糊,但或多或​​少保持在其開始的位置。這在當時似乎是個好主意,但總是有缺陷。正確的做法是讓結果像素位於左上角。然後,您可以簡單而無需額外的內存,只需用掃描線迭代整個圖像即可。大部分顏色重量向上移動並保留一個像素。但是,這是一個像素,如果用右下角的結果像素向後迭代,則可以將它向下並向左移。儘管這可能會破壞緩存命中。

但是,現在很多現代架構都有GPU,所以整個圖像可以同時完成。使其成爲一種有爭議的問題。但是,奇怪的是,圖形中最重要的算法之一嚴重缺陷是一個可怕的需求,這使得最簡單的方法來執行操作。

因此,像Matt這樣的人會說這樣的話:「然而,應用卷積矩陣時常見的錯誤是用新值覆蓋當前正在檢查的像素。 - 真的,這是正確的方法,錯誤是將結果像素寫入中心而不是左上角。

「這會影響您爲當前像素旁邊的像素提供的值。」 - 如果您將其作爲掃描進行處理,將其寫入左上角,則會覆蓋您再也不需要的數據。使用一堆額外的內存不是一個更好的解決方案。

因此,這可能是您見過的最快的Java模糊。

private static void applyBlur(int[] pixels, int stride) { 
    int v0, v1, v2, r, g, b; 
    int pos; 
    pos = 0; 
    try { 
     while (true) { 
      v0 = pixels[pos]; 
      v1 = pixels[pos+1]; 
      v2 = pixels[pos+2]; 

      r = ((v0 >> 16) & 0xFF) + ((v1 >> 16) & 0xFF) + ((v2 >> 16) & 0xFF); 
      g = ((v0 >> 8) & 0xFF) + ((v1 >> 8) & 0xFF) + ((v2 >> 8) & 0xFF); 
      b = ((v0  ) & 0xFF) + ((v1  ) & 0xFF) + ((v2  ) & 0xFF); 
      r/=3; 
      g/=3; 
      b/=3; 
      pixels[pos++] = r << 16 | g << 8 | b; 
     } 
    } 
    catch (ArrayIndexOutOfBoundsException e) { } 
    pos = 0; 
    try { 
    while (true) { 
      v0 = pixels[pos]; 
      v1 = pixels[pos+stride]; 
      v2 = pixels[pos+stride+stride]; 

      r = ((v0 >> 16) & 0xFF) + ((v1 >> 16) & 0xFF) + ((v2 >> 16) & 0xFF); 
      g = ((v0 >> 8) & 0xFF) + ((v1 >> 8) & 0xFF) + ((v2 >> 8) & 0xFF); 
      b = ((v0  ) & 0xFF) + ((v1  ) & 0xFF) + ((v2  ) & 0xFF); 
      r/=3; 
      g/=3; 
      b/=3; 
      pixels[pos++] = r << 16 | g << 8 | b; 
     } 
    } 
    catch (ArrayIndexOutOfBoundsException e) { } 
}