航拍图像视觉效果提升--去雾算法的应用

全文总结:测试了几种对比度提升对于航拍彩色图像的视觉提升效果。总结图像效果差的原因后,利用去雾算法获得了绝大多数场景适用的处理效果,CPU单帧处理时间为160ms(3*1920*1080)。

种种原因,于8.15开始成为一只社畜,找的工作呢是关于图像处理的。入职第一天,我的组长给了我一个任务,就是处理无人机航拍的图像,主要目的是想提升一下视觉效果。

图像类似下图这种,可以看到拍摄环境是光照比较强的,但是图像看起来似乎有一层灰蒙蒙的东西。其他光照较弱的图像中,图像质量较这张差更多。这样的图像主要原因,我认为是由于公司设计的无人机的飞行高度比较高,当拍摄地面时,空气中会有较多的散射微粒影响成像,所以出现这样的效果。

最开始组长让我试试各种对比度提升的算法,让图像看起来好一些,于是我按着这个思路试了一下。

 

直方图增强

直方图增强的算法肯定就不必多说了,就是通过扩展像素值的范围,让图像的对比度提升,看起来图像会有更多细节突出。

在代码中我使用了openCV自带的直方图增强函数,将图像分为三个通道,分别使用再将通道合并。

equalizeHist( InputArray src, OutputArray dst )

                                                                          图1 起飞场地图

                                                                               图2 地面植被图

                                                                                 图3 天空效果图

使用直方图增强时,地面效果图很差,地面植被场景的效果还不错,在天空效果图中,远处的天空则会出现色块,说明这种方法不是场景普遍适用的。

总结原因:1. 因为RGB通道分开进行直方图均衡化会出现颜色的失真。

                  2.天空有色块,则是因为直方图均衡化对于一些图像区域灰度范围不一致时,效果会很差。

基于以上的原因,我还想测试的几种简单的图像增强算法,如伽马校正,拉普拉斯锐化等等,就被一一否决了。因为这些算法也大部分是RGB分开处理再融合,避免不了颜色失真的影响。出于对颜色失真的考虑,我决定再使用颜色空间转换到HSI空间或者YCrCb空间处理,直方图增强其强度分量。

Mat ycrcb;
Mat result;
vector<Mat> channels;
cvtColor(frame, ycrcb, COLOR_RGB2YCrCb);       //RGB到YCRCB
split(ycrcb, channels);                        //通道分离
equalizeHist(channels[0], channels[0]);        //直方图增强强度分量
merge(channels, ycrcb);                         //通道合并
cvtColor(ycrcb, result, COLOR_YCrCb2RGB);       //YCRCB到RGB

                                                                                           图4 地面图

                                                                                           图5 植被                                               

图4 图5给出了增强强度分量的图像结果,还是不理想,因为整个图像变得像灰度图一样,并且天空处仍然有色块(此处没图)。

花了一天测试这些基本的图像增强算法,效果都不能达到要求。于是我开始另外思考航拍图像有这种效果的原因,正如我在开头所讲,是空气中的散射粒子导致了这种现象。我突然觉得这种图像类似于有雾的图像,因为散射粒子就类似于雾对图像的影响,觉得去雾的算法可能有效果,同时我还需要一种算法处理速度较快,来满足我后期的需求。

全网搜索了一下,发现了这样一篇宝藏文章,文章没有源码,但是讲解十分清楚。看完后,我对于这个算法的流程理解的很好,于是自己用了半天多时间在自己的电脑环境下编写了程序,并且测试了效果。参考文章点下图,作者人very nice,我还问了几个问题都帮我解答了。该文章里面有的内容我不再赘述,我只写一下我自己的代码实现步骤和想法。

一种可实时处理 O(1)复杂度图像去雾算法的实现。

                                                                                  图6 算法步骤

1. 求暗通道及图像最大值(0-255),图像均值(0-1)

		 //step 1 求出暗通道,以及暗通道均值,图像最大值
		 for (Y = 0; Y < rows; Y++)
		 {
			 ImgPt = frame.ptr<uchar>(Y);
			 DarkPt = dark.ptr<uchar>(Y);
			 for (X = 0; X < cols; X++)
			 {
				 Min = double(*ImgPt) ;
				 //Max = double(*ImgPt) ;
				 if (Max < (*(ImgPt + 1))) { Max = *(ImgPt + 1); }
				 if (Max < (*(ImgPt)))     { Max = *(ImgPt); }
				 if (Max < (*(ImgPt + 2))) { Max = *(ImgPt + 2); }


				 if (Min > (*(ImgPt + 1))) { Min = *(ImgPt + 1);}
				 if (Min > (*(ImgPt + 2))) { Min = *(ImgPt + 2); }
				 *DarkPt = uint(Min);                                 //    三通道的最小值
				 Sum += Min ;                                     //  累积以方便后面求平均值
				 ImgPt += 3;
				 DarkPt++;


			 }
		 }
		 Mean = (double)Sum / (rows*cols*255);

