阅读(4936) (9)

高动态范围成像

2017-10-20 11:07:00 更新

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

有不同的获取HDR图像的方法,但最常见的方法是使用不同曝光值拍摄的场景照片。要结合这种曝光,了解您的相机的响应功能是有用的,并且有算法来估计它。HDR图像混合后,必须将其转换回8位才能在通常的显示屏上进行查看。这个过程叫做tonemapping。当场景或相机的对象在拍摄之间移动时,会出现额外的复杂性,因为具有不同曝光的图像应该被注册和对准。

在本教程中,我们将介绍如何从曝光序列生成和显示HDR图像。在我们的情况下,图像已经对齐,没有移动的对象。我们还展示了一种称为曝光融合的替代方法,产生低动态范围图像。HDR管道的每一步都可以使用不同的算法来实现,所以看看参考手册来看看。

曝光顺序

高动态范围成像

源代码

#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, char**argv)
{
    vector<Mat> images;
    vector<float> times;
    loadExposureSeq(argv[1], 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<TonemapDurand> tonemap = createTonemapDurand(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 + std::string("/");
    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();
}

说明

  • 加载图像和曝光时间
vector<Mat> images;
vector<float> times;
loadExposureSeq(argv[1], 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图像
Mat hdr;
Ptr<MergeDebevec> merge_debevec = createMergeDebevec();
merge_debevec->process(images, hdr, times, response);

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

  • Tonemap HDR图像

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

由于我们希望通过LDR显示我们的结果,我们必须将我们的HDR图像映射到保留大部分细节的8位范围。这是吨位法的主要目标。我们使用tonemapper进行双边滤波,并将2.2设置为伽马校正值。

  • 进行曝光融合

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

如果我们不需要HDR图像,可以选择合并我们的曝光。该过程称为曝光融合,并产生不需要伽马校正的LDR图像。它也不使用照片的曝光值。

  • 写结果

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

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

结果

回收图像

高动态范围成像

曝光融合

高动态范围成像