1. 程式人生 > >Single Image Haze Removal(影象去霧)-CVPR’09 Best Paper

Single Image Haze Removal(影象去霧)-CVPR’09 Best Paper

真正瞭解了什麼叫最簡單的就是最美好的

真正的好文章不需要大堆公式堆積顯得充實,而是最最平實的思想!

這篇文章的:原文PDF、PPT。感興趣的可以瞭解一下。

這篇文章的目的就是以最簡單的思路將影象達到去霧效果。用Matlab編了一下,效果圖特別好啊哈:

下面是摘錄的:

CVPR的中文名是計算機視覺與模式識別會議,是計算機視覺領域最頂尖的國際 會議之一。09年的CVPR共收到約1450篇投稿,其中393篇文章被接收,接收率為26%。只有一篇文章被選為今年的最佳論文。這是CVPR創立25年 以來首次由中國人獲得這個獎項。

2010年的結果也已經出來了,一共1724篇文章,CVPR2010 Paper Acceptance Rates: 78 papers were accepted for ORAL Presentation (4.5%).384 papers were accepted for POSTER Presentation (22.3%).

下面是作者的感想摘錄,值得借鑑:

這篇論文研究的問題是影象的去霧技術,它可 以還原影象的顏色和能見度,同時也能利用霧的濃度來估計物體的距離,這些在計算機視覺上都有重要應用(例如三維重建,物體識別)。但是之前人們還沒找到簡 單有效的方法來達到這個目的。在這篇論文裡,我們找到了一個非常簡單的,甚至說令人驚訝統計規律,並提出了有效的去霧方法。

與之前的方法不同,我們把注意力放到了無霧影象的統計特徵上。我們發 現,在無霧影象中,每一個區域性區域都很有可能會有陰影,或者是純顏色的東西,又或者是黑色的東西。因此,每一個區域性區域都很有可能有至少一個顏色通道會有 很低的值。我們把這個統計規律叫做Dark Channel Prior。直觀來說,Dark Channel Prior認為每一個區域性區域都總有一些很暗的東西。這個規律很簡單,但在我們研究的去霧問題上卻是本質的基本規律。

由於霧總是灰白色的,因此一旦影象受到霧的影響,那麼這些本來應該很暗 的東西就會變得灰白。不僅如此,根據物理上霧的形成公式,我們還能根據這些東西的灰白程度來判斷霧的濃度。因此,我們提出的Dark Channel Prior能很有效地去除霧的影響,同時利用物的濃度來估算物體的距離。

大道之行在於簡

我們這篇文章的三個審稿人都給出了最高的評分。他們認為我們的方法簡單 而有效。其中一位評委說,Dark Channel Prior的想法聽起來很不可思議,但我們卻證明了其真實性。另一位評委認為很少有文章能夠用如此簡單的方法使實驗結果獲得如此大的提升。還有一位評委甚 至親自實現了我們的方法並確認其可行。孫劍說閱讀這樣的評審結果是一件讓人快樂的事情。而湯老師認為,這篇文章的成功在於三個方面。第一,方法非常簡單; 第二,對於一個很困難的問題,給出了很好的結果;第三,發現了一個基本的自然規律並且應用在實際的問題中。在邁阿密的演講結束後,觀眾也給予了很高的評 價。他們跟我說,這是這次CVPR上最有趣的一個演講。

一位與會的研究員說,最好的idea,往往就是那些看起來很簡單,但說 出來大家都會覺得怎麼沒有人想到過的idea。而我們的idea正好就符合了這一點。我們論文摘要的第一句話是這麼說的,“我們提出了一個簡單而有效的方 法”。或許,這就是對我們這次工作最好的概括——簡單的,就是美的。

程式碼如下:

1.MATLAB

