详解OpenCV曝光融合


Posted in Python onApril 29, 2022

1 什么是曝光融合

曝光融合是一种将使用不同曝光设置拍摄的图像合成为一张看起来像色调映射的高动态范围(HDR)图像的图像的方法。当我们使用相机拍摄照片时,每个颜色通道只有8位来表示场景的亮度。然而,我们周围世界的亮度理论上可以从0(黑色)到几乎无限(直视太阳)。因此,傻瓜相机或移动相机根据场景决定曝光设置,以便使用相机的动态范围(0-255值)来表示图像中最有趣的部分。例如,在许多相机中,使用面部检测来查找面部并设置曝光,使得面部看起来很好。这引出了一个问题-我们可以在不同的曝光设置下拍摄多张照片并拍摄更大范围的场景亮度吗?答案是肯定的。传统上使用HDR成像然后进行色调映射的方式。具体见上篇文章:

HDR成像要求我们知道精确的曝光时间。HDR图像本身看起来很暗,看起来不太漂亮。DR图像中的最小强度为0,但理论上没有最大值。所以我们需要将其值映射到0到255之间,以便我们可以显示它。将HDR图像映射到常规的每通道8位彩色图像的过程称为色调映射。如您所见,组装HDR图像和色调映射有点麻烦。我们不能不使用HDR就使用多个图像创建色调映射图像。结果证明我们可以用曝光融合来实现。

2 曝光融合的原理

应用曝光融合的步骤如下所述:

  • 使用不同曝光拍摄多张图像

详解OpenCV曝光融合

首先,我们需要在不移动相机的情况下捕获同一场景的一系列图像。如上所示,序列中的图像具有不同的曝光。这是通过改变相机的快门速度来实现的。通常,我们选择一些曝光不足的图像,一些曝光过度的图像和一个正确曝光的图像。

在“正确”曝光的图像中,选择快门速度(由相机或摄影师自动选择),以便每通道8位动态范围用于表示图像中最有趣的部分。太暗的区域被剪切为0,而太亮的区域被饱和到255。

在曝光不足的图像中,快门速度很快,图像很暗。因此,图像的8位用于捕获亮区域,而暗区域被剪切为0。在曝光过度的图像中,快门速度较慢,因此传感器捕获的光线更多,因此图像更亮。传感器的8位用于捕获暗区域的强度,而亮区域饱和到255的值。大多数单反相机都有一个称为自动曝光包围(AEB)的功能,只需按一下按钮,我们就可以在不同曝光下拍摄多张照片。当我们在iPhone中使用HDR模式时,它需要三张照片(安卓可以下载超级相机这个软件)。

图像对齐:

即使使用三脚架获取序列中的图像也需要对齐,因为即使较小的相机抖动也会降低最终图像的质量。OpenCV提供了一种使用对齐这些图像的简便方法AlignMTB。该算法将所有图像转换为中值阈值位图(MTB)。通过将值1分配给比中值亮度更亮的像素来计算图像的MTB,否则为0。MTB 对曝光时间不变。因此,可以对准MTB而无需我们指定曝光时间。

图像融合:

具有不同曝光的图像捕获不同范围的场景亮度。根据Tom MertensJan KautzFrank Van Reeth 题为Exposure Fusion的论文。论文见:曝光融合通过仅保留多重曝光图像序列中的“最佳”部分来计算所需图像。

作者提出了三个质量指标:

1曝光良好:如果序列中的图像中的像素接近零或接近255,则不应使用该图像来查找最终像素值。其值接近中间强度(128)的像素是比较合适的。

2对比度:高对比度通常意味着高品质。因此,对于该像素,给予特定像素的对比度值高的图像具有更高的权重。

3饱和度:类似地,更饱和的颜色更少被淘汰并且代表更高质量的像素。因此,特定像素的饱和度高的图像被赋予该像素的更高权重。

三种质量度量用于创建权重图详解OpenCV曝光融合 该权重图表示详解OpenCV曝光融合, 图像在位置处的像素的最终强度中的贡献详解OpenCV曝光融合, 对权重图详解OpenCV曝光融合,进行归一化,使得对于任何像素详解OpenCV曝光融合所以所有图像的贡献总计为1。

结合权重图使用以下等式组合图像是很有效的:

详解OpenCV曝光融合

其中,详解OpenCV曝光融合,是原始图像,详解OpenCV曝光融合, 是输出图像。问题在于,由于像素是从不同曝光的图像中拍摄的,因此详解OpenCV曝光融合

使用上述等式获得的输出图像将显示许多裂缝。该论文的作者使用拉普拉斯金字塔来混合图像。我们将在以后的文章中介绍这项技术的细节。

