多帧合成HDR技术:短帧与长帧的曝光策略、帧对齐与融合

多帧合成HDR,说白了就是让摄像头“看”两次——一次看暗处,一次看亮处,然后把两张图里最好的部分拼起来。车载场景下,这个技术尤其关键。你想想看,隧道出口那几秒钟,一边是刺眼的阳光,一边是黑洞洞的隧道,单帧传感器根本扛不住。

我个人习惯把多帧HDR分成三个核心环节:曝光策略、帧对齐、权重融合。下面一个一个拆开讲。

1. 短帧与长帧的曝光策略

曝光策略决定了“拍什么”。短帧负责高亮区域,长帧负责暗部细节。但问题来了——长短帧的曝光时间怎么配?

我在项目中遇到过最典型的场景:车辆以80km/h行驶,前方突然出现一个逆光的行人。如果长帧曝光时间太长,运动模糊会把行人“抹掉”;如果短帧太短,信噪比又不够。嗯,这里要注意一个经验值:长短帧的曝光比通常控制在4:1到16:1之间。

场景 短帧曝光 长帧曝光 曝光比
隧道出口(强逆光) 1/2000 s 1/125 s 16:1
城市夜间(路灯+暗区) 1/500 s 1/30 s ~16:1
高速路日间(均匀光照) 1/4000 s 1/500 s 8:1
雨天/雾天(低对比度) 1/1000 s 1/250 s 4:1
我的经验:曝光比超过16:1时,长帧的运动模糊会变得非常严重,帧对齐算法容易失效。我一般把上限卡在12:1,留点余量给后处理。

2. 帧对齐算法:光流法与特征匹配

长短帧不是同时拍的,中间有几十毫秒的时间差。车辆在动,场景在变,直接融合会出现重影。所以必须先做帧对齐。

车载领域主流有两种对齐思路:光流法和特征匹配。我两种都用过,说说我的感受。

2.1 光流法

光流法假设像素在短时间内的运动是连续的、微小的。它计算每个像素的运动矢量,然后根据这个矢量把长帧“扭曲”到短帧的坐标系上。

我曾经在一条颠簸的乡间小路上测试过光流法。路面坑洼导致车辆高频振动,光流场变得非常杂乱。后来我加了一个金字塔分层策略——先在大尺度上估计整体运动,再逐层细化到像素级。效果好了很多。

// 简化版金字塔光流伪代码
for level = max_level down to 0:
    // 在当前金字塔层计算光流
    flow = calc_optical_flow(img1_pyr[level], img2_pyr[level], flow_init)
    // 上采样到下一层
    flow_init = upsample(flow) * 2
end
// 用最终光流场做图像扭曲
warped_img2 = warp_image(img2, flow)
注意:光流法计算量大,在嵌入式平台上跑实时(30fps)需要硬件加速或降分辨率处理。我曾在TDA4平台上把输入从1080p降到720p,光流计算时间从45ms降到了18ms,对齐精度损失在1个像素以内。

2.2 特征匹配

特征匹配的思路更“粗暴”——先提取图像中的角点、边缘等特征,然后找两帧之间的特征对应关系,最后算出一个全局单应性矩阵或仿射变换矩阵。

特征匹配的优势是对大运动鲁棒。车辆急转弯时,光流法容易发散,但特征匹配只要还能找到几个稳定的特征点,就能算出变换矩阵。

我个人习惯在静态背景占主导的场景(比如高速路)用光流法,在动态物体多、运动幅度大的场景(比如城市路口)用特征匹配。当然,也有混合方案——先用特征匹配做粗对齐,再用光流法做细对齐。

对比项 光流法 特征匹配
计算量 大(逐像素) 中等(稀疏特征)
大运动鲁棒性 弱(容易发散) 强(全局变换)
精度 高(亚像素级) 中等(像素级)
适用场景 小运动、静态背景 大运动、动态场景

3. 权重映射与融合

帧对齐做完后,就到了融合环节。融合不是简单的“短帧取亮部、长帧取暗部”,那样会有明显的拼接痕迹。我们需要一个平滑的权重映射。

权重映射的核心思想:对每个像素,根据它的亮度、对比度、饱和度等信息,计算一个融合权重。亮部区域给短帧更高的权重,暗部区域给长帧更高的权重,中间区域做线性过渡。

我常用的权重函数包含三个分量:

  • 亮度权重:像素值越接近中间灰度(128),权重越高。避免过曝和欠曝区域被过度信任。
  • 对比度权重:局部梯度越大,权重越高。梯度大的区域通常是纹理丰富的区域,融合后细节保留更好。
  • 饱和度权重:颜色越饱和,权重越高。防止融合后颜色发灰。
// 权重计算示例(简化版)
float compute_weight(float pixel_value, float local_gradient, float saturation) {
    float w_brightness = 1.0 - abs(pixel_value - 128.0) / 128.0;
    float w_contrast   = min(local_gradient / 50.0, 1.0);
    float w_saturation = min(saturation / 0.5, 1.0);
    return w_brightness * 0.5 + w_contrast * 0.3 + w_saturation * 0.2;
}
关键点:权重图一定要做高斯模糊(通常σ=5~10像素),否则融合后的图像会出现“鬼影”边缘。我曾经因为忘了做这一步,融合结果在物体边缘有一圈明显的亮边,被测试同事一眼就发现了。

最终融合公式很简单:

// 多帧融合
float total_weight = 0;
float3 final_pixel = 0;
for each frame i:
    final_pixel += weight_i(x,y) * frame_i(x,y);
    total_weight += weight_i(x,y);
end
final_pixel /= total_weight;

整体流程框架

下面这张图把整个多帧合成HDR的流程串起来了。从传感器曝光开始,到帧对齐、权重计算,最后融合输出。你可以把它当作一个参考管线。

多帧合成HDR管线 传感器原始帧 短帧(高亮区) 长帧(暗部区) 光流法对齐 特征匹配对齐 权重映射与融合 HDR合成帧 ⚠ 关键参数 曝光比:4:1 ~ 16:1 权重模糊σ:5~10像素
避坑指南:我曾经在权重融合后直接输出16bit线性图像,结果在ISP后端做色调映射时发现暗部噪声被放大了。后来我在融合前对长帧做了一次自适应降噪,信噪比提升了约3dB,最终效果才达标。

多帧合成HDR不是“拍两张图叠一起”那么简单。曝光策略要适配场景,帧对齐要平衡精度和算力,权重映射要平滑自然。每一步都有坑,但踩过之后,你会发现这套管线在车载视觉系统里几乎是不可替代的。