1. 程式人生 > >高斯影象模糊演算法及其 C 實現

高斯影象模糊演算法及其 C 實現

高斯模糊的基本思路是根據二維 正太分佈 正態分佈 (感謝 xhr 大牛指正錯別字) 公式生成一個高斯矩陣, 求新影象中的每一點時, 將高斯矩陣的中心對準舊影象的這一點, 並將所有點根據高斯矩陣上對應的點加權平均. 二維正態分佈公式如下:

u, v 分別為水平、豎直距離. 觀察可得, 當 r>3σ 時, 高斯矩陣上對應的權值已經小得可以忽略. 因此可以只計算一個大小為 (6σ+1)^2 的矩陣. 我們設定一個 radius 引數, 表示要計算的高斯矩陣的半徑.

實驗時發現, 當 radius 很小 (比如 1) 時, 矩陣內的所有值之和可能比 1 小一些, 這樣就會出現偏差. 因此不如在生成矩陣的時候不去除以 2πσ^2, 而是用計入一個累加變數, 之後再將矩陣中的所有值除以這個變數. 這樣可以在 radius 很小時也不至於影響影象的 '對比度'.

用 C 實現的程式碼如下:

  1. #include <windows.h>
  2. int gaussBlur(int *data, int width, int height, double sigma, int radius)   
  3. {   
  4. double *gaussMatrix, gaussSum = 0.0, _2sigma2 = 2 * sigma * sigma;   
  5. int x, y, xx, yy, xxx, yyy;   
  6. double *pdbl, a, r, g, b, d;   
  7.     unsigned char *bbb, *pout, *poutb;   
  8.     pout = poutb = (unsigned char *)LocalAlloc(LMEM_FIXED, width * height * 4);   
  9. if (!pout) return 0;   
  10.     gaussMatrix = pdbl = (double *)LocalAlloc(LMEM_FIXED, (radius * 2 + 1) * (radius * 2 + 1) * sizeof(double));   
  11. if (!gaussMatrix) {   
  12.         LocalFree(pout);   
  13. return 0;   
  14.     }   
  15. for (y = -radius; y <= radius; y++) {   
  16. for (x = -radius; x <= radius; x++) {   
  17.             a = exp(-(double)(x * x + y * y) / _2sigma2);   
  18.             *pdbl++ = a;   
  19.             gaussSum += a;   
  20.         }   
  21.     }   
  22.     pdbl = gaussMatrix;   
  23. for (y = -radius; y <= radius; y++) {   
  24. for (x = -radius; x <= radius; x++) {   
  25.             *pdbl++ /= gaussSum;   
  26.         }   
  27.     }   
  28. for (y = 0; y < height; y++) {   
  29. for (x = 0; x < width; x++) {   
  30.             a = r = g = b = 0.0;   
  31.             pdbl = gaussMatrix;   
  32. for (yy = -radius; yy <= radius; yy++) {   
  33.                 yyy = y + yy;   
  34. if (yyy >= 0 && yyy < height) {   
  35. for (xx = -radius; xx <= radius; xx++) {   
  36.                         xxx = x + xx;   
  37. if (xxx >= 0 && xxx < width) {   
  38.                             bbb = (unsigned char *)&data[xxx + yyy * width];   
  39.                             d = *pdbl;   
  40.                             b += d * bbb[0];   
  41.                             g += d * bbb[1];   
  42.                             r += d * bbb[2];   
  43.                             a += d * bbb[3];   
  44.                         }   
  45.                         pdbl++;   
  46.                     }   
  47.                 } else {   
  48.                     pdbl += (radius * 2 + 1);   
  49.                 }   
  50.             }   
  51.             *pout++ = (unsigned char)b;   
  52.             *pout++ = (unsigned char)g;   
  53.             *pout++ = (unsigned char)r;   
  54.             *pout++ = (unsigned char)a;   
  55.         }   
  56.     }   
  57.     RtlMoveMemory(data, poutb, width * height * 4);   
  58.     LocalFree(gaussMatrix);   
  59.     LocalFree(poutb);   
  60. return 1;   
  61. }  

sigma 引數表示模糊程度, radius 引數表示影象質量. radius = 0 時等同於什麼都沒做.

下面是 sigma = 1.0, radius = 3 時的效果:

同樣的演算法, 用 VB.NET 實現, 就硬生生地慢了 10 倍.
原因是沒有指標, 資料必須拷來拷去的.

看來託管程式碼的確不適合寫演算法.