OpenCV高动态范围成像(HDR)介绍与使用

2021-11-04 17:08:29 浏览数 (1)

点击下方卡片,关注“OpenCV与AI深度学习

视觉/图像重磅干货,第一时间送达

来源:OpenCV4.5.4官方文档

翻译整理:Color Space

背景介绍

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

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

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

曝光顺序

源代码

代码语言:javascript复制
#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可以从这里下载包含图像、曝光时间和文件的数据目录。

解释说明

  • 加载图像和曝光时间
代码语言:javascript复制
vector<Mat> images;
vector<float> times;
loadExposureSeq(parser.get<String>( "@input" ), images, times);

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

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

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

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

  • 制作 HDR 图像
代码语言:javascript复制
Mat hdr;
Ptr<MergeDebevec> merge_debevec = createMergeDebevec();
merge_debevec->process(images, hdr, times, response);

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

  • 色调映射 HDR 图像
代码语言:javascript复制
Mat ldr;
Ptr<Tonemap> tonemap = createTonemap(2.2f);
tonemap->process(hdr, ldr);

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

  • 执行曝光融合
代码语言:javascript复制
Mat fusion;
Ptr<MergeMertens> merge_mertens = createMergeMertens();
merge_mertens->process(images, fusion);

当我们不需要 HDR 图像时,有一种替代方法可以合并我们的曝光。此过程称为曝光融合,可生成不需要伽马校正的 LDR 图像。它也不使用照片的曝光值。

  • 写入结果
代码语言:javascript复制
imwrite ( "fusion.png" , fusion * 255);
imwrite ( "ldr.png" , ldr * 255);
imwrite ( "hdr.hdr" , hdr);

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

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

结果

  • 色调映射图像

  • 曝光融合
  • 参考链接: https://docs.opencv.org/4.5.4/d3/db7/tutorial_hdr_imaging.html https://blog.csdn.net/LuohenYJ/article/details/89712234

—版权声明—

仅用于学术分享,版权属于原作者。

如有侵权,请联系微信号:Color_Space_001 删除或修改!

0 人点赞