1867 字
9 分钟
多张曝光怎样合成 HDR?从辐照度恢复到运动去鬼影

站在室内拍向明亮窗外,短曝光能看清云层,室内却接近黑色;长曝光照亮房间,窗户又变成没有纹理的白块。多曝光 HDR 的想法很直接:让不同曝光各自负责最可靠的亮度区间,再把它们恢复到同一个场景尺度。

真正的 HDR Merge 不是把三张 JPEG 调透明度叠起来。曝光时间不同、相机响应可能非线性,暗部噪声和高光饱和的可信度不同;画面里的人和树叶还会在三次拍摄之间移动。

三张曝光不是三个不同场景#

在线性传感器的简化模型中:

zi=clip(Eti)z_i=\operatorname{clip}(E\,t_i)

EE 是场景辐照度,tit_i 是第 ii 张曝光时间,ziz_i 是 RAW 值。只要没有饱和:

E^i=ziti\hat E_i=\frac{z_i}{t_i}

三张曝光,各自看见不同区域

短曝光保护高光,长曝光抬起阴影。没有任何一张同时拥有全部可靠信息。

0.25×
1×
4×
场景跨度267:1
短曝高光1.00
长曝阴影0.12

值到达 1.0 后只是白色,无法知道原本是 1.2 还是 12。HDR 合并只能使用未饱和曝光提供的比例。

短曝光中的阴影可能只有几个有效 DN,read noise 占比很高;长曝光中的高光达到 white level 后只剩一个裁剪值。HDR 的信息增益来自不同曝光的可靠区间互补,而不是从已经饱和的数据中恢复纹理。

合并前先回到共同坐标系#

RAW 输入至少要处理 BLC、white level 和曝光比例。如果输入是 JPEG 或其他非线性编码,还要知道相机响应函数 gg

z=g(Et)z=g(E t)

先应用逆响应 g1g^{-1},才能得到与曝光成比例的量。直接用 sRGB 数字除快门时间不成立,因为 sRGB 不是线性光。

光圈和 ISO 也会改变总曝光。更一般的曝光尺度应来自完整元数据与标定,而不是只看快门分母。模拟增益变化还会改变读出噪声和饱和容量,不能简单假设所有帧噪声相同。

为什么每个样本需要一个权重#

一个常见合并式是:

E^=iw(zi)zi/tiiw(zi)\hat E=\frac{\sum_i w(z_i)\,z_i/t_i}{\sum_i w(z_i)}

靠近黑位的样本信噪比较差,靠近白位的样本可能饱和,中间值通常更可靠。

为什么最黑和最白的样本权重低

靠近黑位时噪声占比高,靠近白位时容易饱和。权重曲线让中间曝光承担更多估计责任。

编码值 z0.82
当前权重0.360
角色有效样本
权重函数

权重不是曝光融合的审美蒙版,而是对每个观测可靠性的建模。响应曲线和噪声模型不同,最佳权重也会不同。

三角权重容易解释,Gaussian 更平滑,门限权重则只排除两端。它们都只是启发式基线。若知道传感器噪声方差 σi2\sigma_i^2,可以让权重与逆方差相关:

wi1σi2w_i\propto\frac{1}{\sigma_i^2}

这样“可靠”不再只由编码位置决定,而与 shot/read noise、模拟增益和曝光模式联系起来。

一颗像素怎样合并#

把三个编码值还原成一个场景辐照度

先除以曝光时间得到各自估计,再按可靠性加权。饱和曝光会自动失去权重。

0.25×0.450w=0.90
1×1.000w=0.00
4×1.000w=0.00
真实辐照度1.800
合并估计1.800
误差0.000
权重

示例假定响应线性且没有噪声。JPEG 合并还必须先逆响应曲线;RAW 则要先正确完成 BLC 和曝光归一化。

在组件中提高场景辐照度。长曝光会先饱和,随后中曝光也失去权重,最终只剩短曝光提供高光比例。若所有曝光都饱和,算法只能知道场景至少超过某个下限,无法恢复准确辐照度。

教学版 NumPy 实现:

import numpy as np
def triangular_weight(value):
return np.maximum(0.0, 1.0 - 2.0 * np.abs(value - 0.5))
def merge_linear_raw(frames, exposure_times):
"""frames 已完成 BLC,并按各自 white level 归一化。"""
frames = np.asarray(frames, dtype=np.float32)
times = np.asarray(exposure_times, dtype=np.float32)[:, None, None]
weights = triangular_weight(frames)
estimates = frames / times
numerator = np.sum(weights * estimates, axis=0)
denominator = np.sum(weights, axis=0)
return numerator / np.maximum(denominator, 1e-8)

