实例应用:基于opencv的单张图像去雾算法
Posted OpenCV学习交流
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实例应用:基于opencv的单张图像去雾算法相关的知识,希望对你有一定的参考价值。
1、暗原色方法的缺陷
使用暗原色先验知识获取到的t估计图关键就是利用最小值滤波器求去最小值,而这个过程受到滤波核ksize的影响较大,滤波核选取的比较小时,t估计图细节较多,层次感好。但是平滑较少,局部错误增多。而滤波核ksize取值较大时,会得到分布过于单一的暗原色图,致使t估计图的细节和层次感不够明显,不能有效区分距离的远近。由于多数真实图像的雾气浓度分布不均,使得去雾效果不明显。
这些因素决定对于一些场景比较复杂的图像来说得不到较为理想的结果,例如下面的图片:
图左侧为t预估图:可以看到很多图像细节都被方格化而导致丢失。右侧为去雾效果 明显失真
2、导向滤波优化t值
查阅论文有很多改进的t值求取方法。其中导向滤波、双边滤波有较好的效果,当引导图G与输入图P为同一个图像的时候,导向图滤波的效果与双边滤波的效果类似,但是不同于双边滤波的是,导向图滤波可以很容易设计一个与滤波半径无关的优化算法。现状拟采用导向滤波来优化t值。导向滤波的理论在百度百科有很详细的介绍(导向滤波链接)。这里在简单罗列一下:
导向滤波技术是一种图像滤波技术,通过一张引导图G(导向图),对目标图像P(输入图像)进行滤波处理,使得最后的输出图像大体上与目标图像P相似,但是纹理部分与引导图G相似。
记引导图(导向图)为G,输入图像为P,输出图像为Q,导向图滤波的目标就是使得原始的输入和输出尽可能相同,同时纹理部分与引导图G相似。
对于目标1:输入图像P和输出图像Q尽可能相似,可以用公式描述为:
对于目标2:输出图像Q的纹理和引导图G尽可能相似,用公式描述为:
上式中如果Q为单通道图像,G为多通道,那么a为一个向量;如果Q和G通道数相同,那么a为标量或者对角矩阵。显然,a的值越小,最后的输出图像也就越平滑。
对于目标2,可以对等式两边取定积分,从而得到:
我们的目标就是求取a和b两个值,下面给出a、b值的求取公式:
导向滤波优化后的t预估图:
3、代码实现
导向图为原始图像的灰度图,这样就可以保证纹理部分与原图相似。而输入就是暗原色先验知识求的的t预估图。导向滤波的滤波核选取是一个很重要的问题,这里根据参考文献1做了选择优化 取最小值滤波核的4倍。
int main() { int r=ksize*8; //导向滤波核 注意:该值一定要取得足够大 Mat gray; cvtColor(src,gray,CV_BGR2GRAY); //经过测试导向图去min(r,g,b)要比去灰度图好 //[2] --求取投射率 导向图为原始图的灰度图 输入为t Mat guild_t; guild_t=guildFilter(gray,t,r); //double eps =1.0e-5; //guild_t=guidedFilter2(gray,t,r,eps); imshow("guild_T_Image",guild_t); //[2] //[3] --根据去雾公式求取去雾图像 J=(I-(1-t)*A)/max(t,min_t) //这一步很重要, 因为要做数学运算,所以先要将图像类设置为double 以防数据部分丢失 Mat deFog1=Mat::zeros(src.rows,src.cols,CV_64FC3); for(int i=0;i<src.rows;i++) for(int j=0;j<src.cols;j++) for(int c=0;c<src.channels();c++) deFog1.at<Vec3d>(i,j)[c]=(src.at<Vec3b>(i,j)[c]- (1-guild_t.at<double>(i,j))*A)/ std::max(guild_t.at<double>(i,j),min_t); deFog1.convertTo(deFog1,CV_8UC3); imshow("guild_T_deFog",deFog1); waitkey(); return 0; } //导向滤波 Mat guildFilter(cv::Mat g, cv::Mat p, int ksize) { const double eps =1.0e-5;//regularization parameter //类型转换 Mat _g; g.convertTo(_g,CV_64FC1); g=_g; Mat _p; p.convertTo(_p,CV_64FC1); p=_p; //[hei, wid] = size(I); int hei = g.rows; int wid = g.cols; //N = boxfilter(ones(hei, wid), r); % the size of each local patch; N=(2r+1)^2 except for boundary pixels. cv::Mat N; cv::boxFilter(cv::Mat::ones(hei, wid, g.type()), N, CV_64FC1,Size(ksize,ksize)); //[1] --使用均值模糊求取各项系数 Mat mean_G; boxFilter(g,mean_G,CV_64FC1,Size(ksize,ksize)); Mat mean_P; boxFilter(p,mean_P,CV_64FC1,Size(ksize,ksize)); Mat GP=g.mul(p); Mat mean_GP; boxFilter(GP,mean_GP,CV_64FC1,Size(ksize,ksize)); Mat GG=g.mul(g); Mat mean_GG; boxFilter(GG,mean_GG,CV_64FC1,Size(ksize,ksize)); Mat cov_GP; cov_GP=mean_GP-mean_G.mul(mean_P); Mat var_G; var_G=mean_GG-mean_G.mul(mean_G); //[1] //求系数a a=(mean(GP)-mean(G)mean(p))/(mean(GG)-mean(G)mean(G)+eps) Mat a=cov_GP/(var_G+eps); //求系数b b=mean(P)-mean(G)*a Mat b=mean_P-a.mul(mean_G); //求两个系数的均值 Mat mean_a; boxFilter(a,mean_a,CV_64FC1,Size(ksize,ksize)); //mean_a=mean_a/N; Mat mean_b; boxFilter(b,mean_b,CV_64FC1,Size(ksize,ksize)); //mean_b=mean_b/N; //输出结果q Mat q=mean_a.mul(g)+mean_b;//满世界找 错误原来在这里 !!大哥乘以的是导向图 不是输入 qDebug()<<q.at<double>(100,100); return q; }
4、结果:
5、注意问题:
1、在对图像像素做数学运算时,要将值的类型转换为double类型,在int类型下做除法很容易丢失数据 导致结果不正确。
2、如果将图像转换为浮点类型,那么其数值范围在0-1之间才是有意义的,也就是说只有转化为0-1(归一化)才能imshow出真确的结果 参考文献
3、实际测试时 导向图使用原图的min[r,g,b]貌似比使用原图的gray效果好一些,没有大量验证,不能最终确定
更新:
本人将该算法完善后,用Qt实现了一个简单的GUI应用程序,效果如下:
https://github.com/pencilmonster/defog
只上传源码 相关联的库包未给出 可以根据readme中的介绍下载对应库包
参考文献:
1、点击打开链接
2、导向滤波
3、float取值范围
4、opencv CV_32FC1类型Mat的元素访问与显示
关注【OpenCV学习交流】
以上是关于实例应用:基于opencv的单张图像去雾算法的主要内容,如果未能解决你的问题,请参考以下文章