您的位置:首页 > 运维架构

OpenCV实践之路——基于暗通道先验的去雾算法简单实现

2016-03-09 17:22 543 查看
研究暗通道去雾是去年的事情了,当时正值全国范围内雾霾肆虐。下面这张图片可谓深得人心。



但是由于当时水平所限,未能实现。在寒假努力了一个假期终于可以简单的实现一下了,虽然还是有一些奇奇怪怪的问题。。。

首先还是简单说下暗通道先验去雾算法,这里只是记录性质,详细推荐看论文,写的非常好:

雾图模型






I(x) ——待去雾的图像

J(x)——无雾图像

A——全球大气光成分

t——折射率(大气传递系数)

暗通道先验

在无雾图像中,每一个局部区域都很有可能会有阴影,或者是纯颜色的东西,又或者是黑色的东西。因此,每一个局部区域都很有可能有至少一个颜色通道会有很低的值。把这个统计规律叫做Dark Channel Prior。

暗通道定义



Jc表示彩色图像的每个通道

Ω(x)表示以像素X为中心的一个窗口

意义:首先求出每个像素RGB分量中的最小值,存入一副和原始图像大小相同的灰度图中,然后再对这幅灰度图进行最小值滤波

对于两个最小化的顺序,我看了下,何凯明的两遍论文用了不同的顺序。

计算折射率



右边第二项其实就是有雾图像的暗通道。由于空间透视现象/浓淡远近,部分雾的存在有助于我们感知距离和深度,加权值修正:



估计大气光

1.选取暗通道图像暗通道最亮的0.1%的像素(一般来说,这些像素表示雾浓度最大的地方)

2.取输入图像里面这些像素对应的像素里面最亮的作为大气光

注:选中的像素未必是全图最亮的,而且要比选取全图最亮的方式鲁棒性更好。

去雾



大致就是这个流程:

1.求图像暗通道

2.利用暗通道计算出折射率

3.利用暗通道估计大气光

4.代回雾图公式去雾

代码如下,比较简陋:

#include<opencv2\core\core.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\imgproc\imgproc.hpp>
#include<iostream>
#include<vector>
#include <algorithm>

using namespace cv;
using namespace std;

//求暗通道
Mat darkChannel(Mat src)
{
	Mat rgbmin = Mat::zeros(src.rows, src.cols, CV_8UC1);
	Mat dark = Mat::zeros(src.rows, src.cols, CV_8UC1);
	Vec3b intensity;

	for (int m = 0; m<src.rows; m++)
	{
		for (int n = 0; n<src.cols; n++)
		{
			intensity = src.at<Vec3b>(m, n);
			rgbmin.at<uchar>(m, n) = min(min(intensity.val[0], intensity.val[1]), intensity.val[2]);
		}
	}

	//模板尺寸
	int scale = 7;
	//cout << "Please enter the mask scale: " << endl;
	//cin >> scale;

	//边界扩充
	int radius = (scale - 1) / 2;
	Mat border;
	//由于要求最小值,所以扩充的边界可以用复制边界填充
	copyMakeBorder(rgbmin, border, radius, radius, radius, radius, BORDER_REPLICATE);

	//最小值滤波
	for (int i = 0; i < src.cols; i++)
	{
		for (int j = 0; j < src.rows; j++)
		{
			//选取兴趣区域
			Mat roi;
			roi = border(Rect(i, j, scale, scale));

			//求兴趣区域的最小值
			double minVal = 0; double maxVal = 0;
			Point minLoc = 0; Point maxLoc = 0;
			minMaxLoc(roi, &minVal, &maxVal, &minLoc, &maxLoc, noArray());

			dark.at<uchar>(Point(i, j)) = (uchar)minVal;
		}
	}
	return dark;
}
uchar light(vector<uchar> inputIamgeMax)
{
	uchar maxA=0;
	for (int i = 0; i < inputIamgeMax.size() - 1; i++)
	{
		
		if (maxA < inputIamgeMax[i + 1])
		{
			maxA = inputIamgeMax[i + 1];
		}
	}
	return maxA;
}

