4. 多帧合成原理:短帧与长帧的曝光策略、帧对齐技术、运动伪影消除

多帧合成,说白了就是让摄像头连续拍几张不同亮度的照片,然后挑好的部分拼成一张。这个思路在车载领域特别实用——你想想看,隧道入口那种场景,一张图根本搞不定。

我最早接触多帧合成是在2016年,当时给一家Tier1做前视方案。客户要求动态范围做到120dB以上,单帧ISP怎么调都差口气。后来上了多帧,效果立竿见影。但坑也不少,今天我把核心要点拆开讲。

4.1 短帧与长帧的曝光策略

多帧合成的第一步,是决定每帧怎么曝光。通常我们拍两到三帧:一帧短曝光(抓亮部细节),一帧长曝光(提暗部细节),中间再来一帧中曝光做过渡。

帧类型 曝光时间(典型值) 目标场景 信噪比特点
短帧 1/4000 s ~ 1/1000 s 天空、车灯、反光路面 亮部不过曝,但暗部噪声大
中帧 1/500 s ~ 1/200 s 建筑物、行人、中等亮度区域 均衡,作为融合过渡
长帧 1/60 s ~ 1/30 s 阴影、隧道内部、夜间暗区 暗部细节丰富,但亮部易饱和

曝光比例怎么定?我个人习惯用 1:4:16 或 1:8 的等比序列。比如短帧1ms,中帧4ms,长帧16ms。这样合成后的线性度最好,融合权重也容易算。

小技巧: 曝光时间不要用整数倍,比如1ms和8ms之间差8倍,容易在融合边界出现亮度跳变。1:4:16这种等比序列,过渡更平滑。

还有一个关键参数——帧间隔。多帧不是连拍三张就完事,帧与帧之间要留足读出时间。我见过有人把三帧塞在同一个垂直消隐区里,结果读出噪声串扰得一塌糊涂。建议每帧之间至少留1行时间的间隔。

4.2 帧对齐技术

三帧拍完了,问题来了——车在动,场景也在动。如果不做对齐,合成出来的图像会有重影。帧对齐就是解决这个问题的。

对齐分两个层次:全局运动补偿和局部运动补偿。

4.2.1 全局运动补偿

全局运动补偿处理的是整车运动。比如车辆直线行驶,画面整体向左平移。这个可以用仿射变换或透视变换来建模。

我常用的做法是:

  1. 以中帧为参考帧,提取角点特征(比如FAST或Shi-Tomasi)
  2. 在短帧和长帧上用光流法或块匹配找到对应点
  3. 用RANSAC剔除误匹配,解算仿射矩阵
  4. 对短帧和长帧做warp变换,对齐到中帧坐标系
// 伪代码:全局运动补偿核心流程
Mat refFrame = midFrame;  // 中帧作为参考
vector<Point2f> refPts, curPts;
goodFeaturesToTrack(refFrame, refPts, 200, 0.01, 10);
calcOpticalFlowPyrLK(curFrame, refFrame, curPts, refPts, status, err);
Mat affineMat = estimateAffinePartial2D(curPts, refPts, RANSAC);
warpAffine(curFrame, alignedFrame, affineMat, refFrame.size());
注意: 车载场景下,RANSAC的阈值要设得保守一些。我曾经用默认阈值,结果把车辆自身的抖动当成了有效运动,对齐后反而引入了伪影。建议阈值设在1~2像素。

4.2.2 局部运动补偿

全局补偿搞定了车体运动,但场景里的独立运动物体呢?比如行人挥手、旁边车道超车的车辆。这些局部运动,全局变换是管不了的。

局部补偿我一般用分块运动估计:

  • 把图像分成16x16或32x32的块
  • 每个块独立做运动搜索,范围±8像素
  • 对运动矢量做中值滤波,去除野点
  • 用双线性插值做逐像素warp

这里有个坑——分块太小了容易受噪声影响,分块太大了又抓不住细节运动。我踩过这个坑,后来总结的经验是:暗部区域用大块(32x32),亮部区域用小块(16x16)。因为暗部信噪比低,小块的运动矢量不可靠。

4.3 运动伪影消除

对齐做得再好,也难免有残留的运动区域。这些区域如果直接融合,就会出现鬼影或拖尾。运动伪影消除,就是处理这些「对齐失败」的像素。

我的处理思路分三步:

  1. 运动检测:计算对齐后帧与参考帧的差异图,用阈值或统计方法标记运动区域
  2. 权重调整:在运动区域降低长帧或短帧的融合权重,甚至直接丢弃
  3. 时域滤波:对运动区域的像素做时域中值滤波,替代直接融合
核心原则: 宁可丢失一点动态范围,也不要引入鬼影。车载场景下,鬼影会误导目标检测算法,后果很严重。

具体实现时,我习惯用基于梯度的一致性检测:

  • 计算参考帧和待融合帧的梯度图
  • 在梯度域做差,得到运动置信度图
  • 置信度低的像素,融合权重直接置零
  • 对置信度图做高斯模糊,避免融合边界出现硬边

举个例子,如果短帧里有个行人抬手,而长帧里手的位置偏了5个像素。梯度一致性检测会发现这个区域的梯度方向不一致,于是自动降低长帧在这个区域的权重。最终合成结果里,手部细节主要来自短帧,虽然暗部噪声大一点,但至少没有鬼影。

下面这张图概括了多帧合成的完整流程:

多帧合成核心流程 短帧 (1ms) 中帧 (4ms) 长帧 (16ms) 全局运动补偿(仿射变换 + RANSAC) 以中帧为参考,对短帧和长帧做warp对齐 局部运动补偿(分块运动估计 + 中值滤波) 16x16/32x32分块,独立搜索运动矢量 运动检测 梯度一致性 + 差异图阈值 权重调整 运动区域权重置零 + 高斯模糊 多帧融合 → HDR输出图像 关键:运动区域宁可丢动态范围,也不引入鬼影

4.4 实战中的避坑指南

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

坑一:曝光时间不能太极端。 我曾经试过短帧0.25ms、长帧32ms,差了128倍。结果对齐时短帧几乎全是噪声,特征点都提不出来。建议曝光比控制在16倍以内。
坑二:帧对齐不要只做一次。 我习惯做两轮对齐——第一轮用降采样图像做粗对齐,第二轮在全分辨率上做精对齐。这样既保证速度,又保证精度。
坑三:运动检测的阈值要自适应。 白天和晚上,运动区域的差异幅度完全不同。固定阈值白天漏检、晚上误检。我后来改用基于局部统计的阈值——每个块的阈值取该块内像素标准差的3倍。

嗯,多帧合成这块内容不少,但核心就三件事:曝光策略定好比例,帧对齐做精细,运动伪影坚决消除。把这三点做到位,车载HDR的效果基本就稳了。