文章目录
前言
最近在做HDR图像的课题,这里对HDR图像处理里面的关键技术进行记录和总结
一、前提opencv知识
1.1、opencv打开一般照片并且显示
import cv2
# 读取照片
img = cv2.imread('photo.jpg')
# 显示照片
cv2.imshow('Photo', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
1.2、opencv打开HDR图像(这里先不讲如何imshow)
import cv2
# 读取照片
img = cv2.imread('photo.hdr',cv2.IMREAD_ANYDEPTH)
cv2.IMREAD_ANYDEPTH参数告诉OpenCV要读取所有像素值,包括高动态范围(HDR)像素值。
除此之外,还可这样读取HDR图像:
import cv2
# 读取照片
img = cv2.imread('photo.hdr',cv2.IMREAD_ANYDEPTH | cv2.IMREAD_COLOR)
以上两句话在读取HDR图像时是等价的(来自chatgpt)
一般情况下,读取了HDR图像后,需要对其进行归一化等操作(也就是映射到LDR域)。如果不进行归一化等操作,直接使用imshow可能会报错,或者图像一片白,因为HDR图像的单个像素点的亮度值非常高,早就超出了0-255的这个范围,比如如下代码:
import cv2
# 读取照片
img = cv2.imread('gt.hdr',cv2.IMREAD_UNCHANGED)
# 显示照片
cv2.imshow('Photo', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
这时候展示出的图像一片白
如果不用opencv,用一些自带色调映射的图像查看编辑器也是可以的,比如我自己用的是2345看图王,可以打开HDR,EXR等文件
二、经典的色调映射技术以及opencv代码实现
这些色调映射方法的提出时间是不同的,大致如下:
Reinhard:2001年
Drago:2003年
Durand:2005年
Mantiuk:2008年
这些方法的提出都是为了解决数字图像处理中的色调映射问题,但是它们的具体实现方式和效果略有不同。在实际应用中,可以根据需要选择适合的色调映射方法。
2.1、Reinhard
1)Reinhard
import cv2
# 读取HDR图像
src_img = cv2.imread('gt.hdr', cv2.IMREAD_ANYDEPTH | cv2.IMREAD_COLOR)
# 创建Reinhard色调映射对象
Reinhard = cv2.createTonemapReinhard()
# 对HDR图像进行色调映射
dst_img = Reinhard.process(src_img)
# 保存输出图像
cv2.imwrite('Reinhard.jpg', dst_img * 255)
img=cv2.imread('Reinhard.jpg')
cv2.imshow('Reinhard', img)
# 等待按键按下
cv2.waitKey(0)
# 释放窗口
cv2.destroyAllWindows()
2.2、Drago
2)Drago
import cv2
# 读取HDR图像
src_img = cv2.imread('gt.hdr', cv2.IMREAD_ANYDEPTH | cv2.IMREAD_COLOR)
# 创建Reinhard色调映射对象
Drago = cv2.createTonemapDrago()
# 对HDR图像进行色调映射
dst_img = Drago.process(src_img)
# 保存输出图像
cv2.imwrite('Drago.jpg', dst_img * 255)
img=cv2.imread('Drago.jpg')
cv2.imshow('Drago', img)
# 等待按键按下
cv2.waitKey(0)
# 释放窗口
cv2.destroyAllWindows()
2.3、Durand
3)Durand
import cv2
# 读取HDR图像
src_img = cv2.imread('gt.hdr', cv2.IMREAD_ANYDEPTH | cv2.IMREAD_COLOR)
# 创建Reinhard色调映射对象
Durand = cv2.createTonemapDurand()
# 对HDR图像进行色调映射
dst_img = Durand.process(src_img)
# 保存输出图像
cv2.imwrite('Durand.jpg', dst_img * 255)
img=cv2.imread('Durand.jpg')
cv2.imshow('Durand', img)
# 等待按键按下
cv2.waitKey(0)
# 释放窗口
cv2.destroyAllWindows()
2.4、Mantiuk
4)Mantiuk
import cv2
# 读取HDR图像
src_img = cv2.imread('gt.hdr', cv2.IMREAD_ANYDEPTH | cv2.IMREAD_COLOR)
# 创建Reinhard色调映射对象
Mantiuk = cv2.createTonemapMantiuk()
# 对HDR图像进行色调映射
dst_img = Mantiuk.process(src_img)
print(dst_img)
# 保存输出图像
cv2.imwrite('Mantiuk.jpg', dst_img * 255)
img=cv2.imread('Mantiuk.jpg')
cv2.imshow('Mantiuk', img)
# 等待按键按下
cv2.waitKey(0)
# 释放窗口
cv2.destroyAllWindows()
值得注意的是,使用cv2.createToneXXXX函数时,返回的是一个归一化到0-1的numpy数组,所以后续的展示imshow中需要乘上255。当然如果不乘上255也是可以显示的:虽然像素值被归一化到[0,1]的浮点数范围内,但在显示图像时,imshow函数会自动将像素值转换为[0,255]的整数,然后再进行显示。这是因为在显示图像时,需要将像素值映射到显示设备的亮度范围内,而通常情况下,显示设备的亮度范围是[0,255]的整数。因此,即使像素值已经归一化到[0,1]的浮点数范围内,imshow函数仍然可以正常显示图像。
2.5、对比
我们来看一下三种算法对比效果(Durand报错了 这里不展示):
从左到右依次为Reinhard Drago Mantiuk
代码如下:
import cv2
import numpy as np
# 加载HDR图像
src_img = cv2.imread('gt.hdr', cv2.IMREAD_ANYDEPTH | cv2.IMREAD_COLOR)
xianxing = cv2.normalize(src_img, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8UC3)
# 创建色调映射对象
Reinhard = cv2.createTonemapReinhard()
Drago = cv2.createTonemapDrago()
Mantiuk = cv2.createTonemapMantiuk()
# 对HDR图像进行色调映射
Reinhard_img = Reinhard.process(src_img)
Drago_img = Drago.process(src_img)
Mantiuk_img = Mantiuk.process(src_img)
# 将四幅图像拼接为2×2的矩阵
result = np.hstack((Reinhard_img, Drago_img, Mantiuk_img))
# 显示结果
cv2.imshow('Result', result)
cv2.waitKey()
cv2.destroyAllWindows()
2.6、线性映射
我们再来看看线性映射的结果(强行弄成0-255):
可以看出效果非常不好
代码如下,主要靠的是这句话:ldr_image = cv2.normalize(hdr_image, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8UC3)
import cv2
# 读取HDR图像
hdr_image = cv2.imread('gt.hdr', cv2.IMREAD_ANYDEPTH | cv2.IMREAD_COLOR )
print(hdr_image.shape)
# 转换图像格式
ldr_image = cv2.normalize(hdr_image, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8UC3)
# 显示图像
cv2.imshow('HDR Image', ldr_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
三.u律压缩(u律色调映射)
在目前很多的HDR图像生成的国内硕士论文里,大多提到了这种方法:
μ律本身是用于压缩音频信号动态范围的算法,但是它也可以用于压缩 HDR 图像的动态范围。μ律色调映射公式如下:
其中,
H 表示被归一化到 [0,1] 范围的 HDR 图像,T 表示色调映射后的图像。
这里u的取值论文里都不太一样 ,有些是500,5000
以下是chatgpt生成的代码:
import cv2
import numpy as np
def mu_law_tonemap(hdr_img, mu=5000):
hdr_img = np.float32(hdr_img)
L = np.log(1 + mu * hdr_img) / np.log(1 + mu)
L = np.uint8(L * 255)
return L
# 读取HDR图像
hdr_img = cv2.imread('gt.hdr', cv2.IMREAD_ANYDEPTH|cv2.IMREAD_COLOR)
# 进行μ律色调映射
tonemapped_img = mu_law_tonemap(hdr_img)
# 显示结果
cv2.imshow('HDR Image', hdr_img)
cv2.imshow('Tonemapped Image', tonemapped_img)
cv2.waitKey(0)
cv2.destroyAllWindows()