4. 传统降噪方法回顾:BM3D、NLM、小波降噪
各位同学,咱们今天聊点“老本行”。
在深度学习把ISP降噪卷得飞起之前,我们这些做图像处理的,靠的就是BM3D、NLM、小波降噪这几板斧。说实话,直到现在,我在某些项目里还是会优先考虑它们——不是情怀,是真的好用。
这一章,我就带大家把这三种经典方法掰开揉碎,看看它们的底牌是什么,坑在哪里,以及什么时候该请它们出山。
4.1 为什么还要学“老方法”?
你可能会想:“都2025年了,直接上CNN不香吗?”
嗯,这个问题我当年也问过自己。后来在做一个低功耗IPC(网络摄像头)项目时,芯片的NPU算力只有0.5 TOPS,跑个轻量级UNet都卡成PPT。最后我硬着头皮上了BM3D,效果居然还不错,功耗还低。
说白了,传统方法有三大不可替代的优势:
- 无需训练数据:拿来就用,不挑场景。
- 计算可预测:没有推理延迟抖动,适合实时系统。
- 理论透明:每个参数都知道在干什么,调试起来心里有底。
我的建议:在深度学习部署中,传统方法可以作为“保底方案”或“混合方案”的一部分。比如白天用轻量CNN,晚上光线差时切到BM3D——我在一个安防项目里就这么干过,效果很稳。
4.2 BM3D:降噪界的“瑞士军刀”
BM3D,全称Block-Matching and 3D Filtering。这名字听着复杂,其实核心思想就两步:找相似块 → 组团去噪。
4.2.1 核心流程
- 块匹配(Block Matching):在图像里滑动窗口,找到与当前块相似的图像块(比如纹理、边缘相似的区域)。
- 3D变换:把这些相似块堆成一个3D数组,然后做3D DCT或小波变换。
- 协同滤波:在变换域里做硬阈值或维纳滤波,把噪声系数砍掉。
- 逆变换与聚合:把滤波后的块变换回空间域,再加权平均放回原位置。
这个过程会做两轮:第一轮粗去噪,估计出噪声水平;第二轮用维纳滤波精细处理。我习惯把第一轮叫“探路”,第二轮叫“精修”。
避坑指南:我曾经在纹理丰富的区域(比如草地、布料)用过BM3D,结果把纹理也当成噪声去掉了,画面变得像塑料一样。后来我学乖了——块匹配的阈值要调松一点,让更多真实纹理保留下来。
4.2.2 优缺点与适用场景
| 维度 | 说明 |
|---|---|
| 优点 | 降噪效果极佳,PSNR提升明显;能保留边缘和细节;对高斯噪声效果最好。 |
| 缺点 | 计算量大(尤其是块匹配阶段);对非高斯噪声(如散粒噪声)效果一般;参数调优较繁琐。 |
| 适用场景 | 静态图像降噪、医学影像、天文图像、离线处理。 |
注意:BM3D在视频降噪里需要做帧间块匹配,计算量会翻倍。我在一个1080p@30fps的项目里试过,单帧处理时间超过200ms——根本跑不动。所以视频实时降噪慎用BM3D。
4.3 NLM:非局部平均的“温柔一刀”
NLM,Non-Local Means。它的想法很朴素:图像里有很多重复的图案(比如墙上的砖、衣服的纹理),把这些相似区域平均一下,噪声自然就消了。
4.3.1 核心思想
传统均值滤波只考虑邻域像素,NLM则在整个图像里搜索相似块。每个像素的最终值,是它所有相似块中心像素的加权平均。权重由块之间的相似度决定——越相似,权重越大。
你想想看,这其实是一种“全局自相似性”的利用。我在做老照片修复项目时,NLM对胶片颗粒噪声的效果特别好,因为那种噪声是随机的,而真实纹理是重复的,NLM正好能区分开。
4.3.2 优缺点与适用场景
| 维度 | 说明 |
|---|---|
| 优点 | 算法简单,容易实现;对纹理保留较好;适合处理周期性噪声。 |
| 缺点 | 计算量巨大(全局搜索);对边缘保持不如BM3D;容易产生“伪影”(类似油画效果)。 |
| 适用场景 | 纹理丰富的图像、老照片修复、医学超声图像。 |
我的经验:NLM的搜索窗口大小很关键。设太大,计算量爆炸;设太小,效果出不来。我一般建议搜索窗口21x21,相似块7x7,这是一个比较平衡的配置。如果算力紧张,可以缩小到15x15。
4.4 小波降噪:频域里的“手术刀”
小波降噪,说白了就是在小波域里做阈值处理。图像经过小波变换后,能量会集中在少数大系数上,而噪声则均匀分布在小系数上。我们只需要把小于某个阈值的小波系数置零,再逆变换回去,噪声就没了。
4.4.1 核心步骤
- 小波分解:对图像做多级小波分解,得到低频近似分量和高频细节分量。
- 阈值处理:对高频分量做软阈值或硬阈值处理。
- 小波重构:用处理后的系数重建图像。
这里有个关键点:阈值怎么选? 我早期做项目时,直接用固定阈值,结果要么去不干净,要么把细节抹掉了。后来我改用VisuShrink(通用阈值)和BayesShrink(贝叶斯自适应阈值),效果好了很多。
代码片段:小波降噪核心逻辑(Python + PyWavelets)
import pywt
import numpy as np
def wavelet_denoise(img, wavelet='db4', level=3, threshold=0.1):
# 小波分解
coeffs = pywt.wavedec2(img, wavelet, level=level)
# 对每一层的高频分量做软阈值
coeffs_thresh = list(coeffs)
for i in range(1, len(coeffs_thresh)):
coeffs_thresh[i] = tuple(
pywt.threshold(detail, threshold, mode='soft')
for detail in coeffs_thresh[i]
)
# 小波重构
return pywt.waverec2(coeffs_thresh, wavelet)
4.4.2 优缺点与适用场景
| 维度 | 说明 |
|---|---|
| 优点 | 计算速度快(适合实时处理);对高斯噪声和脉冲噪声都有效;多尺度分析,能区分不同频率的噪声。 |
| 缺点 | 对纹理保留不如BM3D;阈值选择依赖经验;容易产生“振铃效应”(Gibbs现象)。 |
| 适用场景 | 实时视频降噪、嵌入式系统、信号处理。 |
注意:小波降噪的振铃效应在边缘附近特别明显。我曾经在一个文字识别项目里用过,结果文字边缘出现了“鬼影”,OCR准确率直接掉了5个点。后来我改用双树复小波(DTCWT),振铃问题才缓解。
4.5 三种方法对比与选择指南
好了,三种方法都讲完了。咱们来做个横向对比,方便你以后选型。
| 维度 | BM3D | NLM | 小波降噪 |
|---|---|---|---|
| 降噪效果 | ★★★★★ | ★★★★ | ★★★ |
| 计算速度 | ★★ | ★ | ★★★★★ |
| 边缘保留 | ★★★★★ | ★★★★ | ★★★ |
| 纹理保留 | ★★★★ | ★★★★★ | ★★ |
| 实现难度 | 高 | 低 | 中 |
| 适用噪声类型 | 高斯噪声 | 高斯、散粒噪声 | 高斯、脉冲噪声 |
我的选择逻辑是这样的:
- 离线处理、追求极致画质 → BM3D
- 纹理丰富、需要保留细节 → NLM
- 实时系统、算力受限 → 小波降噪
4.6 知识体系总览
下面这张图,我把三种方法的核心逻辑和适用场景串起来了。你可以把它当作一个“选型地图”。