2

这些矩阵是如何工作的?我是否需要对每个像素进行多个处理?没有周围像素的左上、右上、左下和左下像素怎么样?矩阵是从左到右,从上到下,还是先从上到下,然后从左到右?

为什么这个内核(边缘增强):http: //i.stack.imgur.com/d755G.png 变成这个图像:http: //i.stack.imgur.com/NRdkK.jpg

4

3 回答 3

1

卷积过滤器应用于每个像素。

在边缘,您可以做一些事情(都留下一种边框或缩小图像):

  1. 跳过边缘并从图像边缘裁剪 1 个像素
  2. 将 0 或 255 替换为图像超出范围的任何像素
  3. 使用介于 0(或 255)和图像边缘像素值之间的三次样条(或其他插值方法)来得出替代值。

应用卷积的顺序无关紧要(从右上到左下最常见),无论顺序如何,您都应该得到相同的结果。

但是,应用卷积矩阵时的一个常见错误是用新值覆盖您正在检查的当前像素。这将影响您为当前像素旁边的像素得出的值。更好的方法是创建一个缓冲区来保存计算值,以便卷积滤波器的先前应用不会影响矩阵的当前应用。

从您的示例图像中,很难说出为什么应用的过滤器会在没有看到原始图像的情况下创建黑白版本。

于 2013-04-29T19:08:43.643 回答
0

Below is a step by step example of applying a convolution kernel to an image (1D for simplicity).

Step by step convolution example

As for the edge enhancement kernel in your post, notice the +1 next to the -1. Think about what that will do. If the region is constant the two pixel under the +/-1 will add to zero (black). If the two pixels are different they will have a non-zero value. So what you are seeing is that pixels next to each other that are different get highlighted, while ones that are the same get set to black. The bigger the difference the brighter (more white) the pixel in the filtered image.

于 2013-04-30T01:29:25.950 回答
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/ 卷积图像。

这意味着几件事。首先,您需要第二块巨大的内存来执行此操作。你尽你所能。

但是,这仅适用于传统方法,而传统方法实际上是不必要的复杂化(明白吗?)。如果您在角落返回结果。您实际上不需要任何额外的内存,并且始终在您开始使用的内存占用空间内完成整个操作。

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,所以整个图像可以同时完成。使它成为一种有争议的观点。但是,奇怪的是,图形中最重要的算法之一在要求这个时很奇怪,因为这使得最简单的操作方法变得不可能,并且会占用内存。

所以像马特这样的人在这个问题上会说“但是,应用卷积矩阵时的一个常见错误是用新值覆盖您正在检查的当前像素。” - 真的这是正确的方法,错误是将结果像素写入中心而不是左上角。因为与左上角不同,您将再次需要中心像素。您将不再需要左上角(假设您正在迭代左->右,上->下),因此将值存储在那里是安全的。

“这将影响您为当前像素旁边的像素得出的值。” -- 如果您在将其作为扫描处理时将其写入左上角,您将覆盖您不再需要的数据。使用一堆额外的内存并不是一个更好的解决方案。

因此,这可能是您见过的最快的 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) { }
}
于 2016-07-12T12:23:58.553 回答