女朋友嫌我拍的照片有雾,连夜用OpenCV写出❤️去雾算法❤️逃过一劫(收藏保命)

Posted 一条coding

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了女朋友嫌我拍的照片有雾,连夜用OpenCV写出❤️去雾算法❤️逃过一劫(收藏保命)相关的知识,希望对你有一定的参考价值。

❤️欢迎订阅《从实战学python》专栏,用python实现爬虫、办公自动化、数据可视化、人工智能等各个方向的实战案例,有趣又有用!❤️

更多精品专栏简介点这里

治愈生活的良方 就是保持对生活的热爱

前言

哈喽,大家好,我是一条。

每次和女朋友出去玩,拍照是必须的,天气好还行,天气要是不好,加上我这破手机,那拍的简直惨不忍睹,自己都不过去。

但是没什么能难倒程序员的,为了不挨骂,连夜写出去雾算法,女朋友更崇拜我了!

⚠️本文高能,专业知识繁多,小白可以直接文末拿代码运行(每行都有注释)。

⚠️想深入研究的同学,万字详解包教包会!

⚠️总之,收藏护体!

最终效果图

话不多说,先看看效果强不强。

自己写的算法,不调百度api,nb克拉斯。

应用:车牌识别

应用:雾天预警

应用:景物识别

概要

目前图像处理技术已非常火热,应用也非常广泛。

图像处理的难度不仅在于成像技术,更在于外界的天气条件,遇到雾、雨、雪等能见度低的恶劣天气时,成像会受到很大影响,其中雾霾天气屡见不鲜,还使采集到的图片辨识度、对比度、色彩饱和度都大大降低。

雾气的消除可以从物理去雾和数字去雾两个方向考虑,物理去雾即在成像镜头上优化,高清去雾的镜头成本昂贵,不能广泛应用,所以研究重点在数字去雾上,即通过后期处理对图像进行优化,从去雾算法提出至今,可用的去雾算法大致可分为基于图像增强的去雾算法和基于图像复原的去雾算法。

本算法基于图像复原的暗通道先验去雾算法,在大气成像模型的基础上,得出图像恢复的基本公式。

暗通道理论指出:在大多数非空局部区域中,始终至少有一个颜色通道的某些像素值较低甚至为0。

公式的关键点在于透射率的计算,为了改进算法,用导向滤波对透射率进行优化

又因为该算法本身对天空部分并不适用,所以考虑对公式增加参数,二次计算透射率,完成对算法的再一次优化

最后再用OpenCV-Python对去雾算法进行具体实现以及运行速度的优化。最终能够实现对绝大多数的有雾图片快速去雾。

图像的相关知识

为了让领域小白理解接下来我要讲的内容,需要普及一些基础知识。

像素

像素是图像的基本要素,也是图像的最小单位。

一张图片实际是由很多个小方格组成,每个小方格有固定的位置和颜色数值,这决定了图片最终呈现的样子,像素的多少决定了在屏幕上显示的大小。

如果是黑白图片,用0-255之间的一个数字来表示颜色的深浅明暗,0表示最暗(黑色),255表示最亮(白色)。

如果是彩色图片,用三个0-255之间的数字组成的数字序列来表示红绿蓝三种颜色所占的比例,则某一个位置的像素就可以用数字序列表示为[24,180,50]。

比如一个5*4像素的彩色图像就可以这样存在计算机中。

通道

通道简单来说就是表示通道表示每个点能存放多少个数。

例如彩色图像中每个像素点存放三个值,即3通道的,黑白图像每个像素点存放一个数字,即单通道。

二值图像

图像的像素点不是0就是1(图像不是黑色就是白色),图像像素点占的位数就是1位,图像的深度就是1,也称作位图。

灰度图像

图像中的像素介于0-255之间,0表示完全黑色,255表示完全白色,并且在0-255之间插入了255级灰度。

当查看单个通道时,每个通道都显示为灰度图像而不是黑白图像。

在通道的灰度图像中,对比度应与通道颜色的对比度匹配,它代表了彩色光在整个图像上的分布。

因为有3个通道,所以也有3个灰度图像。

暗通道

暗通道是一个基本假设,即在大多数非空局部区域中,始终至少有一个颜色通道的某些像素值较低。

形成这种现象的原因很多,比如色彩鲜艳的物体的表面,汽车,建筑物或城市的阴影,较暗的物体表面的阴影,这些场景的暗通道始终较暗。

暗通道实际上使用三个RGB通道中的最小通道来形成灰度图像。

作者计算大量没有雾的图像,并找到规则:

每个图像中三个RGB其中一个颜色通道具有一个灰色通道,阶值非常低,几乎接近零。

如果有雾,则有一定的灰色;

如果没有雾,则有很多黑色(像素接近0)。

共计算了5,000多个图像属性,基本上都满足这样的先验定理。

所以只需分别计算出三个通道中RGB的最小值即可。

大气散射模型

大气颗粒的散射是雾霾的主要原因。

无论是用肉眼观察还是从拍摄的图像中观察,有很多雾的场景总是会受到对比度降低和视野降低的困扰。

在1925年,Keim和Nemnich等人提出雾图像的低可见性是由于大气中空气中的微粒吸收和散射了光。

1976年,约翰·威利(JohnWiley)等人提出,粒子散射会导致光在传输过程中在目标和相机之间衰减,并增加了一层大气散射光。

为了解决1999年大雾天能见度差的问题,SrinivasaG和Narasimhan等人建立了数学模型来改善雾像的成像过程以及雾像中包含的各种元素。

该模型认为,在强散射介质中,检测系统的成像结果劣化的主要原因有两个:

  • 首先,目标的反射光被空气中的粒子吸收和散射,使系统的成像结果是导致反射光能量的衰减并导致检测,亮度和对比度降低。
  • 第二,诸如太阳光的环境光被大气中的散射介质散射而形成背景光。通常,该区域中的背景光强度高于目标光,导致检测系统的成像结果模糊。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UrrwKMG3-1630474072464)(file:private/var/folders/j0/jf6_lq_s2wjcqpt53gnd88b80000gn/T/com.kingsoft.wpsoffice.mac/wps-libiao/ksohtml/wps6EBduQ.jpg)]

图中显示探测系统成像时接收到的光源主要来自两个部分,

  • 一是目标反射光经粒子衰减到达探测系统的光,
  • 二是来自于光源经粒子散射形成的大气光。

通过此物理模型建立得到雾天成像的数学模型为:

该公式的物理意义为经过粒子衰减能够达到探测系统的那部分光的比例。

大多数团队和学者在通过探测系统获得含雾图像并对其进行去雾图像处理时均以上述大气散射模型作为雾天成像的理论模型。

其主要思路是根据各种先验知识或者图像处理手段,从含雾图像中估计传输函数或大气光,将求解得到的参量代入到大气散射模型中,即可恢复出目标图像。

在强分散介质中,检测系统成像质量的下降主要包括以下两个原因:

  • 目标的反射光在光的过程中被悬浮在介质中的颗粒吸收和扩散。通常,图像的亮度会降低,从而导致图像的对比度下降;周围的光(例如日光和天光)会被粒子散射。在中间形成散射光,通常会模糊图像并使图像的颜色不自然。以在多云环境下对目标成像为例,成像系统获得的图像主要由两部分组成,一是目标反射光被大气的色散和吸收所衰减.
  • 另一个是由大气散射形成的大气光,反射光在传播的过程中,随着传输距离的增加光强逐渐衰减。

暗通道先验去雾算法原理

暗通道先验去雾算法最早由何凯明博士中提出。先验与经验相对,就是基于某种经验所得出的结论去研究解决问题的方法,此处就是指何凯明博士统计了5000多副图像后得出的先验定理:

在每一幅图像的RGB三个颜色通道中,总有一个通道的灰度值很低,几乎趋向于0。

可以发现,有雾的时候会呈现一定的灰色,而无雾的时候咋会呈现大量的黑色(像素为接近0)。如图:有雾图片的灰度图整体呈灰色,无雾图片的非天空部分呈黑色。

在现实生活中产生这种现象的原因的有很多且很普遍,例如汽车,建筑物等城市中的阴影,有色物体的表面,比如绿叶,各种鲜艳的花朵,较暗的物体表面,这些场景图像的RGB颜色通道中,总有一个通道的灰度值很低。

算法公式

此处省去推导过程,直接上公式。

在日常生活中,即使是晴空万里,雾也不是完全不存在的,只是浓度比较低,依然会影响人们看远处的物体;

另外,人眼能感到景深的存在也是因为雾。

所以,去雾时也不能做到绝对,应该保留一定程度的雾,通过在式中加入一个在[0,1]之间的系数来实现。

未知参数的计算

明确需要计算哪些值。

大气光

在实际中,可以借助于暗通道图来从有雾图像中获取该值。

首先根据亮度从暗通道图中获取前0.1%的像素,

然后在这些位置中,在原始有雾图像I中寻找对应的具有最高亮度的点的值,作为A值,

