详解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微信库itchat实现微信自动回复功能
May 18 Python
django开发教程之利用缓存文件进行页面缓存的方法
Nov 10 Python
解决nohup执行python程序log文件写入不及时的问题
Jan 14 Python
python获取交互式ssh shell的方法
Feb 14 Python
pycharm配置当鼠标悬停时快速提示方法参数
Jul 31 Python
对tensorflow 中tile函数的使用详解
Feb 07 Python
tensorflow 分类损失函数使用小记
Feb 18 Python
Pandas —— resample()重采样和asfreq()频度转换方式
Feb 26 Python
Python数据相关系数矩阵和热力图轻松实现教程
Jun 16 Python
jupyter notebook更换皮肤主题的实现
Jan 07 Python
python自动化办公操作PPT的实现
Feb 05 Python
Python使用sql语句对mysql数据库多条件模糊查询的思路详解
Apr 12 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
提高define性能的php扩展hidef的安装和使用
2011/06/14 PHP
基于PHP文件操作的详细诠释
2013/06/21 PHP
php简单实现无限分类树形列表的方法
2015/03/27 PHP
IIS 7.5 asp Session超时时间设置方法
2017/04/17 PHP
PHP数组遍历的几种常见方式总结
2019/02/15 PHP
PHP页面静态化――纯静态与伪静态用法详解
2020/06/05 PHP
javascript 判断字符串是否包含某字符串及indexOf使用示例
2013/10/18 Javascript
选择复选框按钮置灰否则按钮可用
2014/05/22 Javascript
jQuery对JSON数据进行排序输出的方法
2015/06/24 Javascript
AngularJs表单验证实例详解
2016/05/30 Javascript
Vuejs第十三篇之组件——杂项
2016/09/09 Javascript
浅谈javascript中的三种弹窗
2016/10/21 Javascript
js 中获取制定的cook信息实现方法
2016/11/19 Javascript
Web前端框架bootstrap实战【第一次接触使用】
2016/12/28 Javascript
jquery ajaxfileupload异步上传插件使用详解
2017/02/08 Javascript
jQuery中的on与bind绑定事件区别实例详解
2017/02/28 Javascript
JavaScript一元正号运算符示例代码
2019/06/30 Javascript
微信小程序实用代码段(收藏版)
2019/12/17 Javascript
使用Python标准库中的wave模块绘制乐谱的简单教程
2015/03/30 Python
Python中使用OpenCV库来进行简单的气象学遥感影像计算
2016/02/19 Python
用python做一个搜索引擎(Pylucene)的实例代码
2017/07/05 Python
用python介绍4种常用的单链表翻转的方法小结
2020/02/24 Python
浅谈Python 参数与变量
2020/06/20 Python
Python实现Word文档转换Markdown的示例
2020/12/22 Python
用python获取txt文件中关键字的数量
2020/12/24 Python
天猫国际进口超市直营:官方直采,一站购齐
2017/12/11 全球购物
曼城官方网上商店:Manchester City
2019/09/10 全球购物
TUMI香港官网:国际领先的行李箱、背囊品牌
2021/03/01 全球购物
妇产科护士自我鉴定
2013/10/15 职场文书
大学生两会学习心得体会
2014/03/10 职场文书
淘宝客服专员岗位职责
2014/04/11 职场文书
初中学生期末评语
2014/04/24 职场文书
计算机专业自荐信范文
2015/03/26 职场文书
Nest.js参数校验和自定义返回数据格式详解
2021/03/29 Javascript
教你怎么用PyCharm为同一服务器配置多个python解释器
2021/05/31 Python
SQL注入篇学习之盲注/宽字节注入
2022/03/03 MySQL