He 提出暗通道去雾方法进行了详细的描述,该方法以大气散射模型为基础,利用暗通道先验原理求出全球大气光成分A和透射率t。先使用了软抠图对透射率图进行优化,但是运算时间过长。后来使用引导滤波精细化透射率图,缩短了一部分运算时间。
暗通道先验:
在绝大多数非天空的局部区域里,某一些像素总会有至少一个RGB颜色通道具有很低的值。换言之,该区域光强度的最小值是个很小的数,值接近于0。
实际生活中造成暗原色中低通道值主要有三个因素:
a)汽车、建筑物和城市中玻璃窗户的阴影,或者是树叶、树与岩石等自然景观的投影;
b)色彩鲜艳的物体或表面,在RGB的三个通道中有些通道的值很低(比如绿色的草地/树/植物,红色或黄色的花朵/叶子,或者蓝色的水面);
c)颜色较暗的物体或者表面,例如灰暗色的树干和石头。总之,自然景物中到处都是阴影或者彩色,这些景物的图像的暗原色总是很灰暗的。
我们给暗通道一个数学定义,对于任意的输入图像J,其暗通道可以用下式表达:
c表示图像R,G,B中的每个通道。
Jc表示彩色图像的某一通道 。
Ω(x)表示以像素X为中心的一个窗口。
式(5)的意义用代码表达也很简单,求解过程如下:
(1)求出每个像素RGB分量中的最小值,存入一副和原始图像大小相同的灰度图中
(2)对这幅灰度图以15x15的窗口进行最小值滤波,即以每个窗口的最小值代替这个像素点的最小值。滤波的半径由窗口大小决 定(论文采用7),一般有WindowSize = 2 * Radius + 1;
def darkChannel(src, r=15):
temp = np.min(src,2)
s = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (r,r))
dst = cv2.erode(temp, s)
return dst
举个栗子:
由上述几幅图像,可以明显的看到暗通道先验理论的普遍性。在作者的论文中,对5000多副无雾图像手动裁剪掉天空区域,重调整图片大小,使得图片长宽不大于500,Ω(x)的大小选取15*15,发现75%的暗通道的像素值都为0,90%的暗通道的像素值都低于25,很好验证暗通道理论的普适性,因此,我们可以认为其实一条定理。
大气散射模型(求解A与t(x)):
在计算机视觉和计算机图形中,下述方程所描述的雾图形成模型被广泛使用:
I(X)就是我们现在已经有的图像(待去雾的图像)。
J(x)是我们要恢复的无雾的图像。
A是全球大气光成分, t(x)为透射率。
将式(1)稍作处理,变形为下式(C表示R/G/B三个通道的意思):
首先假设在每一个窗口内透射率t(x)为常数,也就是假设在同一窗口的上的透射率是相同的,定义他为,且A值已经给定,然后对式(7)两边求两次最小值运算得到下式:
上式中,J是待求的无雾的图像,根据前述的暗原色先验理论有:
可推导出:
把式(10)带入式(8)中,得到:
这里使用guideFilter引导滤波,优化已经求得的通道图片,所以上式变为t=1-V/A,这里V为透射率图。
引导图应该与原图尽可能相似,取np.min(src,2)
也就是:V = guideFilter(np.min(m,2) , dc, r, eps),这里应该在[0,1]范围内进行,也就是引导图和预估的投射图都必须从[0,255]->[0,1]进行计算。导向滤波的r值应当不小于进行最小值滤波半径r的4倍。
即使是晴天白云,空气中也存在着一些颗粒,因此,看远处的物体还是能感觉到雾的影响,另外,雾的存在让人类感到景深的存在,因此,有必要在去雾的时候保留一定程度的雾,这可以通过在式(11)中引入一个在[0,1] 之间的因子,则式(11)修正为:
注:文中所有的测试结果依赖于: ω=0.95
上述推论中都是假设全球达气光A值时已知的,在实际中,我们可以借助于暗通道图来从有雾图像中获取该值。具体步骤如下:
(1) 从暗通道图中按照亮度的大小取前0.1%的像素。(经过导向滤波这应为投射率图)
(2)在这些位置中,在原始有雾图像I中寻找对应的具有最高亮度的点的值,作为A值。
使用累计灰度直方图来实现求A值
bins = 256
hist = np.histogram(V, bins) #灰度直方图
这里V是待统计数据数组,bins为等分数。返回值有两个:
hist[0]:hist:array,返回数组V中的数据在每个等分区间的个数。
hist[1]:bin_degs,长度为len(hist)+1,为分组的边界。(bin_degs[0], bin_edgs[1])=hist[0]
normHist = hist[0]/float(V.size) #归一化灰度直方图
概率直方图,灰度值k的像素点个数占的图比例
accumulativeHist = np.cumsum(normHist) #累计直方图
代表图像组成成分在灰度级的累计概率分布情况,每一个概率值代表小于等于此灰度值的概率。
for k in range(bins-1, 0, -1):
if accumulativeHist[k]<=0.999: #取前0.1%的像素,并获得该位置k
break
A = np.mean(m,2)[V>=hist[1][k]].max() #在原始有雾的图像I中寻找对应的具有最高亮度的点的值
这里返回的k值就是边界值k。
到这一步,我们就可以进行无雾图像的恢复了。由式(1)可知: J = ( I - A)/t + A
现在I,A,t都已经求得了,因此,完全可以进行J的计算。
当投射图t 的值很小时,会导致J的值偏大,从而使淂图像整体向白场过度,因此一般可设置一阈值T0,当t值小于T0时,令t=T0,本文中所有效果图均以T0=0.1为标准计算。
因此,最终的恢复公式如下:
实验结果对比: