本文主要是记录个人的开发过程,及过程中遇到的问题及解决方法,对个人工作的一个总结。
一、HDR图像合成
由不同曝光设置拍摄的多张图像创建高动态范围High Dynamic Range(HDR)图像。
使用的是c++和OpenCV4.x。
主程序主要参考对象:使用 OpenCV 进行高动态范围(HDR)成像
1.捕获不同曝光度的多张图像
使用相机拍摄三张曝光时间不同的图像
- 曝光不足的图像:该图像比正确曝光的图像更暗。 目标是捕捉非常明亮的图像部分。
- 正确曝光的图像:这是相机将根据其估计的照明拍摄的常规图像。
- 曝光过度的图像:该图像比正确曝光的图像更亮。 目标是拍摄非常黑暗的图像部分。
如果场景的动态范围很大,我们可以拍摄三张以上的图片来合成 HDR 图像。
读取不同曝光时间的图像:
void readImagesAndTimes(vector<Mat> &images, vector<float> ×)
{
int numImages = 3;
static const float timesArray[] = {
0.000038, 0.000304, 0.003432 }; // 单位秒
times.assign(timesArray, timesArray + numImages); // 将timesArray里的值全部赋给times
static const char* filenames[] = {
"./pic/welding/10013_2_38.jpg", "./pic/welding/10013_0_304.jpg", "./pic/welding/10013_1_3432.jpg" };
for(int i=0; i < numImages; i++)
{
Mat im = imread(filenames[i]); //默认读取BGR格式
images.push_back(im); //全部存储在images
}
}
2.对齐图像
合成 HDR 图像时使用的图像如果未对齐可能会导致严重的伪影。
OpenCV 提供了一种简单的方法,使用 AlignMTB 对齐这些图像。 该算法将所有图像转换为中值阈值位图median threshold bitmaps(MTB)。 图像的 MTB 生成方式为将比中值亮度的更亮的分配为 1,其余为 0。 MTB 不随曝光时间的改变而改变。 因此不需要我们指定曝光时间就可以对齐 MTB。
// Align input images 中值阈值位图MTB方式 对齐图片
cout << "Aligning images ... " << endl;
Ptr<AlignMTB> alignMTB = createAlignMTB();
alignMTB->process(images, images);
3.提取相机响应函数
典型相机的响应与场景亮度不成线性关系。
假设有两个物体由同一个相机拍摄,在现实世界中其中一个物体是另一个物体亮度的两倍。 当您测量照片中两个物体的像素亮度时,较亮物体的像素值将不会是较暗物体的两倍。 在不估计相机响应函数Camera Response Function(CRF)的情况下,我们将无法将图像合并到一个HDR图像中。
如果我们知道每个图像的曝光时间,则可以从图像估计 CRF。
使用 OpenCV 的 CalibrateDebevec 或者 CalibrateRobertson 就可以用 2 行代码找到 CRF。本篇使用 CalibrateDebevec。
// Obtain Camera Response Function (CRF) 提取相机响应函数
cout << "Calculating Camera Response Function (CRF) ... " << endl;
Mat responseDebevec;
Ptr<CalibrateDebevec> calibrateDebevec = createCalibrateDebevec();
calibrateDebevec->process(images, responseDebevec, times);
4.合并图像
一旦 CRF 评估结束,可以曝光图像合并成一个HDR图像。
分别使用了两种方法合成HDR图像。
- Debevec方法合并图像
- 输入图片、曝光时间、响应函数
// Debevec方法合并图像
Mat Merging_Debevec(vector<Mat>& images, vector<float>& times, Mat& responseDebevec)
{
cout << "Merging images into one HDR image ... " << endl;
Mat hdrDebevec;
Ptr<MergeDebevec> mergeDebevec = createMergeDebevec();
mergeDebevec->process(images, hdrDebevec, times, responseDebevec);
// Save HDR image. 保存图像
imwrite("./pic/welding/hdrDebevec.hdr", hdrDebevec);
cout << "saved hdrDebevec.hdr " << endl;
return hdrDebevec;
}
// Robertson方法合并图像
Mat Merging_Robertson(vector<Mat>& images, vector<float>& times, Mat& responseDebevec)
{
Mat hdrRobertson;
Ptr<MergeRobertson> mergeRobertson = createMergeRobertson();
mergeRobertson->process(images, hdrRobertson, times, responseDebevec);
// Save HDR image. 保存图像
imwrite("./pic/welding/hdrRobertson.hdr", hdrRobertson);
cout << "saved hdrRobertson.hdr " << endl;
return hdrRobertson;
}
保存的 HDR 图像可以在 Photoshop 中加载并进行色调映射。
5.色调映射
将高动态范围(HDR)图像转换为 8 位单通道图像的过程称为色调映射。这个过程的同时还需要保留尽可能多的细节。
有几种色调映射算法,OpenCV 实现了其中的四个。
没有一个绝对正确的方法来做色调映射。 通常,我们希望在色调映射图像中看到比任何一个曝光图像更多的细节。 有时色调映射的目标是产生逼真的图像,而且往往是产生超现实图像的目标。 在 OpenCV 中实现的算法倾向于产生现实的并不那么生动的结果。
来看看各种选项。 以下列出了不同色调映射算法的一些常见参数。
- 伽马gamma:该参数通过应用伽马校正来压缩动态范围。 当伽马等于 1 时,不应用修正。 小于 1 的伽玛会使图像变暗,而大于 1 的伽马会使图像变亮。
- 饱和度saturation:该参数用于增加或减少饱和度。 饱和度高时,色彩更丰富,更浓。 饱和度值接近零,使颜色逐渐消失为灰度。
- 对比度contrast:控制输出图像的对比度(即 log(maxPixelValue/minPixelValue))。
(1)Drago 色调映射
Drago 色调映射的参数如下
createTonemapDrago(
float gamma = 1.0f,
float saturation = 1.0f,
float bias = 0.85f
)
bias 是 [0, 1] 范围内偏差函数的值。 从 0.7 到 0.9 的值通常效果较好。 默认值是 0.85。
下面是使用方式。
最后的结果乘以 3 只是因为它给出了最令人满意的结果。
// Drago算法色调映射
Mat Tonemap_DDrago(Mat& hdr)
{
cout << "Tonemaping using Drago's method ... " << endl;
Mat ldrDDrago;
Ptr<TonemapDrago> tonemapDrago = createTonemapDrago(1.0, 0.7);
tonemapDrago->process(hdr, ldrDDrago);
ldrDDrago = 3 * ldrDDrago;
imwrite("./pic/welding/ldrD-Drago.jpg", ldrDDrago * 255);
cout << "saved ldr-Drago.jpg" << endl;
return ldrDDrago;
}
(2)Durand 色调映射
该算法需付费,这里没有使用。
算法参数及使用在参考文章有。
(3)Reinhard 色调映射
Reinhard 色调映射的参数如下所示。
createTonemapReinhard(
float gamma = 1.0f,
float intensity = 0.0f,
float light_adapt = 1.0f,
float color_adapt = 0.0f
)
参数说明:
- intensity 参数应在 [-8, 8] 范围内。 更高的亮度值会产生更明亮的结果。
- light_adapt 控制灯光,范围为 [0, 1]。 值 1 表示仅基于像素值的自适应,而值 0 表示全局自适应。 中间值可以用于两者的加权组合。
- 参数 color_adapt 控制色彩,范围为 [0, 1]。 如果值被设置为 1,则通道被独立处理,如果该值被设置为 0,则每个通道的适应级别相同。中间值可以用于两者的加权组合。
下面是使用方式。
// Reinhard算法色调映射
Mat Tonemap_DReinhard(Mat&