描述
上一篇文章,对何恺明的论文进行了逐行分析,但总体内容还是有些繁琐,不利于学习总结。
本篇将重点思想按照工程顺序给出,便于理解去雾的核心思想。我还列出了python版本的实现,是我按照论文的公式去复现的。
算法核心思想
1. 雾的模型
有雾的图像,与正常图像有什么区别呢?作者结合参考文献,给出了这样的公式
意思是说:我们拿到的有雾图像素值,是正确图像经过一个t的衰减,增加一个大气光引起的增益后的像素值。
这是论文的基础公式
2. 暗通道先验
作者对5000张没有雾的图片进行了统计,发现了这样一件事情:
除了天空等一些特殊情况,图片中每个像素总有一个颜色通道的值会比较低
同时,有雾的图像是不满足这个先验的。也就是说,如果提取每个像素的最低颜色通道值,无雾图会比较黑,有雾图像会比较白。
暗通道先验是论文的核心思想
3. 明确去雾问题
我们去雾的目标,就是已知有雾图像I,想办法求出大气光A和透射t,最终得到去雾图像J
根据论文里的计算,这个关系的公式就是:
t_0取值为0.1,x为每个像素点。如公式所示,如果我们知道了大气光A和透射t,就可以还原无雾图像了。
4. 确定大气光A
根据统计或者可以说是分析,我们发现天空的颜色总是与大气光接近,天空的暗通道会很亮同时透射率很接近0。
因此何恺明这样来确定大气光:求出原始有雾图像的暗通道图像,在暗通道图像中找到像素值最高的、占总像素数目前0.1%的像素点位置,这些位置中原始图像像素值最高的那个像素点,就是大气光A
这样做,我们就有可能排除图中蓝色框中的错误选择,显然像素值最高的白车和白建筑并不是A。黄色框是暗通道最亮的0.1%,这些位置中在原始图像最亮的像素点才最可能是大气光
5. 根据大气光计算出透射率
有了大气光A之后,还要计算的就是透射率。
每个像素点,都有一个对应的透射值,公式如下:
\omega取值为0.95,Ω(x)为像素的15×15邻域,c代表颜色rgb三个通道
6. 透射率需要优化
何恺明提到,因为假设的15×15邻域透射率一致并不一定成立,所以计算出的透射率t并不是最优的。何恺明在去雾文章使用了Soft Matting的方式优化,看图会发现暗通道的边缘信息被很好的保留了下来。但这种方法速度过慢,实现起来也稍微复杂些。
而后在2010年何恺明又提出了导向滤波的方式,计算速度更快效果更好。
工程实现步骤
为了复现这篇有意思的文章,我按照文章的意思编写了python程序来复现。具体的工程步骤如下:
- 求出原始图像的暗通道图像
- 暗通道图像中的像素值进行升序,取像素值大小前0.1%,再从原始图中确定大气光A
- 利用大气光A,求出每个像素点的透射率
- 透射率t优化(使用导向滤波)
- 根据大气光A和透视率t,计算出了无雾图像
值得说明的一点就是:由于Soft Matting复现复杂且速度过慢(还由于我比较懒,Soft matting得写很多行代码的。。。),透射率t的优化部分,我直接借鉴了一个开源的导向滤波函数。透射率优化这步,虽然对结果有重要影响,但不影响理解何恺明的思想。为我的偷懒说一句抱歉。
论文复现(python版本)
import cv2
import numpy as np
import sys
import copy
# 计算大气光A
def estimating_atmospheric_light(image, dark_channel):
i = dark_channel.shape
num_sum = dark_channel.shape[0] * dark_channel.shape[1]
num = int(num_sum * 0.001)
print("image has " + str(num_sum) + " points. 0.1% has " + str(num) + " points")
pixels = [] # 储存全部的像素值
for i in range(dark_channel.shape[0]):
for j in range(dark_channel.shape[1]):
pixels.append(dark_channel[i][j])
pixels_sorted_id = sorted(range(len(pixels)), key=lambda k: pixels[k]) # 像素值排序,储存升序后的id
value_threshold = pixels[pixels_sorted_id[len(pixels)-num]]
print("dark channel value >= " + str(value_threshold) + " belong 0.1%")
max_value = 0
row = 0
col = 0
for i in range(dark_channel.shape[0]):
for j in range(dark_channel.shape[1]):
if dark_channel[i][j] > value_threshold:
value = int(image[i][j][0]) + int(image[i][j][1]) + int(image[i][j][2])
if value > max_value:
max_value = value
row = i
col = j
A = image[row][col]
print("A at: [" + str(row) + ", " + str(col) + "], value: " + str(A))
# cv2.circle(image, (col, row), 10, (0, 0, 255), 1)
return A
# 计算图像的暗通道
def dark_channel(img, size=15):
r, g, b = cv2.split(img)
min_img = cv2.min(r, cv2.min(g, b))
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (size, size))
dc_img = cv2.erode(min_img, kernel)
return dc_img
# 导向滤波
def guidedFilter(p, i, r, e):
mean_I = cv2.boxFilter(i, cv2.CV_64F, (r, r))
mean_p = cv2.boxFilter(p, cv2.CV_64F, (r, r))
corr_I = cv2.boxFilter(i * i, cv2.CV_64F, (r, r))
corr_Ip = cv2.boxFilter(i * p, cv2.CV_64F, (r, r))
var_I = corr_I - mean_I * mean_I
cov_Ip = corr_Ip - mean_I * mean_p
# 3
a = cov_Ip / (var_I + e)
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 * i + mean_b
return q
# 去雾步骤
def dehaze(image):
dark_prior = dark_channel(image) # 原图像的暗通道
cv2.imshow("dark channel of origin", dark_prior)
A = estimating_atmospheric_light(image, dark_prior) # 确定大气光
t = image / A
t_min = dark_channel(t)
# 导向滤波优化image/A的暗通道
img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY).astype('float64') / 255
t_min = guidedFilter(t_min, img_gray, 225, 0.0001)
cv2.imshow("transmission", t_min)
# 计算透射率
t_x = [[0 for i in range(t_min.shape[1])] for i in range(t_min.shape[0])]
for i in range(t_min.shape[0]):
for j in range(t_min.shape[1]):
t_x[i][j] = 1 - 0.95 * t_min[i][j]
# 计算去雾图
image_noHaze = copy.copy(image)
for i in range(image_noHaze.shape[0]):
for j in range(image_noHaze.shape[1]):
image_noHaze[i][j][0] = max(min((int(image[i][j][0]) - int(A[0])) / max(0.1, t_x[i][j]) + int(A[0]), 255), 0)
image_noHaze[i][j][1] = max(min((int(image[i][j][1]) - int(A[1])) / max(0.1, t_x[i][j]) + int(A[1]), 255), 0)
image_noHaze[i][j][2] = max(min((int(image[i][j][2]) - int(A[2])) / max(0.1, t_x[i][j]) + int(A[2]), 255), 0)
image_noHaze = np.uint8(image_noHaze)
return image_noHaze
if __name__ == '__main__':
image_origin = cv2.imread('2.jpg')
print("origin image shape: " + str(image_origin.shape))
result = dehaze(image_origin)
cv2.imshow("origin", image_origin)
cv2.imshow("result", result)
cv2.imwrite('defog.jpg', result)
while True:
k = cv2.waitKey(1)
if k == 27: # esc键
break
sys.exit(0)
代码结果
原图
去雾后
总结
这篇文章针对何恺明去雾论文,给出了python版本的复现。代码可以直接运行。
评论(0)
您还未登录,请登录后发表或查看评论