//Mat dark(Mat image)
//{
//	Mat minColor(image.rows, image.cols, CV_8UC1, Scalar(180, 120, 50));
//	Mat darkChannel(image.rows, image.cols, CV_8UC1, Scalar(180, 120, 50));
//
//	//求每个像素BGR三通道最小值
//	for (int i = 0; i < image.cols; i++)
//	{
//		for (int j = 0; j < image.rows; j++)
//		{
//			uchar blue, green, red;
//			blue = image.at<Vec3b>(Point(i, j))[0];
//			green = image.at<Vec3b>(Point(i, j))[1];
//			red = image.at<Vec3b>(Point(i, j))[2];
//			minColor.at<uchar>(Point(i, j)) = minBGR(blue, green, red);
//		}
//	}
//
//	//模板尺寸
//	int scale;
//	cout << "Please enter the mask scale: " << endl;
//	cin >> scale;
//
//	//边界扩充
//	int radius = (scale - 1) / 2;
//	Mat border;
//	//由于要求最小值,所以扩充的边界可以用复制边界填充
//	copyMakeBorder(minColor, border, radius, radius, radius, radius, BORDER_REPLICATE);
//
//	//最小值滤波
//	for (int i = 0; i < image.cols; i++)
//	{
//		for (int j = 0; j < image.rows; j++)
//		{
//			//选取兴趣区域
//			Mat roi;
//			roi = border(Rect(i, j, scale, scale));
//
//			//求兴趣区域的最小值
//			double minVal = 0; double maxVal = 0;
//			Point minLoc = 0; Point maxLoc = 0;
//			minMaxLoc(roi, &minVal, &maxVal, &minLoc, &maxLoc, noArray());
//
//			darkChannel.at<uchar>(Point(i, j)) = (uchar)minVal;
//		}
//	}
//	return darkChannel;
//}

int main(int argc, char* argv[])

{
	Mat image = imread("mai4.jpg");
	imshow("image",image);
	Mat darkChannel1 = darkChannel(image);
	imshow("darkChannel1", darkChannel1);

	namedWindow("dehazed");

	//估计大气光
	Mat temp; darkChannel1.copyTo(temp);
	vector<Point> darkMaxPoint;
	vector<uchar> inputMax;
	for (long i = 0; i < ((darkChannel1.rows*darkChannel1.cols) / 1000); i++)
	{		
		double minVal = 0; double maxVal = 0;
		Point minLoc = 0; Point maxLoc = 0;
		minMaxLoc(temp, &minVal, &maxVal, &minLoc, &maxLoc, noArray());
		
		darkMaxPoint.push_back(maxLoc);
		inputMax.push_back(image.at<uchar>(maxLoc));
		circle(temp, maxLoc,5, Scalar(0), 1, 8, 0);
		temp.at<uchar>(maxLoc) = temp.at<uchar>(minLoc);
	}
	
	uchar A = light(inputMax);

	double w = 0.65;

	//createTrackbar("w1", "dehazed", &w1, 100, NULL);

	//求折射率
	Mat T = Mat::zeros(image.rows, image.cols, CV_8UC3);
	Scalar intensity;

	for (int m = 0; m<image.rows; m++)
	{
		for (int n = 0; n<image.cols; n++)
		{
			intensity = darkChannel1.at<uchar>(m, n);
			T.at<Vec3b>(m, n)[0] = (1 - w * intensity.val[0] / A) * 255;
			T.at<Vec3b>(m, n)[1] = (1 - w * intensity.val[0] / A) * 255;
			T.at<Vec3b>(m, n)[2] = (1 - w * intensity.val[0] / A) * 255;
		}
	}
	

	//去雾
	Mat J(image.rows, image.cols, CV_8UC3, Scalar(180, 120, 50));
	Mat temp1(image.rows, image.cols, CV_8UC3, Scalar(180, 120, 50));

	//subtract(image, Scalar(A, A, A), temp1);
	temp1 = abs(image - Scalar(A, A, A));
	double t0 = 0.1;

	Scalar T1;
	Vec3b intsrc;
	for (int i = 0; i < image.cols; i++)
	{
		for (int j = 0; j < image.rows; j++)
		{
			T1 = T.at<uchar>(Point(i, j));
			intsrc = image.at<Vec3b>(Point(i, j));
			double tmax = (T1.val[0] / 255) < t0 ? t0 : (T1.val[0] / 255);

			for (int k = 0; k < 3; k++)
			{
				J.at<Vec3b>(Point(i, j))[k] = abs((intsrc.val[k] - A) / tmax + A) > 255 ? 255 : abs((intsrc.val[k] - A) / tmax + A);
			}
		}
	}

	imshow("dehazed", J);
	while (char(waitKey(1)) != 'q') {}
	return 0;
}
这个有时对图像有些去雾效果,比如下面这样:



右上角的亮光是什么鬼?还有下面这样莫名其妙的事情:



这都是什么鬼??看来还需要进行细部调节和更深入的研究。改进之后再补充吧。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: