4. 去噪(NR)调试:空域去噪与时域去噪的原理、参数调节与 trade-off
去噪这章,我把它放在调试流程靠前的位置,是有原因的。你想想看,噪声如果不在前端压住,后面锐化、色彩增强都会把噪声放大,到时候再回头调,事倍功半。说白了,去噪是 ISP pipeline 里的第一道防线。
我个人习惯把去噪分成两大类:空域去噪和时域去噪。这两兄弟各有各的脾气,也各有各的坑。今天咱们就把它们掰开揉碎了讲清楚。
4.1 空域去噪:单帧里的“像素和事佬”
空域去噪,顾名思义,只依赖当前这一帧图像里的像素信息。它不看前后帧,所以对运动物体很友好——不会拖尾。但代价是,它容易把细节也抹掉。
4.1.1 核心原理:滤波器的“温柔一刀”
空域去噪的本质就是滤波。常见的滤波器有这几种:
| 滤波器类型 | 原理简述 | 典型应用场景 |
|---|---|---|
| 均值滤波 | 邻域像素取平均 | 均匀区域,噪声方差小 |
| 高斯滤波 | 按距离加权平均 | 轻度噪声,边缘保留一般 |
| 双边滤波 | 距离+像素值差异双重加权 | 边缘保留去噪,人像皮肤 |
| 非局部均值(NLM) | 在全图找相似块加权 | 纹理保留要求高的场景 |
我在项目中用得最多的是双边滤波和NLM。为什么?因为均值滤波太粗暴,高斯滤波又太“软”。双边滤波能在去噪的同时保住边缘,这对后续的锐化模块特别重要。
关键参数:
- 空间域sigma(σ_s):控制像素距离的权重。值越大,考虑的范围越广,去噪越强,但细节丢失也越多。
- 值域sigma(σ_r):控制像素值差异的权重。值越大,对边缘的保护越弱,容易把边缘也磨平。
4.1.2 参数调节的“手感”
调空域去噪参数,我有个口诀:先调σ_r,再调σ_s。
为什么?σ_r决定了“哪些算噪声,哪些算边缘”。σ_r设小了,噪声去不干净;设大了,边缘也跟着糊。我一般会先给一个偏大的σ_r,看看边缘保留情况,再慢慢收窄。
举个例子,一个典型的调试流程:
// 伪代码:双边滤波参数调节步骤
1. 固定 σ_s = 3(像素半径)
2. 设置 σ_r = 0.1(归一化值)
3. 观察:噪声还在,但边缘清晰 → 增大 σ_r 到 0.15
4. 观察:噪声没了,但边缘模糊 → 减小 σ_r 到 0.12
5. 微调 σ_s:如果噪声颗粒大,增大 σ_s;如果细节丢失,减小 σ_s
我的经验:在暗光场景下,σ_r 可以适当放大,因为人眼对暗部的细节敏感度低,但对噪声敏感度高。这时候“丢一点细节换干净画面”是划算的。
4.2 时域去噪:多帧之间的“时间旅行者”
时域去噪利用的是视频序列中前后帧的相关性。同一位置的像素,如果连续几帧都差不多,那大概率是真实信号;如果突然跳变,那很可能是噪声。
4.2.1 核心原理:帧间平均与运动补偿
最简单的时域去噪就是帧间平均:
output_frame = (1 - α) * current_frame + α * previous_frame
α 是融合系数,α 越大,去噪越强,但拖尾也越明显。
但实际场景哪有这么简单?物体一动,帧间对应关系就乱了。所以现代 ISP 都用运动补偿(Motion Compensation, MC):先做运动估计,找到像素在前后帧的位置偏移,再做加权平均。
我曾经踩过的坑:有一次调试夜景视频,时域去噪开得很强,画面确实干净了。但人物一挥手,手上就拖出一条“鬼影”。后来发现是运动估计的块大小设得太大了(32x32),小物体的运动没被正确补偿。把块大小降到16x16,鬼影就消失了。
4.2.2 关键参数与 trade-off
| 参数 | 作用 | 调大后果 | 调小后果 |
|---|---|---|---|
| 融合系数 α | 控制时域滤波强度 | 去噪强,拖尾严重 | 去噪弱,闪烁明显 |
| 运动估计块大小 | 决定运动检测粒度 | 小物体运动补偿不准 | 计算量大,噪声敏感 |
| 运动阈值 | 判断像素是否运动 | 运动区域被误判为静止 | 静止区域被误判为运动 |
这里有个 trade-off 特别值得注意:去噪强度 vs 运动拖尾。你想想看,α 设大了,静止背景确实干净,但运动物体就像“慢动作回放”一样,拖着一串残影。α 设小了,运动物体是清晰了,但背景噪声又开始闪烁。
4.3 空域 vs 时域:什么时候用谁?
这个问题,我经常被问到。我的回答是:别二选一,要组合使用。
你看这个流程图,核心判断就是场景是否包含运动物体:
- 运动场景(比如走路、挥手):以空域去噪为主,时域只做轻度滤波。否则鬼影会让你怀疑人生。
- 静止场景(比如三脚架拍夜景):以时域去噪为主,空域只做辅助。这时候时域去噪的效果远好于空域,因为可以利用多帧信息。
4.4 实战中的 trade-off 平衡
在实际调试中,我经常要面对这几个 trade-off:
4.4.1 去噪 vs 细节保留
这是最经典的矛盾。去噪越强,细节丢得越多。我的做法是:分频段处理。低频区域(平坦区域)用强去噪,高频区域(纹理边缘)用弱去噪。很多 ISP 芯片都支持这种“自适应去噪”,利用边缘检测结果来控制去噪强度。
4.4.2 去噪 vs 计算量
NLM 去噪效果好,但计算量巨大。在移动平台上,我一般只用双边滤波或者引导滤波。时域去噪的计算量也不小,尤其是运动估计那块。嗯,这里要注意:别为了去噪把帧率拖垮了。我曾经在一个项目里,时域去噪的块匹配算法太复杂,导致预览帧率从 30fps 掉到 20fps,最后只能换用更简单的递归滤波。
4.4.3 空域 vs 时域的权重分配
我一般用这个公式来分配权重:
final_strength = α * temporal_strength + (1 - α) * spatial_strength
α 根据运动检测结果动态调整。运动区域 α 小,静止区域 α 大。这样既能保证运动清晰度,又能保证背景干净。
总结一下我的调试顺序:
- 先确定场景类型(运动/静止)
- 再调空域去噪参数(σ_r → σ_s)
- 然后调时域去噪参数(α → 运动阈值)
- 最后做联合调试,观察边缘保留和鬼影情况
去噪调试没有银弹。每个场景、每个 sensor、每个芯片都有自己的脾气。多试、多看、多对比,慢慢你就能找到手感了。