2. 多帧降噪(MFNR)原理

多帧降噪,说白了就是拍多张照片,然后合成一张干净的。听起来简单,但里面的门道不少。我在高通平台调了这么多年,踩过的坑能写满一本笔记本。

这一章我们重点聊四个核心模块:时域滤波、空域滤波、运动补偿和帧选择策略。它们就像四根柱子,缺一根房子就塌。

2.1 时域滤波:让时间帮你降噪

时域滤波的核心思想很简单——同一场景拍多帧,像素值取平均。噪声是随机的,信号是稳定的,平均之后噪声自然就弱了。

公式长这样:

I_out(x,y) = (1/N) * Σ I_i(x,y)   (i = 1 to N)

但实际项目里没人这么干。为什么?因为帧与帧之间一定有运动。哪怕是三脚架固定拍摄,树叶也会动,云也会飘。

关键点:时域滤波的强度取决于两帧之间的相关性。相关性越高,降噪效果越好。

我习惯把时域滤波分成两种模式:

模式 适用场景 帧数 降噪强度
简单平均 静态场景(夜景、文档拍摄) 4~8帧 高
加权平均 轻微运动场景(手持夜景) 3~5帧 中

加权平均里,权重怎么给?我一般用帧间差异图来算。差异大的像素权重低,差异小的权重高。这样能避免运动区域糊掉。

我的经验:高通平台里有个叫 MCTF(Motion Compensated Temporal Filter)的硬件模块,专门做这个。你只需要配好权重表和搜索窗口大小,剩下的硬件帮你算。

2.2 空域滤波:在单帧里下功夫

时域滤波搞不定所有噪声。比如运动区域,时域滤波基本不敢动,一动就糊。这时候就得靠空域滤波。

空域滤波的本质是利用像素邻域信息来估计当前像素的真实值。常见的算法有:

  • 高斯滤波:简单,但会模糊边缘
  • 双边滤波:保边效果好,但计算量大
  • NLM(非局部均值):效果好,但慢得离谱
  • 小波阈值去噪:频域方法,适合特定噪声类型

在高通平台上,我推荐用改进的双边滤波。为什么?因为硬件里有专门的 BF 模块,支持可配置的空间核和范围核。

// 双边滤波伪代码
for each pixel (x,y):
    w_sum = 0
    result = 0
    for each neighbor (i,j) in window:
        w_spatial = exp(-((i-x)^2 + (j-y)^2) / (2 * σ_s^2))
        w_range = exp(-(|I(i,j) - I(x,y)|^2) / (2 * σ_r^2))
        w = w_spatial * w_range
        result += I(i,j) * w
        w_sum += w
    I_out(x,y) = result / w_sum

注意:σ_s 和 σ_r 这两个参数非常敏感。σ_s 太大,边缘会糊;σ_r 太大,噪声去不干净。我一般先用 σ_s = 2~3,σ_r = 10~20(8-bit 图像),然后根据实际效果微调。

2.3 运动补偿:让时域滤波真正可用

没有运动补偿的时域滤波,就是耍流氓。你想想看,手持拍摄时手会抖,拍出来的帧之间肯定有偏移。直接平均,画面就重影了。

运动补偿的核心是估计帧间运动矢量,然后把参考帧对齐到当前帧。高通平台用的是块匹配法:

  1. 把图像分成 16x16 或 8x8 的块
  2. 在参考帧的搜索窗口内找最匹配的块
  3. 计算运动矢量 (dx, dy)
  4. 用运动矢量做 warp 变换

我遇到过一个问题:运动矢量太粗糙。块匹配只能给出整像素精度,但实际运动可能是亚像素级的。怎么办?

解决方案:在块匹配之后,再做一次亚像素插值。高通平台的 MEC(Motion Estimation and Compensation)模块支持 1/4 像素精度,效果比整像素好很多。

运动补偿的流程我画了张图,你看一眼就明白了:

运动补偿流程图 当前帧 参考帧 块匹配 运动矢量 亚像素插值 对齐帧 时域滤波 输出帧 运动补偿后,时域滤波才能安全地工作 否则画面会出现重影和模糊

2.4 帧选择策略:不是所有帧都值得保留

多帧降噪不是帧数越多越好。我见过有人拍 10 帧合成,结果还不如 4 帧的效果好。为什么?因为帧质量参差不齐。

帧选择策略要解决三个问题:

  • 哪些帧能用?——剔除模糊帧、过曝帧、欠曝帧
  • 哪些帧优先?——质量高的帧给更高权重
  • 帧数不够怎么办?——降级策略,用更少的帧合成

我在项目中常用的帧质量评价指标:

指标 计算方法 阈值参考
拉普拉斯方差 对帧做拉普拉斯变换,求方差 > 100(清晰)
平均亮度 像素值求和取平均 80~180(正常曝光)
帧间差异 与参考帧的差求和 < 20%(运动小)

我曾经踩过的坑:有一次把拉普拉斯方差的阈值设得太低,结果把轻微模糊的帧也放进去了。合成出来的照片边缘发虚,怎么看都不对劲。后来我把阈值从 50 调到 100,问题就解决了。

帧选择的具体流程:

  1. 拍完 N 帧后,先做快速质量评估
  2. 剔除质量不合格的帧
  3. 对剩余帧按质量排序
  4. 取前 M 帧(M ≤ N)参与合成
  5. 质量高的帧给高权重

我的建议:高通平台的 ISP 里有个 AFM(Auto Focus Metric)寄存器,可以直接读出每帧的清晰度评分。用这个做帧选择,比你自己算拉普拉斯方差快得多。

2.5 三者如何协同工作

时域滤波、空域滤波、运动补偿、帧选择,这四个模块不是各自为战的。它们需要紧密配合:

  • 帧选择在最前面,把烂帧挡在门外
  • 运动补偿把好帧对齐到同一坐标系
  • 时域滤波在对齐后的帧上做平均
  • 空域滤波在时域滤波之后,处理残留噪声

这个顺序不能乱。我曾经试过先做空域滤波再做时域滤波,结果噪声是去掉了,但细节也丢了不少。嗯,后来老老实实按顺序来。