导向滤波之图像融合(C++版) Image Fusion with Guided Filtering
导向滤波之图像融合(C++版) Image Fusion with Guided Filtering本次代码效果图。关于这篇IEEE上高被引的论文的算法还原工作如下:首先关于这篇论文的思路分析,就是通过各种滤波之间的组合,筛选出两张图中各自细节丰富的那一部分,从而经行不同权值的融合,实现双重曝光的融合。如下图所示分为ABC三个步骤。下面按照步骤经行详细操作:首先A步:一、对原图I1和I2进行均值滤
导向滤波之图像融合(C++版) Image Fusion with Guided Filtering
本次代码效果图。
关于这篇IEEE上高被引的论文的算法还原工作如下:
首先关于这篇论文的思路分析,就是通过各种滤波之间的组合,筛选出两张图中各自细节丰富的那一部分,从而经行不同权值的融合,实现双重曝光的融合。如下图所示分为ABC三个步骤。
下面按照步骤经行详细操作:
首先A步:
一、对原图I1和I2进行均值滤波,由此得到基层(Base layer) B1和B2。
二、用原图I1和I2减去基层B1和B2得到细节层(Detali layer)D1和D2。
三、对原图I1和I2进行拉普拉斯滤波,由此得到显著分布图(Saliency map)S1和S2。
四、对S1和S2的每个像素点比较,选出对应像素点最大值得到S_max。
五、用S1和S2分别和S_max经行比较,若相等则为1,不等则为0,得到权重分布图(Weight map)P1和P2。
六、用原图I为导向,权重图P为被引导图,进行导向滤波,第一次滤波核为9,正则化参数为0.03,得到提炼的权重分布图(Refined weight mapWb1和Wb2),第二次滤波核为5,正则化参数为0.003,得到提炼的权重分布图(Refined weight mapWd1和Wd2)
七、将Wb1和Wb2相加得到Wb_max,Wd1和Wd2相加得到Wd_max。再将他们全部除以Wb_max得到归一化的Wb1、Wb2、Wd1、Wd2。
八、B1 x Wb1+ B2 x Wb2得到B_bar,D1 x Wd1+ D2 x Wd2得到D_bar
九、输出图像九等于B_bar+D_bar。
输出图像和原图像比较:
代码先不说,这篇论文我也是给予很高的厚望,但是最后发现,这种方法对于曝光度差别很大的图片效果很不错,但是一旦曝光度差异不大,那么就会出现亮区暗区过渡区间的色彩严重失真。影响观感。最后我仔细端详论文中这两幅图我发现,高曝光那个图,周围的颜色很单一,就是墙的颜色,低曝光那个图中间看似色彩丰富,但是恰恰这一点,让你发现不了里面的是真,妙啊真的妙。
后我自己尝试使用提取图像的光照分量,对光照分量进行单独的引导滤波融合后再与原来的图像融合。细节存在,但是颜色失真。想法失败。
#include<opencv2/opencv.hpp>
#include <iostream>
#include<vector>
using namespace std;
using namespace cv;
//--------------------------------------------------------------
//函数名:guidedFilter_oneChannel
//函数功能:用于单通道图像(灰度图)的引导滤波函数;
//参数:Mat srcImg:输入图像,为单通道图像;
//参数:Mat guideImg:引导图像,为单通道图像,尺寸与输入图像一致;
//参数:Mat dstImg:输出图像,尺寸、通道数与输入图像吻合;
//参数:const int rad:滤波器大小,应该保证为奇数,默认值为9;
//参数:const double eps :防止a过大的正则化参数,
bool guidedFilter_oneChannel(Mat srcImg, Mat guideImg, Mat dstImg, const int rad = 9, const double eps = 0.01)
{
//--------------确保输入参数正确-----------
{
if (!srcImg.data || srcImg.channels() != 1)
{
cout << "输入图像错误,请重新输入图像" << endl;
return false;
}
if (!guideImg.data || guideImg.channels() != 1)
{
cout << "输入引导图像错误,请重新输入图像" << endl;
return false;
}
if (guideImg.cols != srcImg.cols || guideImg.rows != srcImg.rows)
{
cout << "输入图像与引导图像尺寸不匹配,请重新确认" << endl;
return false;
}
if (dstImg.cols != srcImg.cols || dstImg.rows != srcImg.rows || dstImg.channels() != 1)
{
cout << "参数输出图像错误,请重新确认" << endl;
return false;
}
if (rad % 2 != 1)
{
cout << "参数“rad”应为奇数,请修改" << endl;
return false;
}
}
//--------------转换数值类型,并归一化-------------
srcImg.convertTo(srcImg, CV_32FC1, 1.0 / 255.0);
guideImg.convertTo(guideImg, CV_32FC1, 1.0 / 255.0);
//--------------求引导图像和输入图像的均值图
Mat mean_srcImg, mean_guideImg;
boxFilter(srcImg, mean_srcImg, CV_32FC1, Size(rad, rad));
boxFilter(guideImg, mean_guideImg, CV_32FC1, Size(rad, rad));
Mat mean_guideImg_square, mean_guideImg_srcImg;
boxFilter(guideImg.mul(guideImg), mean_guideImg_square, CV_32FC1, Size(rad, rad));
boxFilter(guideImg.mul(srcImg), mean_guideImg_srcImg, CV_32FC1, Size(rad, rad));
Mat var_guideImg = mean_guideImg_square - mean_guideImg.mul(mean_guideImg);
Mat cov_guideImg_srcImg = mean_guideImg_srcImg - mean_guideImg.mul(mean_srcImg);
Mat aImg = cov_guideImg_srcImg / (var_guideImg + eps);
Mat bImg = mean_srcImg - aImg.mul(mean_guideImg);
Mat mean_aImg, mean_bImg;
boxFilter(aImg, mean_aImg, CV_32FC1, Size(rad, rad));
boxFilter(bImg, mean_bImg, CV_32FC1, Size(rad, rad));
dstImg = (mean_aImg.mul(guideImg) + mean_bImg);
//dstImg.convertTo(dstImg, CV_8UC1, 255);//加这句最后返回的是CV_8UC1的图像
return true;
}
//--------------------------------------------------------------
//函数名:guidedFilter_threeChannel
//函数功能:用于三通道图像(RGB彩色图)的引导滤波函数;
//参数:Mat srcImg:输入图像,为三通道图像;
//参数:Mat guideImg:引导图像,为三通道图像,尺寸与输入图像一致;
//参数:Mat dstImg:输出图像,尺寸、通道数与输入图像吻合;
//参数:const int rad:滤波器大小,应该保证为奇数,默认值为9;
//参数:const double eps :防止a过大的正则化参数,
bool guidedFilter_threeChannel(Mat srcImg, Mat guideImg, Mat dstImg, const int rad = 9, const double eps = 0.01)
{
//----------------确保输入参数正确-------------
{
if (!srcImg.data || srcImg.channels() != 3)
{
cout << "输入图像错误,请重新输入图像" << endl;
return false;
}
if (!guideImg.data || guideImg.channels() != 3)
{
cout << "输入引导图像错误,请重新输入图像" << endl;
return false;
}
if (guideImg.cols != srcImg.cols || guideImg.rows != srcImg.rows)
{
cout << "输入图像与引导图像尺寸不匹配,请重新确认" << endl;
return false;
}
if (rad % 2 != 1)
{
cout << "参数“rad”应为奇数,请修改" << endl;
return false;
}
}
vector<Mat> src_vec, guide_vec, dst_vec;
split(srcImg, src_vec);
split(guideImg, guide_vec);
for (int i = 0; i < 3; i++)
{
Mat tempImg = Mat::zeros(srcImg.rows, srcImg.cols, CV_32FC1);
guidedFilter_oneChannel(src_vec[i], guide_vec[i], tempImg, rad, eps);
dst_vec.push_back(tempImg);
}
merge(dst_vec, dstImg);
return true;
}
//根据2013年论文"Image Fusion with Guided Filtering"引导滤波的图像融合
//double_exposure_HDR
//函数名:double_exposure_HDR
//函数功能:用于同一场景的不同曝光融合
//参数:Mat Mat I1:输入的短曝光图像光照分量
//参数:Mat Mat I2:输入的长曝光图像光照分量
//返回:Mat 融合图像
Mat double_exposure_HDR(Mat I1, Mat I2)
{
//论文中的A步Two-Scale Image Decomposition(双尺度分解)****************************************************
Mat B1, B2;
//average filtering得到基础层base layer
blur(I1, B1, Size(3,3));
blur(I2, B2, Size(3,3));
//imshow("短曝光基础层", B1);
//imshow("长曝光基础层", B2);
//相减得到细节层detail layer
Mat D1, D2;
D1 = I1 - B1;
D2 = I2 - B2;
//imshow("短曝光细节层", D1);
//imshow("长曝光细节层", D2);
//论文中的B步Weight Map Construction With Guided Filtering(构建权重图)*********************************
Mat H1;
Mat H2;
//对原图来一次拉普拉斯滤波
Laplacian(I1, H1,0.3);
Laplacian(I2, H2,0.3);
//排除负值
H1 = abs(H1);
H2 = abs(H2);
//imshow("短曝光拉普拉斯滤波", H1);
//imshow("长曝光拉普拉斯滤波", H2);
//高斯滤波一次
Mat S1, S2;
GaussianBlur(H1, S1, Size(11, 11), 9);
GaussianBlur(H2, S2, Size(11, 11), 9);
//imshow("短曝光显著像素分布图Saliency map", S1);
//imshow("长曝光显著像素分布图Saliency map", S2);
//再去根据论文的像素显著性 和 空间连续性 求权重图 P1 P2
Mat P1;
Mat P2;
P1 = Mat(I1.rows, I1.cols, CV_8UC3);
P2 = Mat(I1.rows, I1.cols, CV_8UC3);
//再求之前要求一个总的显著分布图s_max
Mat S_max;
S_max = Mat(I1.rows, I1.cols, CV_8UC3);
for (int k = 0; k < I1.channels(); k++)
{
for (int j = 0; j < I1.cols; j++)
{
for (int i = 0; i < I1.rows; i++)
{
S_max.at<Vec3b>(i, j)[k] = ((S1.at<Vec3b>(i, j)[k] > S2.at<Vec3b>(i, j)[k]) ? S1.at<Vec3b>(i, j)[k] : S2.at<Vec3b>(i, j)[k]);
}
}
}
//imshow("最大显著分布图", S_max);
//下面求那两个权重分布图P1,P2
for (int k = 0; k < I1.channels(); k++)
{
for (int j = 0; j < I1.cols; j++)
{
for (int i = 0; i < I1.rows; i++)
{
P1.at<Vec3b>(i, j)[k] = (S1.at<Vec3b>(i, j)[k] == S_max.at<Vec3b>(i, j)[k]) ? 1 : 0;
P2.at<Vec3b>(i, j)[k]= (S2.at<Vec3b>(i, j)[k] == S_max.at<Vec3b>(i, j)[k]) ? 1 : 0;
}
}
}
//imshow("短曝光权重分布图weight map", P1*255);//乘以255是为了可视化,白色代表1黑色代表0
//imshow("长曝光权重分布图weight map", P2*255);//乘以255是为了可视化,白色代表1黑色代表0
//这样我们就得到了关于各个源图像的粗略权重值。
//但是由于多个图像间容易产生噪声,并可能存在不完全对齐等问题,很容易造成融合后的图像有伪影等。
//因此在基于空间连续性的思想下,需要对得到的权重图进行导向滤波。
Mat Wb1, Wb2;
Wb1 = Mat(I1.rows, I1.cols, CV_32FC3);
Wb2 = Mat(I1.rows, I1.cols, CV_32FC3);
guidedFilter_threeChannel(P1, I1, Wb1,9, 0.03);
guidedFilter_threeChannel(P2, I2, Wb2,9, 0.03);
//imshow("短曝光提炼权重分布图refined weight map Wb1", Wb1*255);//乘以255是为了可视化
//imshow("长曝光提炼权重分布图redined weight map Wb2", Wb2*255);//乘以255是为了可视化
Mat Wd1, Wd2;
Wd1 = Mat(I1.rows, I1.cols, CV_32FC3);
Wd2 = Mat(I1.rows, I1.cols, CV_32FC3);
guidedFilter_threeChannel(P1, I1, Wd1, 5, 0.003);
guidedFilter_threeChannel(P2, I2, Wd2, 5, 0.003);
//imshow("短曝光提炼权重分布图refined weight map Wd1", Wd1*255);//乘以255是为了可视化
//imshow("长曝光提炼权重分布图redined weight map Wd2", Wd2*255);//乘以255是为了可视化
//得到A步骤中四个图对应的四个权重图
//论文中的C步Two-Scale Image Reconstruction(融合重建)****************************************************
Mat Wb_max, Wd_max;
Wb_max = Mat(I1.rows, I1.cols, CV_32FC3);
Wd_max = Mat(I1.rows, I1.cols, CV_32FC3);
Wb_max = Wb1 + Wb2;
Wd_max = Wd1 + Wd2;
//归一化
for (int k = 0; k < I1.channels(); k++)
{
for (int j = 0; j < I1.cols; j++)
{
for (int i = 0; i < I1.rows; i++)
{
Wb1.at<Vec3f>(i, j)[k] /= Wb_max.at<Vec3f>(i, j)[k];
Wb2.at<Vec3f>(i, j)[k] /= Wb_max.at<Vec3f>(i, j)[k];
Wd1.at<Vec3f>(i, j)[k] /= Wd_max.at<Vec3f>(i, j)[k];
Wd2.at<Vec3f>(i, j)[k] /= Wd_max.at<Vec3f>(i, j)[k];
}
}
}
//imshow("归一化短曝光提炼权重分布图Wb1", Wb1*255);
//imshow("归一化长曝光提炼权重分布图Wb2", Wb2*255);
//imshow("归一化短曝光提炼权重分布图Wd1", Wd1*255);
//imshow("归一化长曝光提炼权重分布图Wd2", Wd2*255);
Mat B_bar, D_bar;
B_bar = Mat(I1.rows, I1.cols, CV_8UC3);
D_bar = Mat(I1.rows, I1.cols, CV_8UC3);
for (int k = 0; k < I1.channels(); k++)
{
for (int j = 0; j < I1.cols; j++)
{
for (int i = 0; i < I1.rows; i++)
{
B_bar.at<Vec3b>(i, j)[k] = B1.at<Vec3b>(i, j)[k] * Wb1.at<Vec3f>(i, j)[k]
+ B2.at<Vec3b>(i, j)[k] * Wb2.at<Vec3f>(i, j)[k];
D_bar.at<Vec3b>(i, j)[k] = D1.at<Vec3b>(i, j)[k] * Wd1.at<Vec3f>(i, j)[k]
+ D2.at<Vec3b>(i, j)[k] * Wd2.at<Vec3f>(i, j)[k];
}
}
}
//融合输出
Mat out;
out = B_bar + D_bar;
//imshow("B_bar", B_bar);
//imshow("D_bar", D_bar);
return out;
}
int main()
{
Mat src_UE = imread("C:\\Users\\hl\\Desktop\\I1.jpg", IMREAD_COLOR);
Mat src_OE = imread("C:\\Users\\hl\\Desktop\\I2.jpg", IMREAD_COLOR);
Mat out;
out=double_exposure_HDR(src_UE,src_OE);
imshow("过曝光", src_OE);
imshow("欠曝光", src_UE);
imshow("引导滤波后", out);
waitKey();
return 0;
}
https://blog.csdn.net/u013921430/article/details/99695647
声明:关于导向滤波C++函数模块部分借鉴了不用先生 2019-08-18 10:28:05的一篇【图像处理】引导滤波(guided image filtering)
更多推荐


所有评论(0)