生产实现还要为“所有权重为零”的像素选择最接近未饱和的曝光,并处理坏点、黑位漂移、噪声模型和颜色通道差异。

对齐:同一个场景位置必须落在一起#

手持拍摄时,相机在多帧之间平移、旋转,广角镜头和视差还会造成非全局形变。若不先对齐,静态边缘也会出现重影。

全局对齐可以估计单应或仿射变换;更复杂场景需要局部光流、多尺度特征和鲁棒匹配。对齐算法最好在不过曝、纹理足够且噪声可控的表示上工作。直接在亮度差异巨大的编码图上做普通 SSD,容易把曝光变化误认成运动。

运动物体与鬼影#

移动物体为什么变成半透明鬼影

不同曝光不是同一时刻。静态背景可以逐像素合并,运动区域必须对齐、选参考帧或拒绝不一致样本。

帧间位移24px
物体副本3
策略直接平均
运动区处理

Deghost 不是简单锐化。它需要区分相机抖动、物体运动、亮度变化与饱和差异,并可能牺牲运动区的动态范围。

背景对齐后,行人、树叶和车辆仍位于不同位置。直接合并会得到多个半透明副本。常见策略包括:

  • 选择一张参考帧,运动区域只用它;
  • 检测跨帧不一致,拒绝异常观测;
  • 使用光流把物体局部对齐;
  • 分割运动对象,在可信帧之间选择或重建。

参考帧策略稳定,却会让运动区只保留单帧动态范围。去鬼影本质上是在动态范围、噪声和时间一致性之间取舍。

曝光融合与辐照度 HDR 的区别#

曝光融合可以直接根据对比度、饱和度和“曝光良好程度”混合显示图,不一定恢复物理辐照度。它容易得到漂亮结果,但输出通常已是显示映射后的 LDR。

辐照度 HDR 先恢复场景线性值,再交给 Tone Mapping 映射到显示范围。两者都使用多曝光,却回答不同问题。不要把融合输出再当作线性 HDR 交给基于物理亮度的算法。

HDR Merge 与 Tone Mapping 的边界#

HDR Merge 解决“不同曝光怎样组成更宽的场景数据”;Tone Mapping 解决“宽范围数据怎样进入有限显示范围”。正确的概念顺序是:

多曝光 RAW → 线性化/对齐/合并 → HDR 场景值 → Tone Mapping → Gamma/OETF

如果相机使用单帧双转换增益、交错曝光或分区读出,合并发生得更靠近传感器,但可靠性、饱和和运动问题依然存在。

颜色为什么也会出问题#

不同通道的 white level、黑位和噪声可能不同。若某帧红通道饱和而绿蓝未饱和,逐通道独立权重会改变 RGB 比例。高光区域应保留通道饱和 mask,并结合其他曝光恢复颜色。

白平衡增益会改变通道噪声;CCM 是通道混合,通常应在各帧恢复到一致相机线性空间后再统一应用。若每帧使用不同自动参数,合并前必须确认它们的含义一致。

最容易踩的坑#

  1. 把 Gamma 编码 JPEG 当线性值相除;
  2. 只使用快门时间,忽略光圈、模拟增益和模式变化;
  3. 把饱和样本当作准确白色参与平均;
  4. 对齐只看全局变换,忽略近景视差;
  5. 去鬼影后不记录哪些区域退回单帧;
  6. 每帧分别做不同 Tone Mapping 后再合并;
  7. 忽略滚动快门和灯光闪烁造成的帧内亮度差;
  8. 只在静态三脚架风景上测试。

验证清单#

  • 已知辐照度和曝光时间的合成序列能否恢复比例;
  • 单帧饱和、全部饱和和接近黑位的边界情况;
  • 不同噪声强度下权重是否合理;
  • 平移、旋转、视差和滚动快门;
  • 行人、树叶、流水和屏幕刷新等运动;
  • RGB 单通道饱和与中性高光;
  • 关闭对齐、去鬼影、噪声权重后的差异是否可诊断;
  • 合并结果是否仍为线性 HDR,而不是被隐式裁剪到 [0,1][0,1]

HDR Merge 的核心不是“多张更亮”,而是把每个曝光还原到同一场景尺度,并在每个位置选择可信证据。曝光归一化回答比例,权重回答可靠性,对齐回答空间对应,去鬼影回答时间不一致;四件事缺一,宽动态范围就可能换来新的伪影。

多张曝光怎样合成 HDR?从辐照度恢复到运动去鬼影
https://cloudsir.top/posts/how-hdr-merge-combines-exposures/
作者
CloudSir
发布于
2026-08-25
许可协议
CC BY-NC-SA 4.0
锐化为什么会产生白边?从 Unsharp Mask 到 Halo
超出色域的颜色怎么办?从逐通道裁剪到色度压缩