高斯模糊的基本思路是根据二维 正太分布 正态分布 (感谢 xhr 大牛指正错别字) 公式生成一个高斯矩阵, 求新图像中的每一点时, 将高斯矩阵的中心对准旧图像的这一点, 并将所有点根据高斯矩阵上对应的点加权平均. 二维正态分布公式如下:
u, v 分别为水平、竖直距离. 观察可得, 当 r>3σ 时, 高斯矩阵上对应的权值已经小得可以忽略. 因此可以只计算一个大小为 (6σ+1)^2 的矩阵. 我们设置一个 radius 参数, 表示要计算的高斯矩阵的半径.
更详细的介绍参见维基百科: http://zh.wikipedia.org/wiki/%E9%AB%98%E6%96%AF%E6%A8%A1%E7%B3%8A
实验时发现, 当 radius 很小 (比如 1) 时, 矩阵内的所有值之和可能比 1 小一些, 这样就会出现偏差. 因此不如在生成矩阵的时候不去除以 2πσ^2, 而是用计入一个累加变量, 之后再将矩阵中的所有值除以这个变量. 这样可以在 radius 很小时也不至于影响图像的 '对比度'.
用 C 实现的代码如下:
- #include <windows.h>
- int gaussBlur(int *data, int width, int height, double sigma, int radius)
- {
- double *gaussMatrix, gaussSum = 0.0, _2sigma2 = 2 * sigma * sigma;
- int x, y, xx, yy, xxx, yyy;
- double *pdbl, a, r, g, b, d;
- unsigned char *bbb, *pout, *poutb;
- pout = poutb = (unsigned char *)LocalAlloc(LMEM_FIXED, width * height * 4);
- if (!pout) return 0;
- gaussMatrix = pdbl = (double *)LocalAlloc(LMEM_FIXED, (radius * 2 + 1) * (radius * 2 + 1) * sizeof(double));
- if (!gaussMatrix) {
- LocalFree(pout);
- return 0;
- }
- for (y = -radius; y <= radius; y++) {
- for (x = -radius; x <= radius; x++) {
- a = exp(-(double)(x * x + y * y) / _2sigma2);
- *pdbl++ = a;
- gaussSum += a;
- }
- }
- pdbl = gaussMatrix;
- for (y = -radius; y <= radius; y++) {
- for (x = -radius; x <= radius; x++) {
- *pdbl++ /= gaussSum;
- }
- }
- for (y = 0; y < height; y++) {
- for (x = 0; x < width; x++) {
- a = r = g = b = 0.0;
- pdbl = gaussMatrix;
- for (yy = -radius; yy <= radius; yy++) {
- yyy = y + yy;
- if (yyy >= 0 && yyy < height) {
- for (xx = -radius; xx <= radius; xx++) {
- xxx = x + xx;
- if (xxx >= 0 && xxx < width) {
- bbb = (unsigned char *)&data[xxx + yyy * width];
- d = *pdbl;
- b += d * bbb[0];
- g += d * bbb[1];
- r += d * bbb[2];
- a += d * bbb[3];
- }
- pdbl++;
- }
- } else {
- pdbl += (radius * 2 + 1);
- }
- }
- *pout++ = (unsigned char)b;
- *pout++ = (unsigned char)g;
- *pout++ = (unsigned char)r;
- *pout++ = (unsigned char)a;
- }
- }
- RtlMoveMemory(data, poutb, width * height * 4);
- LocalFree(gaussMatrix);
- LocalFree(poutb);
- return 1;
- }
sigma 参数表示模糊程度, radius 参数表示图像质量. radius = 0 时等同于什么都没做.
下面是 sigma = 1.0, radius = 3 时的效果:
![](https://i-blog.csdnimg.cn/blog_migrate/040ec1aa113cd880c50c1bd80170e389.jpeg)
同样的算法, 用 VB.NET 实现, 就硬生生地慢了 10 倍.
原因是没有指针, 数据必须拷来拷去的.
看来托管代码的确不适合写算法.
http://hi.baidu.com/iceboy_/item/d5e0c643a3ec2692823ae1fe