雾霾是由空气中的灰尘和烟雾等小的漂浮颗粒产生的常见大气现象。这些漂浮的颗粒极大地吸收和散射光,导致图像质量下降。在雾霾影响下,视频监控,远程感应,自动驾驶等许多实际应用很容易受到威胁,检测和识别等高级计算机视觉任务很难完成。因此,图像去雾(除雾)成为一种越来越重要的技术。
暗通道去雾算法
Kaiming(何凯明)早在09年以MSRA实习生的身份发布的Single Image Haze Removal Using Dark Channel Prior获得CVPR best paper,其内容就是给图像去雾。当时并没有用深度学习,却能实现让人震惊的效果。
1、雾图模型
计算机视觉中,下面这个雾图形成模型是被广泛使用的:
其中I(x)是现有的图像(待去雾),J(x)是要恢复的原无雾图像,A是全球大气光成分,t(x)是大气折射率。
变形可得:
2、暗通道与折射率预估
所谓暗通道是一个基本假设,这个假设认为,在绝大多数的非天空的局部区域中,某一些像素总会有至少一个颜色通道具有很低的值。这个其实很容易理解,实际生活中造成这个假设的原因有很多,比如汽车,建筑物或者城市中的阴影,或者说色彩鲜艳的物体或表面(比如绿色的树叶,各种鲜艳的花,或者蓝色绿色的睡眠),颜色较暗的物体或者表面,这些景物的暗通道总是变现为比较暗的状态。
我们来看一下有雾图像和无雾图像暗通道的区别:
可以发现,有雾的时候会呈现一定的灰色,而无雾的时候则会呈现大量的黑色(像素为接近0)。
所以暗通道是什么呢?其实比较简单,作者认为暗通道是:
暗通道先验理论指出:
由此可以用于预估折射率
但是现实生活中,即使是晴天白云,空气中也会存在一些颗粒,看远方的物体还是能够感觉到雾的影响,另外,雾的存在可以让人们感觉到景深的存在,所以我们保留一部分的雾,上式修正为:
其中w是[0-1]之间的一个值,一般取0.95差不多。
3、计算全球大气光
现在唯一未知的就是全球大气光A了。实际中,我们可以借助暗通道图来从有雾图像中来获取该值:
第一步,从暗通道图中按照亮度大小取前0.1%的像素。
第二步,在这些位置中,在原始图像中寻找对应具有最高亮度点的值,作为A值。
4、代码实现
# coding=utf-8
import numpy as np
from PIL import Image
import cv2 as cv
import matplotlib.pyplot as plt
import math
def guidedfilter(I, p, r, eps):
'''
引导滤波
'''
height, width = I.shape
m_I = cv.boxFilter(I, -1, (r,r))
m_p = cv.boxFilter(p, -1, (r,r))
m_Ip = cv.boxFilter(I*p, -1, (r,r))
cov_Ip = m_Ip-m_I*m_p
m_II = cv.boxFilter(I*I, -1, (r,r))
var_I = m_II-m_I*m_I
a = cov_Ip/(var_I+eps)
b = m_p-a*m_I
m_a = cv.boxFilter(a, -1, (r,r))
m_b = cv.boxFilter(b, -1, (r,r))
return m_a*I+m_b
def get_dc_A(I, r, eps, w, maxGray):
'''
计算暗通道图dc和光照值A:V1 = 1-t/A
INPUT -> 归一化图像数组, 滤波器半径, 噪声, 灰度范围, maxGray
'''
# 分离三通道
B,G,R = cv.split(I)
# 求出每个像素RGB分量中的最小值, 得到暗通道图
dc = cv.min(cv.min(R,G), B)
# 使用引导滤波优化
dc = guidedfilter(dc, cv.erode(dc, np.ones((2*r+1, 2*r+1))), r, eps)
bins = 2000
# 计算大气光照A
ht = np.histogram(dc, bins)
d = np.cumsum(ht[0])/float(dc.size)
for lmax in range(bins-1, 0, -1):
if d[lmax]<=0.999:
break
A = np.mean(I, 2)[dc>=ht[1][lmax]].max()
# 对值范围进行限制
dc = np.minimum(dc*w, maxGray)
return dc, A
if __name__ == '__main__':
src = np.array(Image.open('231.jpg'))
I = src.astype('float64')/255
# 得到遮罩图像和大气光照
dc, A = get_dc_A(I, 111, 0.001, 0.95, 0.80)
# 调用雾图模型
J = np.zeros(I.shape)
for k in range(3):
J[:,:,k] = (I[:,:,k]-dc)/(1-dc/A)
J = np.clip(J, 0, 1)
# 伽马校正
J = J**(np.log(0.5)/np.log(J.mean()))
# 拼接结果
output = np.hstack((I, J))
plt.imshow(Image.fromarray(np.uint8(output*255)))
plt.show()
网友评论