该代码部分实现了何凯明博士的论文暗通道去雾Single Image Haze Removal Using Dark Channel Prior,导向滤波算法尚未实现。
基于暗通道先验的单幅图去雾算法大致如下:
雾图I,目标无雾图J,雾图形成模型I=J*t+A(1-t)。
1、暗通道,窗口。
2、大气光成分:A。
3、去雾因子:w。
4、透射图:1-w*min(min(I/A))
5、导向滤波求透射率:t。
6、还原无雾图:J=(I-A)/t+A。
涉及到的滤波技术为soft matting和guidedfilter,boxfilter。(matlab和opencv实现导向滤波)
#include "cv.h" #include "highgui.h" //#include "GuidedFilter.h" /* //这个求暗通道过于简单粗暴,与何凯明论文中的描述有出入!!! //正确的方法是求像素邻域的局部最小值,类似局部最小值滤波。 //方法不难,请自行实现。 IplImage* MinFilter(IplImage *img,int y,int x) { IplImage *img_dark=cvCreateImage(cvSize(img->width,img->height),img->depth,1); IplImage *temp=cvCreateImage(cvSize(x,y),img->depth,img->nChannels); IplImage *temp0=cvCreateImage(cvSize(x,y),img->depth,1); CvScalar scalar={0.}; double min=0.;d ouble min1=0.; double min2=0.; double min3=0.; CvRect rect=cvRect(0,0,x,y); CvRect rect0=cvRect(0,0,x,y); int img_h=img->height; int img_w=img->width; for(int i=0;i<=img_h/y;i++){ if(i*y+1>img_h) continue; rect.y=i*y; if(i*y>img_h-y){ rect.height=img_h-i*y; rect0.height=img_h-i*y; } rect.width=x; rect0.width=x; for(int j=0;j<=img_w/x;j++) { if(j*x+1>img_w) continue; rect.x=j*x; if(j*x>img_w-x) { rect.width=img_w-j*x; rect0.width=img_w-j*x; } cvSetImageROI(img,rect); cvSetImageROI(temp,rect0); cvCopy(img,temp); cvResetImageROI(img); cvSetImageCOI(temp,1); cvMinMaxLoc(temp,&min1,NULL,NULL,NULL); cvSetImageCOI(temp,2); cvMinMaxLoc(temp,&min2,NULL,NULL,NULL); cvSetImageCOI(temp,3); cvMinMaxLoc(temp,&min3,NULL,NULL,NULL); cvSetImageCOI(temp,0); if(min1<min2) min=min1; else min=min2; if(min>min3) min=min3; scalar=cvScalar(min,0,0,0); cvSet(temp0,scalar,NULL); cvSetImageROI(temp0,rect0); cvSetImageROI(img_dark,rect); cvCopy(temp0,img_dark); cvResetImageROI(temp0); cvResetImageROI(img_dark); } } cvReleaseImage(&temp); cvReleaseImage(&temp0); return img_dark; } */ int main(){ int block_y=4; int block_x=4; double w=0.98; IplImage *img=cvLoadImage("train.bmp",-1); //定义原始图像 int img_h=img->height; int img_w=img->width; IplImage *des=cvCreateImage(cvSize(img->width,img->height),img->depth,img->nChannels); IplImage *img_dark=cvCreateImage(cvSize(img->width,img->height),img->depth,1); IplImage *img_t=cvCreateImage(cvSize(img->width,img->height),img->depth,1); img_dark=MinFilter(img,block_y,block_x); //暗通道图 //计算大气光值 CvPoint max_point; double a_max=0.; cvMinMaxLoc(img_dark,NULL,&a_max,NULL,&max_point,NULL); int x=(max_point.x>(img_w-block_x))?(img_w-block_x):max_point.x; int y=(max_point.y>(img_h-block_y))?(img_h-block_y):max_point.y; CvScalar a={0.}; double a_max0=0; double a_max1=0; double a_max2=0; for(int i=y;i<y+block_y;i++){ for(int j=x;j<x+block_x;j++){ a=cvGet2D(img,i,j); if(a_max0<a.val[0])a_max0=a.val[0]; if(a_max1<a.val[1])a_max1=a.val[1]; if(a_max2<a.val[2])a_max2=a.val[2]; } } //计算透射率 CvScalar p={0.}; CvScalar q={0.}; for(int i=0;i<img->height;i++) { for(int j=0;j<img->width;j++) { p=cvGet2D(img_dark,i,j); //q.val[0]=1-w*p.val[0]/a_max; q=cvScalar(255-w*p.val[0]); cvSet2D(img_t,i,j,q); } } cvSmooth(img_t,img_t,CV_BLUR,block_y*3,block_x*3); //还原图像 CvScalar I={0.}; CvScalar t={0.}; CvScalar dst={0.}; double dst0,dst1,dst2; for(int i=0;i<img->height;i++) { for(int j=0;j<img->width;j++) { I=cvGet2D(img,i,j); double t=cvGetReal2D(img_t,i,j); t=t/255; if(t<0.1)t=0.1; //dst.val[0]=(I.val[0]-a_max0)/t+a_max0; //dst.val[1]=(I.val[1]-a_max1)/t+a_max1; //dst.val[2]=(I.val[2]-a_max2)/t+a_max2; dst0=(I.val[0]-a_max0)/t+a_max0; dst1=(I.val[1]-a_max1)/t+a_max1; dst2=(I.val[2]-a_max2)/t+a_max2; dst=cvScalar(dst0,dst1,dst2,0); cvSet2D(des,i,j,dst); } } cvNamedWindow("原图",CV_WINDOW_AUTOSIZE); cvShowImage("原图",img); cvNamedWindow("暗通道先验图",CV_WINDOW_AUTOSIZE); cvShowImage("暗通道先验图",img_dark); cvNamedWindow("透射率图",CV_WINDOW_AUTOSIZE); cvShowImage("透射率图",img_t); cvNamedWindow("目标图",CV_WINDOW_AUTOSIZE); cvShowImage("目标图",des);cvWaitKey(0); cvReleaseImage(&img); cvReleaseImage(&img_dark); cvReleaseImage(&img_t); cvReleaseImage(&des); cvDestroyWindow("原图"); cvDestroyWindow("暗通道先验图"); cvDestroyWindow("透射率图"); cvDestroyWindow("目标图"); return 0; }