3. 经典多帧降噪算法

多帧降噪,说白了就是「用多张照片换一张干净照片」。你想想看,单帧降噪再怎么折腾,细节和噪声之间总得有个取舍。但如果你手里有 4 帧、8 帧甚至 16 帧同一场景的图像呢?那操作空间就大多了。

我刚开始做 ISP 时,觉得多帧降噪不就是「叠一叠」嘛。后来踩了不少坑才明白——怎么叠、叠多少、每帧信多少,这里面门道很深。今天咱们就把三种经典方法掰开揉碎了讲清楚。

3.1 均值帧叠加:最朴素的暴力美学

均值叠加的原理一句话就能说清:对同一像素位置,取 N 帧的像素值求平均。假设你有 N 帧图像 \(I_1, I_2, ..., I_N\),输出帧 \(I_{out}\) 在位置 \((x,y)\) 的值为:

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

为什么有效?因为噪声是零均值的随机信号。叠加 N 帧后,信号部分保持不变(因为每帧信号相同),而噪声的标准差会降低到原来的 \(1/\sqrt{N}\)。换句话说,4 帧叠加,噪声降低一半;16 帧叠加,噪声降低四分之三。

关键指标:均值叠加的 SNR 提升 = 10 * log10(N) dB。N=4 时提升 6dB,N=8 时提升 9dB。

我在项目中遇到过一个问题:用均值叠加处理夜景视频,结果画面发虚。后来发现是帧间有微小抖动,直接平均把边缘给「糊」了。所以均值叠加有个前提——帧间必须严格对齐,否则细节全毁。

我的习惯:均值叠加适合静态场景或经过精准运动补偿后的帧序列。如果运动补偿残差大于某个阈值,我会把这帧的权重直接置零。

3.2 中值帧叠加:对抗离群值的利器

均值叠加有个致命弱点——怕「脏数据」。如果某一帧恰好有热像素、坏点或者运动物体,均值会把异常值「平均」到结果里,导致整张图出现鬼影或亮点。

中值叠加的思路是:对每个像素位置,取 N 帧在该位置的像素值,排序后取中间值。比如 5 帧,取第 3 大的值;7 帧,取第 4 大的值。

I_out(x,y) = median( I_1(x,y), I_2(x,y), ..., I_N(x,y) )

中值叠加的好处很明显:对离群值不敏感。即使某一帧有热像素(比如值特别大),只要其他帧正常,中值结果基本不受影响。我曾经用中值叠加处理过一批有坏点的 sensor 数据,效果比均值好太多。

但中值叠加也有代价:计算量大。每个像素都要排序,N 越大越慢。另外,中值叠加对高斯噪声的抑制效果不如均值——因为中值只取一个值,没有利用所有帧的信息做统计平均。

方法 高斯噪声抑制 离群值鲁棒性 计算复杂度 适用场景
均值叠加 优(理论最优) 差 低(O(N)) 静态场景、已对齐帧
中值叠加 良 优 高(O(N log N)) 有坏点/热像素、轻度运动

注意:中值叠加在 N 为偶数时,通常取中间两个值的平均。但偶数帧的中值估计偏差较大,我建议尽量用奇数帧(3、5、7、9)。

3.3 自适应加权叠加:让「好帧」多说话

均值和中值都是「一刀切」——要么所有帧平等,要么只取一帧。但实际情况是:不同帧的质量不一样。比如手持拍摄时,有些帧抖得厉害,有些帧比较稳;或者某些帧的局部区域有运动,其他区域是静止的。

自适应加权叠加的思路是:给每帧的每个像素分配一个权重,权重越高,该帧对该像素的贡献越大。输出为:

I_out(x,y) = Σ w_i(x,y) * I_i(x,y)  /  Σ w_i(x,y)

权重怎么算?常见的方法有:

  • 基于运动检测:计算当前帧与参考帧的差异,差异大的区域权重降低。
  • 基于梯度/纹理:纹理丰富的区域权重高,平坦区域权重低(因为平坦区噪声更明显)。
  • 基于噪声估计:噪声大的帧整体权重降低。

我个人习惯用 双边权重:同时考虑像素值相似度和空间距离。这样既能抑制噪声,又能保护边缘。代码实现大致如下:

