图像降噪算法——非局部均值降噪算法

  • 图像降噪算法——非局部均值降噪算法
    • 1. 基本原理
    • 2. C++代码实现
    • 3. 结论

图像降噪算法——非局部均值降噪算法

1. 基本原理

非局部均值降噪算法(Non-Local Means)是空间降噪算法的一种,和中值滤波、高斯滤波这些局部滤波算法不同的是,非局部均值降噪算法是一种全局的算法,思路是利用整幅图像中相似像素的灰度值来代替当前像素的灰度值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之间的权重;B(p,r)B(p, r)B(p,r)为噪声图像中,以像素ppp为中心,宽为2r+12r+12r+1的区域,C(p)C(p)C(p)为权重归一化系数,计算公式为: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)

公式很好理解,中间比较重要的就是权重如何设计,权重需要描述两个像素之间的相似度,而这个相似度通常是通过这两个像素邻域像素间的欧拉距离来描述: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次求和是对于彩色图而言的,B(p,f)B(p, f)B(p,f)为噪声图像中,以像素ppp为中心,宽为2f+12f+12f+1的区域,在这个基础上,添加指数核函数来计算权值: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是我们人为设定的参数,以上就完成了非局部均值降噪算法的理论介绍。

2. C++代码实现

这里我基于OpenCV完成了两份代码,其中第一份是我根据上面公式自己实现,比较容易理解,但是运行速度较慢。因为太慢了,所以我尝试写了第二份代码。第二份是参考他人的代码基于Mat指针实现的,因为是指针操作,所以运行速度会相对较快。

第一份代码

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);//构建边界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;}//遍历图像上每一个像素for(int i = 0; i<src.rows; i++){for(int j = 0; j<src.cols; j++){cout<<i<<" "<<j<<endl;//遍历搜索区域每一个像素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);//构建边界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);//搜索区域中心指针uchar* pUpLeft = pad.data + pad.step * i + j;//搜索区域左上角指针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;
}

下面是输出结果
首先是原图:

添加高斯噪声后:

通过非局部均值降噪算法降噪效果:

可以看出这个效果还是非常感人的

3. 结论

  1. 可以看到非局部均值降噪算法的效果视觉上是非常可以接受的,但是它的缺点是时间复杂度太高,可以清楚看到程序里面有六个for循环,当我把搜索尺寸设为21,模板尺寸设为7是:
    第一份代码运行时间:135.034秒
    第二份代码运行时间:29.6425秒
    OpenCV库函数运行时间:0.512942秒
    通过对比可以发现,Mat通过at函数操作速度要慢于指针操作,而我写的指针操作的代码要远慢于OpenCV库函数,OpenCV中的库函数应该是采用多线程操作。
  2. 2014年有一篇paper讲的是通过积分图的方式优化NLM,我对其进行的C++代码实现,单线程下速度能提高到7秒左右,同时我也简单读了下OpenCV的原理,同样是使用了空间换时间的思想,不过相对积分图的方式会更加巧妙,值得进一步学习。

此外,这里我写一个各种算法的总结目录图像降噪算法——图像降噪算法总结,对图像降噪算法感兴趣的同学欢迎参考

