1.简介
Mertens et al. (2009)算法通过考虑图像的亮度、对比度和细节来融合曝光时间不同的图像。它能够有效地处理高对比度场景,并在合成图像中保留细节和阴影细节。
2.Mertens算法原理
-
图像预处理:
- 去除暗角效应(vignetting):通过校正图像的亮度不均匀性来去除暗角效应,可以使用高斯模糊或均值滤波等方法。
- 白平衡校准:使用灰度世界假设或基于灰度世界的方法来校正图像的颜色偏移,使其具有更准确的颜色平衡。
- 响应函数校准:根据相机的响应函数,对图像进行校准,从而消除曝光时间不同引起的响应差异。
-
曝光融合:
- 图像对齐:使用图像对齐算法将不同曝光的图像进行对齐,以使特征点对齐。可以使用特征点匹配算法,如SIFT、SURF、ORB等来实现。
- 多尺度图像融合:使用拉普拉斯金字塔实现多尺度图像融合。对于每个曝光的图像,生成一系列金字塔图像,然后将对应层的图像进行融合,得到一系列融合后的图像。
- 融合权重计算:通过计算每个像素的融合权重来确定不同曝光图像的贡献度。权重可以根据像素的亮度、对比度和细节等进行计算。
-
色调映射:
- 色调映射函数:根据用户的偏好和显示设备的约束,选择合适的色调映射函数,如线性映射、曲线映射、全局映射或局部映射等。
- 色调映射操作:将HDR图像通过选定的色调映射函数进行映射,得到最终的输出图像。在映射过程中,可以调整图像的亮度、对比度和颜色饱和度等。
3.opencv中的单通道图像的Mertens算法实现
void cjhdr::process(cv::InputArrayOfArrays src, cv::OutputArray dst)
{
std::vector<cv::Mat> images;
src.getMatVector(images);
cv::Size size = images[0].size();
int CV_32FCC = CV_MAKETYPE(CV_32F, 1);
std::vector<cv::Mat> weights(images.size());
cv::Mat weight_sum = cv::Mat::zeros(size, CV_32F);
std::vector<cv::Mat> gray(3);
for(size_t i = 0; i < images.size(); i++)
{
images[i].convertTo(gray[i], CV_32F, 1.0f/255.0f);//3.859
}
for(size_t i = 0; i < images.size(); i++)
{
cv::Mat contrast, wellexp;
std::vector<cv::Mat> splitted(1);
cv::Laplacian(gray[i], contrast, CV_32F);
contrast = cv::abs(contrast);
cv::pow(contrast, 2.0f, contrast);
wellexp = cv::Mat::ones(size, CV_32F);
cv::split(gray[i], splitted);
cv::Mat expo = splitted[0] - 0.5f;
cv::pow(expo, 2.0f, expo);
expo = -expo / 0.08f;
cv::exp(expo, expo);
wellexp = wellexp.mul(expo);
cv::pow(wellexp, 3.0f, wellexp);
weights[i] = contrast;
weights[i] = weights[i].mul(wellexp);
weight_sum += weights[i];
}
int maxlevel = static_cast<int>(logf(static_cast<float>(cv::min(size.width, size.height))) / logf(2.0f));//7us
std::vector<cv::Mat> res_pyr(maxlevel + 1);
for(size_t i = 0; i < images.size(); i++)
{
weights[i] /= weight_sum;
std::vector<cv::Mat> img_pyr, weight_pyr;
cv::buildPyramid(gray[i], img_pyr, maxlevel);
cv::buildPyramid(weights[i], weight_pyr, maxlevel);
for(int lvl = 0; lvl < maxlevel; lvl++)
{
cv::Mat up;
cv::pyrUp(img_pyr[lvl + 1], up, img_pyr[lvl].size());
img_pyr[lvl] -= up;
}
for(int lvl = 0; lvl <= maxlevel; lvl++)
{
std::vector<cv::Mat> splitted(1);
cv::split(img_pyr[lvl], splitted);
splitted[0] = splitted[0].mul(weight_pyr[lvl]);
cv::merge(splitted, img_pyr[lvl]);
if(res_pyr[lvl].empty())
{
res_pyr[lvl] = img_pyr[lvl];
}
else
{
res_pyr[lvl] += img_pyr[lvl];
}
}
}
for(int lvl = maxlevel; lvl > 0; lvl--)
{
cv::Mat up;
cv::pyrUp(res_pyr[lvl], up, res_pyr[lvl - 1].size());
res_pyr[lvl - 1] += up;
}
dst.create(size, CV_32FCC);
res_pyr[0].copyTo(dst);
}
4.说明
首先,从输入数组src
中获取图像数据,并将其转换为CV_32F
格式的灰度图像。然后,对每个图像进行以下处理步骤:
- 计算图像的拉普拉斯算子
- 对拉普拉斯算子结果取绝对值并进行平方处理
- 创建一个与图像相同大小的全1矩阵,并对图像进行减法操作,得到
expo
矩阵 - 将
expo
矩阵进行平方和指数运算,得到wellexp
矩阵 - 将计算得到的拉普拉斯平方结果和
wellexp
矩阵相乘,得到每个图像的权重矩阵 - 将所有权重矩阵相加,得到总的权重和矩阵
weight_sum
接下来,根据图像的大小计算出金字塔的最大层数maxlevel
,并创建一个保存处理结果的金字塔数组res_pyr
。
然后,对每个图像进行以下处理步骤:
- 将权重矩阵除以总的权重和矩阵,得到归一化的权重矩阵
- 使用
cv::buildPyramid
函数分别构建原图像和归一化权重矩阵的金字塔 - 对金字塔层级进行循环,从较高层级往下操作
- 使用
cv::pyrUp
函数对原图像金字塔进行上采样操作,并与下一层级的金字塔图像做差 - 将差值结果与对应层级的归一化权重矩阵相乘,并合并通道
- 将合并后的结果与之前层级的结果进行累加操作
- 使用
最后,对金字塔进行反向操作,使用cv::pyrUp
函数对金字塔图像进行上采样,并与较低层级的金字塔图像累加。文章来源:https://www.toymoban.com/news/detail-508351.html
最终,将处理结果保存在输出数组dst
中。文章来源地址https://www.toymoban.com/news/detail-508351.html
到了这里,关于opencv 下的 Mertens et al. (2009-hdr) 算法剖析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!