https://blog.csdn.net/wc781708249/article/details/78490976

什么是高动态范围(HDR)成像?

大多数数码相机和显示器都将彩色图像捕获或显示为24位矩阵。 每个颜色通道有8位,因此每个通道的像素值在0-255范围内。 换句话说,普通的相机或显示器的动态范围是有限的。

但是,我们周围的世界有一个非常大的动态范围。 当灯光关闭时,它可以在车库内变黑,如果你直接看着太阳,它会变得非常亮。 即使没有考虑到这些极端情况,在日常情况下,8位勉强可以捕捉现场。 因此,相机会尝试估计光照并自动设置曝光,以使图像最有趣的方面具有良好的动态范围,而太暗和太亮的部分分别被裁剪为0和255。

iPhone如何拍摄HDR图像? 它实际上需要三个不同的曝光3张图像。 图像连续拍摄,三个镜头之间几乎没有移动。 三幅图像然后被组合以产生HDR图像。 我们将在下一节看到细节。

将在不同曝光设置下获取的相同场景的不同图像组合的过程称为高动态范围(HDR)成像。

高动态范围(HDR)成像如何工作?

在本节中,我们将通过使用OpenCV创建HDR图像的步骤。

要轻松学习本教程,请点击此处下载C ++和Python代码和图像。 如果您有兴趣了解更多关于人工智能,计算机视觉和机器学习的信息,请订阅我们的通讯。

第1步:用不同的曝光拍摄多张图像

当我们使用相机拍照时,每个通道只有8位来表示场景的动态范围(亮度范围)。 但是,通过改变快门速度,我们可以在不同的曝光条件下拍摄多个场景图像。 大多数单反相机有一个称为自动包围曝光(AEB)的功能,使我们可以在一次按下一个按钮的情况下以不同的曝光拍摄多张照片。 如果你正在使用iPhone,你可以使用这个AutoBracket HDR应用程序,如果你是一个Android用户,你可以尝试一个更好的相机应用程序。

在相机上使用自动包围曝光或在手机上使用自动包围应用程序,我们可以一个接一个地快速拍摄多张照片,所以场景不会改变。 当我们在iPhone中使用HDR模式时,需要三张照片。
1、曝光不足的图像:此图像比正确曝光的图像更暗。 目标是捕捉非常明亮的图像部分。
2、曝光正确的图像:这是相机根据估计的照度拍摄的常规图像。
3、曝光过度的图像:此图像比正确曝光的图像亮。 目标是拍摄非常黑暗的图像部分。

但是,如果场景的动态范围很大,我们可以拍摄三张以上的图片来组成HDR图像。 在本教程中,我们将使用曝光时间为1/30,0.25,2.5和15秒的4张图像。 缩略图如下所示。

关于单反相机或手机使用的曝光时间和其他设置的信息通常存储在JPEG文件的EXIF元数据中。 查看此链接可查看Windows和Mac中存储在JPEG文件中的EXIF元数据。 或者,您可以使用我最喜欢的名为EXIFTOOL的EXIF命令行工具。

我们先从读取图像开始分配曝光时间


C++

