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

什么是高动态范围成像

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

但是我们周围世界动态范围极大。 在车库内关灯就会变黑直接看着太阳就会变得非常亮。 即使不考虑这些极端在日常情况下8 位的通道勉强可以捕捉到现场场景。 因此相机会尝试去评估光照并且自动设置曝光这样图像的最关注区域就会有良好的动态范围并且太暗和太亮的部分会被相应截取为 0 和 255。

在下图中左侧的图像是正常曝光的图像。 请注意由于相机决定使用拍摄主体我的儿子的设置所以背景中的天空已经完全流失了但是明亮的天空也因此被刷掉了。 右侧的图像是由 iPhone 生成的HDR图像。

High Dynamic Range (HDR)

iPhone 是如何拍摄 HDR 图像的呢 它实际上采用三种不同的曝光度拍摄了 3 张图像3 张图像拍摄非常迅速在 3 张图像之间几乎没有产生位移。然后组合三幅图像来产生 HDR 图像。 我们将在下一节看到一些细节。

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

高动态范围HDR成像是如何工作的

在本节中我们来看下使用 OpenCV 创建 HDR 图像的步骤。

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

第 1 步捕获不同曝光度的多张图像

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

场景没有变化时在相机上使用自动包围式曝光或在手机上使用自动包围式应用程序我们可以一张接一张地快速拍摄多张照片。 当我们在 iPhone 中使用 HDR 模式时会拍摄三张照片。

曝光不足的图像该图像比正确曝光的图像更暗。 目标是捕捉非常明亮的图像部分。

正确曝光的图像这是相机将根据其估计的照明拍摄的常规图像。

曝光过度的图像该图像比正确曝光的图像更亮。 目标是拍摄非常黑暗的图像部分。

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

Auto Exposure Bracketed HDR image sequence

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

我们先从读取分配到不同曝光时间的图像开始。

C++

void readImagesAndTimes(vector &images, vector &times)

{

int numImages = 4;

// 曝光时间列表

static const float timesArray[] = {1/30.0f,0.25,2.5,15.0};

times.assign(timesArray, timesArray + numImages);

// 图像文件名称列表

static 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]);

images.push_back(im);

}

}

Python

def readImagesAndTimes():

# 曝光时间列表

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(filename)

images.append(im)

return images, times

第 2 步对齐图像

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

Misalignment problem in HDR

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

幸运的是OpenCV 提供了一种简单的方法使用 AlignMTB 对齐这些图像。 该算法将所有图像转换为中值阈值位图median threshold bitmapsMTB。 图像的 MTB 生成方式为将比中值亮度的更亮的分配为 1其余为 0。 MTB 不随曝光时间的改变而改变。 因此不需要我们指定曝光时间就可以对齐 MTB。

基于 MTB 的对齐方式的代码如下。

C++

// 对齐输入图像

Ptr alignMTB = createAlignMTB();

alignMTB->process(images, images);

Python

# 对齐输入图像

alignMTB = cv2.createAlignMTB()

alignMTB.process(images, images)

第 3 步提取相机响应函数

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

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

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

但是 CRF 不是线性的 我们需要评估 CRF 把图像强度变成线性然后才能合并或者平均它们。

好消息是如果我们知道每个图像的曝光时间则可以从图像估计 CRF。 与计算机视觉中的许多问题一样找到 CRF 的问题本质是一个最优解问题其目标是使由数据项和平滑项组成的目标函数最小化。 这些问题通常会降维到线性最小二乘问题这些问题可以使用奇异值分解Singular Value DecompositionSVD来解决奇异值分解是所有线性代数包的一部分。 CRF 提取算法的细节在从照片提取高动态范围辐射图这篇论文中可以找到。

使用 OpenCV 的 CalibrateDebevec 或者 CalibrateRobertson 就可以用 2 行代码找到 CRF。本篇教程中我们使用 CalibrateDebevec

C++

// 获取图像响应函数 (CRF)

Mat responseDebevec;

Ptr calibrateDebevec = createCalibrateDebevec();

calibrateDebevec->process(images, responseDebevec, times);

Python

# 获取图像响应函数 (CRF)

calibrateDebevec = cv2.createCalibrateDebevec()

responseDebevec = calibrateDebevec.process(images, times)

下图显示了使用红绿蓝通道的图像提取的 CRF。

Camera Response Function

第 4 步合并图像

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

C++

// 将图像合并为HDR线性图像

Mat hdrDebevec;

Ptr mergeDebevec = createMergeDebevec();

mergeDebevec->process(images, hdrDebevec, times, responseDebevec);

// 保存图像

imwrite("hdrDebevec.hdr", hdrDebevec);

Python

# 将图像合并为HDR线性图像

mergeDebevec = cv2.createMergeDebevec()

hdrDebevec = mergeDebevec.process(images, times, responseDebevec)

