這些矩陣如何工作?我需要多個每個像素嗎?在沒有周圍像素的情況下,左上角,右上角,左下角和左下角的像素如何?矩陣是從左到右,從上到下,還是從上到下,然後從左到右?卷積矩陣如何工作?
爲什麼這個內核(邊緣增強):http://i.stack.imgur.com/d755G.png 變成這個形象:http://i.stack.imgur.com/NRdkK.jpg
這些矩陣如何工作?我需要多個每個像素嗎?在沒有周圍像素的情況下,左上角,右上角,左下角和左下角的像素如何?矩陣是從左到右,從上到下,還是從上到下,然後從左到右?卷積矩陣如何工作?
爲什麼這個內核(邊緣增強):http://i.stack.imgur.com/d755G.png 變成這個形象:http://i.stack.imgur.com/NRdkK.jpg
卷積濾鏡應用到每一個像素。
在邊有幾件事情可以做(全部離開邊框類型或縮小圖像):
您應用卷積的順序並不重要(右上角到左下角是最常見的),無論順序如何,您都應該得到相同的結果。
但是,應用卷積矩陣時常見的錯誤是用新值覆蓋正在檢查的當前像素。這會影響您爲當前像素旁邊的像素提出的值。更好的方法是創建一個緩衝區來保存計算值,這樣卷積濾波器的以前的應用程序不會影響矩陣的當前應用。
從您的示例圖像中,很難判斷爲什麼應用的濾鏡創建黑白版本而未看到原始圖像。
在方法2和方法3之間可以選擇哪種副作用並克隆邊緣的最近像素值?那麼,這些方法對於圖像的最終結果最大的區別是什麼? PS:很奇怪......一個月前你是如何找到這個問題的? – user1095340 2013-05-02 21:01:55
這真的取決於你的過濾器和你正在使用的圖像,結果會是什麼。例如,如果您有全黑圖像,那麼選擇0會非常接近您的圖像,並且像索貝爾濾鏡不會檢測到這種變化。全白影像的類似說法。如果使用選項3,那麼添加的幻影邊界像素將非常接近現有的邊緣像素。這對於非常鮮豔的圖像有幫助,其中全背面或白色幻影像素會導致問題。 – 2013-05-03 05:58:07
在實踐中,大多數人選擇選項1,因爲它更容易,一個像素的損失通常是不相關的。 我正在拖動未解答的問題(我的代表低度添加評論,所以我試圖改進它)。看起來圖像處理選項卡沒有得到太多的愛。 – 2013-05-03 05:58:46
下面是將卷積核應用於圖像的一步一步示例(爲了簡單起見,1D)。
至於你的後邊緣增強內核,注意旁邊-1 +1。想想那會做什麼。如果該區域恆定,則+/- 1下的兩個像素將增加爲零(黑色)。如果兩個像素不同,它們將具有非零值。因此,您所看到的是彼此相鄰的不同像素會突出顯示,而相同的像素會被設置爲黑色。濾波圖像中的像素越亮(更白),差異就越大。
是的,你乘以每個像素,與矩陣。傳統的方法是找到相關的像素相關的像素被卷積,多個因素,並將其平均。所以一個3x3模糊:
1, 1, 1,
1, 1, 1,
1, 1, 1
這個矩陣,意味着你把各種組件的相關值,並乘以他們。然後除以元素的數量。所以你會得到3乘3的方框,將所有的紅色值相加,然後除以9.你會得到3乘3的方框,將所有的綠色值相加,然後除以9.你會得到3乘3中,添加了所有的藍色值,然後除以9
這意味着兩件事情。首先,您需要第二大塊內存來執行此操作。你可以做每個像素。
但是,這只是針對傳統方法,傳統方法實際上存在嚴重缺陷。正確完成你永遠不需要任何額外的內存,並始終在你開始的內存佔用內完成整個操作。
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) { }
}
抄粘貼您的標題,以谷歌:http://www.roborealm.com/help/Convolution.php – Maroun 2013-03-12 08:15:32
仍然沒有解釋如何從upperleft轉到BOTTOMLEFT ......這也說:「注意3x3」窗口「向右移一位,並且新像素值不被使用,但作爲第二個新圖像存儲。」這並不能說明你是否在每個像素上使用它! – user1095340 2013-03-12 08:20:33
有很多鏈接,我只發佈其中的一個,在谷歌上查找它,你會發現很多。 – Maroun 2013-03-12 08:21:36