void readImagesAndTimes(vector<Mat> &images, vector<float> &times)
{int numImages = 4;// List of exposure timesstatic const float timesArray[] = {1/30.0f,0.25,2.5,15.0};//将timesArray里的值全部赋给timestimes.assign(timesArray, timesArray + numImages);// List of image filenamesstatic const char* filenames[] = {"img_0.033.jpg", "img_0.25.jpg", "img_2.5.jpg", "img_15.jpg"};for(int i=0; i < numImages; i++){Mat im = imread(filenames[i]);//默认读取BGR格式images.push_back(im);//全部存储在images}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

Python

def readImagesAndTimes():# List of exposure timestimes = np.array([ 1/30.0, 0.25, 2.5, 15.0 ], dtype=np.float32)# List of image filenamesfilenames = ["img_0.033.jpg", "img_0.25.jpg", "img_2.5.jpg", "img_15.jpg"]images = []for filename in filenames:im = cv2.imread(filename)images.append(im)return images, times
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
第2步:对齐图像

构成HDR图像时使用的图像未对齐可能会导致严重的伪影。 在下图中,左侧的图像是使用未对齐的图像组成的HDR图像,而右侧的图像是使用对齐的图像的图像。 通过放大图像的一部分(使用红色圆圈显示),我们会在左侧图像中看到严重的鬼影伪影。

自然,在拍摄照片制作HDR图像时,专业摄影师将相机安装在三脚架上。 他们还使用称为镜像锁定功能来减少额外的振动。 即使如此,图像可能不完美对齐,因为没有办法保证无振动的环境。 当使用手持相机或手机拍摄图像时,对齐问题变得更糟。

幸运的是,OpenCV提供了一种使用AlignMTB对齐这些图像的简单方法。 该算法将所有图像转换为中值阈值位图(MTB)。 通过将值1分配给比中间亮度更亮的像素来计算图像的MTB,否则为0。 MTB对曝光时间不变。 因此,MTB可以调整,而不需要我们指定曝光时间。

基于MTB的对齐使用以下代码行来执行。

C++

// Align input images
Ptr<AlignMTB> alignMTB = createAlignMTB();
alignMTB->process(images, images);
  • 1
  • 2
  • 3

Python

# Align input images
alignMTB = cv2.createAlignMTB()
alignMTB.process(images, images)
  • 1
  • 2
  • 3
步骤3:恢复相机响应功能

典型相机的响应与场景亮度不成线性关系。 这意味着什么? 假设有两个物体由相机拍摄,其中一个物体是现实世界中其他物体的两倍。 当您测量照片中两个物体的像素强度时,较亮物体的像素值将不会是较暗物体的两倍。 在不估计相机响应函数(CRF)的情况下,我们将无法将图像合并到一个HDR图像中。

将多个曝光图像合并为HDR图像意味着什么?

在图像的某个位置(x,y)只考虑一个像素。 如果CRF是线性的,则像素值将直接与曝光时间成比例,除非像素在特定图像中太暗(即接近0)或太亮(即接近255)。 我们可以过滤出这些不好的像素(太暗或太亮),并通过将像素值除以曝光时间来估计像素的亮度,然后在像素不差的所有图像上平均亮度值(太暗或 太亮了 )。 我们可以对所有像素进行这样的处理,并通过对“好”像素进行平均来获得所有像素的单个图像。

但CRF不是线性的,我们需要通过首先估计CRF来使图像强度成为线性的,然后才能合并/平均它们。

好消息是,如果我们知道每个图像的曝光时间,则可以从图像估计CRF。 与计算机视觉中的许多问题一样,找到CRF的问题被设置为一个优化问题,其目标是使由数据项和平滑项组成的目标函数最小化。 这些问题通常会减少到线性最小二乘问题,这些问题可以使用奇异值分解(SVD)来解决,奇异值分解是所有线性代数包的一部分。 CRF恢复算法的细节在题为“从照片恢复高动态范围发光图”的论文中。

使用CalibrateDebevec或CalibrateRobertson在OpenCV中使用两行代码来完成CRF。 在本教程中,我们将使用CalibrateDebevec

C++

// Obtain Camera Response Function (CRF)
Mat responseDebevec;
Ptr<CalibrateDebevec> calibrateDebevec = createCalibrateDebevec();
calibrateDebevec->process(images, responseDebevec, times);
  • 1
  • 2
  • 3
  • 4

Python

# Obtain Camera Response Function (CRF)
calibrateDebevec = cv2.createCalibrateDebevec()
responseDebevec = calibrateDebevec.process(images, times)
  • 1
  • 2
  • 3

第4步:合并图像

一旦CRF被估计,我们可以使用MergeDebevec将曝光图像合并成一个HDR图像。 C ++和Python代码如下所示。

C++

// Merge images into an HDR linear image
Mat hdrDebevec;
Ptr<MergeDebevec> mergeDebevec = createMergeDebevec();
mergeDebevec->process(images, hdrDebevec, times, responseDebevec);
// Save HDR image.
imwrite("hdrDebevec.hdr", hdrDebevec);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Python

# Merge images into an HDR linear image
mergeDebevec = cv2.createMergeDebevec()
hdrDebevec = mergeDebevec.process(images, times, responseDebevec)
# Save HDR image.
cv2.imwrite("hdrDebevec.hdr", hdrDebevec)
  • 1
  • 2
  • 3
  • 4
  • 5

上面保存的HDR图像可以在Photoshop中加载并进行色调映射。 一个例子如下所示。

第5步:色调映射

现在我们已经将我们的曝光图像合并到一个HDR图像中。 你能猜出这个图像的最小和最大像素值吗? 对于黑色条件,最小值显然为0。 什么是理论最大值? 无穷! 在实践中,不同情况下的最大值是不同的。 如果场景包含非常明亮的光源,我们将看到一个非常大的最大值。

尽管我们已经使用多个图像恢复了相对亮度信息,但是我们现在面临将这些信息保存为24位图像用于显示的挑战。

将高动态范围(HDR)图像转换为8位每通道图像的过程同时保留尽可能多的细节称为色调映射。

有几种色调映射算法。 OpenCV实现了其中的四个。 要记住的是,没有正确的方法来做色调映射。 通常,我们希望在色调映射图像中看到比任何一个曝光图像更多的细节。 有时色调映射的目标是产生逼真的图像,而且往往是产生超现实图像的目标。 在OpenCV中实现的算法倾向于产生现实的,因此较不显着的结果。

我们来看看各种选项。 以下列出了不同色调映射算法的一些常见参数。
1、gamma:该参数通过应用gamma 校正来压缩动态范围。 当gamma等于1时,不应用修正。 小于1的gamma会使图像变暗,而大于1的gamma会使图像变亮。
2、饱和度:该参数用于增加或减少饱和度。 饱和度高时,色彩更丰富,更浓。 饱和度值接近零,使颜色逐渐消失为灰度。
3、对比度:控制输出图像的对比度(即log(maxPixelValue / minPixelValue))。

让我们来探索OpenCV中可用的四种色调映射算法。


Drago Tonemap

Drago Tonemap的参数如下所示

createTonemapDrago
(
float   gamma = 1.0f,
float   saturation = 1.0f,
float   bias = 0.85f
)   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这里,bias 是[0,1]范围内偏差函数的值。 从0.7到0.9的值通常会给出最好的结果。 默认值是0.85。 有关更多技术细节,请参阅本文。

C ++和Python代码如下所示。 参数是通过试验和错误获得的。 最后的结果乘以3只是因为它给出了最令人满意的结果。

C++

// Tonemap using Drago's method to obtain 24-bit color image
Mat ldrDrago;
Ptr<TonemapDrago> tonemapDrago = createTonemapDrago(1.0, 0.7);
tonemapDrago->process(hdrDebevec, ldrDrago);
ldrDrago = 3 * ldrDrago;
imwrite("ldr-Drago.jpg", ldrDrago * 255);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Python

# Tonemap using Drago's method to obtain 24-bit color image
tonemapDrago = cv2.createTonemapDrago(1.0, 0.7)
ldrDrago = tonemapDrago.process(hdrDebevec)
ldrDrago = 3 * ldrDrago
cv2.imwrite("ldr-Drago.jpg", ldrDrago * 255)
  • 1
  • 2
  • 3
  • 4
  • 5


HDR tone mapping using Drago’s algorithm


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
); 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