# 保存图像

cv2.imwrite("hdrDebevec.hdr", hdrDebevec)

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

第 5 步色调映射

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

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

将高动态范围HDR图像转换为 8 位单通道图像的过程称为色调映射。这个过程的同时还需要保留尽可能多的细节。

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

我们来看看各种选项。 以下列出了不同色调映射算法的一些常见参数。

伽马gamma该参数通过应用伽马校正来压缩动态范围。 当伽马等于 1 时不应用修正。 小于 1 的伽玛会使图像变暗而大于 1 的伽马会使图像变亮。

饱和度saturation该参数用于增加或减少饱和度。 饱和度高时色彩更丰富更浓。 饱和度值接近零使颜色逐渐消失为灰度。

对比度contrast控制输出图像的对比度即 log(maxPixelValue/minPixelValue)。

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

Drago 色调映射

Drago 色调映射的参数如下所示

createTonemapDrago

(

float   gamma = 1.0f,

float   saturation = 1.0f,

float   bias = 0.85f

)

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

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

C++

// 使用Drago色调映射算法获得24位彩色图像

Mat ldrDrago;

Ptr tonemapDrago = createTonemapDrago(1.0, 0.7);

tonemapDrago->process(hdrDebevec, ldrDrago);

ldrDrago = 3 * ldrDrago;

imwrite("ldr-Drago.jpg", ldrDrago * 255);

Python

# 使用Drago色调映射算法获得24位彩色图像

tonemapDrago = cv2.createTonemapDrago(1.0, 0.7)

ldrDrago = tonemapDrago.process(hdrDebevec)

ldrDrago = 3 * ldrDrago

cv2.imwrite("ldr-Drago.jpg", ldrDrago * 255)

结果如下

使用Drago算法的HDR色调映射

Durand 色调映射

Durand 色调映射的参数如下所示

createTonemapDurand

(

float     gamma = 1.0f,

float     contrast = 4.0f,

float     saturation = 1.0f,

float     sigma_space = 2.0f,

float     sigma_color = 2.0f

);

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

C++

// 使用Durand色调映射算法获得24位彩色图像

Mat ldrDurand;

Ptr tonemapDurand = createTonemapDurand(1.5,4,1.0,1,1);

tonemapDurand->process(hdrDebevec, ldrDurand);

ldrDurand = 3 * ldrDurand;

imwrite("ldr-Durand.jpg", ldrDurand * 255);

Python

# 使用Durand色调映射算法获得24位彩色图像

tonemapDurand = cv2.createTonemapDurand(1.5,4,1.0,1,1)

ldrDurand = tonemapDurand.process(hdrDebevec)

ldrDurand = 3 * ldrDurand

cv2.imwrite("ldr-Durand.jpg", ldrDurand * 255)

结果如下

使用Durand算法的HDR色调映射

Reinhard 色调映射

createTonemapReinhard

(

float   gamma = 1.0f,

float   intensity = 0.0f,

float   light_adapt = 1.0f,

float   color_adapt = 0.0f

)

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

有关更多详细信息请查看这篇论文。

C++

// 使用Reinhard色调映射算法获得24位彩色图像

Mat ldrReinhard;

Ptr tonemapReinhard = createTonemapReinhard(1.5, 0,0,0);

tonemapReinhard->process(hdrDebevec, ldrReinhard);

imwrite("ldr-Reinhard.jpg", ldrReinhard * 255);

Python

# 使用Reinhard色调映射算法获得24位彩色图像

tonemapReinhard = cv2.createTonemapReinhard(1.5, 0,0,0)

ldrReinhard = tonemapReinhard.process(hdrDebevec)

cv2.imwrite("ldr-Reinhard.jpg", ldrReinhard * 255)

结果如下

使用Reinhard算法的HDR色调映射

Mantiuk 色调映射

createTonemapMantiuk

(

float   gamma = 1.0f,

float   scale = 0.7f,

float   saturation = 1.0f

)

参数 scale 是对比度比例因子。 从 0.7 到 0.9 的值通常效果较好

C++

// 使用Mantiuk色调映射算法获得24位彩色图像

Mat ldrMantiuk;

Ptr tonemapMantiuk = createTonemapMantiuk(2.2,0.85, 1.2);

tonemapMantiuk->process(hdrDebevec, ldrMantiuk);

ldrMantiuk = 3 * ldrMantiuk;

imwrite("ldr-Mantiuk.jpg", ldrMantiuk * 255);

Python

# 使用Mantiuk色调映射算法获得24位彩色图像

tonemapMantiuk = cv2.createTonemapMantiuk(2.2,0.85, 1.2)

ldrMantiuk = tonemapMantiuk.process(hdrDebevec)

ldrMantiuk = 3 * ldrMantiuk

cv2.imwrite("ldr-Mantiuk.jpg", ldrMantiuk * 255)

