如何在不使用任何内置高斯函数的情况下对图像进行高斯模糊处理?

59

我想使用本地高斯模糊公式来模糊我的图像。我阅读了维基百科文章,但我不确定如何实现。

我该如何使用公式来决定权重?

我不想使用任何类似MATLAB的内置函数。


2
基本上,您需要实现一个卷积运算符,相当于MATLAB中的**conv2()**函数。然而,由于2D高斯可以分解为两个1D高斯,所以您只需要在1D上实现卷积函数,并配合正确的核矩阵即可。 - Amro
5个回答

137

编写一个朴素的高斯模糊其实非常容易。它与任何其他卷积滤波器一样完成。唯一不同的是盒式滤波器和高斯滤波器使用的矩阵不同。

想象一下,你有一个定义如下的图像:

 0  1  2  3  4  5  6  7  8  9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
30 31 32 33 34 35 36 37 38 39
40 41 42 43 44 45 46 47 48 49
50 51 52 53 54 55 56 57 58 59
60 61 62 63 64 65 66 67 68 69
70 71 72 73 74 75 76 77 78 79
80 81 82 83 84 85 86 87 88 89
90 91 92 93 94 95 96 97 98 99

3x3盒子滤波器矩阵定义如下:

0.111 0.111 0.111
0.111 0.111 0.111
0.111 0.111 0.111

应用高斯模糊,您需要执行以下操作:

对于像素11,您需要加载像素0、1、2、10、11、12、20、21、22。

然后,您将用3x3的模糊滤镜的左上部分乘以像素0,用顶部中间乘以像素1,用右上方乘以像素2和像素3,用中间左侧乘以像素10等等。

然后将它们全部加起来,并将结果写入像素11。如您所见,像素11现在是自身和周围像素的平均值。

边缘情况会变得更加复杂。对于纹理边缘的值,您要使用什么值呢?一种方法是将其绕到另一侧。这适用于稍后铺设的图像。另一种方法是将像素推入周围位置。

因此,对于左上角,您可以将样本放置如下:

 0  0  1
 0  0  1
10 10 11
我希望您能看到这如何轻松地扩展到大的滤波核(例如 5x5 或 9x9 等)。
高斯滤波和框式滤波的区别在于矩阵中所填写的数字。高斯滤波器使用行列上的高斯分布。
例如,对于一个任意定义的滤波器(即不是高斯滤波器,但可能接近)。
0.1 0.8 0.1

第一列将相同,但乘以上一行的第一个项目。

0.01 0.8 0.1
0.08 
0.01 

第二列将是相同的,但其值将乘以上面行中的0.8(依此类推)。

0.01 0.08 0.01
0.08 0.64 0.08
0.01 0.08 0.01
将上述所有内容相加的结果应为1。与原始盒式滤波器相比,上述滤波器的不同之处在于写入的最终像素会更加偏向于中心像素(即已经在该位置的像素)。这种模糊是因为周围的像素确实模糊到了该像素,尽管没有那么明显。使用这种类型的滤波器可以实现模糊效果,但不会破坏太多高频信息(即从像素到像素快速变化的颜色)。

这些滤波器可以做很多有趣的事情。您可以使用此类滤波器进行边缘检测,方法是将当前像素减去周围像素。这将只留下颜色变化很大(高频率)的部分。

编辑:5x5滤波器核完全按照上述定义。

例如,如果您的行是0.1 0.2 0.4 0.2 0.1,则如果将它们中的每个值乘以第一个项目以形成一列,然后将每个值乘以第二个项目以形成第二列,以此类推,您最终将得到一个滤波器。

0.01 0.02 0.04 0.02 0.01
0.02 0.04 0.08 0.04 0.02
0.04 0.08 0.16 0.08 0.04
0.02 0.04 0.08 0.04 0.02
0.01 0.02 0.04 0.02 0.01

在任意位置上,您可以看到0,0位置是简单的0.1*0.1。0,2位置是0.1*0.4,2,2位置是0.4*0.4,而1,2位置是0.2*0.4。