最后计算A的平均值。

透射率

大气光计算出之后,根据如下公式,可得到透射率。

代码实现

理论已经完成,咱们直接代码实现。

OpenCV简介

OpenCV主要提供一组通用的基础结构。

用于开发人工视觉程序,并改善商业产品中机器的感知。

OpenCV库具有2500多种优化算法,包括经典和最先进的机器视觉以及机器学习算法。

这些算法可用于检测和识别面部,跟踪移动的对象,提取对象的三维模型,从立体相机生成3D点云并组合图像以生成高分辨率全景图像。从图像数据库中查找相似图像等等。

使用OpenCV的项目已植入街景图像,在监视期间检测入侵,监视采矿设备,帮助机器人导航和收集物体,并检测泳池中的溺水事件等很多。

OpenCV支持各种编程语言,如C++,Python,Java等,OpenCV-Python是OpenCV的PythonAPI,结合了C++和Python语言的最佳特性。

综上所述,OpenCV-Python的功能非常强大,所以本文用它来实现去雾算法。

图像的形态学处理

OpenCV在很多领域都有应用,比如图像分割、物体识别、动作识别、人脸识别、结构分析等众多领域,功能复杂繁多。在暗通道先验去雾算法中,除去一些计算函数,最关键的就是OpenCV的形态学处理。

形态变换是对图像形状进行的简单操作,通常是指对二值图像进行的操作,常见的形态变换包括腐蚀,膨胀和张开操作。

腐蚀

腐蚀是对前景中物体边缘的腐蚀,其腐蚀效果与现实相同。

其原理为卷积核沿着图像滚动,如果相应区域的图像的像素值是1,则对应于卷积核中心的像素值保持不变,否则将全部变为0。

在卷积核中,图像的边缘,部分为0,则该区域的部分将全变为0并保持不变。

膨胀

膨胀是腐蚀的逆过程,与腐蚀相对,

其原理同样为卷积核沿着图像流动,如与卷积核对应的图像像素值之一为1,该区域将全部变为1,否则保持不变。

开运算

进行腐蚀操作后再进行膨胀操作就是开运算

闭运算

闭与开相对,所以开运算的逆过程就是闭运算,进行膨胀操作后再进行腐蚀造作,原理和开运算一样,可以用于去除二值化图像的背景色噪点。

获取大气光

首先计算图像的总像素点数,取其前0.1%。

再按像素值从小到大的顺序获取像素所在位置的下标,按按照下标从雾图中找到对应位置的三通道值。

这样就完成了从暗通道图中按照亮度的大小取前0.1%的像素。然后取这些值的平均值作为大气光A的值。

代码如下:

def AtmLight(im,dark):
		[h,w]=im.shape[:2]                         #获取图像的长宽
		imsz=h*w                                #长*宽得到总像素点个数
		numpx=int(max(math.floor(imsz/1000),1))      #取总个数的前0.1%
		darkvec=dark.reshape(imsz,1);                #复制原图
		imvec=im.reshape(imsz,3);            #复制暗通道图
		darkvec1=np.transpose(darkvec)        #横纵坐标互换
		indices=darkvec1.argsort();            #按像素值从小到大的顺序获取像素所在位置的下标
		indices=indices[0][imsz-numpx::]       #取前0.1%大的
		atmsum=np.zeros([1,3])               #一行三列以0填充的矩阵
		for ind in range(0,numpx):             #按下标到原图找到对应位置的像素值并求和
			atmsum=atmsum+imvec[indices[ind]]
		A=atmsum/numpx;                   #计算平均值
	return A

计算透射率

依据公式粗略得到透射率的值,该值有待优化,后续会处理。

def  TransmissionEstimate(im,A,sz):
		 omega=0.95;                             #声明参数
		 im3=np.empty(im.shape,im.dtype);           #复制原图
		 for ind in range(0,3):                       #按公式计算
				im3[:,:,ind]=im[:,:,ind]/A[0,ind]
		 transmission=1-omega*DarkChannel(im3,sz);
    return transmission

恢复图像

根据前面得出的恢复公式进行计算。

def Recover(im, t, A, tx=0.1):
    res = np.empty(im.shape, im.dtype);
		t = cv2.max(t, tx);                         #如果t小于tx,取tx
    for ind in range(0, 3):                      #按公式计算
        res[:, :, ind] = (im[:, :, ind] - A[0, ind]) / t + A[0, ind]
    return res

含天空部分的图像去雾之后只能观察到少许的去雾效果,图像亮度大大降低,且天空部分过度曝光,去雾效果并不理想。

