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

时间:2009-11-08 11:38:12

标签: c image-processing gaussianblur

我想使用原生高斯模糊公式模糊我的图像。我看过this,但我不确定如何实现这一点。

如何使用公式来确定权重?

我不想使用像MATLAB那样的内置函数

5 个答案:

答案 0 :(得分:128)

写一个天真的高斯模糊实际上很容易。它的完成方式与任何其他卷积滤波器完全相同。盒子和高斯滤波器之间的唯一区别就是你使用的矩阵。

想象一下,您的图像定义如下:

 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。

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

然后完全添加它们并将结果写入像素11.正如您所见,Pixel 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。

我希望能给你一个足够好的解释。

答案 1 :(得分:12)

这是我在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 * sqrtTwoPiTimesRadiusRecip);
    r++;
}

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

希望这可以提供帮助。

答案 2 :(得分:9)

要使用维基百科文章中讨论的过滤器内核,您需要实现(离散)convolution。这个想法是你有一个小的矩阵值(内核),你在图像中从像素到像素移动这个内核(即矩阵的中心在像素上),矩阵元素与重叠的图像相乘元素,对结果中的所有值求和,并用此总和替换旧像素值。

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

答案 3 :(得分:3)

我不清楚您是否要将此限制为某些技术,但如果不是SVG (ScalableVectorGraphics)则执行高斯模糊。我相信它适用于所有基元,包括像素。 SVG具有开放标准和广泛实施的优势。

答案 4 :(得分:0)

嗯,高斯内核是一个可分离的内核 因此,您只需要一个支持可分离2D卷积的函数,如 - ImageConvolutionSeparableKernel()

一旦你拥有它,所有需要的是一个包装器来生成1D高斯内核并将其发送到函数ImageConvolutionGaussianKernel()

该代码是由SIMD(SSE)和多线程(OpenMP)加速的2D图像卷积的直接C实现。

整个项目由 - Image Convolution - GitHub提供。