- HDR高动态范围图像处理
- 完成UI界面
一、HDR原理
大多数数码相机和显示器都将彩色图像捕获或显示为24位矩阵。 每个颜色通道有8位,因此每个通道的像素值在0-255范围内。 换句话说,普通的相机或显示器的动态范围是有限的。但通过改变快门速度,我们可以在不同的曝光条件下拍摄多个场景图像,从而合成HDR图像。
HDR——即高动态范围图像(High-Dynamic Range,简称HDR),相比普通的图像,可以提供更多的动态范围和图像细节,根据不同的曝光时间的LDR(Low-Dynamic Range)图像,利用每个曝光时间相对应最佳细节的LDR图像来合成最终HDR图像,能够更好的反映出真实环境中的视觉效果。
简而言之,就是尽可能地同时显示一张图片最亮和最暗的地方。
二、代码实现
(一)从读取图像开始分配曝光时间
# Read images and exposure times
print("Reading images ... ")
#需要小窗口输入数值
times = np.array([1 / 30.0, 0.25, 2.5, 15.0], dtype=np.float32)
filenames = ["img_0.033.jpg", "img_0.25.jpg", "img_2.5.jpg", "img_15.jpg"]
images = []
# for filename in filenames:
# im = cv2.imread(instance.root_path+'/part4/images_hdr/'+filename)
# images.append(im)
im1=cv2.imread(instance.hdr1_original[0][0])
images.append(im1)
im2=cv2.imread(instance.hdr2_original[0][0])
images.append(im2)
im3=cv2.imread(instance.hdr3_original[0][0])
images.append(im3)
im4=cv2.imread(instance.hdr4_original[0][0])
images.append(im4)
-
曝光时间:1/30s
-
曝光时间:0.25s
-
曝光时间:2.5s
-
曝光时间:15s
(二)图片对齐
构成HDR图像时使用的图像未对齐可能会导致严重的伪影。OpenCV提供了一种使用AlignMTB对齐这些图像的简单方法。 该算法将所有图像转换为中值阈值位图(MTB)。 通过将值1分配给比中间亮度更亮的像素来计算图像的MTB,否则为0。
# Align input images
print("Aligning images ... ")
alignMTB = cv2.createAlignMTB()
alignMTB.process(images, images)
(三)估计相机响应函数
由于典型相机的响应与场景亮度不成线性关系,即曝光时间与像素强度并不构成对应的线性关系。 在不估计相机响应函数(CRF)的情况下,我们将无法将图像合并到一个HDR图像中。我们需要估计CRF来使图像强度成为线性的,然后才能合并它们。
通过每个图像的曝光时间,可以从图像估计CRF,找到CRF的问题被设置为一个优化问题,其目标是使由数据项和平滑项组成的目标函数最小化,可以使用奇异值分解(SVD)来解决,奇异值分解是所有线性代数包的一部分。
在OpenCV中使用CalibrateDebevec来完成CRF。
# Obtain Camera Response Function (CRF)
print("Calculating Camera Response Function (CRF) ... ")
calibrateDebevec = cv2.createCalibrateDebevec()
responseDebevec = calibrateDebevec.process(images, times)
(四)图片合成
使用MergeDebevec将曝光图像合并成一个HDR图像
# Merge images into an HDR linear image
print("Merging images into one HDR image ... ")
mergeDebevec = cv2.createMergeDebevec()
hdrDebevec = mergeDebevec.process(images, times, responseDebevec)
# Save HDR image.
cv2.imwrite("hdrDebevec.hdr", hdrDebevec)
print("saved hdrDebevec.hdr ")
(五)色调映射
将高动态范围(HDR)图像转换为8位每通道图像的过程同时保留尽可能多的细节的过程称为色调映射。即在恢复了多个图像的相对亮度信息后,需要将这些信息保存为24位图像。
色调映射没有称之为正确的方法。通常,我们希望在色调映射图像中看到比任何一个曝光图像更多的细节。 有时色调映射的目标是产生逼真的图像,而且往往是产生超现实图像的目标。 在OpenCV中实现的算法倾向于产生现实的,因而较不显著的结果。
以下列出了不同色调映射算法的一些常见参数。
1、gamma:该参数通过应用gamma 校正来压缩动态范围。 当gamma等于1时,不应用修正。 小于1的gamma会使图像变暗,而大于1的gamma会使图像变亮。
2、饱和度:该参数用于增加或减少饱和度。 饱和度高时,色彩更丰富,更浓。 饱和度值接近零,使颜色逐渐消失为灰度。
3、对比度:控制输出图像的对比度(即log(maxPixelValue / minPixelValue))。
以下三种算法有不同的参数设置。
- Durand Tonemap
createTonemapDurand
(
float gamma = 1.0f,
float contrast = 4.0f,
float saturation = 1.0f,
float sigma_space = 2.0f,
float sigma_color = 2.0f
);
- Reinhard Tonemap
createTonemapReinhard
(
float gamma = 1.0f,
float intensity = 0.0f,
float light_adapt = 1.0f,
float color_adapt = 0.0f
)
- Mantiuk Tonemap
createTonemapMantiuk
(
float gamma = 1.0f,
float scale = 0.7f,
float saturation = 1.0f
)
代码如下:
# Tonemap using Drago's method to obtain 24-bit color image
print("Tonemaping using Drago's method ... ")
tonemapDrago = cv2.createTonemapDrago(1.0, 0.7)
ldrDrago = tonemapDrago.process(hdrDebevec)
ldrDrago = 3 * ldrDrago
cv2.imwrite(instance.root_path+'/part4/images_hdr/'+"ldr-Drago.jpg", ldrDrago * 255)
print("saved ldr-Drago.jpg")
# Tonemap using Reinhard's method to obtain 24-bit color image
print("Tonemaping using Reinhard's method ... ")
tonemapReinhard = cv2.createTonemapReinhard(1.5, 0, 0, 0)
ldrReinhard = tonemapReinhard.process(hdrDebevec)
cv2.imwrite(instance.root_path+'/part4/images_hdr/'+"ldr-Reinhard.jpg", ldrReinhard * 255)
print("saved ldr-Reinhard.jpg")
# Tonemap using Mantiuk's method to obtain 24-bit color image
print("Tonemaping using Mantiuk's method ... ")
tonemapMantiuk = cv2.createTonemapMantiuk(2.2, 0.85, 1.2)
ldrMantiuk = tonemapMantiuk.process(hdrDebevec)
ldrMantiuk = 3 * ldrMantiuk
cv2.imwrite(instance.root_path+'/part4/images_hdr/'+"ldr-Mantiuk.jpg", ldrMantiuk * 255)
print("saved ldr-Mantiuk.jpg")
三、完成UI界面
# 原图图片路径——HDR——选择第一张图
instance.hdr1_img=""
instance_button_choose_original_image=QPushButton("选择原图一",instance.widget_hdr)
instance_button_choose_original_image.setGeometry(30,10,100,50)
instance_button_choose_original_image.clicked.connect(instance.open_file_and_change_name_hdr1)
# 用于显示路径名的label
instance.label_hdr1_original = QLabel(instance.widget_hdr)
instance.label_hdr1_original.setGeometry(30,120,500,50)
instance.label_hdr1_original.setText("文件路径1")
# 原图图片路径——HDR——选择第二张图
instance.hdr2_img=""
instance_button_choose_original_image=QPushButton("选择原图二",instance.widget_hdr)
instance_button_choose_original_image.setGeometry(150,10,100,50)
instance_button_choose_original_image.clicked.connect(instance.open_file_and_change_name_hdr2)
# 用于显示路径名的label
instance.label_hdr2_original = QLabel(instance.widget_hdr)
instance.label_hdr2_original.setGeometry(30,180,500,50)
instance.label_hdr2_original.setText("文件路径2")
# 原图图片路径——HDR——选择第三张图
instance.hdr3_img=""
instance_button_choose_original_image=QPushButton("选择原图三",instance.widget_hdr)
instance_button_choose_original_image.setGeometry(270,10,100,50)
instance_button_choose_original_image.clicked.connect(instance.open_file_and_change_name_hdr3)
# 用于显示路径名的label
instance.label_hdr3_original = QLabel(instance.widget_hdr)
instance.label_hdr3_original.setGeometry(30,240,500,50)
instance.label_hdr3_original.setText("文件路径3")
# 原图图片路径——HDR——选择第四张图
instance.hdr4_img=""
instance_button_choose_original_image=QPushButton("选择原图四",instance.widget_hdr)
instance_button_choose_original_image.setGeometry(390,10,100,50)
instance_button_choose_original_image.clicked.connect(instance.open_file_and_change_name_hdr4)
# 用于显示路径名的label
instance.label_hdr4_original = QLabel(instance.widget_hdr)
instance.label_hdr4_original.setGeometry(30,300,500,50)
instance.label_hdr4_original.setText("文件路径4")