幸运的是使用OpenCV,这种图像曝光融合合并只是使用MergeMertens该类的两行代码。请注意,这个名字取决于Exposure Fusion论文的第一作者Tom Mertens 。

3 代码与结果

代码地址:

C++:

#include "pch.h"
#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>
#include <fstream>
using namespace cv;
using namespace std;

// Read Images
void readImages(vector<Mat> &images)
{
    int numImages = 16;
    static const char* filenames[] =
    {
      "image/memorial0061.jpg",
      "image/memorial0062.jpg",
      "image/memorial0063.jpg",
      "image/memorial0064.jpg",
      "image/memorial0065.jpg",
      "image/memorial0066.jpg",
      "image/memorial0067.jpg",
      "image/memorial0068.jpg",
      "image/memorial0069.jpg",
      "image/memorial0070.jpg",
      "image/memorial0071.jpg",
      "image/memorial0072.jpg",
      "image/memorial0073.jpg",
      "image/memorial0074.jpg",
      "image/memorial0075.jpg",
      "image/memorial0076.jpg"
    };
    //读图
    for (int i = 0; i < numImages; i++)
    {
        Mat im = imread(filenames[i]);
        images.push_back(im);
    }
}

int main()
{
    // Read images 读取图像
    cout << "Reading images ... " << endl;
    vector<Mat> images;

    //是否图像映射
    bool needsAlignment = true;

    // Read example images 读取例子图像
    readImages(images);
    //needsAlignment = false;

    // Align input images
    if (needsAlignment)
    {
        cout << "Aligning images ... " << endl;
        Ptr<AlignMTB> alignMTB = createAlignMTB();
        alignMTB->process(images, images);
    }
    else
    {
        cout << "Skipping alignment ... " << endl;
    }

    // Merge using Exposure Fusion 图像融合
    cout << "Merging using Exposure Fusion ... " << endl;
    Mat exposureFusion;
    Ptr<MergeMertens> mergeMertens = createMergeMertens();
    mergeMertens->process(images, exposureFusion);

    // Save output image 图像保存
    cout << "Saving output ... exposure-fusion.jpg" << endl;
    imwrite("exposure-fusion.jpg", exposureFusion * 255);

    return 0;
}

Python:

import cv2
import numpy as np
import sys

def readImagesAndTimes():

  filenames = [
               "image/memorial0061.jpg",
               "image/memorial0062.jpg",
               "image/memorial0063.jpg",
               "image/memorial0064.jpg",
               "image/memorial0065.jpg",
               "image/memorial0066.jpg",
               "image/memorial0067.jpg",
               "image/memorial0068.jpg",
               "image/memorial0069.jpg",
               "image/memorial0070.jpg",
               "image/memorial0071.jpg",
               "image/memorial0072.jpg",
               "image/memorial0073.jpg",
               "image/memorial0074.jpg",
               "image/memorial0075.jpg",
               "image/memorial0076.jpg"
               ]

  images = []
  for filename in filenames:
    im = cv2.imread(filename)
    images.append(im)

  return images

if __name__ == '__main__':

  # Read images
  print("Reading images ... ")

  if len(sys.argv) > 1:
    # Read images from the command line
    images = []
    for filename in sys.argv[1:]:
      im = cv2.imread(filename)
      images.append(im)
    needsAlignment = False
  else :
    # Read example images
    images = readImagesAndTimes()
    needsAlignment = False

  # Align input images
  if needsAlignment:
    print("Aligning images ... ")
    alignMTB = cv2.createAlignMTB()
    alignMTB.process(images, images)
  else :
    print("Skipping alignment ... ")

  # Merge using Exposure Fusion
  print("Merging using Exposure Fusion ... ");
  mergeMertens = cv2.createMergeMertens()
  exposureFusion = mergeMertens.process(images)

  # Save output image
  print("Saving output ... exposure-fusion.jpg")
  cv2.imwrite("exposure-fusion.jpg", exposureFusion * 255)

本文第一张图获得的不同曝光的图像,通过这种方法所得结果如下:

详解OpenCV曝光融合

在输入图像中,我们可以获得过度曝光图像中光线昏暗区域和曝光不足图像中明亮区域的细节。但是,在合并的输出图像中,像素在图像的每个部分都具有明亮的细节。我们还可以在之前的帖子中看到我们用于HDR成像的图像的这种效果。用于产生最终输出的四个图像显示在左侧,输出图像显示在右侧。结果如下图所示:

详解OpenCV曝光融合