该算法基于将图像分解为基础层和细节层。 使用称为双边滤波器的边缘保留滤波器来获得基本层。 sigma_space和sigma_color是双边滤波器的参数,分别控制空间域和彩色域中的平滑量。

有关更多详细信息,请查看本文。

C++

// Tonemap using Durand's method obtain 24-bit color image
Mat ldrDurand;
Ptr<TonemapDurand> tonemapDurand = createTonemapDurand(1.5,4,1.0,1,1);
tonemapDurand->process(hdrDebevec, ldrDurand);
ldrDurand = 3 * ldrDurand;
imwrite("ldr-Durand.jpg", ldrDurand * 255);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Python

# Tonemap using Durand's method obtain 24-bit color imagetonemapDurand = cv2.createTonemapDurand(1.5,4,1.0,1,1)ldrDurand = tonemapDurand.process(hdrDebevec)ldrDurand = 3 * ldrDurandcv2.imwrite("ldr-Durand.jpg", ldrDurand * 255)
  • 1
  • 2
  • 3
  • 4
  • 5

Reinhard Tonemap

createTonemapReinhard
(
float   gamma = 1.0f,
float   intensity = 0.0f,
float   light_adapt = 1.0f,
float   color_adapt = 0.0f
)   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

参数强度应在[-8,8]范围内。 更高的强度值会产生更明亮的结果。 light_adapt控制灯光适应并处于[0,1]范围内。 值1表示仅基于像素值的自适应,而值0表示全局自适应。 中间值可以用于两者的加权组合。 参数color_adapt控制色适应,并在[0,1]范围内。 如果值被设置为1,则通道被独立处理,如果该值被设置为0,则每个通道的适应级别相同。中间值可以用于两者的加权组合。

