高斯模糊毛刺/分割

问题描述 投票:0回答:1

我正在尝试从头开始实现高斯模糊(使用C ++)。在下面的代码中,我已经硬编码了我正在使用的高斯内核。在尝试使用已阅读的优化方法时,我只保留了一个尺寸,可以在其中进行水平卷积遍历和垂直遍历遍历,以提高模糊效率。不幸的是,我遇到了一些问题。这是我的代码:

float gKern[5] = {0.05448868, 0.24420134, 0.40261995, 0.24420134, 0.05448868};
int** gaussianBlur(int** image, int height, int width) {
    int **ret  = new int*[height];
    for(int i = 0; i < height; i++) {
        ret[i] = new int[width];
    }

    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            if (i == 0) {
                ret[i][j] = (gKern[0] * image[2][j]) + (gKern[1] * image[1][j]) + (gKern[2] * image[0][j]) + (gKern[3] * image[1][j]) + (gKern[4] * image[2][j]);
            } else if (i == 1) {
                ret[i][j] = (gKern[0] * image[1][j]) + (gKern[1] * image[0][j]) + (gKern[2] * image[1][j]) + (gKern[3] * image[2][j]) + (gKern[4] * image[3][j]);
            } else if (i == (height - 2)) {
                ret[i][j] = (gKern[0] * image[i - 2][j]) + (gKern[1] * image[i - 1][j]) + (gKern[2] * image[i][j]) + (gKern[3] * image[i + 1][j]) + (gKern[4] * image[i][j]);
            } else if (i == (height - 1)) {
                ret[i][j] = (gKern[0] * image[i - 2][j]) + (gKern[1] * image[i - 1][j]) + (gKern[2] * image[i][j]) + (gKern[3] * image[i - 1][j]) + (gKern[4] * image[i - 2][j]);
            } else {
                ret[i][j] = (gKern[0] * image[i - 2][j]) + (gKern[1] * image[i - 1][j]) + (gKern[2] * image[i][j]) + (gKern[3] * image[i + 1][j]) + (gKern[4] * image[i + 2][j]);
            }
        }
    }

    int** temp = image;
    image = ret;

    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            if (j == 0) {
                ret[i][j] = (gKern[0] * image[i][2]) + (gKern[1] * image[i][1]) + (gKern[2] * image[i][0]) + (gKern[3] * image[i][1]) + (gKern[4] * image[i][2]);
            } else if (j == 1) {
                ret[i][j] = (gKern[0] * image[i][1]) + (gKern[1] * image[i][0]) + (gKern[2] * image[i][1]) + (gKern[3] * image[i][2]) + (gKern[4] * image[i][3]);
            } else if (j == (width - 2)) {
                ret[i][j] = (gKern[0] * image[i][j - 2]) + (gKern[1] * image[i][j - 1]) + (gKern[2] * image[i][j]) + (gKern[3] * image[i][j + 1]) + (gKern[4] * image[i][j]);
            } else if (j == (width - 1)) {
                ret[i][j] = (gKern[0] * image[i][j - 2]) + (gKern[1] * image[i][j - 1]) + (gKern[2] * image[i][j]) + (gKern[3] * image[i][j - 1]) + (gKern[4] * image[i][j - 2]);
            } else {
                ret[i][j] = (gKern[0] * image[i][j - 2]) + (gKern[1] * image[i][j - 1]) + (gKern[2] * image[i][j]) + (gKern[3] * image[i][j + 1]) + (gKern[4] * image[i][j + 2]);
            }
        }
    }

    image = temp;

    return ret;
}

第一遍(第一遍为块)似乎工作正常,当我注释掉第二遍时,确实得到了稍微模糊的图像。但是当我同时使用这两个图像时,会得到一个不稳定的“怪异”图像,如下所示(第一个图像是我的grayscale input,第二个图像是choppy output):

c++ image computer-vision convolution gaussianblur
1个回答
0
投票

问题出在您使用的指针上。

该功能以image作为输入,ret作为第一步的中间结果。

第二步必须使用ret作为输入,并写入原始输入(覆盖输入图像)或新图像。相反,您可以这样做:

int** temp = image;
image = ret;
// read from image and write to ret
image = temp;
return ret;

也就是说,进入第二遍时,imageret都指向相同的数据,然后读取和写入相同的数据。接下来,您进行一个无效的指针分配(此后永远不会使用image)并返回中间缓冲区。

如果要写入输入图像,只需在第二遍之前交换imageret指针:

std::swap(image, res);

如果您不想这样做,则必须new写入另一张图像。


使用数组数组存储图像是一种不好的做法。如果您查看任何图像处理库的源代码,就会发现它们为图像分配了一个大内存块,用于存储所有串联的图像行。知道图像的宽度,就知道如何索引:image[x + y*width]

这不仅简化了代码(没有循环来分配单个图像),而且还大大加快了代码的速度:不再有指针查找,并且所有数据都聚在一起以充分利用高速缓存。

通过遵循上述建议,可以大大简化整个代码:可以使用相同的代码完成两次遍历。编写一个过滤图像一行的函数。它需要一个指向第一个像素的指针,一条线的长度和一个台阶(水平线为1,垂直线为width)。然后,在另一个函数的行循环中调用此一维函数。然后调用第二个函数一次进行水平遍历,一次进行垂直遍历。 (有关详细信息,请参见here。)

在这种情况下,通过使用单个图像行大小的缓冲区,很容易避免出现中间图像。写入该缓冲区,然后在过滤后将整行复制回输入图像。这意味着您只有一个大小为max(width,height)的缓冲区,而不是大小为width*height的缓冲区。

一维过滤功能也可以简化。该循环不应包含任何if语句,它们会大大减慢速度。执行。相反,在特殊情况下,前两个像素和后两个像素是特殊情况,并且仅在不需要担心图像边缘的大部分像素上循环。

© www.soinside.com 2019 - 2024. All rights reserved.