说明此算法本身确实对大面积天空部分并不适用。

导向滤波优化

针对上文得到的较为粗略的透射率,在本章对其进行优化,

何凯明曾提出过软遮光的方法,该方法虽然获得的透射率非常精细,但是运行速度极慢,成为致命弱点。

在实际使用中,效果并不理想。所以在本文中使用引导滤波的方法获得更精准的透射率。

该方法的核心在于正方形的简单模糊,而有很多个快速且独立于半径的算法都可以用于正方形的简单模糊,使算法的运行速度大大提高,可以实际使用。

原理部分略过,因过于晦涩,需要深入了解的同学。

代码

def Guidedfilter(im, p, r, eps):
    mean_I = cv2.boxFilter(im, cv2.CV_64F, (r, r));
    mean_p = cv2.boxFilter(p, cv2.CV_64F, (r, r));
    mean_Ip = cv2.boxFilter(im * p, cv2.CV_64F, (r, r));
    cov_Ip = mean_Ip - mean_I * mean_p;

    mean_II = cv2.boxFilter(im * im, cv2.CV_64F, (r, r));
    var_I = mean_II - mean_I * mean_I;

    a = cov_Ip / (var_I + eps);
    b = mean_p - a * mean_I;

    mean_a = cv2.boxFilter(a, cv2.CV_64F, (r, r));
    mean_b = cv2.boxFilter(b, cv2.CV_64F, (r, r));

    q = mean_a * im + mean_b;
    return q;

效果

针对天空部分的优化

算法对天空部分的处理效果并不理想,天空部分被和非天空部分会出现明显的过渡区域和过度曝光现象。

这是算法从提出就存在的问题,为了解决这跟问题,从以下两种思路进行优化。

设置大气光的阈值

在计算大气光值时提到,A是所有按亮度取前0.1%像素点的平局值,由于天空部分的大气光过大,影响了最后的平均值。

所以考虑设置一个最大全球大气光值作为所有像素点的阈值,如果有像素点的值大于阈值,该值就等于阈值。

这里将阈值设置为220。

因为这种直接取阈值的方法有些绝对,所以多次实验之后发现结果并无较大改变。

增加可控参数K

考虑到上面直接取阈值的方法有些绝对,所以想到设置一个参数来重新计算透射率。

正常情况下,天空部分的大气光值和A使比较接近的,而只有远离天空的地方才能用暗通道先验算法去雾。

所以将符合条件的像素点的值与A相减,如果结果小于某个值,认为这个区域可能是天空,就需要重新计算透射率。

如果结果小于某个值,则认为该区域不是天空,不用重新计算透射率。

这里的某个值,设置为k,每个像素处的透射率用它来调节,则改良后的透射率计算公式为

经过实验,该方法对去雾校果有非常大的改善。

最终优化效果

各参数对去雾效果的影响

窗口的大小

窗口的大小对去雾效果来说是个关键的参数。

从理论角度分析,窗口更大,就有更大的概率包含暗通道,暗通道也就越暗,与无雾图像的对比越不明显,去雾效果也就不好。

实验得到:窗口越大,去雾的效果越不好。窗口大小在11-51之间去雾效果最好。

ω的大小

ω最初是为考虑维持景深,所以保留一定程度的雾,所以其值越小,保留的雾越多,当然去雾效果越不好。

实验后发现,取0.95最佳。

导向滤波半径的大小

关于导向滤波中的半径r值,因为在前面进行形态学处理暗通道的图像成一块一块的。

为了使透射率率更加精准,实验后发现,r的取值大于等于窗口大小的4倍最好。

完整源码

源码下载地址:待审核

完整论文下载地址:待审核

免费地址

🌈寻宝

⭐今天是坚持刷题更文的第48/100天

⭐各位的点赞、关注、收藏、评论、订阅就是一条创作的最大动力

为了回馈各位粉丝,礼尚往来,给大家准备了一条多年积累下来的优质资源,包括 学习视频、面试资料、珍藏电子书等

大家可以点这里领取

以上是关于女朋友嫌我拍的照片有雾,连夜用OpenCV写出❤️去雾算法❤️逃过一劫(收藏保命)的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV ⚠️实战⚠️ 女子深夜久久不能入眠,300行写出全能扫描王! ☢️建议手收藏☢️

杂谈拍了几张照片

Windows如何打开heic 哪个软件可以转换图片格式

热门现在的美颜特效有多可怕?基于Opencv的美颜相机告诉你!

手机qq拍的照片保存在哪里

手机微信小程序拍的照片存在哪?