最近对图像去雾算法产生了兴趣,查阅学习了大量论文和大牛博客后,决定动手用自己熟悉的opencv来编写程序完成。文章专门记录了具体算法实现过程以及其中遇见的大量问题。供自己以后参考,当然也希望能给广大博友提供一些参考。本文着重讲实现过程,图像去雾理论会同过参考文献形式给出。整个内容拟大概分成三个章节来介绍。

开发环境:

计算机系统:win10(64-bit) 


   编译器:MSVC 2013(64 bit)

   Qt版本:5.4.2(64 bit)

   OpenCV:3.0.0


   1、去雾原理(简述)

 

opencv 算法的烟雾检测 opencv图像去雾算法_dehaze

 其中,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, 

opencv 算法的烟雾检测 opencv图像去雾算法_dehaze_02

具体的理论描述可以见参考文献1

     t(x)的求解公式如下:

      

opencv 算法的烟雾检测 opencv图像去雾算法_最小值_03

      其中,后半部分是含雾图像的暗原色图。 w系数用来保留一部分雾。本文取w=0.9

      通过一系列先验知识求取了A和t(x)就可以根据公式(1)的变形求取J(x),其中t0是透射率的最小值,防止出现为0的情况,本文取t0=0.1

                

opencv 算法的烟雾检测 opencv图像去雾算法_opencv_04


        2、代码实现:

           有了上面的原理就可以实现一个基于暗原色先验的图像去雾算法。该算法的核心是设计最小值滤波函数(minFilter).该函数有一个重要的参数滤波核ksize需要设置,为了能适应不同图像的处理,采取了一种自适应滤波核,其思想也很简单:选取图像行、列的1%中的最大值作为滤波核,同时设置最小滤波核min_ksize=3作为下限  算法代码如下:

1. #include <QCoreApplication>  
2. #include <opencv2/opencv.hpp>  
3. #include <vector>  
4. #include <QDebug>  
5. using namespace cv;  
6.   
7. const double kernRatio=0.01; //自适应核比例  
8. const int minAtomLigth=220; //最小大气光强  
9. const double wFactor=0.95; //w系数 用来调节  
10. const double min_t =0.1; //最小透射率  
11. void minFilter(cv::Mat src, Mat &dst, int ksize=3);
12. int main(int argc, char *argv[])  
13. {  
14.     QCoreApplication a(argc, argv);  
15. //读取数据  
16. "f:/fog8.png");  
17. "[picture]",src);  
18.   
19. //--------------------基于暗原色先验去雾算法---------------------  
20. //[1] --求取min(R,G,B)  
21.     Mat minRgb = Mat::zeros(src.rows,src.cols,CV_8UC1);  
22. for(int i=0;i<src.rows;i++)  
23. for(int j=0;j<src.cols;j++)  
24.         {  
25.             uchar g_minvalue =255;  
26. for(int c=0;c<3;c++)  
27.             {  
28. if(g_minvalue>src.at<Vec3b>(i,j)[c])  
29.                     g_minvalue=src.at<Vec3b>(i,j)[c];  
30.             }  
31.             minRgb.at<uchar>(i,j)=g_minvalue;  
32.         }  
33. "min[r,g,b]",minRgb);  
34. //[1]  
35.   
36. //[2] --求取暗源色图像  
37.     Mat darkChannelImage;  
38. int ksize=std::max(3,std::max((int)(src.cols*kernRatio),(int)(src.rows*kernRatio))); //求取自适应核大小  
39.     minFilter(minRgb,darkChannelImage,ksize);  
40. "Dark_Channel_Image",darkChannelImage);  
41. //[2]  
42.   
43. //[3] --求取投射率图(根据暗原色先验原理)  
44.     Mat t=Mat::zeros(src.rows,src.cols,CV_64FC1);  
45. for(int i=0;i<src.rows;i++)  
46. for(int j=0;j<src.cols;j++)  
47.         {  
48. double>(i,j)=(255.0-  
49. double)darkChannelImage.at<uchar>(i,j)*wFactor)/255;  
50.         }  
51. "T_Image",t);//浮点图像 如果要显示 只有范围在0到1之间才有意义  
52. //[3]  
53.   
54. //[4] --求取全球大气光强A(全局量)  
55. double A;Point maxLoc;  
56.     minMaxLoc(darkChannelImage,0,&A,0,&maxLoc);  
57.     A=std::max(src.at<Vec3b>(maxLoc.y,maxLoc.x)[0],  
58.             std::max(src.at<Vec3b>(maxLoc.y,maxLoc.x)[1],  
59.             src.at<Vec3b>(maxLoc.y,maxLoc.x)[2]));  
60. //[4]  
61.   
62. //[5] --根据去雾公式求取去雾图像  J=(I-(1-t)*A)/max(t,min_t)  
63.     Mat deFog=Mat::zeros(src.rows,src.cols,CV_8UC3);  
64. for(int i=0;i<src.rows;i++)  
65. for(int j=0;j<src.cols;j++)  
66. for(int c=0;c<src.channels();c++)  
67.                 deFog.at<Vec3b>(i,j)[c]=(src.at<Vec3b>(i,j)[c]-  
68. double>(i,j))*A)/  
69. double>(i,j),min_t);  
70. "defog",deFog);  
71. //[5]<pre name="code" class="cpp">//最小值滤波 模式模板大小为3*3  
72. void minFilter(const cv::Mat src,cv::Mat &dst,int ksize)  
73. {  
74. //[1] --检测原始图像  
75. if(src.channels()!=1)  
76. return;  
77. if(src.depth()>8)  
78. return;  
79. //[1]  
80.   
81. int r=(ksize-1)/2; //核半径  
82. //初始化目标图像  
83.     dst=Mat::zeros(src.rows,src.cols,CV_8UC1);  
84. //[3] --最小值滤波  
85. for(int i=0;i<src.rows;i++)  
86. for(int j=0;j<src.cols;j++)  
87.         {  
88. //[1] --初始化滤波核的上下左右边界  
89. int top=i-r;  
90. int bottom=i+r;  
91. int left=j-r;  
92. int right=j+r;  
93. //[1]  
94.   
95. //[2] --检查滤波核是否超出边界  
96. if(i-r<0)  
97.                 top=0;  
98. if(i+r>src.rows)  
99.                 bottom=src.rows;  
100. if(j-r<0)  
101.                 left=0;  
102. if(j+r>src.cols)  
103.                 right=src.cols;  
104. //[2]  
105.   
106. //[3] --求取模板下的最小值  
107.             Mat ImROI=src(Range(top,bottom),Range(left,right));  
108. double min,max;  
109.             minMaxLoc(ImROI,&min,&max,0,0);  
110.             dst.at<uchar>(i,j)=min;  
111. //[3]  
112.         }  
113. //[3]  
114. }




3、效果图:

注:左边为原图  中间为投射率图  右面为去雾图像

opencv 算法的烟雾检测 opencv图像去雾算法_opencv_05

opencv 算法的烟雾检测 opencv图像去雾算法_最小值_06

opencv 算法的烟雾检测 opencv图像去雾算法_opencv_07


参考文献:

【1】去雾原理