图像降噪算法——非局部均值降噪算法相关推荐

  1. 非局部均值滤波算法(NL-means)

    非局部均值滤波算法(NL-means) 今天来学习一下另一类滤波算法:非局部均值滤波算法(NL-means).非局部均值滤波算法最早于2005年由Buades等人发表在CVPR上,论文原文:A non ...

  2. 非局部相似性 matlab,基于引导核聚类的非局部均值图像去噪算法

    非局部均值(nonlocal means, NLM)图像去噪算法是根据图像中存在的大量冗余信息,用非局部自相似性原理抑制噪声的算法.最初的NLM算法由文献[ 在NLM改进算法中,文献[[在相似窗结构张 ...

  3. 全极化雷达遥感图像的迭代优化非局部均值去噪法

    文章提出了一种迭代优化的PolSAR的非局部均值去噪方法.该方法在每次迭代去噪过程中,通过同时考虑原始图像全极化噪声统计特性和前一次迭代所得影像的全极化信息来完善像素间极化相似性的度量,从而实现对影像 ...

  4. 非局部相似性 matlab,非局部均值滤波(NLM)和MATLAB程序详解视频教程保持图像细节...

    [内容简介]<非局部均值滤波与应用和MATLAB程序详解视频>共6章28节视频,总学时698分钟,合11.6小时.主要内容包括:非局部均值滤波类算法入门,基于滤波参数自适应的非局部均值滤波 ...

  5. 【图像去噪】基于非局部均值(NLM)滤波图像去噪含Matlab源码

    1 简介 图像在获取和传输过程中,不可避免地受到外部和内部的干扰,常常因为各种因素的影响而被加入很多噪声,这十分严重的影响了人们对传输后图像信息的读取.因此通过一定方法将被噪声污染的图像进行去噪处理一 ...

  6. 详解非局部均值滤波原理以及用MATLAB源码实现

    详解非局部均值滤波原理以及用MATLAB源码实现 序言 均值滤波.中值滤波.高斯滤波在滤除噪声的过程中,无可避免的使图像的边缘细节和纹理信息所被滤除.针对此问题,Buades[1]等人提出了非局部均值 ...

  7. 图像保边滤波算法集锦--非局部均值NLM滤波器

    本文介绍非局部均值滤波,这种滤波器效果非常好,但是算法耗时严重,这里以效果为先,来给大家讲解. 非局部均值滤波(Non-Local Means,NLM)是Buades等人于2005年在论文" ...

  8. 经典非局部均值滤波(NLM)算法python实现(1)

    经典非局部均值滤波(NLM)算法python实现(单通道图像版本) 概述:非局部均值(NL-means)是近年来提出的一项新型的去噪技术.该方法充分利用了图像中的冗余信息,在去噪的同时能最大程度地保持 ...

  9. 经典非局部均值滤波(NLM)算法python实现(2)

    经典非局部均值滤波(NLM)算法python实现(三通道图像版本) 单通道图像版本已发布: https://blog.csdn.net/yy0722a/article/details/11392408 ...

最新文章

  1. pycharm禁用pytest
  2. Socket相关操作超时
  3. 在 C++Builder 工程里调用 DLL 函数
  4. hadoop hdfs 单机配置
  5. python测试系列教程 —— YAML配置文件语法教程
  6. fedora13上安装mhvtl报错
  7. 古代埃及希腊,数学用的什么进制
  8. 如何制作印章水印?教你在线制作电子印章水印
  9. Tilera 服务器上OpenJDK的安装尝试
  10. 面试测试工程师遇到的面试题——非技术方面
  11. 日本华人IT派遣那点事儿(2)
  12. scrapy爬取微信公众号内容,多管道储存,orm数据储存
  13. MP4文件格式简要解析
  14. vs2017 无法打开源文件afx.h
  15. Homography estimation(旋转估计)
  16. 真正的帅哥没人说帅_男生长得帅的标准五官 教你判断谁才是真正的帅哥
  17. GIS本科毕业如何防止结束GIS职业生涯
  18. SpringIOC对象管理
  19. 2021自编译NEWIFI3最新openwrt固件
  20. 微分先行PID控制器的实现

热门文章

  1. java 快排_百度在年前会在打击一轮快排!
  2. 【JUC并发编程04】线程间定制化通信(单标志法存在的问题)
  3. JVM 面试题 87 题详解
  4. 给新手的 11 个 Docker 免费上手项目
  5. 深入理解Java虚拟机-Java内存区域透彻分析
  6. 【小练习01】CSS--PS提示框制作
  7. 网络编程3之TCP/IP协议
  8. 拓展欧几里得模板/求逆元模板(java)
  9. 2020 我的C++的学习之路 第十章 对象和类
  10. html css移动位置,html – 如何使用CSS移动对象?