高斯影象模糊演算法及其 C 實現
阿新 • • 發佈:2018-12-31
高斯模糊的基本思路是根據二維 正太分佈 正態分佈 (感謝 xhr 大牛指正錯別字) 公式生成一個高斯矩陣, 求新影象中的每一點時, 將高斯矩陣的中心對準舊影象的這一點, 並將所有點根據高斯矩陣上對應的點加權平均. 二維正態分佈公式如下:
u, v 分別為水平、豎直距離. 觀察可得, 當 r>3σ 時, 高斯矩陣上對應的權值已經小得可以忽略. 因此可以只計算一個大小為 (6σ+1)^2 的矩陣. 我們設定一個 radius 引數, 表示要計算的高斯矩陣的半徑.
實驗時發現, 當 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 時的效果:
同樣的演算法, 用 VB.NET 實現, 就硬生生地慢了 10 倍.
原因是沒有指標, 資料必須拷來拷去的.
看來託管程式碼的確不適合寫演算法.