结果如下

使用Mantiuk算法的HDR色调映射

图片致谢

本文中使用的四个曝光图像获得 CC BY-SA 3.0 许可并从维基百科的 HDR 页面下载。 图像由 Kevin McCoy拍摄。

android opencv hdr,用 OpenCV 进行高动态范围(HDR)成像相关推荐

  1. Android Studio不安装opencv manager配置

    Android Studio不安装opencv manager配置 from: http://jingyan.baidu.com/article/60ccbceb53533364cab197db.ht ...

  2. 在Android Studio上进行OpenCV 3.1开发

    在Android Studio上进行OpenCV 3.1开发 发布于 2016年1月27日 作者: John Hany 5,466次阅读 2016.07.08更新:增加Android Studio 2 ...

  3. Android Studio中安装OpenCV SDK

    Android Studio中安装OpenCV SDK Open Source Computer Vision (OpenCV) is a library used for computer visi ...

  4. android安装python opencv_MacLinux环境在Android Studio中安装OpenCV

    在Android Studio中安装OpenCV 对于女程序猿来说,每次安装个什么软件,或者是配置个什么环境啊,经常整得很崩溃.本程序猿阿姨也是如此~(啊,我说我是阿姨了吗?) 好了,言归正传~ 我们 ...

  5. android代码查找图像,Android平台上利用opencv进行图像的边沿检测

    原标题:Android平台上利用opencv进行图像的边沿检测 近开始接触opencv for Android,从网上down了图像的边沿检测的代码. 测试图片: 在Android2.3.1模拟器上跑 ...

  6. 【Android探索】基于OpenCV的微液滴粒径分析APP

    前言:这个App是之前<数字图像处理>课程的一次课程设计中的产物,现在整理一下记录下来,里面涉及到了比较多的控件以及拓展包,功能不是很丰富但是也比较算齐全,其中使用的技术原理包括在安卓上使 ...

  7. Android 基于 dlib 和 opencv 实现换脸(不需要依赖第三方关键点检测)

    支持技术分享,转载或复制,请指出文章来源 此博客作者为Jack__0023 感谢(一些理论和关键代码的来源链接) dlib 模块组来源 变脸部分关键代码的来源 效果如下 将中间红色女性脸(以下简称C脸 ...

  8. android 调用hdr功能吗,什么是HDR和如何在Android上使用它 | MOS86

    近年来, 智能手机摄影技术进步了跨越式发展.我们大多数人完全放弃了传统的数码相机,有利于我们的智能手机的便利.通过在我们的手机中实现更好的硬件和软件,这种转变已经成为可能.随着手机上的相机功能变得越来 ...

  9. 高动态范围HDR 360全景图片制作与应用学习教程

    现在你可以像专业人士一样创建HDR 360全景! 你会学到什么 创建高动态范围360全景的整个工作流程 核心摄影原则:曝光.HDR.360°全景摄影 寻找你的相机和镜头的节点 计算你的射击角度公式 拼 ...

最新文章

  1. 干货 | 时间序列预测类问题下的建模方案探索实践
  2. SpringBoot配置文件值注入方式
  3. flutter 泛型_Flutter/Dart - 泛型
  4. AI 还原宋代皇帝,原来这么帅?!
  5. shell 脚本编写使用
  6. akoj-1153-p次方求和
  7. 使用二维NDRange workgroup
  8. Linux常用命令介绍(一)——文件与文件夹操作相关命令
  9. (日常搬砖)windows下如何查看并导出文件夹目录
  10. BestCoder Round #67 (div.2) N*M bulbs
  11. selenium 各浏览器driver下载地址
  12. hp 126NW驱动安装
  13. 用Python模拟QQ界面之QQ登录界面的奥秘
  14. linux命令join的用法,linux join命令
  15. 基于ShardingSphere的Encrypt-JDBC数据脱敏实战
  16. 利用elasticsearch实现搜索引擎
  17. jquery validate 验证单个
  18. E.03.08. Scrapped Plans for London Concert Hall Sour Mood for U.K. Musicians
  19. POJ1061青蛙的约会(拓展欧几里得)
  20. FlyMcu串口下载

热门文章

  1. Windows更新后MySQL服务启动失败问题总结
  2. 【ROS2】为什么要使用ROS2?《ROS2系统特性介绍》
  3. 调焦后焦实现不同距离成像_网红神机有多强?佳能G5 X Mark II评测
  4. html audio加载卡顿,audio 设置了currentTime后为什么会卡顿?
  5. Python命令使用大全
  6. 题解:HEOI2014 人人尽说江南好 【博弈论】
  7. 微信小程序实战 (HelloWorld入门)
  8. python编码错误:UnicodeDecodeError: 'utf8' codec can't decode
  9. 网易WEB白帽子-WEB安全基础
  10. ps改变图片局部颜色