详解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文件和目录操作详解
Feb 08 Python
Python中元组,列表,字典的区别
May 21 Python
Android分包MultiDex策略详解
Oct 30 Python
python获取多线程及子线程的返回值
Nov 15 Python
Python实现一个Git日志统计分析的小工具
Dec 14 Python
Python静态类型检查新工具之pyright 使用指南
Apr 26 Python
200行python代码实现2048游戏
Jul 17 Python
Python 日志logging模块用法简单示例
Oct 18 Python
使用django和vue进行数据交互的方法步骤
Nov 11 Python
keras 特征图可视化实例(中间层)
Jan 24 Python
详解tensorflow2.x版本无法调用gpu的一种解决方法
May 25 Python
python opencv人脸识别考勤系统的完整源码
Apr 26 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 5.0 Pear安装方法
2006/12/06 PHP
DISCUZ 分页代码
2007/01/02 PHP
php将图片文件转换成二进制输出的方法
2015/06/10 PHP
PHP实现mysqli批量执行多条语句的方法示例
2017/07/22 PHP
类之Prototype.js学习
2007/06/13 Javascript
JS中confirm,alert,prompt函数使用区别分析
2010/04/01 Javascript
treepanel动态加载数据实现代码
2012/12/15 Javascript
JavaScript基础教程之alert弹出提示框实例
2014/10/16 Javascript
JS实现文件动态顺序载入的方法
2015/03/07 Javascript
总结AngularJS开发者最常犯的十个错误
2016/08/31 Javascript
微信小程序 网络请求(GET请求)详解
2016/11/16 Javascript
javascript基本数据类型及类型检测常用方法小结
2016/12/14 Javascript
javascript阻止事件冒泡和浏览器的默认行为
2017/01/21 Javascript
jQuery阻止事件冒泡实例分析
2018/07/03 jQuery
微信小程序仿知乎实现评论留言功能
2018/11/28 Javascript
利用d3.js实现蜂巢图表带动画效果
2019/09/03 Javascript
微信小程序自定义导航栏(模板化)
2019/11/15 Javascript
Django后台admin的使用详解
2019/07/08 Python
Python CVXOPT模块安装及使用解析
2019/08/01 Python
python中提高pip install速度
2020/02/14 Python
Python响应对象text属性乱码解决方案
2020/03/31 Python
PyQt5的相对布局管理的实现
2020/08/07 Python
pandas参数设置的实用小技巧
2020/08/23 Python
Pycharm中使用git进行合作开发的教程详解
2020/11/17 Python
HTML5本地存储之Web Storage应用介绍
2013/01/06 HTML / CSS
详解window.open被浏览器拦截的解决方案
2019/07/18 HTML / CSS
波兰珠宝品牌:YES
2019/08/09 全球购物
Harrods英国:世界领先的奢侈品百货商店
2020/09/23 全球购物
高中生学习生活的自我评价
2013/10/09 职场文书
分公司任命书
2014/06/06 职场文书
我的中国梦演讲稿500字
2014/08/19 职场文书
光棍节联谊晚会活动策划书
2014/10/10 职场文书
2014年医院科室工作总结
2014/12/20 职场文书
微观世界观后感
2015/06/10 职场文书
九年级语文教学反思
2016/03/03 职场文书
SSM项目使用拦截器实现登录验证功能
2022/01/22 Java/Android