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


前言

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

在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