前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >暗通道去雾的好文合集

暗通道去雾的好文合集

作者头像
zy010101
发布2020-03-25 12:53:22
3250
发布2020-03-25 12:53:22
举报
文章被收录于专栏:程序员程序员
  1. https://www.cnblogs.com/Imageshop/p/3281703.html
  2. https://blog.csdn.net/qq_34902877/article/details/103432959
  3. https://blog.csdn.net/qq_29462849/article/details/84848631
  4. https://www.cnblogs.com/herenzhiming/articles/5280759.html

下面给出第3篇文章中代码的详细注释版本。

首先是头文件

代码语言:javascript
复制
#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);
	//步骤四:计算系数a\b的均值
	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

下面是主函数

代码语言:javascript
复制
#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;
}

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档