// 伪代码:自适应加权叠加
for each pixel (x,y):
    total_weight = 0
    total_value = 0
    for each frame i:
        // 计算当前帧与参考帧的亮度差异
        diff = abs(I_i(x,y) - I_ref(x,y))
        // 权重 = 高斯函数,差异越大权重越小
        w = exp(-diff^2 / (2 * sigma^2))
        total_weight += w
        total_value += w * I_i(x,y)
    I_out(x,y) = total_value / total_weight

避坑指南:我曾经把 sigma 设得太小,结果只有参考帧有贡献,其他帧几乎被忽略——等于没做降噪。sigma 一般取图像动态范围的 5%-10%,比如 8-bit 图像取 12-25。

3.4 基于噪声方差的帧融合:理论最优解

如果你知道每帧的噪声方差,那就可以用 最小方差融合——这是理论上的最优加权方式。假设第 i 帧的噪声方差为 \(\sigma_i^2\),那么最优权重为:

w_i = 1 / σ_i^2

为什么?因为噪声方差越大,该帧的「可信度」越低,权重就应该越小。这种加权方式能保证输出帧的噪声方差最小:

σ_out^2 = 1 / Σ (1/σ_i^2)

实际应用中,噪声方差怎么估计?我常用的方法:

  1. 暗区估计:取图像中最暗的 5% 像素,计算其方差作为噪声基底。
  2. 差值法:用两帧相减,差值的方差 ≈ 2 * 噪声方差。
  3. 基于 sensor 模型:根据增益和曝光时间查表得到噪声参数。
方法 优点 缺点 我推荐的使用场景
暗区估计 简单、实时 暗区不一定代表全局噪声 快速原型验证
差值法 准确、无偏 需要两帧,且帧间不能有运动 静态场景标定
sensor 模型 最准确、可预测 需要 sensor 标定数据 量产产品

核心结论:基于噪声方差的融合是「理论上限」,但实际中噪声方差估计不准反而会引入 artifacts。我通常把它作为 baseline,然后根据实际效果微调权重。

3.5 四种方法对比与选型指南

说了这么多,到底该用哪种?我整理了一张对比表,方便你快速决策:

方法 降噪能力 细节保留 计算开销 鲁棒性 实现难度
均值叠加 ★★★★★ ★★★☆☆ ★☆☆☆☆ ★☆☆☆☆ ★☆☆☆☆
中值叠加 ★★★★☆ ★★★★☆ ★★★★☆ ★★★★★ ★★☆☆☆
自适应加权 ★★★★☆ ★★★★★ ★★★☆☆ ★★★★☆ ★★★★☆
噪声方差融合 ★★★★★ ★★★★☆ ★★☆☆☆ ★★★☆☆ ★★★☆☆

我的个人建议:

  • 刚入门或快速验证:先上均值叠加,简单有效。
  • sensor 有坏点或热像素:中值叠加是首选。
  • 手持拍摄或场景有轻微运动:自适应加权叠加最稳。
  • 追求理论最优且能准确估计噪声:噪声方差融合值得一试。
多帧降噪算法选型流程图 多帧输入序列 帧间是否有运动/抖动? 无运动 均值叠加 有运动 运动补偿 是否有坏点/热像素? 无坏点 自适应加权叠加 有坏点 中值叠加 提示:若已知每帧噪声方差,可在任意分支中叠加「噪声方差融合」作为权重优化

3.6 实战中的避坑与调优

最后分享几个我踩过的坑,希望能帮你少走弯路:

  • 帧数不是越多越好。超过 8 帧后,降噪收益递减,但计算量和存储开销线性增长。我一般用 4-8 帧。
  • 运动补偿要谨慎。补偿不准还不如不补。我习惯先做粗对齐(全局仿射),再做细对齐(局部光流),最后用自适应加权兜底。
  • 权重平滑很重要。自适应加权时,权重图如果变化太剧烈,输出帧会出现「块状感」。我会对权重图做高斯模糊,让权重过渡自然。
  • 别忘了色彩空间。多帧降噪通常在 YUV 或 RAW 域做,不要在 sRGB 域做——因为 gamma 校正会破坏噪声的统计特性。

我曾经犯过的错:有一次在 sRGB 域做均值叠加,结果降噪后颜色偏暗,细节也丢了。后来才意识到是 gamma 的问题。从那以后,我所有多帧处理都在线性空间完成。