4. 非局部均值去噪:NLM算法原理、块匹配与权重计算、加速优化策略
各位同学,今天我们来聊聊非局部均值去噪,也就是NLM算法。说实话,这个算法在我刚入行那会儿,简直是神一样的存在。为什么?因为它打破了传统去噪算法的思维定式——不再只盯着像素周围的邻居,而是放眼整张图像,寻找相似的“块”。
我记得第一次在项目中用NLM,是在一个医疗影像的降噪任务上。CT图像噪声大,但细节又极其重要。传统的双边滤波一上,边缘是保住了,但纹理细节糊得一塌糊涂。后来换成NLM,效果立竿见影。嗯,今天我就把这块硬骨头给大家啃透了。
4.1 NLM算法的核心思想
传统去噪算法,比如均值滤波、高斯滤波,都是基于一个假设:相邻像素的灰度值相近。但你想啊,图像里到处都是边缘和纹理,这个假设在边缘处就崩了。双边滤波虽然加了像素值差异的权重,但本质上还是局限在一个小窗口里。
NLM的思路完全不同。它认为:图像中存在大量重复的纹理结构。比如一片草地,虽然像素位置不同,但局部纹理模式是相似的。我们可以利用这些相似块来互相加权平均,从而去除噪声。
说白了,就是“用相似的块去救被噪声污染的块”。
核心公式:
NLv(i) = Σ w(i,j) * v(j)
其中 v(j) 是像素 j 的灰度值,w(i,j) 是像素 i 和 j 之间的权重,权重由两个像素周围块的相似度决定。
4.2 块匹配与权重计算
这里有个关键问题:怎么判断两个像素“相似”?
NLM的做法是:以像素 i 为中心取一个块(比如7x7),再以像素 j 为中心取同样大小的块。然后计算这两个块之间的欧氏距离。距离越小,说明两个块越相似,权重就越大。
权重计算公式长这样:
w(i,j) = (1/Z(i)) * exp(-||v(Ni) - v(Nj)||² / h²)
这里有几个参数要注意:
- Ni, Nj:以 i 和 j 为中心的图像块
- ||·||²:两个块的欧氏距离平方
- h:滤波参数,控制权重的衰减速度。h越大,平滑越强;h越小,细节保留越好
- Z(i):归一化因子,保证所有权重之和为1
我在项目中遇到过一个问题:h参数调不好,图像要么太糊,要么噪声还在。后来我总结了一个经验——h一般取噪声标准差σ的0.5到1倍之间。你可以先估计一下噪声水平,再从这个范围开始调。
避坑指南:我曾经在低光照图像上直接套用默认参数,结果图像出现了严重的“伪影”。后来发现,低光照下噪声不是高斯分布,而是泊松分布。这时候需要先做方差稳定变换(比如Anscombe变换),再用NLM去噪,效果会好很多。
4.3 搜索窗口与相似块匹配
理论上,NLM需要遍历整张图像的所有像素来计算权重。但你想啊,一张1080p的图像有200多万像素,每个像素都要跟其他所有像素算相似度,这计算量谁也扛不住。
所以实际实现中,我们会限制搜索范围。一般做法是:
- 设定一个搜索窗口,比如21x21或31x31
- 只在搜索窗口内寻找相似块
- 窗口大小通常设为块大小的3-5倍
举个例子:块大小7x7,搜索窗口21x21。这样每个像素只需要跟窗口内的441个像素做比较,而不是全图的200万个。计算量直接降了几个数量级。
| 参数 | 推荐值 | 说明 |
|---|---|---|
| 块大小 | 5x5 ~ 9x9 | 太小则相似度不可靠,太大则细节丢失 |
| 搜索窗口 | 21x21 ~ 41x41 | 越大效果越好,但计算量平方增长 |
| 滤波参数h | 0.5σ ~ 1.0σ | σ为噪声标准差 |
4.4 加速优化策略
说实话,NLM最大的痛点就是慢。即使限制了搜索窗口,对于实时应用来说还是太慢了。我当年在嵌入式平台上做NLM,一帧图像要跑好几秒,根本没法用。后来我试了几种加速方法,效果还不错。
4.4.1 积分图加速
计算两个块的欧氏距离时,需要逐像素做差再平方求和。这个操作在搜索窗口内要重复很多次。我们可以用积分图来加速。
具体做法是:
- 先计算图像的积分图
- 再计算图像平方的积分图
- 两个块的距离可以用积分图快速计算,复杂度从O(块大小)降到O(1)
// 伪代码:积分图加速块距离计算
// S1: 原始图像的积分图
// S2: 平方图像的积分图
// 块Ni和Nj的距离 = S2(Ni) + S2(Nj) - 2*S1(Ni)*S1(Nj) / 块面积
4.4.2 预筛选策略
搜索窗口里很多块其实跟目标块一点都不像。我们可以先做一个快速筛选,只保留那些“有可能相似”的块。
常用的预筛选方法:
- 均值筛选:计算每个块的均值,只保留均值接近的块
- 方差筛选:计算每个块的方差,只保留方差接近的块
- 梯度筛选:计算块的梯度直方图,只保留梯度分布相似的块
我一般用均值+方差的组合筛选。先算一遍所有块的均值和方差,然后设定一个阈值,比如均值差不超过10,方差比不超过1.5。这样能过滤掉80%以上的不相似块,剩下的再精确计算距离。
注意:预筛选的阈值不能设得太严。我曾经为了追求速度,把阈值设得很小,结果很多真正相似的块被过滤掉了,去噪效果大打折扣。建议先松后紧,逐步调整。
4.4.3 对称性与权重复用
NLM的权重计算有个对称性:w(i,j) = w(j,i)。也就是说,像素i对像素j的权重,等于像素j对像素i的权重。我们可以利用这个性质,只计算一半的权重,另一半直接复用。
另外,如果两个块的距离已经计算过了,可以缓存起来。下次遇到同样的块对,直接查表就行。这在处理视频序列时特别有用——相邻帧的块匹配结果可以复用。
4.4.4 多尺度加速
对于大尺寸图像,可以先降采样到低分辨率,在低分辨率上做NLM去噪,然后再上采样回原分辨率。这样做的好处是:
- 低分辨率图像像素少,计算量小
- 低分辨率下噪声被压缩,块匹配更稳定
- 上采样后可以用原分辨率的NLM做一次精细去噪
我常用的策略是:先降采样到1/2或1/4,做一次NLM,然后上采样,再用小搜索窗口做一次NLM。这样整体速度能提升3-5倍,效果损失很小。
4.5 实战经验总结
最后,我给大家总结几条实战经验:
- 参数调优:先固定块大小和搜索窗口,只调h参数。h太小噪声残留,h太大细节丢失。用PSNR或SSIM做客观评价,同时肉眼观察。
- 边界处理:图像边界处的块会超出图像范围。我习惯用镜像填充,效果比零填充好很多。
- 彩色图像:彩色图像可以转成YUV空间,只在Y通道做NLM,UV通道做简单去噪。这样既保留了色彩,又节省了计算量。
- 实时性要求:如果需要在手机上实时处理,建议用积分图+预筛选的组合,再配合多线程并行。我曾在骁龙855上做到30fps处理1080p图像。
好了,NLM的核心内容就这些。下一章我们会讲BM3D——可以看作是NLM的进阶版,把块匹配从2D扩展到了3D。到时候你会发现,很多加速策略是通用的。