正如您在本文中所看到的,Exposure Fusion允许我们在不明确计算HDR图像的情况下实现类似于HDR + Tonemapping的效果。因此,我们不需要知道每张图像的曝光时间,但我们能够获得非常合理的结果。那么,为什么要费心去做HDR呢?好吧,在很多情况下,Exposure Fusion产生的输出可能不符合您的喜好。没有旋钮可以调整以使其变得不同或更好。另一方面,HDR图像捕获场景的原始亮度。如果您不喜欢色调映射的HDR图像,请尝试使用不同的色调映射算法。总之,Exposure Fusion代表了一种权衡。在速度和不太严格的要求下使得算法更加灵活(例如,不需要暴露时间)

到此这篇关于python 使用OpenCV进行曝光融合的文章就介绍到这了木!


Tags in this post...

Python 相关文章推荐
Python操作json数据的一个简单例子
Apr 17 Python
Python的Bottle框架中返回静态文件和JSON对象的方法
Apr 30 Python
使用Python来开发Markdown脚本扩展的实例分享
Mar 04 Python
利用Python脚本实现ping百度和google的方法
Jan 24 Python
python日志记录模块实例及改进
Feb 12 Python
Python3实现发送QQ邮件功能(文本)
Dec 15 Python
Python 解决中文写入Excel时抛异常的问题
May 03 Python
Python 元组拆包示例(Tuple Unpacking)
Dec 24 Python
Python @property装饰器原理解析
Jan 22 Python
浅谈在django中使用filter()(即对QuerySet操作)时踩的坑
Mar 31 Python
Python爬虫谷歌Chrome F12抓包过程原理解析
Jun 04 Python
Python 虚拟环境工作原理解析
Dec 24 Python
python使用shell脚本创建kafka连接器
Apr 29 #Python
python中pycryto实现数据加密
Apr 29 #Python
Python如何快速找到多个字典中的公共键(key)
Apr 29 #Python
Python如何让字典保持有序排列
Apr 29 #Python
Python按顺序遍历并读取文件夹中文件
Apr 29 #Python
Python保存并浏览用户的历史记录
Apr 29 #Python
python解析json数据
Apr 29 #Python
You might like
PHP的文件操作与算法实现的面试题示例
2015/08/10 PHP
laravel5.4生成验证码的实例讲解
2017/08/05 PHP
jquery 分页控件实现代码
2009/11/30 Javascript
Javascript 键盘事件的组合使用实现代码
2012/05/04 Javascript
jQuery替换字符串(实例代码)
2013/11/13 Javascript
鼠标经过tr时,改变tr当前背景颜色
2014/01/13 Javascript
ztree获取当前选中节点子节点id集合的方法
2015/02/12 Javascript
轻松实现js图片预览功能
2016/01/18 Javascript
BootStrapValidator校验方式
2016/12/19 Javascript
微信小程序 利用css实现遮罩效果实例详解
2017/01/21 Javascript
Vue监听数组变化源码解析
2017/03/09 Javascript
BootStrap Table复选框默认选中功能的实现代码(从数据库获取到对应的状态进行判断是否为选中状态)
2017/07/11 Javascript
通过说明与示例了解js五种设计模式
2019/06/17 Javascript
vue中组件通信的八种方式(值得收藏!)
2019/08/09 Javascript
JS三级联动代码格式实例详解
2019/12/30 Javascript
Vue页面渲染中key的应用实例教程
2021/01/12 Vue.js
Nginx搭建HTTPS服务器和强制使用HTTPS访问的方法
2015/08/16 Python
Python入门_浅谈for循环、while循环
2017/05/16 Python
获取python文件扩展名和文件名方法
2018/02/02 Python
python写入已存在的excel数据实例
2018/05/03 Python
python模块之subprocess模块级方法的使用
2019/03/26 Python
pandas取出重复数据的方法
2019/07/04 Python
django框架基于queryset和双下划线的跨表查询操作详解
2019/12/11 Python
Python Sphinx使用实例及问题解决
2020/01/17 Python
Python filter过滤器原理及实例应用
2020/08/18 Python
阿拉伯世界最大的电子商务网站:Souq沙特阿拉伯
2016/10/28 全球购物
事业单位个人应聘自荐信
2013/09/21 职场文书
大学毕业生通用自荐信范文
2013/10/31 职场文书
新闻学专业大学生职业生涯规划范文
2014/03/02 职场文书
社区党建工作汇报材料
2014/08/14 职场文书
一般党员对照检查材料
2014/09/24 职场文书
党的群众路线教育实践活动学习笔记范文
2014/11/06 职场文书
小学运动会加油稿
2015/07/22 职场文书
redis不能访问本机真实ip地址的解决方案
2021/07/07 Redis
通过T-SQL语句创建游标与实现数据库加解密功能
2022/03/16 SQL Server
MySQL分布式恢复进阶
2022/07/23 MySQL