天天看点

opencv暗通道图像去雾(Mat风格代码)

最近《数字图像处理》课需要做图像去雾的工作,然后我们百度了一下,找到下面的链接(主要的方法有图像增强和图像复原两大类):

http://www.cspmag.cn/jscx/spjk/201406/1336.html

最终,我和队友选择了使用暗通道先验的方法,我们参考的是下面的代码:

http://blog.sina.com.cn/s/blog_4d8730df0100m8lz.html

上面的代码是对何恺明博士《Single Image Haze Removal Using Dark Channel Prior》 一文的实现,但是没有使用soft matting,在最小值滤波以及求A的时候也有一些出入,我们直接跑了一下,大致的效果还行,但是代码的风格是OpenCV1.0的风格,使用的是IplImage,我们决定将它改成OpenCV2.0风格的代码,我们修改的代码如下,具体的思路没有改变:

我们使用的环境是VS2013+OpenCV3.1

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

#include<iostream>

#include<opencv2\opencv.hpp>

using namespace std;

using namespace cv;

string tbarname1 = "block:";

//定义两个滑动条,用于调节参数

string tbarname2 = "w:";

//w是为了保留一部分的雾

int block = 5;

int w1 = 95;

double w = (double)w1/100;

Mat src;

Mat dst;

//定义去雾函数如下

Mat haze_removal(Mat src, int block, double w)

{

//图像分别有三个颜色通道

vector<cv::Mat>dst(src.channels());

Mat imgROI1;

//dst[0]的ROI

Mat imgROI2;

//dst[1]的ROI

Mat imgROI3;

//dst[2]的ROI

Mat darkROI;

//dark channel的ROI

Mat darkChannel;

//暗原色先验

Mat imgTransmissivity;

//透射率

//去雾算法运算后的三个通道

Mat removalDst1;

Mat removalDst2;

Mat removalDst3;

//去雾后的图像,三通道合并成

Mat dst3D;

//源图像ROI位置以及大小

Rect ROI_rect;

//为各个ROI分配内存

imgROI1 = Mat(block, block, CV_8UC1);

imgROI2 = Mat(block, block, CV_8UC1);

imgROI3 = Mat(block, block, CV_8UC1);

darkROI = Mat(block, block, CV_8UC1);

//为removalDst1 removalDst2 removalDst3分配大小

removalDst1 = Mat(src.rows, src.cols, CV_8UC1);

removalDst2 = Mat(src.rows, src.cols, CV_8UC1);

removalDst3 = Mat(src.rows, src.cols, CV_8UC1);

//为暗原色先验分配大小

darkChannel = Mat(src.rows, src.cols, CV_8UC1);

//为透射率分配大小

imgTransmissivity = Mat(src.rows, src.cols, CV_8UC1);

//dst3D分配大小

dst3D = Mat(src.rows, src.cols, CV_8UC3);

//将原彩色图像分离成三通道

split(src, dst);

//求暗原色

ROI_rect.width = block;

ROI_rect.height = block;

ROI_rect.x = 0;

ROI_rect.y = 0;

int i;

int j;

double min1 = 0;

double max1 = 0;

double min2 = 0;

double max2 = 0;

double min3 = 0;

double max3 = 0;

double min = 0;

for (i = 0; i<src.cols / block; i++)

{

for (j = 0; j<src.rows / block; j++)

{

//分别计算三个通道内ROI的最小值

imgROI1 = dst[0](ROI_rect);

minMaxIdx(imgROI1, &min1, &max1);

imgROI2 = dst[1](ROI_rect);

minMaxIdx(imgROI2, &min2, &max2);

imgROI3 = dst[2](ROI_rect);

minMaxIdx(imgROI3, &min3, &max3);

//求三个通道内最小值的最小值

if (min1<min2)

min = min1;

else

min = min2;

if (min>min3)

min = min3;//min为这个ROI中暗原色

//min赋予darkChannel中相应的ROI

darkROI = darkChannel(ROI_rect);

Mat tmp = Mat(block, block, CV_8UC1, Scalar(min));

tmp.copyTo(darkROI);

//转入下一个ROI

ROI_rect.x = block*i;

ROI_rect.y = block*j;

}

}

//保存暗原色先验的图像

imwrite("F:\\My_Desktop\\Haze_Removal\\darkChannel.jpg", darkChannel);

//利用得到的暗原色先验dark_channel_prior.jpg求大气光强

double minDark;

double maxDark;

int minLoc[2] = { 255, 255 };

int maxLoc[2] = { 255, 255 };//maxLoc是暗原色先验最亮一小块的原坐标

minMaxIdx(darkChannel, &minDark, &maxDark, minLoc, maxLoc);

cout << maxLoc[1] << " " << maxLoc[0] << endl;

ROI_rect.x = maxLoc[1];

ROI_rect.y = maxLoc[0];

double dst1A;//定义大气光成分的估计值

double minDst1;

double dst2A;

double minDst2;

double dst3A;

double minDst3;

//按照论文方法求大气光强估计值

imgROI1 = dst[0](ROI_rect);

minMaxIdx(imgROI1, &minDst1, &dst1A);

imgROI2 = dst[1](ROI_rect);

minMaxIdx(imgROI2, &minDst2, &dst2A);

imgROI3 = dst[2](ROI_rect);

minMaxIdx(imgROI3, &minDst3, &dst3A);

cout << dst1A << " " << dst2A << " " << dst3A << endl;//这三值为大气光强度估计值

//求透射率

int k;

int l;

uchar m;

uchar n;//暗原色先验各元素值

for (k = 0; k<src.rows; k++)

{

for (l = 0; l<src.cols; l++)

{

m = darkChannel.at<uchar>(k, l);

n = 255 - w * m;

//w目的是保留一部分的雾,使图像看起来真实些

imgTransmissivity.at<uchar>(k, l) = n;

}

}

imwrite("F:\\My_Desktop\\Haze_Removal\\imgTransmissivity.jpg", imgTransmissivity);

//求无雾图像

int p, q;

double tx;

uchar srcB, srcG, srcR;

CvScalar ix, jx;

for (p = 0; p<src.rows; p++)

{

for (q = 0; q<src.cols; q++)

{

tx = imgTransmissivity.at<uchar>(p, q);

tx = tx / 255;

if (tx<0.1)

tx = 0.1;

srcB = (src.at<Vec3b>(p, q)[0] - dst1A) / tx + dst1A;//根据雾产生模型运算,还原出无雾图像

srcG = (src.at<Vec3b>(p, q)[1] - dst2A) / tx + dst2A;

srcR = (src.at<Vec3b>(p, q)[2] - dst3A) / tx + dst3A;

dst3D.at<Vec3b>(p, q)[0] = srcB;

dst3D.at<Vec3b>(p, q)[1] = srcG;

dst3D.at<Vec3b>(p, q)[2] = srcR;

}

}

imwrite("F:\\My_Desktop\\Haze_Removal\\removed_haze.jpg", dst3D);

return dst3D;

}

void on_trackbar1(int h,void *pt)

{

dst = haze_removal(src, block, w);

imshow("目的图像", dst);

}

void on_trackbar2(int h,void *pt)

{

w = (double)w1 / 100;

dst = haze_removal(src, block, w);

imshow("目的图像", dst);

}

//主函数如下

void main()

{

//打开图像

src = imread("F:\\My_Desktop\\Haze_Removal\\wu-035.jpg");

//创造窗口

namedWindow("有雾图像", CV_WINDOW_AUTOSIZE);

imshow("有雾图像", src);

namedWindow("目的图像", CV_WINDOW_AUTOSIZE);

createTrackbar(tbarname1, "目的图像", &block, 15, on_trackbar1);

createTrackbar(tbarname2, "目的图像", &w1, 100, on_trackbar2);

waitKey(0);

}