3、多帧合成降噪原理:均值降噪、中值降噪、基于光流的对齐降噪
多帧合成降噪,说白了就是「用多张烂照片,拼出一张好照片」。你想想看,单帧画面里的噪点通常是随机的,但画面内容是有规律的。如果我们能对齐多帧,再把噪点平均掉,信噪比自然就上去了。
我个人习惯把多帧降噪分成三个流派:均值降噪、中值降噪和基于光流的对齐降噪。它们各有各的脾气,选对了事半功倍,选错了……嗯,画面会糊成一团。
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 to N)
信噪比提升多少?理论上,N 帧平均后,噪声标准差降低到原来的 \(1/\sqrt{N}\)。4 帧降一半,16 帧降四分之一。听起来很美对吧?
适用场景:
- 天文摄影(赤道仪跟踪,几乎无位移)
- 显微摄影(载物台固定)
- 工业检测(静止工件)
3.2 中值降噪:专治「离群点」
中值降噪和均值降噪的区别在哪?均值是「大家投票」,中值是「取中间那个人的意见」。
对于 N 帧图像,在每个像素位置,把 N 个像素值排序,取中间那个值作为输出。
I_out(x,y) = median( I_1(x,y), I_2(x,y), ..., I_N(x,y) )
为什么中值降噪在某些场景下比均值强?因为中值对「极端值」不敏感。举个例子:
| 帧序号 | 像素值 | 说明 |
|---|---|---|
| 帧 1 | 128 | 正常信号 |
| 帧 2 | 130 | 正常信号 |
| 帧 3 | 132 | 正常信号 |
| 帧 4 | 255 | 过曝/坏点/运动物体闯入 |
| 帧 5 | 126 | 正常信号 |
均值结果:(128+130+132+255+126)/5 = 154.2,被那个 255 拉偏了。中值结果:排序后取中间值 = 130,几乎没受影响。
但中值降噪也有短板:
- 计算量大(每帧每个像素都要排序)
- 对纹理细节有损失(不如均值保留得好)
- 帧数太少时效果不稳定(3 帧以下基本没用)
3.3 基于光流的对齐降噪:真正的「黑科技」
前面两种方法都假设「多帧之间已经对齐了」。但现实世界哪有那么完美?手持拍摄、物体运动、呼吸起伏……这些都会导致帧间位移。
基于光流的对齐降噪,核心思路是:先算运动,再对齐,最后融合。
光流(Optical Flow)是什么?简单说,就是计算每个像素从上一帧到下一帧的「运动向量」。比如一个人从左走到右,光流算法会算出每个像素的移动方向和速度。
整个流程分三步:
- 光流估计:对相邻帧计算稠密光流场(每个像素一个运动向量)
- 帧间对齐:根据光流场,把其他帧 warp(扭曲)到参考帧的坐标系
- 融合降噪:对齐后的多帧做均值或中值融合
# 伪代码:基于光流的对齐降噪流程
def optical_flow_denoise(frames):
ref_frame = frames[0] # 选第一帧为参考
aligned_frames = [ref_frame]
for i in range(1, len(frames)):
# 步骤1:计算光流
flow = calc_optical_flow(ref_frame, frames[i])
# 步骤2:根据光流对齐
warped = warp_frame(frames[i], flow)
aligned_frames.append(warped)
# 步骤3:融合(这里用均值)
result = np.mean(aligned_frames, axis=0)
return result
3.4 三种方法对比
| 方法 | 对齐要求 | 计算量 | 降噪效果 | 边缘保留 | 典型场景 |
|---|---|---|---|---|---|
| 均值降噪 | 极高(必须严格对齐) | 低 | 好(N 帧提升 √N 倍) | 差(容易重影) | 固定机位、天文 |
| 中值降噪 | 较高(允许微小偏移) | 中 | 中等(抗离群点) | 中等 | 延时摄影、监控 |
| 光流对齐降噪 | 低(自动对齐) | 高 | 最好(可处理运动) | 好 | 手持夜景、视频 |
3.5 知识结构图
下面这张图帮你理清三种方法的定位和关系:
3.6 避坑指南
- 直接用均值降噪处理手持拍摄: 结果画面边缘全是重影,尤其是高光区域(路灯、招牌),看起来像鬼影重重。后来我加了光流对齐,问题才解决。
- 中值降噪帧数选太少: 3 帧中值降噪基本没用,噪点只是稍微柔和了一点。我建议至少 5 帧起步,7-9 帧效果才明显。
- 光流参数调太激进: 光流搜索窗口设得太大,结果把噪声也当成运动去对齐了,反而引入了伪影。记住:光流是「对齐信号」,不是「对齐噪声」。
3.7 小结
三种方法,三种哲学:
- 均值降噪:相信「平均能消除随机」——前提是你得对齐。
- 中值降噪:相信「中间值最可靠」——专治各种 outlier。
- 光流对齐降噪:相信「运动可以预测」——用计算换质量。
实际项目中,我经常把光流对齐 + 中值融合结合起来用:先用光流对齐,再用中值融合。这样既保留了边缘,又不怕个别帧的坏点或运动物体。嗯,这个组合拳,推荐你试试。