下面给出第3篇文章中代码的详细注释版本。
首先是头文件
#ifndef FUNCTION_H #define FUNCTION_H #include<opencv2/opencv.hpp> #include<opencv2/imgproc/imgproc.hpp> #include<opencv2/imgproc/types_c.h> #include<opencv2/highgui/highgui.hpp> #include<iostream> #include<map> using namespace std; using namespace cv; //导向滤波,用来优化t(x),针对单通道,代码来自何凯明博士 //这段代码大大改进了该方法的效率。使得该方法成为了去雾的最好算法 Mat guidedfilter(Mat& srcImage, Mat& srcClone, int r, double eps) { //转换源图像信息 srcImage.convertTo(srcImage, CV_32FC1, 1 / 255.0); srcClone.convertTo(srcClone, CV_32FC1); int nRows = srcImage.rows; int nCols = srcImage.cols; Mat boxResult; //步骤一:计算均值 boxFilter(Mat::ones(nRows, nCols, srcImage.type()), boxResult, CV_32FC1, Size(r, r)); //生成导向均值mean_I Mat mean_I; boxFilter(srcImage, mean_I, CV_32FC1, Size(r, r)); //生成原始均值mean_p Mat mean_p; boxFilter(srcClone, mean_p, CV_32FC1, Size(r, r)); //生成互相关均值mean_Ip Mat mean_Ip; boxFilter(srcImage.mul(srcClone), mean_Ip, CV_32FC1, Size(r, r)); Mat cov_Ip = mean_Ip - mean_I.mul(mean_p); //生成自相关均值mean_II Mat mean_II; //应用盒滤波器计算相关的值 boxFilter(srcImage.mul(srcImage), mean_II, CV_32FC1, Size(r, r)); //步骤二:计算相关系数 Mat var_I = mean_II - mean_I.mul(mean_I); Mat var_Ip = mean_Ip - mean_I.mul(mean_p); //步骤三:计算参数系数a,b Mat a = cov_Ip / (var_I + eps); Mat b = mean_p - a.mul(mean_I); //步骤四:计算系数ab的均值 Mat mean_a; boxFilter(a, mean_a, CV_32FC1, Size(r, r)); mean_a = mean_a / boxResult; Mat mean_b; boxFilter(b, mean_b, CV_32FC1, Size(r, r)); mean_b = mean_b / boxResult; //步骤五:生成输出矩阵 Mat resultMat = mean_a.mul(srcImage) + mean_b; return resultMat; } //计算暗通道图像矩阵,针对三通道彩色图像 Mat dark_channel(Mat src) { //滤波半径越大,最后去雾的图片中出现色斑几率越小,但是半径的大小影响效率 int border = 15; vector<Mat> rgbChannels; //和原图尺寸一样的灰度图像 Mat min_mat(src.size(), CV_8UC1, Scalar(0)), min_mat_expansion; Mat dark_channel_mat(src.size(), CV_8UC1, Scalar(0)); split(src, rgbChannels); //色彩分离 for (int i = 0; i < src.rows; i++) { for (int j = 0; j < src.cols; j++) { int min_val = 0; int val_1, val_2, val_3; val_1 = rgbChannels[0].at<uchar>(i, j); val_2 = rgbChannels[1].at<uchar>(i, j); val_3 = rgbChannels[2].at<uchar>(i, j); min_val = min(val_1, val_2); min_val = min(min_val, val_3); //得到RGB分量中最小值组成的灰度图像 min_mat.at<uchar>(i, j) = min_val; } } //扩充灰度图像的边缘,得到min_mat_expansion。向四周扩展的范围刚好是滤波半径 //BORDER_REPLICATE表示扩充边缘的方式是边界复制扩充 copyMakeBorder(min_mat, min_mat_expansion, border, border, border, border, BORDER_REPLICATE); for (int m = border; m < min_mat_expansion.rows - border; m++) { for (int n = border; n < min_mat_expansion.cols - border; n++) { Mat imageROI; int min_num = 256; //掩膜是滤波半径加1,即:2*border+1。在这个大小(15*15)下的进行最小值滤波 imageROI = min_mat_expansion(Rect(n - border, m - border, 2 * border + 1, 2 * border + 1)); //最小值滤波:用邻域范围(15*15)内最小值去替换邻域中心的值。 for (int i = 0; i < imageROI.rows; i++) { for (int j = 0; j < imageROI.cols; j++) { int val_roi = imageROI.at<uchar>(i, j); min_num = min(min_num, val_roi); } } //生成暗通道图像 dark_channel_mat.at<uchar>(m - border, n - border) = min_num; } } Mat temp = dark_channel_mat.clone(); bilateralFilter(temp, dark_channel_mat, 9, 18, 4.5); return dark_channel_mat; } //全球大气光成分A int calculate_A(Mat src, Mat dark_channel_mat) { std::vector<cv::Mat> rgbChannels(3); split(src, rgbChannels); //分离原图颜色通道 //准备存储原图的图结构,STL实现的map本质是一棵红黑树,它具备自动排序的能力。 //这点将省去排序的工作,Point同时存储了像素的位置。 map<int, Point> pair_data; map<int, Point>::iterator iter; vector<Point> cord; int max_val = 0; for (int i = 0; i < dark_channel_mat.rows; i++) { for (int j = 0; j < dark_channel_mat.cols; j++) { int val = dark_channel_mat.at<uchar>(i, j); Point pt; pt.x = j; pt.y = i; //当我们向一颗极其严格的平衡二叉树中插入元素的时候,也就是完成排序的时候。 pair_data.insert(make_pair(val, pt)); } } //遍历这棵二叉树, for (iter = pair_data.begin(); iter != pair_data.end(); iter++) { //取出Hash表中的value,而不是key。 cord.push_back(iter->second); } //根据按通道图像中像素的位置找出原图像中相应的像素,并求出具有最高亮度的像素值。 for (int m = 0; m < cord.size(); m++) { Point tmp = cord[m]; int val_1, val_2, val_3; val_1 = rgbChannels[0].at<uchar>(tmp.y, tmp.x); val_2 = rgbChannels[1].at<uchar>(tmp.y, tmp.x); val_3 = rgbChannels[2].at<uchar>(tmp.y, tmp.x); max_val = max(val_1, val_2); max_val = max(max_val, val_3); } return max_val; //A的值求出来了。 } //透射率t(x) Mat calculate_tx(Mat& src, int A, Mat& dark_channel_mat) { Mat dst;//是用来计算t(x) Mat tx; float dark_channel_num; dark_channel_num = A / 255.0; dark_channel_mat.convertTo(dst, CV_32FC3, 1 / 255.0); dst = dst / dark_channel_num; tx = 1 - 0.7 * dst;//修正因子影响去雾的效果,这个因子越大去雾越好,但是也会使得图像变得失真 return tx; } //去雾结果 Mat haze_removal_img(Mat& src, int A, Mat& tx) { Mat result_img(src.rows, src.cols, CV_8UC3); vector<Mat> srcChannels(3), resChannels(3); split(src, srcChannels); split(result_img, resChannels); for (int i = 0; i < src.rows; i++) { for (int j = 0; j < src.cols; j++) { for (int m = 0; m < 3; m++) { int value_num = srcChannels[m].at<uchar>(i, j); float max_t = tx.at<float>(i, j); //阈值设置为0.1 if (max_t < 0.1) { max_t = 0.1; } //计算去雾结果 resChannels[m].at<uchar>(i, j) = (value_num - A) / max_t + A; } } } merge(resChannels, result_img); return result_img; } #endif下面是主函数
#include<opencv2/core/core.hpp> #include<opencv2/imgproc/imgproc.hpp> #include<opencv2/highgui/highgui.hpp> #include<iostream> #include"function.h" #include<algorithm> #include<set> #include<map> using namespace std; using namespace cv; int main() { Mat src = imread("C:/Users/18301/Desktop/1.png"); Mat dst; cvtColor(src, dst, CV_BGR2GRAY); Mat dark_channel_mat = dark_channel(src);//输出的是暗通道图像 int A = calculate_A(src, dark_channel_mat);//计算大气光成分 Mat tx = calculate_tx(src, A, dark_channel_mat);//计算透光率 Mat tx_ = guidedfilter(dst, tx, 30, 0.001);//导向滤波后的tx,优化tx Mat haze_removal_image; haze_removal_image= haze_removal_img(src, A, tx_);//去雾 namedWindow("去雾后的图像", 0); namedWindow("原始图像", 0); imshow("原始图像", src); imshow("去雾后的图像", haze_removal_image); waitKey(0); return 0; } ---来自腾讯云社区的---zy010101
微信扫一扫打赏
支付宝扫一扫打赏