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

  • 图像降噪算法——非局部均值降噪算法
    • 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. Java 获取操作系统名字、系统版本、cpu信息
  2. springboot springmvc 抛出全局异常解决方法
  3. 【计算机网络】聊一聊那些常见的网络通信的性能指标
  4. IDEA将项目上传至码云/GitHub托管
  5. [react] react是什么?它的主要特点是什么?
  6. 电脑二维码怎么扫描_扫描模组方案是如何满足多种应用场景需求?
  7. 包头昆区多大面积_包头地铁“胎死腹中”,何时“卷土重来”?
  8. 树莓派与node.js —— onoff、dht
  9. Spring Cloud 微服务下的权限解决方案
  10. Axure9修改汉化包解决”用例“中”匹配所有“异常的问题
  11. 华为数通VRRP配置实验
  12. Radasm出现error LNK2001
  13. 《2022年中国网络安全市场全景图》
  14. 华为手机相册怎么镜像翻转_怎么制作照片视频?利用手机相册快速制作卡点视频...
  15. html 树 excel,用Excel实现简易树状关系
  16. 程序员今年最值得关注的 23 种新移动技术
  17. Android流媒体播放器
  18. 基础篇:6.3)形位公差-要素 Feature
  19. 半胱氨酸表面修饰CdTe量子点;半胱氨酸修饰CdTe/CdS量子点;L-半胱氨酸修饰CdTe/CdS量子点(Cys)-CdTe/Cds定制供应
  20. 黑马JAVA知识点总结

热门文章

  1. 记一次使用 Lombok 翻车造成的事故!
  2. Java 中这些常用关键字,总有那么些被你遗忘的
  3. java中Date与String的相互转化
  4. Oracle三种循环:for,while,do...while(PL/SQL)
  5. springMVC实现文件下载(附带Servlet方式)
  6. Servlet3.1 新增的非阻塞式IO
  7. 二叉树的前序、中序和后序遍历介绍及案例
  8. android信息中字符个数,在android中指定编辑文本中的字符数
  9. eclipse 64位 免安装_Python-3.6.6(32/64)位 软件安装教程
  10. python 爬取直播弹幕视频_python爬取斗鱼B总直播弹幕