影象保邊濾波演算法集錦--非區域性均值NLM濾波器
本文介紹非區域性均值濾波,這種濾波器效果非常好,但是演算法耗時嚴重,這裡以效果為先,來給大家講解。
非區域性均值濾波(Non-Local Means,NLM)是Buades等人於2005年在論文“A non-local algorithm for image denoising”中提出的對傳統鄰域濾波方法的一種改進濾波,考慮到了影象的自相似性質,它充分利用了影象中的冗餘資訊,在去噪的同時能夠最大程度的保持影象的細節特徵。
該演算法需要計算影象中所有畫素與當前畫素之間的相似性,考慮到這個計算量與效率的問題,一般會設定兩個固定大小的視窗,一個大的搜尋視窗(D×D)和一個小的鄰域視窗(d×d),鄰域視窗在搜尋視窗中進行滑動,根據鄰域間的相似性來確定對應中心畫素對當前畫素的影響度,也就是權值。
下圖是NLM演算法執行過程,大視窗是以目標畫素為中心的搜尋視窗,兩個灰色小視窗分別是以x,y為中心的鄰域視窗。其中以y為中心的鄰域視窗在搜尋視窗中滑動,通過計算兩個鄰域視窗間的相似程度為y賦以權值w(x,y) 。
NLM的演算法流程如下:
關於NLM的快速演算法,可以參考論文:
FromentJ. Parameter-Free Fast Pixelwise Non-Local Means Denoising[J]. Image ProcessingOn Line, 2014, 4: 300-326
本人使用C語言實現程式碼如下(沒有使用快速演算法,速度在10S以上,使用DEMO時輕耐心等待):
#include "string.h" #include "stdio.h" #include "stdlib.h" #include "math.h" #include"f_NLM.h" #define MIN2(a, b) ((a) < (b) ? (a) : (b)) #define MAX2(a, b) ((a) > (b) ? (a) : (b)) #define CLIP3(x, a, b) MIN2(MAX2(a,x), b) void NLM(unsigned char* srcData, int width, int height, int D, int d, float h) { unsigned char* tempData = (unsigned char*)malloc(sizeof(unsigned char) * width * height); memcpy(tempData, srcData, sizeof(unsigned char) * height * width); float sw = 0; float sum = 0; int px, py, cx, cy; float zx; float vxsy = 0; float DD = d * d; float HH = h * h; for(int j = 0; j < height; j++) { for(int i = 0; i < width; i++) { sw = 0; zx = 0; sum = 0; for(int n = -D; n <= D; n++) { for(int m = -D; m <= D; m++) { vxsy = 0; for(int kn = -d; kn <= d; kn++) { for(int km = -d; km <= d; km++) { cx = CLIP3(i - d + km, 0, width - 1); cy = CLIP3(j - d + kn, 0, height - 1); px = CLIP3(i + m + km, 0, width - 1); py = CLIP3(j + n + kn, 0, height - 1); vxsy += (tempData[px + py * width] - tempData[cx + cy * width]) * (tempData[px + py * width] - tempData[cx + cy * width]); } } vxsy = vxsy / DD; sw = exp(-vxsy / HH); zx += sw; int ox = CLIP3(i + m, 0, width - 1); int oy = CLIP3(j + n, 0, height - 1); sum += sw * tempData[ox + oy * width]; } } srcData[i + j * width] = zx == 0 ? srcData[i + j * width] : CLIP3(sum / zx, 0, 255); } } free(tempData); }; void f_NLMFilter(unsigned char* srcData, int nWidth, int nHeight, int nStride, int dRadius, int sRadius, int h) { if (srcData == NULL) { return; } if(dRadius == 0 || sRadius == 0 || h == 0 || dRadius <= sRadius) return; unsigned char* rData = (unsigned char*)malloc(sizeof(unsigned char) * nWidth * nHeight); unsigned char* gData = (unsigned char*)malloc(sizeof(unsigned char) * nWidth * nHeight); unsigned char* bData = (unsigned char*)malloc(sizeof(unsigned char) * nWidth * nHeight); unsigned char* pSrc = srcData; unsigned char* pR = rData; unsigned char* pG = gData; unsigned char* pB = bData; for(int j = 0; j < nHeight; j++) { for(int i = 0; i < nWidth; i++) { *pR = pSrc[2]; *pG = pSrc[1]; *pB = pSrc[0]; pR++; pG++; pB++; pSrc += 4; } } NLM(rData, nWidth, nHeight, dRadius, sRadius, h); NLM(gData, nWidth, nHeight, dRadius, sRadius, h); NLM(bData, nWidth, nHeight, dRadius, sRadius, h); pSrc = srcData; pR = rData; pG = gData; pB = bData; for(int j = 0; j < nHeight; j++) { for(int i = 0; i < nWidth; i++) { pSrc[2] = * pR; pSrc[1] = * pG; pSrc[0] = * pB; pR++; pG++; pB++; pSrc += 4; } } free(rData); free(gData); free(bData); }
非區域性均值濾波的效果如下圖所示:
上面就是LNM濾波器的效果了,從效果上看,用來做美顏磨皮完全沒有問題,但是由於速度限制,基本上沒有用它來做的,當然,作為演算法學習,還是可以的。
最後給出一個原始碼+DEMO連結:NLM濾波
注意,本文演算法部分參考了網上部落格內容,測試圖片也均來自網路,若有侵權,敬請告知,謝謝。
本人QQ1358009172, 公眾號:SF影象演算法