C++

// Tonemap using Reinhard's method to obtain 24-bit color image
Mat ldrReinhard;
Ptr<TonemapReinhard> tonemapReinhard = createTonemapReinhard(1.5, 0,0,0);
tonemapReinhard->process(hdrDebevec, ldrReinhard);
imwrite("ldr-Reinhard.jpg", ldrReinhard * 255);
  • 1
  • 2
  • 3
  • 4
  • 5

Python

# Tonemap using Reinhard's method to obtain 24-bit color image
tonemapReinhard = cv2.createTonemapReinhard(1.5, 0,0,0)
ldrReinhard = tonemapReinhard.process(hdrDebevec)
cv2.imwrite("ldr-Reinhard.jpg", ldrReinhard * 255)
  • 1
  • 2
  • 3
  • 4

Mantiuk Tonemap

createTonemapMantiuk
(
float   gamma = 1.0f,
float   scale = 0.7f,
float   saturation = 1.0f
)   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

参数scale 是对比度比例因子。 从0.6到0.9的值产生最好的结果。

C++

// Tonemap using Mantiuk's method to obtain 24-bit color image
Mat ldrMantiuk;
Ptr<TonemapMantiuk> tonemapMantiuk = createTonemapMantiuk(2.2,0.85, 1.2);
tonemapMantiuk->process(hdrDebevec, ldrMantiuk);
ldrMantiuk = 3 * ldrMantiuk;
imwrite("ldr-Mantiuk.jpg", ldrMantiuk * 255);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Python

# Tonemap using Mantiuk's method to obtain 24-bit color image
tonemapMantiuk = cv2.createTonemapMantiuk(2.2,0.85, 1.2)
ldrMantiuk = tonemapMantiuk.process(hdrDebevec)
ldrMantiuk = 3 * ldrMantiuk
cv2.imwrite("ldr-Mantiuk.jpg", ldrMantiuk * 255)