这一步的基本思路还是按参考文章写得,不同的是,我将求最大值的代码也加入了这段程序中。我觉得值得注意的一点是,平均值Mean的范围。算法步骤和其对应论文中,表示图像的像素值的范围是0-1,但是因为openCV的像素都是0-255,我为了方便,只将平均值算到0-1,而不改变其它的值。如果变量的范围不注意一下,很有可能下面的计算环境光一步得不到结果。

2. 暗通道图像模糊化

//step 2 对暗通道均值滤波(0-255)运行时间109ms左右
blur(dark, mdark, Size(64, 64));

这一步就比较简单了,就是用openCV内置的blur函数,对图像做了一次均值滤波。这里的size取决于图像大小,也看后面的测试效果。因为我的应用场景比较关心时间,所以测算了一下这个模糊函数的时间在110ms左右(彩色图像1920*1080)。参考文章的作者有个时间更快的方法,如果读者对于CPU速度加快有兴趣,可以参考。因为我的代码最终是要用GPU实现的,所以不再考虑那些优化的方法。

3. 环境光求解及暗通道最大值

         //step 3 求解Lx,环境光(0-255)
		 for (Y = 0; Y < rows; Y++)
		 {
			 ImgPt = mdark.ptr<uchar>(Y);
			 p1 = Lx.ptr<uchar>(Y);
			 DarkPt = dark.ptr<uchar>(Y);
			 for (X = 0; X < cols; X++)
			 {
				 
				 if (dMax <(*(ImgPt))) { dMax = (*(ImgPt)); }

				 Min = 0.9;
				 if ((rou*Mean) < Min) Min = rou * Mean;

				 Min = Min * (*ImgPt);
				 //cout << uint(*ImgPt);
			     if (Min >(*DarkPt)) Min = (*DarkPt);
				 *p1 = uint(Min);

				 ImgPt += 1;
				 DarkPt++;
				 p1++;
			 }
			}

这一步也很简单,就是根据公式求出环境光,注意环境光的像素值是0到255,这个Lx矩阵是一个单通道矩阵。

4. 计算原始图像

 //step 4 求解A,大气指数,为1*3数组
		 A =(0.5*(Max + dMax));

 //step 5  计算原始图像,使用LUT方法
//建立查找表
	 unsigned char * Table = (unsigned char  *)malloc(256 * 256 * sizeof(unsigned char));
			 for (Y = 0; Y < 256; Y++)
			 {
				 Index = Y << 8;
				 for (X = 0; X < 256; X++)
				 {
					 Value = (Y -X) / (1 - X * (1 / A));
					 //cout << Value << endl;
					 if (Value > 255)
						 Value = 255;
					 else if (Value < 0)
						 Value = 0;

					 Table[Index++] = Value;

				 }
			 }

 //查表计算原始图像
			 for (Y = 0; Y < rows; Y++)
			 {
				 ImgPt = frame.ptr<uchar>(Y);
				 p1 = Ipro.ptr<uchar>(Y);
				 p2 = Lx.ptr<uchar>(Y);
				 for (X = 0; X < cols; X++)
				 {
					  
					 *p1= Table[(*ImgPt<< 8) + uint((*p2))];
					 *(p1 + 1) = Table[(*(ImgPt+1) << 8) + uint((*p2))];
					 *(p1 + 2) = Table[(*(ImgPt+2) << 8) + uint((*p2))];
					 ImgPt += 3;
					 p1+=3; 
					 p2++;


				 }
			 }

这里也是根据公式算出原始图像,值得注意的是,参考博文作者用的是查表法。就C++写openCV的程序来说,最费时间的其实就是循环读取像素的过程,再计算每个像素。那么有没有什么较快的方法呢?

OpenCV官方文档提供了一些参考,How to scan images, lookup tables and time measurement with OpenCV。最快的就是查表法,这是通过牺牲一部分内存的方法来降低运算量。计算前和计算后每一个像素值,存在一种映射关系,如果找到这种映射关系,就可以直接赋值,而不是还在每个像素点计算。官方文档也提供了一些解释,有兴趣可以去看看上面的网址。

从以上这一段程序,可以看书来,计算的时候就直接用查表计算,免去了很多计算量。openCV还提供一种内置函数来实现查表法,即。其中I是输入图像;lookUpTable就是我们建立的查找表,按照像素一一对应的关系,应该是256*256大小;J就是输出图像。

LUT(I, lookUpTable, J);

LUT函数由于是openCV的内置函数,已经实现了加速,我认为其实就是简单的放到了GPU下,每个像素并行计算了,消耗的时间也就是GPU和CPU来回读写的时间。

以上就完成了去雾算法的全部过程,由于我只是在实现这个算法,并没有对算法的原理有过多研究,可能没能在这方面提供帮助。下面是处理完的图像效果

这个算法的实现就告一段落,效果以满足公司的要求,由于处理时间还是太长,所以还是要探究加快计算速度的办法。后面也需要将该算法移植到公司的视频播放器中,所以最近也在学习D3D11的部分,希望用GPU并行计算实现这部分代码,还在探索中。这篇博客的内容也是来自两周前了,今天周五接近下班,所以就写了写。

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值