希望这能给您一个足够好的解释。


可以提到关键词:神经网络或人工神经网络。 - Bitterblue
因为它们是相连的吗?同样的技术,只是没有阈值这个东西。 - Bitterblue
1
@Bitterblue:真的吗?怎么做到的?神经网络是一种机器学习算法。这是关于二维图像空间过滤的问题。 - Goz
你在A中自己描述了这种方法。你使用了9个节点,给它们赋予权重,对输入值(像素)进行了相同的数学计算,并得到了输出值。如果你想进行检测(例如线检测),你也可以教你的人工神经网络。 - Bitterblue
1
@Bitterblue:说得好,您可以使用过滤器来提取用于机器学习的特征。然而,这与此问题完全无关... - Goz
显示剩余3条评论

11

这是我在C#中用于计算内核的伪代码。虽然我不敢说我正确处理了结束条件:

double[] kernel = new double[radius * 2 + 1];
double twoRadiusSquaredRecip = 1.0 / (2.0 * radius * radius);
double sqrtTwoPiTimesRadiusRecip = 1.0 / (sqrt(2.0 * Math.PI) * radius);
double radiusModifier = 1.0;

int r = -radius;
for (int i = 0; i < kernel.Length; i++)
{
    double x = r * radiusModifier;
    x *= x;
    kernel[i] = sqrtTwoPiTimesRadiusRecip * Exp(-x * twoRadiusSquaredRecip);
    r++;
}

double div = Sum(kernel);
for (int i = 0; i < kernel.Length; i++)
{
    kernel[i] /= div;
}

希望这能帮到你。


3
我相信,这行代码: sqrtTwoPiTimesRadiusRecip * Exp(-x * sqrtTwoPiTimesRadiusRecip); 应该改为: sqrtTwoPiTimesRadiusRecip * Exp(-x * twoRadiusSquaredRecip); - ashagi
4
因为您已经对核进行了归一化,所以根本不需要乘以 sqrtTwoPiTimesRadiusRecip - Emily L.
@Simson,我不确定你的编辑除了剥夺我正式建议之外还做了什么,因为我在使用我的同事代码时也遇到了同样的问题。 - Charlie G
@Charlie,我的编辑让你的声望立即增加了2分,因为当我点击“改进编辑”时,你的编辑不需要等待第二个审核人确认。当在审查队列中有更多事情要做时,这是解决问题的方法。检查编辑历史记录,没有从你那里窃取任何信用。 - Simson
1
@Simson 我理解了,但是我正在为我的名字/照片出现在问题页面而感到兴奋,而不仅仅是在历史记录中 :( 哈哈,无论如何,谢谢你的解释。 - Charlie G

9
要使用维基百科文章中讨论的滤波器核,您需要实现(离散)卷积。其思想是,您有一个小的值矩阵(核),将此核从像素移动到图像中的像素(即使矩阵的中心位于像素上),将矩阵元素与重叠的图像元素相乘,在结果中求和所有值并用此总和替换旧像素值。

高斯模糊可以分为两个1D卷积(一个垂直和一个水平),而不是2D卷积,这也可以加快速度。


3

我不确定您是否想要限制这个技术只用于某些特定的技术领域,但是如果没有的话,SVG(可缩放矢量图形)有一个高斯模糊的实现。我相信它适用于所有基元,包括像素。 SVG具有开放标准和广泛实现的优势。


我想使用高斯滤波器的基本定义,而不是使用任何内置实现。 我想自己来实现它。 - Moeb

1
好的,高斯核是一个可分离的核函数。
因此,您只需要一个支持可分离2D卷积的函数,例如- ImageConvolutionSeparableKernel()
一旦您拥有它,所需的所有内容都是生成1D高斯核的包装器,并像ImageConvolutionGaussianKernel()中所做的那样将其发送到函数中。
代码是SIMD(SSE)和多线程(OpenMP)加速的2D图像卷积的直接C实现。
整个项目由- Image Convolution - GitHub提供。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接