2013-03-12 69 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) { } 
}