http://blog.csdn.net/cfqcfqcfqcfqcfq/article/details/52848087
最近对图像去雾算法产生了兴趣,查阅学习了大量论文和大牛博客后,决定动手用自己熟悉的opencv来编写程序完成。文章专门记录了具体算法实现过程以及其中遇见的大量问题。供自己以后参考,当然也希望能给广大博友提供一些参考。本文着重讲实现过程,图像去雾理论会同过参考文献形式给出。整个内容拟大概分成三个章节来介绍。
开发环境:
计算机系统:win10(64-bit)
编译器:MSVC 2013(64 bit)
Qt版本:5.4.2(64 bit)
OpenCV:3.0.0
1、去雾原理(简述)
雾图形成模型:
其中,I(X)就是我们现在已经有的图像(待去雾的图像),J(x)是我们要恢复的无雾的图像,A是全球大气光成分(未知), t(x)为透射率(未知)。现在的已知条件就是I(X),要求目标值J(x),显然,这是个有无数解的方程。所以要求解J(x),必须通过一系列的先验知识求取t(x)和A.
由先验知识,A是一个全局变量,它可以选取含雾图像的最大像素值来作为A值,这是最简单的办法,还有一些求解方法可以见参考文献1.
暗原色先验知识可以作为求取t(x)的理论依据。 该理论认为,在绝大多数户外无雾图像的局部区域里,至少存在一些像素, 其一个颜色通道的强度值很低。通过对每个像素块R、G和B三通道同时取最小化可求得Jdark,
具体的理论描述可以见参考文献1
t(x)的求解公式如下:
其中,后半部分是含雾图像的暗原色图。 w系数用来保留一部分雾。本文取w=0.9
通过一系列先验知识求取了A和t(x)就可以根据公式(1)的变形求取J(x),其中t0是透射率的最小值,防止出现为0的情况,本文取t0=0.1
2、代码实现:
有了上面的原理就可以实现一个基于暗原色先验的图像去雾算法。该算法的核心是设计最小值滤波函数(minFilter).该函数有一个重要的参数滤波核ksize需要设置,为了能适应不同图像的处理,采取了一种自适应滤波核,其思想也很简单:选取图像行、列的1%中的最大值作为滤波核,同时设置最小滤波核min_ksize=3作为下限 算法代码如下:
- #include <QCoreApplication>
- #include <opencv2/opencv.hpp>
- #include <vector>
- #include <QDebug>
- using namespace cv;
-
- const double kernRatio=0.01;
- const int minAtomLigth=220;
- const double wFactor=0.95;
- const double min_t =0.1;
- void minFilter(cv::Mat src, Mat &dst, int ksize=3);
- int main(int argc, char *argv[])
- {
- QCoreApplication a(argc, argv);
-
- Mat src = imread("f:/fog8.png");
- imshow("[picture]",src);
-
-
-
- Mat minRgb = Mat::zeros(src.rows,src.cols,CV_8UC1);
- for(int i=0;i<src.rows;i++)
- for(int j=0;j<src.cols;j++)
- {
- uchar g_minvalue =255;
- for(int c=0;c<3;c++)
- {
- if(g_minvalue>src.at<Vec3b>(i,j)[c])
- g_minvalue=src.at<Vec3b>(i,j)[c];
- }
- minRgb.at<uchar>(i,j)=g_minvalue;
- }
- imshow("min[r,g,b]",minRgb);
-
-
-
- Mat darkChannelImage;
- int ksize=std::max(3,std::max((int)(src.cols*kernRatio),(int)(src.rows*kernRatio)));
- minFilter(minRgb,darkChannelImage,ksize);
- imshow("Dark_Channel_Image",darkChannelImage);
-
-
-
- Mat t=Mat::zeros(src.rows,src.cols,CV_64FC1);
- for(int i=0;i<src.rows;i++)
- for(int j=0;j<src.cols;j++)
- {
- t.at<double>(i,j)=(255.0-
- (double)darkChannelImage.at<uchar>(i,j)*wFactor)/255;
- }
- imshow("T_Image",t);
-
-
-
- double A;Point maxLoc;
- minMaxLoc(darkChannelImage,0,&A,0,&maxLoc);
- A=std::max(src.at<Vec3b>(maxLoc.y,maxLoc.x)[0],
- std::max(src.at<Vec3b>(maxLoc.y,maxLoc.x)[1],
- src.at<Vec3b>(maxLoc.y,maxLoc.x)[2]));
-
-
-
- Mat deFog=Mat::zeros(src.rows,src.cols,CV_8UC3);
- for(int i=0;i<src.rows;i++)
- for(int j=0;j<src.cols;j++)
- for(int c=0;c<src.channels();c++)
- deFog.at<Vec3b>(i,j)[c]=(src.at<Vec3b>(i,j)[c]-
- (1-t.at<double>(i,j))*A)/
- std::max(t.at<double>(i,j),min_t);
- imshow("defog",deFog);
-
- void minFilter(const cv::Mat src,cv::Mat &dst,int ksize)
- {
-
- if(src.channels()!=1)
- return;
- if(src.depth()>8)
- return;
-
-
- int r=(ksize-1)/2;
-
- dst=Mat::zeros(src.rows,src.cols,CV_8UC1);
-
- for(int i=0;i<src.rows;i++)
- for(int j=0;j<src.cols;j++)
- {
-
- int top=i-r;
- int bottom=i+r;
- int left=j-r;
- int right=j+r;
-
-
-
- if(i-r<0)
- top=0;
- if(i+r>src.rows)
- bottom=src.rows;
- if(j-r<0)
- left=0;
- if(j+r>src.cols)
- right=src.cols;
-
-
-
- Mat ImROI=src(Range(top,bottom),Range(left,right));
- double min,max;
- minMaxLoc(ImROI,&min,&max,0,0);
- dst.at<uchar>(i,j)=min;
-
- }
-
- }
3、效果图:
注:左边为原图 中间为投射率图 右面为去雾图像
参考文献:
【1】去雾原理