这些矩阵如何工作?我需要多个每个像素吗?在没有周围像素的情况下,左上角,右上角,左下角和左下角的像素如何?矩阵是从左到右,从上到下,还是从上到下,然后从左到右?卷积矩阵如何工作?
为什么这个内核(边缘增强):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