HDR图像处理HDR图像的色调映射 | python+opencv代码实现总结

Posted 今天一定要洛必达

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDR图像处理HDR图像的色调映射 | python+opencv代码实现总结相关的知识,希望对你有一定的参考价值。

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录


前言

最近在做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()

摄影之HDR

摄影之HDR

高动态范围图像(High-Dynamic Range,简称HDR),相比普通的图像,可以提供更多的动态范围和图像细节,根据不同的曝光时间的LDR(Low-Dynamic Range)图像,利用每个曝光时间相对应最佳细节的LDR图像来合成最终HDR图像,能够更好的反映出真实环境中的视觉效果。

上面是百度百科的解释,其实HDR就是一种把不同曝光的图片中的细节合成的技术。最直观的理解方式请看下图:

下面的三张图片是相机在同一位置不同曝光的情况下拍摄得到的三张图片。

可以很明显的看到,第一张很暗,近处的物体基本上看不到具体细节,而第三张很亮,远处的山都看不到了。

而在hdr制作软件中,下面三张的合成效果就会将三张相片的细节整合,得到最上面的那张HDR图片了。

制作流程:

拍摄:在拍摄时,需要固定相机,拍摄间隔相同曝光的多张图片。用来制作HDR图片。

制作:直接导入HDR合成软件即可合成。

合成软件:

  常见的软件包括PS本身Camera Raw的HDR功能、Photomatix Pro、PhotoEngine和NIK HDR Efex Pro软件。

  我使用的是第三个:NIK HDR Efex Pro。

  截图:

  

 

以上是关于HDR图像处理HDR图像的色调映射 | python+opencv代码实现总结的主要内容,如果未能解决你的问题,请参考以下文章

Retinex图像增强和暗通道去雾的关系及其在hdr色调恢复上的应用

Tone Mapping Correction

Tone Mapping Correction

Tone Mapping Correction

Tone Mapping Correction

HDR 高动态范围图像