拜读 何恺明 博士的论文,醍醐灌顶。
    Single_Image_Haze_Removal_Using_Dark_Channel_Prior.pdf
    暗通道先验滤波方法
    水下去雾.pdf
    实现代码

    1. #include <opencv2/opencv.hpp>
    2. #include<opencv2/imgproc/imgproc.hpp>
    3. #include<opencv2/highgui/highgui.hpp>
    4. #include<opencv2/core/core.hpp>
    5. #include<iostream>
    6. void makeDepth32f(Mat& source, Mat& output)
    7. {
    8. if ((source.depth() != CV_32F) > FLT_EPSILON)
    9. source.convertTo(output, CV_32F);
    10. else
    11. output = source;
    12. }
    13. void guidedFilter(Mat& source, Mat& guided_image, Mat& output, int radius, float epsilon)
    14. {
    15. //CV_Assert(radius >= 2 && epsilon > 0);
    16. CV_Assert(source.data != NULL && source.channels() == 1);//可改变输入图像类型(通道)
    17. CV_Assert(guided_image.channels() == 1); //导向图一般为单通道
    18. CV_Assert(source.rows == guided_image.rows && source.cols == guided_image.cols);
    19. Mat guided;
    20. if (guided_image.data == source.data)
    21. guided_image.copyTo(guided);
    22. else
    23. guided = guided_image;
    24. Mat source_32f, guided_32f;
    25. makeDepth32f(source, source_32f);//将输入扩展为32位浮点型,以便以后做乘法
    26. makeDepth32f(guided, guided_32f);
    27. Mat mat_Ip, mat_I2; //计算I*p和I*I
    28. multiply(guided_32f, source_32f, mat_Ip);
    29. multiply(guided_32f, guided_32f, mat_I2);
    30. Mat mean_p, mean_I, mean_Ip, mean_I2; //计算各种均值
    31. Size win_size(2 * radius + 1, 2 * radius + 1);
    32. boxFilter(source_32f, mean_p, CV_32F, win_size);
    33. boxFilter(guided_32f, mean_I, CV_32F, win_size);
    34. boxFilter(mat_Ip, mean_Ip, CV_32F, win_size);
    35. boxFilter(mat_I2, mean_I2, CV_32F, win_size);
    36. Mat cov_Ip = mean_Ip - mean_I.mul(mean_p);//计算Ip的协方差和I的方差
    37. Mat var_I = mean_I2 - mean_I.mul(mean_I);
    38. var_I += epsilon;
    39. Mat a, b; //求a和b
    40. divide(cov_Ip, var_I, a);
    41. b = mean_p - a.mul(mean_I);
    42. Mat mean_a, mean_b; //对包含像素i的所有a、b做平均
    43. boxFilter(a, mean_a, CV_32F, win_size);
    44. boxFilter(b, mean_b, CV_32F, win_size);
    45. output = mean_a.mul(guided_32f) + mean_b;//计算输出 (depth == CV_32F)
    46. }
    47. void HazeRemoval(Mat& image, Mat& imageRGB)
    48. {
    49. CV_Assert(!image.empty() && image.channels() == 3);
    50. Mat fImage;
    51. image.convertTo(fImage, CV_32FC3, 1.0 / 255, 0);//图片归一化
    52. Mat fImageBorder;
    53. int hPatch = 15,vPatch = 15;//设定最小滤波patch的大小,且均为奇数
    54. copyMakeBorder(fImage, fImageBorder, vPatch / 2, vPatch / 2, hPatch / 2, hPatch / 2, BORDER_REPLICATE);//给归一化的图片添加边界
    55. vector<Mat> fImageBorderVector(3);
    56. split(fImageBorder, fImageBorderVector);//分离通道
    57. Mat darkChannel(image.rows, image.cols, CV_32FC1);//创建darkChannel
    58. double minTemp, minPixel;
    59. for (unsigned int r = 0; r < darkChannel.rows; r++)
    60. {
    61. for (unsigned int c = 0; c < darkChannel.cols; c++)
    62. {
    63. minPixel = 1.0;
    64. for (vector<Mat>::iterator it = fImageBorderVector.begin(); it != fImageBorderVector.end(); it++)
    65. {
    66. Mat roi(*it, Rect(c, r, hPatch, vPatch));
    67. minMaxLoc(roi, &minTemp);
    68. minPixel = min(minPixel, minTemp);
    69. }
    70. darkChannel.at<float>(r, c) = float(minPixel);
    71. }
    72. }
    73. //darkChannel.convertTo(darkChannel8U, CV_8UC1, 255, 0);
    74. //求出A(global atmospheric light),计算出darkChannel中前top个亮的值,论文中取值为0.1%
    75. float top = 0.001;
    76. float numberTop = top * darkChannel.rows*darkChannel.cols;
    77. Mat darkChannelVector;
    78. darkChannelVector = darkChannel.reshape(1, 1);//reshape的一个参数表示通道数,第二个表示矩阵行数
    79. Mat_<int> darkChannelVectorIndex;
    80. sortIdx(darkChannelVector, darkChannelVectorIndex, CV_SORT_EVERY_ROW + CV_SORT_DESCENDING);//降序,返回像素索引
    81. int count = 0, temp = 0;
    82. unsigned int x, y; //映射回暗通道图的像素位置
    83. Mat mask(darkChannel.rows, darkChannel.cols, CV_8UC1);//制作掩码,注意mask的类型必须是CV_8UC1
    84. for (unsigned int r = 0; r < darkChannelVectorIndex.rows; r++)
    85. {
    86. for (unsigned int c = 0; c < darkChannelVectorIndex.cols; c++)
    87. {
    88. temp = darkChannelVectorIndex.at<int>(r, c);
    89. x = temp / darkChannel.cols;
    90. y = temp % darkChannel.cols;
    91. if (count < numberTop) {
    92. mask.at<uchar>(x, y) = 1;
    93. count++;
    94. }
    95. else
    96. mask.at<uchar>(x, y) = 0;
    97. }
    98. }
    99. vector<double> A(3); //分别存取B,G,R通道的最大A值
    100. vector<Mat> fImageBorderVectorA(3);//在求第三步的t(x)时,会用到以下的矩阵,这里可以提前求出
    101. vector<double>::iterator itA = A.begin();
    102. vector<Mat>::iterator it = fImageBorderVector.begin();
    103. vector<Mat>::iterator itAA = fImageBorderVectorA.begin();
    104. for (; it != fImageBorderVector.end() && itA != A.end() && itAA != fImageBorderVectorA.end(); it++, itA++, itAA++)
    105. {
    106. Mat roi(*it, Rect(0, 0, darkChannel.cols, darkChannel.rows));
    107. minMaxLoc(roi, 0, &(*itA), 0, 0, mask);
    108. (*itAA) = (*it) / (*itA); //注意:这个地方有除号,但是没有判断是否等于0,*itA等于零的可能性很小
    109. }
    110. //求出t(x)
    111. Mat darkChannelA(darkChannel.rows, darkChannel.cols, CV_32FC1);
    112. float omega = 0.95; //论文中取值为0.95
    113. for (unsigned int r = 0; r < darkChannel.rows; r++)
    114. {
    115. for (unsigned int c = 0; c < darkChannel.cols; c++)
    116. {
    117. minPixel = 1.0;
    118. for (itAA = fImageBorderVectorA.begin(); itAA != fImageBorderVectorA.end(); itAA++)
    119. {
    120. Mat roi(*itAA, Rect(c, r, hPatch, vPatch));
    121. minMaxLoc(roi, &minTemp);
    122. minPixel = min(minPixel, minTemp);
    123. }
    124. darkChannelA.at<float>(r, c) = float(minPixel);
    125. }
    126. }
    127. Mat tx1 = 1.0 - omega * darkChannelA;
    128. Mat tx(darkChannel.rows, darkChannel.cols, CV_32FC1);
    129. guidedFilter(tx1, tx1,tx, 8, 500);
    130. namedWindow("tx", CV_WINDOW_AUTOSIZE);
    131. imshow("tx", tx);
    132. //求出J(x)
    133. float t0 = 0.1;//论文中取0.1
    134. //Mat jx(image.rows, image.cols, CV_32FC3);
    135. for (size_t r = 0; r < imageRGB.rows; r++)
    136. {
    137. for (size_t c = 0; c < imageRGB.cols; c++)
    138. {
    139. imageRGB.at<Vec3f>(r, c) = Vec3f((fImage.at<Vec3f>(r, c)[0] - A[0]) / max(tx.at<float>(r, c), t0) + A[0], (fImage.at<Vec3f>(r, c)[1] - A[1]) / max(tx.at<float>(r, c), t0) + A[1], (fImage.at<Vec3f>(r, c)[2] - A[2]) / max(tx.at<float>(r, c), t0) + A[2]);
    140. }
    141. }
    142. }
    143. int main()
    144. {
    145. Mat image = imread("C:\\AllProgram\\testimage\\haze1.bmp");//设置0写入灰度图像
    146. if (image.empty())
    147. {
    148. std::cout << "打开图片失败,请检查" << std::endl;
    149. system("pause");
    150. return -1;
    151. }
    152. Mat testimage(image.size(), CV_32FC3);
    153. HazeRemoval(image,testimage);
    154. namedWindow("原图", CV_WINDOW_AUTOSIZE);
    155. namedWindow("变换后的图", CV_WINDOW_AUTOSIZE);
    156. imshow("原图", image);
    157. imshow("变换后的图", testimage);
    158. waitKey(0);
    159. destroyAllWindows();
    160. system("pause");
    161. return 0;
    162. }