本章将学习如下内容:
- 如何从曝光序列生成并显示HDR图像
- 运用曝光融合技术合并曝光序列
理论基础
高动态范围成像(HDRI或HDR)是一种能够再现比标准数字成像技术更广亮度范围的摄影技术。 人眼虽能适应各种光照条件,但多数成像设备每通道仅用8位存储,导致只能记录256级亮度。 拍摄现实场景时,单次曝光无法同时保留亮部与暗部细节。 HDR成像采用每通道超过8位(通常为32位浮点数)的图像格式,从而获得更宽的动态范围。
获取HDR图像有多种方法,最常用的是对同一场景拍摄不同曝光值的照片进行合成。 合成时需要相机响应函数(可通过算法估算)。HDR图像合成后需通过色调映射转换为8位图像才能在普通显示器上显示。 若拍摄过程中存在物体或相机移动,还需进行图像配准对齐。
本教程将演示两种HDR生成算法(Debevec、Robertson),以及不需要曝光时间数据的曝光融合法(Mertens)。 我们还将估算相机响应函数(这对许多计算机视觉算法至关重要)。 HDR流程的每个步骤均可选用不同算法和参数,具体可参考官方手册。
曝光序列HDR示例
本教程使用包含4张曝光时间分别为15秒、2.5秒、1/4秒和1/30秒的场景图像序列(图像可从维基百科下载)。
将曝光图像加载到列表中
第一阶段需要将所有图像载入列表。对于常规HDR算法,还需注意图像数据要求,单通道或三通道的8位格式(np.uint8
);
曝光时间数据,需转换为float32类型并以秒为单位。
import cv2 as cv
import numpy as np
# Loading exposure images into a list
img_fn = ["/data/cvdata/hdr0.jpeg", "/data/cvdata/hdr1.jpeg", "/data/cvdata/hdr2.jpeg", "/data/cvdata/hdr3.jpeg"]
img_list = [cv.imread(fn) for fn in img_fn]
exposure_times = np.array([15.0, 2.5, 0.25, 0.0333], dtype=np.float32)
# Merge exposures to HDR image
merge_debevec = cv.createMergeDebevec()
hdr_debevec = merge_debevec.process(img_list, times=exposure_times.copy())
merge_robertson = cv.createMergeRobertson()
hdr_robertson = merge_robertson.process(img_list, times=exposure_times.copy())
# Tonemap HDR image
tonemap1 = cv.createTonemap(gamma=2.2)
res_debevec = tonemap1.process(hdr_debevec.copy())
[ WARN:0@0.087] global matrix_expressions.cpp:1333 assign OpenCV/MatExpr: processing of multi-channel arrays might be changed in the future: https://github.com/opencv/opencv/issues/16739
res_robertson = tonemap1.process(hdr_robertson.copy())
# Exposure fusion using Mertens
merge_mertens = cv.createMergeMertens()
res_mertens = merge_mertens.process(img_list)
# Convert datatype to 8-bit and save
res_debevec_8bit = np.clip(res_debevec*255, 0, 255).astype('uint8')
res_robertson_8bit = np.clip(res_robertson*255, 0, 255).astype('uint8')
res_mertens_8bit = np.clip(res_mertens*255, 0, 255).astype('uint8')
cv.imwrite("xx_ldr_debevec.jpg", res_debevec_8bit)
cv.imwrite("xx_ldr_robertson.jpg", res_robertson_8bit)
cv.imwrite("xx_fusion_mertens.jpg", res_mertens_8bit)
True
%matplotlib inline
import matplotlib.pyplot as plt
plt.imshow(res_debevec_8bit)
<matplotlib.image.AxesImage at 0x7f103f42f2f0>
# Estimate camera response function (CRF)
cal_debevec = cv.createCalibrateDebevec()
crf_debevec = cal_debevec.process(img_list, times=exposure_times)
hdr_debevec = merge_debevec.process(img_list, times=exposure_times.copy(), response=crf_debevec.copy())
cal_robertson = cv.createCalibrateRobertson()
crf_robertson = cal_robertson.process(img_list, times=exposure_times)
hdr_robertson = merge_robertson.process(img_list, times=exposure_times.copy(), response=crf_robertson.copy())
相机响应函数(CRF)通过每个颜色通道的256维向量来表示。 针对当前曝光序列,获得以下估算结果:
扩展阅读
- Paul E Debevec与Jitendra Malik合著的《从照片中恢复高动态范围辐射图》,收录于ACM SIGGRAPH 2008课程论文集第31页(ACM,2008)@cite DM97
- Mark A Robertson、Sean Borman和Robert L Stevenson合著的《通过多重曝光改善动态范围》,发表于1999年国际图像处理会议论文集第三卷第159-163页(IEEE,1999)@cite RB99
- Tom Mertens、Jan Kautz和Frank Van Reeth合著的《曝光融合技术》,收录于2007年太平洋地区计算机图形与应用会议论文集第382-390页(IEEE,2007)@cite MK07
- 实验图像来源:维基百科-HDR词条
练习
- 尝试所有色调映射算法:
cv::TonemapDrago
、cv::TonemapMantiuk
和cv::TonemapReinhard
。 - 调整 HDR 校准与色调映射方法中的参数组合。