提示:基于暗通道先验方法的图像去雾–何博士_DCP_DEHAZE
提示: 何凯明博士的主页链接
文章目录
前言
暗通道先验:作者通过对大量的户外无雾图像进行统计,发现无雾图像大多数局部色块都包含一些像素,这些像素至少有一个颜色通道中的强度非常低,趋近于0。
大气散射模型:
I(x)为有雾图像,J(x)为无雾图像,A 是全局大气光值,t(x)为透射率,通过t和A就可以根据公式反演得到去雾后的图像。
根据暗通道先验,对无雾图像求暗通道即公式(5),求得的值趋近于0,
一、暗通道(Dark Channel)求取
opencv读取图片的图片通道顺序是BGR
import cv2
def DarkChannel(image, win_size):
b, g, r = cv2.split(image)
min_val = cv2.min(cv2.min(b, g), r)
dark = cv2.erode(min_val, np.ones((win_size, win_size)))
return dark
二、全局大气光值(A)估计
全局大气光值A的估计方法原文说的是:We first pick the top 0.1 percent brightest pixels in the dark channel. Among these pixels,the pixels with highest intensity in the input image I are selected as the atmospheric light.
我的理解:
(1)先求暗通道,挑选出暗通道图像中0.1%像素值最大的。
(2)从暗通道中挑选出来的像素对应到原图像中的像素点,取这些对应的像素点强度最大的像素作为全局大气光值A。注意这里的A值是3个具体的值(3个通道嘛)。
具体实现见如下代码:
def GlobalAtmosphereLight(image, dark):
[h, w] = image.shape[:2]
total_pixel = h * w
# 选暗通道中0.1%最亮像素,对应到原图像中,找到像素强度最大的作为A, A:(1,1,3)
select_num = int(math.floor(total_pixel / 1000))
dark_vector = dark.reshape(total_pixel, 1) # (12, 1)
indexs = np.argsort(dark_vector, 0) # 按列排序,小到大
image_vector = image.reshape(total_pixel, 3) # (h*w, 3)
indexs = indexs[total_pixel - select_num::]
b, g, r = cv2.split(image)
gray_image = r * 0.299 + g * 0.587 + b * 0.114
gray_image = gray_image.reshape(total_pixel, 1)
location = np.where(gray_image == max(gray_image[indexs]))
x = location[0][0]
A = np.array(image_vector[x]) # (1, 1, 3)
A = A.reshape(1, 3)
return A
三、粗投射率t的估计
计算公式:
def CoarseTransmission(image, A, win_size):
omega = 0.95
img = np.empty(image.shape)
for c in range(0, 3):
img[:, :, c] = image[:, :, c] / A[0, c]
coarse_transmission = 1 - omega * DarkChannel(img, win_size)
return coarse_transmission
四、引导滤波(GIF)
关于引导滤波的讲解可以参见以下链接:
(1)何博士-Guided Image Filtering主页链接
(2)具体公式推倒理解
说明:
在Guided Image Filtering 一文中有提到对彩色图像进行引导滤波引导图可以采用三通道的引导图。所以后面也放上三通道的引导滤波。读者可以自己下去尝试三通道的引导滤波的滤波保持边缘的效果。
但是,暗通道先验方法去雾中估计得到的透射率图是单通道的,所以用引导滤波对粗透射率图进行细化时,引导图像也是单通道的(这里引导图用的是雾图的灰度图像)。
如下是单通道的引导滤波:
def GuideFilter(image, guide_image, gf_radius, eps):
# 当normalize=True时,与均值滤波结果相同
mean_i = cv2.boxFilter(guide_image, -1, (gf_radius, gf_radius), normalize=True)
mean_p = cv2.boxFilter(image, -1, (gf_radius, gf_radius), normalize=True)
mean_ii = cv2.boxFilter(guide_image * guide_image, -1, (gf_radius, gf_radius), normalize=True)
mean_ip = cv2.boxFilter(guide_image * image, -1, (gf_radius, gf_radius), normalize=True)
var_i = mean_ii - mean_i * mean_i
cov_ip = mean_ip - mean_i * mean_p
ak = cov_ip / (var_i + eps)
bk = mean_p - ak * mean_i
mean_ak = cv2.boxFilter(ak, -1, (gf_radius, gf_radius), normalize=True)
mean_bk = cv2.boxFilter(bk, -1, (gf_radius, gf_radius), normalize=True)
q = mean_ak * guide_image + mean_bk
return q
三通道的引导滤波:
import cv2
def GuideFilterThreeChanel(image, guide_image, gf_radius, eps):
"""
:param input_p: 输入图(3通道)
:param guide_i: 引导图(可以是输入图也可其他)
:param guide_radius: 引导滤波的滤波器大小
:param eps: 正则化参数 0.01
:return:
"""
p_b, p_g, p_r = cv2.split(image) # The channel order for cv2 to read images is BGR
i_b, i_g, i_r = cv2.split(guide_image)
gf_b = Guidefilter_onechannel(p_b, i_b, gf_radius, eps)
gf_g = Guidefilter_onechannel(p_g, i_g, gf_radius, eps)
gf_r = Guidefilter_onechannel(p_r, i_r, gf_radius, eps)
gf_bgr = cv2.merge([gf_b, gf_g, gf_r])
return gf_bgr
# 五、投射率t的细化
def FineTransmission(transmission, guide_image, gf_radius, eps):
fine_transmission = GuideFilter(transmission, guide_image, gf_radius, eps)
return fine_transmission
六、去雾(Dehaze)
def Dehaze(image_path, win_size, guidefilter_radius, eps):
hazeimage = cv2.imread(img_path)
grayimage = cv2.cvtColor(haze_image, cv2.COLOR_BGR2GRAY) / 255.0
haze_image = haze_image / 255.0
dehaze_image = np.empty(haze_image.shape)
A = GlobalAtmosphereLight(haze_image, DarkChannel(haze_image, win_size))
coarse_transmission = CoarseTransmission(haze_image, A, win_size)
fine_transmission = FineTransmission(coarse_transmission, gray_image, gf_radius, eps)
# 求无雾图J(x): J(x) = (I(x)-A) / t(x) + A
for cin range(0, 3):
dehaze_image[:, :, c] = (hazeimage[:, :, c] - A[0, c]) / cv2.max(finetransmission , 0.1) + A[0, index]
cv2.imwrite(dehaze_image * 255)
七、完整程序
Dehaze.py文件:
import cv2, math
import numpy as np
def DarkChannel(image, win_size):
b, g, r = cv2.split(image)
min_val = cv2.min(cv2.min(b, g), r)
dark = cv2.erode(min_val, np.ones((win_size, win_size)))
return dark
def GlobalAtmosphereLight(image, dark):
[h, w] = image.shape[:2]
total_pixel = h * w
# 选暗通道中0.1%最亮像素,对应到原图像中,找到像素强度最大的作为A, A:(1,1,3)
select_num = int(math.floor(total_pixel / 1000))
dark_vector = dark.reshape(total_pixel, 1) # (12, 1)
indexs = np.argsort(dark_vector, 0) # 按列排序,小到大
image_vector = image.reshape(total_pixel, 3) # (h*w, 3)
indexs = indexs[total_pixel - select_num::]
b, g, r = cv2.split(image)
gray_image = r * 0.299 + g * 0.587 + b * 0.114
gray_image = gray_image.reshape(total_pixel, 1)
location = np.where(gray_image == max(gray_image[indexs]))
x = location[0][0]
A = np.array(image_vector[x]) # (1, 1, 3)
A = A.reshape(1, 3)
return A
def CoarseTransmission(image, A, win_size):
omega = 0.95
img = np.empty(image.shape)
for c in range(0, 3):
img[:, :, c] = image[:, :, c] / A[0, c]
coarse_transmission = 1 - omega * DarkChannel(img, win_size)
return coarse_transmission
def GuideFilter(image, guide_image, gf_radius, eps):
# 当normalize=True时,与均值滤波结果相同
mean_i = cv2.boxFilter(guide_image, -1, (gf_radius, gf_radius), normalize=True)
mean_p = cv2.boxFilter(image, -1, (gf_radius, gf_radius), normalize=True)
mean_ii = cv2.boxFilter(guide_image * guide_image, -1, (gf_radius, gf_radius), normalize=True)
mean_ip = cv2.boxFilter(guide_image * image, -1, (gf_radius, gf_radius), normalize=True)
var_i = mean_ii - mean_i * mean_i
cov_ip = mean_ip - mean_i * mean_p
ak = cov_ip / (var_i + eps)
bk = mean_p - ak * mean_i
mean_ak = cv2.boxFilter(ak, -1, (gf_radius, gf_radius), normalize=True)
mean_bk = cv2.boxFilter(bk, -1, (gf_radius, gf_radius), normalize=True)
q = mean_ak * guide_image + mean_bk
return q
def FineTransmission(transmission, guide_image, gf_radius, eps):
fine_transmission = GuideFilter(transmission, guide_image, gf_radius, eps)
return fine_transmission
Main.py文件:
import os, glob, argparse
from Dehaze import *
parser = argparse.ArgumentParser()
# 路径
parser.add_argument('--Haze_path', type=str, default='Haze')
parser.add_argument('--Dehaze_path', type=str, default='Dehaze')
parser.add_argument('--DarkChannel_path', type=str, default='DarkChannel')
parser.add_argument('--Transmission_path', type=str, default='Transmission')
# 参数配置
parser.add_argument('--win_size', type=int, default='15')
parser.add_argument('--gf_radius', type=int, default='30')
parser.add_argument('--eps', type=float, default='0.001')
config = parser.parse_args()
def main(mode):
if mode == "jpg":
src_list = glob.glob(os.path.join(config.Haze_path, '*.png'))
else:
src_list = glob.glob(os.path.join(config.Haze_path, '*.jpg'))
for img_path in src_list:
haze_image = cv2.imread(img_path)
gray_image = cv2.cvtColor(haze_image, cv2.COLOR_BGR2GRAY) / 255.0
haze_image = haze_image / 255.0
A = GlobalAtmosphereLight(haze_image, DarkChannel(haze_image, config.win_size))
coarse_transmission = CoarseTransmission(haze_image, A, config.win_size)
fine_transmission = FineTransmission(coarse_transmission, gray_image, config.gf_radius, config.eps)
dehaze_image = np.empty(haze_image.shape)
for c in range(0, 3):
dehaze_image[:, :, c] = (haze_image[:, :, c] - A[0, c]) / cv2.max(0.1, fine_transmission) + A[0, c]
cv2.imwrite(config.Dehaze_path + '/' + img_path.split(os.sep)[-1], dehaze_image * 255)
# 保存粗投射率图、细化的透射率图
cv2.imwrite(config.Transmission_path + '/' +
'CoarseTransMap_' + img_path.split(os.sep)[-1], coarse_transmission * 255)
cv2.imwrite(config.Transmission_path + '/' +
'FineTransMap_' + img_path.split(os.sep)[-1], fine_transmission * 255)
# 保存暗通道
darkchannel = DarkChannel(haze_image, config.win_size)
cv2.imwrite(config.DarkChannel_path + '/' + img_path.split(os.sep)[-1], darkchannel*255)
if __name__ == '__main__':
main(mode='png')
main(mode='jpg')
print("dehaze done!")
八、去雾效果
总结
基于暗通道先验的图像去雾方法,其去雾的效果总的来说还是蛮好的,但该方法对天空区域透射率的估计不准确,去雾是可能产生光晕效应。