3、HDR合成基础原理:多曝光融合原理、相机响应曲线(CRF)、辐射度图构建
好,咱们进入第三章。这一章是HDR合成的核心地带,说白了就是搞明白「多张照片怎么拼成一张高动态范围图」的底层逻辑。
我刚开始接触HDR时,以为就是把亮图暗图直接叠在一起。结果做出来的图要么发灰,要么有鬼影。后来才明白,这里面的门道深着呢。
3.1 多曝光融合:为什么不是简单叠加?
你想想看,一张照片里,暗部细节和亮部细节是互相矛盾的。曝光时间长了,暗部亮了,但亮部过曝成一片白。曝光时间短了,亮部有细节,暗部又死黑。
多曝光融合,就是取长补短。把不同曝光下「表现最好」的像素挑出来,合成一张图。
但问题来了——怎么定义「表现最好」?
我个人习惯用三个权重:
- 对比度权重:边缘清晰的像素更可靠
- 饱和度权重:颜色鲜艳的区域保留更多信息
- 曝光度权重:中间调(不黑不白)的像素最稳定
我在项目中遇到过一种情况:用简单的平均融合,结果天空出现奇怪的条纹。后来改用加权融合,问题就解决了。
核心公式(简化版):
最终像素 = Σ(权重 × 像素值) / Σ(权重)
权重 = 对比度权重 × 饱和度权重 × 曝光度权重
嗯,这里要注意:权重不是随便给的。曝光度权重通常用高斯曲线,中间调给最高权重,过暗过亮都压低。
3.2 相机响应曲线(CRF):从像素值到真实亮度
相机拍出来的像素值(0-255),和真实世界的亮度(辐射度)不是线性关系。为什么?因为相机传感器和图像处理芯片会做各种映射。
CRF就是描述这个映射关系的曲线。有了它,我们才能把像素值「反推」回真实亮度。
我曾经踩过一个坑:直接用线性假设去反推,结果合成出来的图颜色完全不对。后来才意识到,不同相机的CRF差异很大。
怎么求解CRF?Debevec在1997年提出了一种方法,核心思路是:
- 拍摄多张不同曝光的照片
- 对同一个场景点,不同曝光下的像素值应该对应同一个辐射度
- 用最小二乘法拟合出CRF曲线
我的经验:拍摄时用三脚架固定相机,光圈固定,只改变快门速度。这样能保证场景的辐射度不变,只改变曝光量。
CRF曲线长什么样?一般来说是S形。暗部被压缩,亮部也被压缩,中间调相对线性。
3.3 辐射度图构建:把像素值还原成真实亮度
有了CRF,我们就可以构建辐射度图了。辐射度图就是一张浮点精度的图像,每个像素存储的是真实世界的亮度值(单位:W/m²/sr)。
构建步骤:
- 对每张输入图像,用CRF反函数把像素值映射成辐射度
- 对同一个场景点,取多张图像中「可靠」的辐射度值
- 加权平均得到最终的辐射度值
这里有个细节:不同曝光下,同一个场景点的信噪比不同。曝光不足的区域噪声大,曝光过度的区域饱和失真。所以加权时,要优先选择中间曝光的像素。
避坑指南:我曾经用3张曝光(-2EV, 0EV, +2EV)合成,结果发现+2EV的暗部噪声特别大。后来改成5张曝光(-4EV, -2EV, 0EV, +2EV, +4EV),噪声问题明显改善。
3.4 知识体系总览
下面这张图,是我自己总结的HDR合成基础原理框架。你看一眼,心里就有谱了。
3.5 代码实战:用Python实现CRF求解
光说不练假把式。下面是我常用的CRF求解代码片段,你拿去就能跑。
import numpy as np
from scipy.optimize import least_squares
def solve_crf(images, exposures):
"""
求解相机响应曲线
images: 多张曝光图像,形状 (N, H, W)
exposures: 对应的曝光时间列表
"""
N, H, W = images.shape
# 采样像素点
sample_points = np.random.choice(H*W, 256, replace=False)
def objective(params):
g = params[:256] # CRF曲线
lnE = params[256:] # 辐射度对数
residuals = []
for i in range(N):
for j, idx in enumerate(sample_points):
z = images[i].flat[idx]
if z > 0 and z < 255: # 忽略饱和像素
residuals.append(g[z] - lnE[j] - np.log(exposures[i]))
return residuals
# 初始值
x0 = np.zeros(256 + len(sample_points))
result = least_squares(objective, x0)
return result.x[:256] # 返回CRF曲线
使用建议:实际项目中,我会先做图像对齐,再采样。采样点要均匀分布在图像各个区域,避免集中在天空或阴影里。
3.6 辐射度图构建的完整流程
有了CRF,构建辐射度图就水到渠成了。流程如下:
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1 | 读取多曝光图像 | 确保图像已对齐,格式为16位或32位浮点 |
| 2 | 应用CRF反函数 | 将像素值映射为辐射度值 |
| 3 | 计算权重 | 基于像素值位置(中间调权重高) |
| 4 | 加权融合 | 对每个像素位置,取多张图的加权平均 |
| 5 | 输出HDR图 | 保存为.exr或.hdr格式 |
嗯,这里要注意:权重计算时,我习惯用三角权重函数。像素值在128附近给最高权重,0和255给0权重。这样能有效抑制噪声和饱和问题。
我曾经踩过的坑:有一次用8位图像做HDR合成,结果辐射度图出现明显的量化噪声。后来改用RAW格式(12位或14位),效果好了不止一个档次。
好了,这一章的内容就到这里。多曝光融合、CRF、辐射度图构建,这三者是HDR合成的基石。你把这些搞明白了,后面的色调映射、局部自适应处理,学起来就轻松多了。