%=========================================================%
%呼叫規則:(有霧時呼叫,否則不呼叫)
%實際操作時,according to experiments:
%percent=under_50/total
%percent<0.1%,取w=0.6
%percent>0.1%&&percent<1%,取w=0.45
%percenet>1%&&percent<2%,取w=0.3
%else not use haze-free-adjust
%有霧:繪製出的直方圖<50的部分<1%
%最後控制檯還會輸出原圖中under_50畫素點所佔比例
%=========================================================%
close all
clear all
clc
blockSize=15;               %每個block為15個畫素
w0=0.6;                    
t0=0.1;
% A=200;
I=imread('D:\畢業設計\Images\pic_loc\1870575550604291415.jpg');
%I=imread('C:\Users\Zrq\Desktop\同濟.jpg');
h = figure;
%set(gcf,'outerposition',get(0,'screensize'));%獲得SystemScreenSize 傳遞給當前影象控制代碼gcf的outerposition屬性
subplot(321)%表示3(行數)*2(列數)的影象,1代表所畫圖形的序號
imshow(I);
title('Original Image');
subplot(323);
grayI=rgb2gray(I);
imshow(grayI,[]);
title('原影象灰度圖')
subplot(324);
imhist(grayI,64);
%統計<50的畫素所佔的比例
%%%%%%%%%%%%%%%%%%%%%%
[COUNT x]=imhist(grayI);
under_50=0;
for i=0:50
    under_50=under_50+COUNT(x==i);
end
under_50
total=size(I,1)*size(I,2)*size(I,3);
percent=under_50/total
%%%%%%%%%%%%%%%%%%%%%%
if(percent>0.02)
    error('This image need not Haze-Free-Proprocessing.');
else if(percent<0.001)
        w=0.6;
    else if (percent>0.01)
            w=0.3;
        else
            w=0.45;
        end
    end
end
 
[h,w,s]=size(I);
min_I=zeros(h,w);
 
for i=1:h                 
    for j=1:w
        dark_I(i,j)=min(I(i,j,:));%取每個點的畫素為RGB分量中最低的那個通道的值
    end
end
 
Max_dark_channel=double(max(max(dark_I)))
dark_channel=double(dark_I);
t=1-w0*(dark_channel/Max_dark_channel);
 
T=uint8(t*255);
 
t=max(t,t0);
 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
I1=double(I);
J(:,:,1) = uint8((I1(:,:,1) - (1-t)*Max_dark_channel)./t);
 
J(:,:,2) = uint8((I1(:,:,2) - (1-t)*Max_dark_channel)./t);
 
J(:,:,3) =uint8((I1(:,:,3) - (1-t)*Max_dark_channel)./t);
subplot(322)
imshow(J);
imwrite(J,'tj2.jpg');
title('Haze-Free Image:');
 
subplot(325);
grayJ=rgb2gray(J);
imshow(grayJ,[]);
title('去霧後灰度圖')
 
subplot(326);
imhist(grayJ,64);

 OpenCV

//Hazefree helper
 
char tbarname1[] = "調節block";
//定義兩個滑動條,用於調節引數
char tbarname2[] = "調節w";
//w是為了保留一部分的霧
int block=5;
int w1=80;
double w;
IplImage *dst=NULL;
 