使用OpenCV进行高动态范围(HDR)成像(C ++ / Python)相关推荐

  1. opencv曝光过度_软件开发|使用 OpenCV 进行高动态范围(HDR)成像

    在本教程中,我们将学习如何使用由不同曝光设置拍摄的多张图像创建高动态范围High Dynamic Range(HDR)图像. 我们将以 C++ 和 Python 两种形式分享代码. 什么是高动态范围成 ...

  2. android opencv hdr,用 OpenCV 进行高动态范围(HDR)成像

    在本教程中我们将学习如何使用由不同曝光设置拍摄的多张图像创建高动态范围High Dynamic RangeHDR图像. 我们将以 C++ 和 Python 两种形式分享代码. 什么是高动态范围成像 大 ...

  3. Python+OpenCV:高动态范围(High Dynamic Range, HDR)

    Python+OpenCV:高动态范围(High Dynamic Range, HDR) 目标 Learn how to generate and display HDR image from an ...

  4. OpenCV hdr成像技术的实例(附完整代码)

    OpenCV hdr成像技术的实例 OpenCV hdr成像技术的实例 OpenCV hdr成像技术的实例 #include "opencv2/photo.hpp" #includ ...

  5. 【OpenCV-Python】教程:8-3 高动态范围 HDR

    OpenCV Python HDR [目标] 学习如何从曝光序列生成和显示HDR图像. 使用曝光融合来合并曝光序列. [理论] 高动态范围成像(HDRI或HDR)是一种用于成像和摄影的技术,用于再现比 ...

  6. HDR 成像技术学习(一)

    在描述一个场景的时候,动态范围(Dynamic Range)指的是其最亮部与最暗部的亮度比值.高动态范围的场景(High Dynamic Range Scene)指的是场景里同时存在非常明亮和非常暗淡 ...

  7. 【zzq‘笔记】HDR成像技术学习(一)

    在描述一个场景的时候,动态范围(Dynamic Range)指的是其最亮部分与最暗部分的亮度比值.高动态范围的场景(High Dynamic Range Scene)指的是场景里同时存在非常明亮和非常 ...

  8. linux下python安装opencv库_Linux下怎么配置python和opencv

    匿名用户 1级 2017-06-20 回答 以下说明在Linux下Python和OpenCV结合安装的过程,Python要使用OpenCV模块,则必须导入OpenCV提供的包,所以要提供Python支 ...

  9. OpenCV+yolov3实现目标检测(C++,Python)

    OpenCV+yolov3实现目标检测(C++,Python) 目标检测算法主要分为两类:一类是基于Region Proposal(候选区域)的算法,如R-CNN系算法(R-CNN,Fast R-CN ...

  10. HDR 成像技术学习(三)—— LOFIC

    HDR 成像技术学习(一) HDR 成像技术学习(二) 我们拍摄的照片来自传感器上的像素,它们将光处理为电信号,组合起来输出画面.当捕捉对象亮度过强,大量电荷挤在单个像素内,生成的图像就会过曝. LO ...

最新文章

  1. Linux sticky bit 目录权限 rwt权限
  2. [置顶] 我的程序员之路(4)---C语言课程设计
  3. log4j 日志配置
  4. 作者:谢波峰(1976-),男,中国人民大学财政金融学院副教授,中国人民大学金融与财税电子化研究所执行所长。...
  5. Java8之Stream详解
  6. mysql添加数据不阻塞_主键表插入数据不提交,外键表插入数据被阻塞
  7. Android 代码动态生成ProgressBar
  8. Eclipse 内部启动 Tomcat,浏览器访问出错的解决方法
  9. 减少.NET应用程序内存占用的一则实践
  10. html自动留言,html 留言板
  11. 王一博、肖战、吴宣仪、杨紫,2019明星沸点榜,谁的颜值更高,Python告诉你
  12. Rhcsa第二次课堂练习
  13. 《Java并发编程的艺术》读书笔记 - 第八章 - Java中的并发工具类
  14. STM32F105RBT6 uart调试
  15. linux下设置MySQL密码
  16. 惠斯通电桥与运算放大器的输入失调电流和输入偏置电流
  17. c语言程序设计第二版(张磊),C语言程序设计教程(第2版) 教学课件 张磊 第1章 程序设计概述.pdf...
  18. window 获取文件大小
  19. FFmpeg —— 对mp4视频按时间剪切,生成新的mp4(附源码)
  20. 迪文触摸串口屏 实例应用(2)——创建工程

热门文章

  1. 为什么人工智能难以达到儿童语言水平?
  2. 服务器视频文件外链,视频图床 视频外链网站 视频上传外链分享
  3. 佛罗里达州立大学计算机专业排名,佛罗里达州立大学有哪些专业_专业排名(QS世界排名)...
  4. 利用软连接实现 OneDrive 任意文件夹同步
  5. python中eof怎么改正_如何修复Python3中读取用户输入时的EOF错误?
  6. CPU设计实战(一)
  7. QCC3040---AppDevice module
  8. android 图片虚化代码,Android模糊图片技术
  9. php图片虚化处理 api PHP实现生成模糊图片
  10. 关于New Date()获取的不是当前电脑时间问题