去雾算法参考了一篇论文:
Kaiming He, Jian Sun, Xiaoou Tang. Single Image Haze Removal Using Dark Channel Prior
大致内容是提出了一个叫做暗原色先验的东西来对有雾图像进行处理,十分巧妙,有兴趣者可以看看。这里使用OpenCV实现文中的去雾算法,然而论文提到的soft matting未在本程序中实现。
代码如下:
- #include<iostream>
- #include<cv.h>
- #include<highgui.h>
- char tbarname1[] = "调节block";
- //定义两个滑动条,用于调节参数
- char tbarname2[] = "调节w";
- //w是为了保留一部分的雾
- int block=5;
- int w1=80;
- double w;
- IplImage *src=NULL;
- IplImage *dst=NULL;
- using namespace std;
- //定义去雾函数如下
- 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:/人脸图片样本/1W 测试图库/T04950003.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:/人脸图片样本/1W 测试图库/T04950005.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;
- }
- void on_trackbar1(int h)
- {
- dst=quw(src,block,w);
- cvShowImage("目的图像",dst);
- // cvWaitKey(0);
- }
- void on_trackbar2(int h)
- {
- w=(double)w1/100;
- dst=quw(src,block,w);
- cvShowImage("目的图像",dst);
- // cvWaitKey(0);
- }
- //主函数如下
- void main()
- {
- //打开图像
- src=cvLoadImage("c:/2008414867469_2.jpg");
- //创造窗口
- cvNamedWindow("有雾图像");
- cvShowImage("有雾图像",src);
- cvNamedWindow("目的图像");
- cvShowImage("目的图像",src);
- cvCreateTrackbar(tbarname1, "目的图像", &block, 15, on_trackbar1);
- cvCreateTrackbar(tbarname2, "目的图像", &w1, 100, on_trackbar2);
- cvWaitKey(0);
- cvReleaseImage(&src);
- cvReleaseImage(&dst);
- }