图像降噪算法——非局部均值降噪算法
圖像降噪算法——非局部均值降噪算法
- 圖像降噪算法——非局部均值降噪算法
- 1. 基本原理
- 2. C++代碼實(shí)現(xiàn)
- 3. 結(jié)論
圖像降噪算法——非局部均值降噪算法
1. 基本原理
非局部均值降噪算法(Non-Local Means)是空間降噪算法的一種,和中值濾波、高斯濾波這些局部濾波算法不同的是,非局部均值降噪算法是一種全局的算法,思路是利用整幅圖像中相似像素的灰度值來(lái)代替當(dāng)前像素的灰度值u^i(p)=1C(p)∑q∈B(p,r)ui(q)w(p,q)\hat{u}_{i}(p)=\frac{1}{C(p)} \sum_{q \in B(p, r)} u_{i}(q) w(p, q)u^i?(p)=C(p)1?q∈B(p,r)∑?ui?(q)w(p,q)其中,ui(q)u_{i}(q)ui?(q)是噪聲圖像像素qqq的灰度值;u^i(p)\hat{u}_{i}(p)u^i?(p)是降噪后圖像像素ppp的灰度值;w(p,q)w(p, q)w(p,q)是像素ppp和qqq之間的權(quán)重;B(p,r)B(p, r)B(p,r)為噪聲圖像中,以像素ppp為中心,寬為2r+12r+12r+1的區(qū)域,C(p)C(p)C(p)為權(quán)重歸一化系數(shù),計(jì)算公式為:C(p)=∑q∈B(p,r)w(p,q)C(p)=\sum_{q \in B(p, r)} w(p, q)C(p)=q∈B(p,r)∑?w(p,q)
公式很好理解,中間比較重要的就是權(quán)重如何設(shè)計(jì),權(quán)重需要描述兩個(gè)像素之間的相似度,而這個(gè)相似度通常是通過(guò)這兩個(gè)像素鄰域像素間的歐拉距離來(lái)描述:d2(B(p,f),B(q,f))=13(2f+1)2∑i=13∑j∈B(0,Ω)(ui(p+j)?ui(q+j))2d^{2}(B(p, f), B(q, f))=\frac{1}{3(2 f+1)^{2}} \sum_{i=1}^{3} \sum_{j \in B(0, \Omega)}\left(u_{i}(p+j)-u_{i}(q+j)\right)^{2}d2(B(p,f),B(q,f))=3(2f+1)21?i=1∑3?j∈B(0,Ω)∑?(ui?(p+j)?ui?(q+j))2其中,333次求和是對(duì)于彩色圖而言的,B(p,f)B(p, f)B(p,f)為噪聲圖像中,以像素ppp為中心,寬為2f+12f+12f+1的區(qū)域,在這個(gè)基礎(chǔ)上,添加指數(shù)核函數(shù)來(lái)計(jì)算權(quán)值:w(p,q)=e?max?(d2?2σ2,0,0)h2w(p, q)=e^{-\frac{\max \left(d^{2}-2 \sigma^{2}, 0,0\right)}{h^{2}}}w(p,q)=e?h2max(d2?2σ2,0,0)?其中,σ\sigmaσ和hhh是我們?nèi)藶樵O(shè)定的參數(shù),以上就完成了非局部均值降噪算法的理論介紹。
2. C++代碼實(shí)現(xiàn)
這里我基于OpenCV完成了兩份代碼,其中第一份是我根據(jù)上面公式自己實(shí)現(xiàn),比較容易理解,但是運(yùn)行速度較慢。因?yàn)樘?#xff0c;所以我嘗試寫了第二份代碼。第二份是參考他人的代碼基于Mat指針實(shí)現(xiàn)的,因?yàn)槭侵羔槻僮?#xff0c;所以運(yùn)行速度會(huì)相對(duì)較快。
第一份代碼
Mat Denoise::NonLocalMeansFilter(const Mat &src, int searchWindowSize, int templateWindowSize, double sigma, double h) {Mat dst, pad;dst = Mat::zeros(src.rows, src.cols, CV_8UC1);//構(gòu)建邊界int padSize = (searchWindowSize+templateWindowSize)/2;copyMakeBorder(src, pad, padSize, padSize, padSize, padSize, cv::BORDER_CONSTANT);int tN = templateWindowSize*templateWindowSize;int sN = searchWindowSize*searchWindowSize;vector<double> gaussian(256*256, 0);for(int i = 0; i<256*256; i++){double g = exp(-max(i-2.0*sigma*sigma, 0.0))/(h*h);gaussian[i] = g;if(g<0.001)break;}//遍歷圖像上每一個(gè)像素for(int i = 0; i<src.rows; i++){for(int j = 0; j<src.cols; j++){cout<<i<<" "<<j<<endl;//遍歷搜索區(qū)域每一個(gè)像素int pX = i+searchWindowSize/2;int pY = j+searchWindowSize/2;vector<vector<double>> weight(searchWindowSize, vector<double>(searchWindowSize, 0));double weightSum = 0;for(int m = searchWindowSize-1; m>=0; m--){for(int n = searchWindowSize-1; n>=0; n--){int qX = i+m;int qY = j+n;int w = 0;for(int x = templateWindowSize-1; x>=0; x--){for(int y = templateWindowSize-1; y>=0; y--){w += pow(pad.at<uchar>(pX+x, pY+y) - pad.at<uchar>(qX+x, qY+y), 2);}}weight[m][n] = gaussian[(int)(w/tN)];weightSum += weight[m][n];}}dst.at<uchar>(i,j) = 0;double sum = 0;for(int m = 0; m<searchWindowSize; m++){for(int n = 0; n<searchWindowSize; n++){sum += pad.at<uchar>(i+templateWindowSize/2+m, j+templateWindowSize/2+n)*weight[m][n];}}dst.at<uchar>(i,j) = (uchar)(sum/weightSum);}}return dst; }第二份代碼
Mat Denoise::NonLocalMeansFilter2(const Mat &src, int searchWindowSize, int templateWindowSize, double sigma, double h) {Mat dst, pad;dst = Mat::zeros(src.rows, src.cols, CV_8UC1);//構(gòu)建邊界int padSize = (searchWindowSize+templateWindowSize)/2;copyMakeBorder(src, pad, padSize, padSize, padSize, padSize, cv::BORDER_CONSTANT);int tN = templateWindowSize*templateWindowSize;int sN = searchWindowSize*searchWindowSize;int tR = templateWindowSize/2;int sR = searchWindowSize/2;vector<double> gaussian(256*256, 0);for(int i = 0; i<256*256; i++){double g = exp(-max(i-2.0*sigma*sigma, 0.0))/(h*h);gaussian[i] = g;if(g<0.001)break;}double* pGaussian = &gaussian[0];const int searchWindowStep = (int)pad.step - searchWindowSize;const int templateWindowStep = (int)pad.step - templateWindowSize;for(int i = 0; i < src.rows; i++){uchar* pDst = dst.ptr(i);for(int j = 0; j < src.cols; j++){cout<<i<<" "<<j<<endl;int *pVariance = new int[sN];double *pWeight = new double[sN];int cnt = sN-1;double weightSum = 0;uchar* pCenter = pad.data + pad.step * (sR + i) + (sR + j);//搜索區(qū)域中心指針uchar* pUpLeft = pad.data + pad.step * i + j;//搜索區(qū)域左上角指針for(int m = searchWindowSize; m>0; m--){uchar* pDownLeft = pUpLeft + pad.step * m;for(int n = searchWindowSize; n>0; n--){uchar* pC = pCenter;uchar* pD = pDownLeft + n;int w = 0;for(int k = templateWindowSize; k>0; k--){for(int l = templateWindowSize; l>0; l--){w += (*pC - *pD)*(*pC - *pD);pC++;pD++;}pC += templateWindowStep;pD += templateWindowStep;}w = (int)(w/tN);pVariance[cnt--] = w;weightSum += pGaussian[w];}}for(int m = 0; m<sN; m++){pWeight[m] = pGaussian[pVariance[m]]/weightSum;}double tmp = 0.0;uchar* pOrigin = pad.data + pad.step * (tR + i) + (tR + j);for(int m = searchWindowSize, cnt = 0; m>0; m--){for(int n = searchWindowSize; n>0; n--){tmp += *(pOrigin++) * pWeight[cnt++];}pOrigin += searchWindowStep;}*(pDst++) = (uchar)tmp;delete pWeight;delete pVariance;}}return dst; }下面是輸出結(jié)果
首先是原圖:
添加高斯噪聲后:
通過(guò)非局部均值降噪算法降噪效果:
可以看出這個(gè)效果還是非常感人的
3. 結(jié)論
第一份代碼運(yùn)行時(shí)間:135.034秒
第二份代碼運(yùn)行時(shí)間:29.6425秒
OpenCV庫(kù)函數(shù)運(yùn)行時(shí)間:0.512942秒
通過(guò)對(duì)比可以發(fā)現(xiàn),Mat通過(guò)at函數(shù)操作速度要慢于指針操作,而我寫的指針操作的代碼要遠(yuǎn)慢于OpenCV庫(kù)函數(shù),OpenCV中的庫(kù)函數(shù)應(yīng)該是采用多線程操作。
此外,這里我寫一個(gè)各種算法的總結(jié)目錄圖像降噪算法——圖像降噪算法總結(jié),對(duì)圖像降噪算法感興趣的同學(xué)歡迎參考
總結(jié)
以上是生活随笔為你收集整理的图像降噪算法——非局部均值降噪算法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 图像降噪算法——中值滤波/均值滤波/高斯
- 下一篇: 图像降噪算法——高斯低通滤波