IplImage *quw(IplImage *src,int block,double w)
{
	//影象分別有三個顏色通道
	IplImage *dst1=NULL;
	IplImage *dst2=NULL;
	IplImage *dst3=NULL;
	IplImage *imgroi1;
	//dst1的ROI
	IplImage *imgroi2;
	//dst2的ROI
	IplImage *imgroi3;
	//dst3的ROI
	IplImage *roidark;
	//dark channel的ROI
	IplImage *dark_channel=NULL;
	//暗原色先驗的指標
	IplImage *toushelv=NULL;
	//透射率
 
	//去霧演算法運算後的三個通道
	IplImage *j1=NULL;
	IplImage *j2=NULL;
	IplImage *j3=NULL;
	//去霧後的影象,三通道合併成
	IplImage *dst=NULL;
	//源影象ROI位置以及大小
	CvRect ROI_rect;
 
	//分離的三個通道
	dst1=cvCreateImage(cvSize(src->width,src->height),IPL_DEPTH_8U,1);
	dst2=cvCreateImage(cvSize(src->width,src->height),IPL_DEPTH_8U,1);
	dst3=cvCreateImage(cvSize(src->width,src->height),IPL_DEPTH_8U,1);
 
	//為各個ROI分配記憶體
	imgroi1=cvCreateImage(cvSize(block,block),IPL_DEPTH_8U,1);
	imgroi2=cvCreateImage(cvSize(block,block),IPL_DEPTH_8U,1);
	imgroi3=cvCreateImage(cvSize(block,block),IPL_DEPTH_8U,1);
	roidark=cvCreateImage(cvSize(block,block),IPL_DEPTH_8U,1);
 
	//為j1 j2 j3分配大小
	j1=cvCreateImage(cvSize(src->width,src->height),IPL_DEPTH_8U,1);
	j2=cvCreateImage(cvSize(src->width,src->height),IPL_DEPTH_8U,1);
	j3=cvCreateImage(cvSize(src->width,src->height),IPL_DEPTH_8U,1);
 
	//為暗原色先驗指標分配大小
	dark_channel=cvCreateImage(cvSize(src->width,src->height),IPL_DEPTH_8U,1);
	//為透射率指標分配大小
	toushelv=cvCreateImage(cvSize(src->width,src->height),IPL_DEPTH_8U,1);
	//dst分配大小
	dst=cvCreateImage(cvSize(src->width,src->height),IPL_DEPTH_8U,3);
	//將原彩色影象分離成三通道
	cvSplit(src,dst1,dst2,dst3,NULL);
	//求暗原色
	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;
	CvScalar value;
	for(i=0;i<src->width/block;i++)
	{        for(j=0;j<src->height/block;j++)
	{
		//分別計算三個通道內ROI的最小值
		cvSetImageROI(dst1,ROI_rect);
		cvCopy(dst1,imgroi1,NULL);
		cvMinMaxLoc(imgroi1,&min1,&max1,NULL,NULL);
		cvSetImageROI(dst2,ROI_rect);
		cvCopy(dst2,imgroi2,NULL);
		cvMinMaxLoc(imgroi2,&min2,&max2,NULL,NULL);
		cvSetImageROI(dst3,ROI_rect);
		cvCopy(dst3,imgroi3,NULL);
		cvMinMaxLoc(imgroi3,&min3,&max3,NULL,NULL);
		//求三個通道內最小值的最小值
		if(min1<min2)
			min=min1;
		else
			min=min2;
		if(min>min3)
			min=min3;//min為這個ROI中暗原色
		value=cvScalar(min,min,min,min);//min放在value中
		//min賦予dark_channel中相應的ROI
		cvSetImageROI(dark_channel,ROI_rect);
		cvSet(roidark,value,NULL);
		cvCopy(roidark,dark_channel,NULL);
		//釋放各個ROI
		cvResetImageROI(dst1);
		cvResetImageROI(dst2);
		cvResetImageROI(dst3);
		cvResetImageROI(dark_channel);
		//轉入下一個ROI
		ROI_rect.x=block*i;
		ROI_rect.y=block*j;
	}
	}
	//儲存暗原色先驗的影象
	cvSaveImage("f:/dark_channel_prior.jpg",dark_channel);
	//利用得到的暗原色先驗dark_channel_prior.jpg求大氣光強
	double min_dark;
	double max_dark;
	CvPoint min_loc;
	CvPoint max_loc;//max_loc是暗原色先驗最亮一小塊的原座標
	cvMinMaxLoc(dark_channel,&min_dark,&max_dark,&min_loc,&max_loc,NULL);
//	cout<<max_loc.x<<" "<<max_loc.y<<endl;
	ROI_rect.x=max_loc.x;
	ROI_rect.y=max_loc.y;
	double A_dst1;//定義大氣光成分的估計值
	double dst1_min;
	double A_dst2;
	double dst2_min;
	double A_dst3;
	double dst3_min;
	cvSetImageROI(dst1,ROI_rect);
	//按照論文方法求大氣光強估計值
	cvCopy(dst1,imgroi1,NULL);
	cvMinMaxLoc(imgroi1,&dst1_min,&A_dst1,NULL,NULL);
	cvSetImageROI(dst2,ROI_rect);
	cvCopy(dst2,imgroi2,NULL);
	cvMinMaxLoc(imgroi2,&dst2_min,&A_dst2,NULL,NULL);
	cvSetImageROI(dst3,ROI_rect);
	cvCopy(dst3,imgroi3,NULL);
	cvMinMaxLoc(imgroi3,&dst3_min,&A_dst3,NULL,NULL);
//	cout<<A_dst1<<" "<<A_dst2<<" "<<A_dst3<<endl;//這三值為大氣光強度估計值
	//求透射率
	int k;
	int l;
	CvScalar m;
	CvScalar n;//暗原色先驗各元素值
 
	for(k=0;k<src->height;k++)
	{
		for(l=0;l<src->width;l++)
		{
			m=cvGet2D(dark_channel,k,l);
			n=cvScalar(255-w*m.val[0]);
			//w目的是保留一部分的霧,使影象看起來真實些
			cvSet2D(toushelv,k,l,n);
		}
	}
	cvSaveImage("f:/toushelv.jpg",toushelv);
 
	//求無霧影象
	int p,q;
	double tx;
	double jj1,jj2,jj3;
	CvScalar ix,jx;
	for(p=0;p<src->height;p++)
	{
		for(q=0;q<src->width;q++)
		{
			tx=cvGetReal2D(toushelv,p,q);
			tx=tx/255;
			if(tx<0.1)
				tx=0.1;
			ix=cvGet2D(src,p,q);
			jj1=(ix.val[0]-A_dst1)/tx+A_dst1;//根據霧產生模型運算,還原出無霧影象
			jj2=(ix.val[1]-A_dst2)/tx+A_dst2;
			jj3=(ix.val[2]-A_dst3)/tx+A_dst3;
			jx=cvScalar(jj1,jj2,jj3,0.0);
			cvSet2D(dst,p,q,jx);
		}
	}
	cvSaveImage("f:/removed_haze.jpg",dst);
 
	//釋放指標
	cvReleaseImage(&dst1);
	cvReleaseImage(&dst2);
	cvReleaseImage(&dst3);
	cvReleaseImage(&imgroi1);
	cvReleaseImage(&imgroi2);
	cvReleaseImage(&imgroi3);
	cvReleaseImage(&roidark);
	cvReleaseImage(&dark_channel);
	cvReleaseImage(&toushelv);
	cvReleaseImage(&j1);
	cvReleaseImage(&j2);
	cvReleaseImage(&j3);
	return dst;
}
 
IplImage *source;
void on_trackbar1(int h)
{
	dst=quw(source,block,w);
	cvShowImage("目的影象",dst);
	//      cvWaitKey(0);
}
void on_trackbar2(int h)
{
	w=(double)w1/100;
	dst=quw(source,block,w);
	cvShowImage("目的影象",dst);
	//      cvWaitKey(0);
}
 
void CCVMFCView::OnImageHazefree()
{
	imageClone(workImg,&source);
	//cvNamedWindow("有霧影象",CV_WINDOW_AUTOSIZE);
	cvFlip(source);
	//cvShowImage("有霧影象",source);
	cvNamedWindow("目的影象",CV_WINDOW_AUTOSIZE);
	cvShowImage("目的影象",source);
	cvCreateTrackbar(tbarname1, "目的影象", &block, 15, on_trackbar1);
	cvCreateTrackbar(tbarname2, "目的影象", &w1, 100, on_trackbar2);
	cvWaitKey(0);
	
	cvReleaseImage(&source);
 
	cvDestroyWindow("目的影象");
	cvDestroyWindow("有霧影象");
	cvFlip(dst);
	m_dibFlag=imageReplace(dst,&workImg);
	Invalidate();
}