我想使用原生高斯模糊公式模糊我的图像。我阅读了 Wikipedia 文章,但我不确定如何实现这一点。
如何使用公式确定权重?
我不想使用任何像 MATLAB 那样的内置函数
写一个朴素的高斯模糊实际上很容易。它的完成方式与任何其他卷积滤波器完全相同。盒子和高斯滤波器之间的唯一区别是您使用的矩阵。
假设您有一个定义如下的图像:
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。如您所见,像素 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。
我希望这能给你一个足够好的解释。
这是我在 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;
}
希望这可以帮助。
要使用维基百科文章中讨论的过滤器内核,您需要实现(离散)卷积。这个想法是你有一个小的值矩阵(内核),你在图像中从一个像素到另一个像素移动这个内核(即矩阵的中心在像素上),将矩阵元素与重叠的图像相乘元素,对结果中的所有值求和,并用这个和替换旧的像素值。
高斯模糊可以分为两个 1D 卷积(一个垂直和一个水平)而不是 2D 卷积,这也加快了速度。
我不清楚您是否要将其限制为某些技术,但如果不是,SVG(ScalableVectorGraphics)具有高斯模糊的实现。我相信它适用于包括像素在内的所有基元。SVG 具有作为开放标准和广泛实施的优势。
好吧,高斯核是一个可分离的核。
因此,您所需要的只是一个支持可分离 2D 卷积的函数,例如 - ImageConvolutionSeparableKernel()
。
一旦你有了它,只需要一个包装器来生成一维高斯内核并将其发送到函数中,如ImageConvolutionGaussianKernel()
.
该代码是由 SIMD (SSE) 和多线程 (OpenMP) 加速的 2D 图像卷积的直接 C 实现。
整个项目由-Image Convolution-GitHub给出。