使用OpenCV进行高动态范围成像

介绍

如今,大多数数字图像和成像设备都使用每通道 8 位,因此将设备的动态范围限制在两个数量级(实际上是 256 个级别),而人眼可以适应变化十个数量级的光照条件。当我们拍摄现实世界场景的照片时,明亮区域可能会曝光过度,而黑暗区域可能会曝光不足,因此我们无法使用单次曝光捕捉所有细节。HDR 成像适用于每通道使用超过 8 位(通常是 32 位浮点值)的图像,从而允许更宽的动态范围。

有多种方法可以获得 HDR 图像,但最常见的方法是使用以不同曝光值拍摄的场景照片。要组合这些曝光,了解相机的响应函数很有用,并且有算法可以估算它。混合 HDR 图像后,必须将其转换回 8 位才能在普通显示器上查看。此过程称为色调映射。当场景或相机中的物体在拍摄之间移动时,会出现额外的复杂性,因为必须注册和对齐具有不同曝光的图像。

在本教程中,我们将展示如何从曝光序列生成和显示 HDR 图像。在我们的案例中,图像已经对齐,并且没有移动物体。我们还演示了一种称为曝光融合的替代方法,该方法可生成低动态范围图像。HDR 管道的每个步骤都可以使用不同的算法来实现,因此请查看参考手册以了解所有算法。

曝光顺序

源代码

C++JavaPython

本教程代码如下所示。您也可以从此处下载

#include "opencv2/photo.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"

#include <vector>
#include <iostream>
#include <fstream>

using namespace cv;
using namespace std;

void loadExposureSeq(String, vector<Mat>&, vector<float>&);

int main(int argc, char**argv)
{
    CommandLineParser parser( argc, argv, "{@input | | Input directory that contains images and exposure times. }" );

    vector<Mat> images;
    vector<float> times;
    loadExposureSeq(parser.get<String>( "@input" ), images, times);

    Mat response;
    Ptr<CalibrateDebevec> calibrate = createCalibrateDebevec();
    calibrate->process(images, response, times);

    Mat hdr;
    Ptr<MergeDebevec> merge_debevec = createMergeDebevec();
    merge_debevec->process(images, hdr, times, response);

    Mat ldr;
    Ptr<Tonemap> tonemap = createTonemap(2.2f);
    tonemap->process(hdr, ldr);

    Mat fusion;
    Ptr<MergeMertens> merge_mertens = createMergeMertens();
    merge_mertens->process(images, fusion);

    imwrite("fusion.png", fusion * 255);
    imwrite("ldr.png", ldr * 255);
    imwrite("hdr.hdr", hdr);

    return 0;
}

void loadExposureSeq(String path, vector<Mat>& images, vector<float>& times)
{
    path = path + "/";
    ifstream list_file((path + "list.txt").c_str());
    string name;
    float val;
    while(list_file >> name >> val) {
        Mat img = imread(path + name);
        images.push_back(img);
        times.push_back(1 / val);
    }
    list_file.close();
}

示例图像

可以从这里list.txt下载包含图像、曝光时间和文件的数据目录。

解释

 

  • 加载图像和曝光时间
    vector<Mat> images;
    vector<float> times;
    loadExposureSeq(parser.get<String>( "@input" ), images, times);

首先,我们从用户定义的文件夹加载输入图像和曝光时间。该文件夹应包含图像和list.txt - 包含文件名和逆曝光时间的文件。

对于我们的图像序列,列表如下:

memorial00.png 0.03125
memorial01.png 0.0625
...
memorial15.png 1024
  • 估计相机响应
    Mat response;
    Ptr<CalibrateDebevec> calibrate = createCalibrateDebevec();
    calibrate->process(images, response, times);

对于许多 HDR 构建算法来说,了解相机响应函数 (CRF) 是必需的。我们使用其中一种校准算法来估计所有 256 个像素值的逆 CRF。

  • 制作 HDR 图像
垫高动态范围;
merge_debevec->process(图像,hdr,时间,响应);

我们使用 Debevec 的加权方案,利用上一项计算的响应来构建 HDR 图像。

  • 色调映射 HDR 图像
ldr;
Ptr<Tonemap> tonemap = createTonemap (2.2f);
色调映射->处理(hdr,ldr);

由于我们希望在普通 LDR 显示器上看到结果,因此我们必须将 HDR 图像映射到 8 位范围以保留大多数细节。这是色调映射方法的主要目标。我们使用具有双边滤波的色调映射器,并将 2.2 设置为伽马校正的值。

  • 执行曝光融合
垫层融合;
merge_mertens->处理(图像,融合);

如果我们不需要 HDR 图像,还有另一种方法来合并曝光。这个过程称为曝光融合,可生成不需要伽马校正的 LDR 图像。它也不使用照片的曝光值。

  • 写入结果
imwrite(“fusion.png”,fusion*255);
imwrite(“ldr.png”,ldr*255);
imwrite(“hdr.hdr”,hdr);

现在是时候查看结果了。请注意,HDR 图像不能以常见的图像格式之一存储,因此我们将其保存为 Radiance 图像 (.hdr)。此外,所有 HDR 成像函数都会在 [0, 1] 范围内返回结果,因此我们应该将结果乘以 255。

您可以尝试其他色调映射算法:cv::TonemapDragocv::TonemapMantiukcv::TonemapReinhard您还可以根据自己的照片调整 HDR 校准和色调映射方法中的参数。

结果

色调映射图像

曝光融合

其他资源

  1. Paul E Debevec 和 Jitendra Malik。从照片中恢复高动态范围辐射图。ACM SIGGRAPH 2008 课程,第 31 页。ACM,2008 年。[68]
  2. Mark A Robertson、Sean Borman 和 Robert L Stevenson。通过多次曝光实现动态范围改善。《图像处理》,1999 年。ICIP 99。会议论文集。1999 年国际会议论文集,第 3 卷,第 159-163 页。IEEE,1999 年。[227]
  3. Tom Mertens、Jan Kautz 和 Frank Van Reeth。曝光融合。《计算机图形学与应用》,2007 年。PG'07。第 15 届太平洋会议,第 382-390 页。IEEE,2007 年。[189]
  4. 维基百科-HDR
  5. 从照片中恢复高动态范围辐射图(网页)高动态范围成像从照片中恢复高动态范围辐射图(网页)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值