├── .idea
├── .gitignore
├── vcs.xml
├── misc.xml
├── inspectionProfiles
│ └── profiles_settings.xml
├── other.xml
├── modules.xml
└── pythonProject2.iml
├── dehaze_img.png
├── Internet_DehazeFog.py
└── README.md
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/dehaze_img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FengEternity/DehazeFog/HEAD/dehaze_img.png
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/other.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/pythonProject2.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Internet_DehazeFog.py:
--------------------------------------------------------------------------------
1 | import math
2 | import time
3 | import cv2
4 | import itertools
5 | import numpy as np
6 |
7 |
8 | def Dark_img(image): # 此处返回的是灰度图
9 | """
10 | 求暗通道图像
11 | :param image: 读取到的原图
12 | :return: 经过三通道最小值处理以及最小值滤波的暗通道灰度图
13 | """
14 | '''rgb三通道中取最小值'''
15 | min_img = np.copy(image)
16 | for h in range(min_img.shape[0]):
17 | for w in range(min_img.shape[1]):
18 | for c in range(min_img.shape[2]):
19 | min_img[h, w, c] = np.min(min_img[h, w, c:c + 3])
20 | '''再进行一个最小值滤波 方盒大小为15'''
21 | dark_img = np.copy(min_img)
22 | dark_img = cv2.cvtColor(dark_img, cv2.COLOR_BGR2GRAY)
23 | r = 15
24 | for h in range(dark_img.shape[0]):
25 | for w in range(dark_img.shape[1]):
26 | if (h+r) >= dark_img.shape[0]:
27 | dark_img[h, w] = np.min(dark_img[h:dark_img.shape[0], w:w + r])
28 | if (w+r) >= dark_img.shape[1]:
29 | dark_img[h, w] = np.min(dark_img[h:h+r, w:dark_img.shape[1]])
30 | if (h+r) < dark_img.shape[0] and (w+r) < dark_img.shape[1]:
31 | dark_img[h, w] = np.min(dark_img[h:h + r, w:w + r])
32 | # cv2.imshow('dark_img', dark_img)
33 | return dark_img
34 |
35 |
36 | def Guided_img(I, p, winSize, eps):
37 | """
38 | 导向滤波
39 | :param I: 原图的灰度图/255.0,以此为导向
40 | :param p: 最小值处理后的按通道图/255.0
41 | :param winSize: 做均值滤波box的大小
42 | :param eps: 0.001
43 | :return: 一个灰度图,对暗通道做导向滤波
44 | """
45 | mean_I = cv2.blur(I, winSize) # I的均值平滑
46 | mean_p = cv2.blur(p, winSize) # p的均值平滑
47 |
48 | mean_II = cv2.blur(I * I, winSize) # I*I的均值平滑
49 | mean_Ip = cv2.blur(I * p, winSize) # I*p的均值平滑
50 |
51 | var_I = mean_II - mean_I * mean_I # 方差
52 | cov_Ip = mean_Ip - mean_I * mean_p # 协方差
53 |
54 | a = cov_Ip / (var_I + eps) # 相关因子a
55 | b = mean_p - a * mean_I # 相关因子b
56 |
57 | mean_a = cv2.blur(a, winSize) # 对a进行均值平滑
58 | mean_b = cv2.blur(b, winSize) # 对b进行均值平滑
59 |
60 | q = mean_a * I + mean_b
61 | # cv2.imshow('guided_img', guided_img)
62 | return np.uint8(np.clip(255 * q, 0, 255))
63 |
64 |
65 | def A(image, dark_img):
66 | """
67 | 求对应三通道大气光A的值 取值最小0.1%的像素点计算平均值
68 | :param image: 原图
69 | :param dark_img: 暗通道(rgb)
70 | :return: 大气光的rgb值
71 | """
72 | height = image.shape[0]
73 | width = image.shape[1]
74 | size = width * height # 计算图像的总像素点数
75 | min_part_size = int(max(math.floor(size/1000), 1)) # 取总个数的0.1%
76 | arr = []
77 | for row in dark_img: # 变为一维数组
78 | arr.extend(row)
79 | indexes = np.argsort(arr)[::-1] # 寻找最大像素的下标
80 | a_sum = np.zeros(3)
81 | for i, j in itertools.product(range(3), range(min_part_size)):
82 | # 根据一维数组下标计算对应二维数组下标
83 | a_sum[i] += image[indexes[j]//width][indexes[j]-width*(indexes[j]//width)][i]
84 | # 计算平均值
85 | A = a_sum / min_part_size
86 | for i in range(3):
87 | A[i] = int(A[i])
88 | return A
89 |
90 |
91 | def tx(guided_img):
92 | """
93 | 求tx
94 | :param guided_img: 暗通道导向图(rgb)/255.0
95 | :return: tx
96 | """
97 | return np.clip(1.0 - guided_img*0.95, 0.30, 1.00)
98 |
99 |
100 | """
101 | 图像去雾——暗通道算法
102 | :param path: 图像路径
103 | :return: none
104 | """
105 |
106 |
107 | def Dehaze(image):
108 | '''读取图像'''
109 | initial_img = np.copy(image)
110 | dark_img = Dark_img(image) # 得到暗通道图像
111 | guided_img = Guided_img(cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)/255.0,
112 | dark_img/255.0, (81, 81), 0.001) # 进行引导滤波
113 | guided_img = cv2.cvtColor(guided_img, cv2.COLOR_GRAY2BGR)
114 |
115 | A_ = A(guided_img, dark_img) # 求大气光照A
116 | tx_ = tx(guided_img/255.0) # 求tx
117 | dehaze_img = (initial_img - A_)/tx_ + A_
118 |
119 | cv2.imwrite('dehaze_img.png', dehaze_img)
120 | time.sleep(1.0)
121 | return initial_img, cv2.imread('dehaze_img.png', 1)
122 |
123 |
124 | path = r"D:\26059\Desktop\DehazeFog\test_images\download.jpg"
125 | image = cv2.imread(path, 1)
126 | height = 500
127 | width = int(height * image.shape[0] / image.shape[1])
128 | image = cv2.resize(image, (height, width))
129 | initial, dehaze = Dehaze(image) # 去雾
130 | cv2.imshow('Dehaze', np.hstack((initial, dehaze)))
131 | cv2.waitKey(0)
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ImageDehazeFog
2 | 何恺明博士《Single Image Haze Removal Using Dark Channel Prior》论文及代码复现
3 | # ImageDehazeFog
4 |
5 | 何恺明博士《Single Image Haze Removal Using Dark Channel Prior》论文及代码复现
6 |
7 | # 暗通道先验去雾算法
8 |
9 | 该算法是计算机视觉领域何恺明大佬于2009年提出的图像去雾经典算法,并获取当年CVPR最佳论文。论文题目为《Single Image Haze Removal Using Dark Channel Prior》。下图是大佬的百科简介,是真的厉害,值得我们大家学习。
10 |
11 | 2003年5月,何恺明拿到保送清华的资格,是当年执信中学唯一保送上清华大学的学生;高考结果出炉以后,何恺明获得满分900分的成绩,成为当年广东省9位满分状元之一。
12 | 2009年,何恺明成为首获计算机视觉领域三大国际会议之一CVPR“最佳论文奖”的中国学者。
13 | 在2015年的ImageNet图像识别大赛中,何恺明和他的团队用“图像识别深度差残学习”系统,击败谷歌、英特尔、高通等业界团队,荣获第一。
14 | 何恺明作为第一作者获得了CVPR 2009,CVPR 2016和ICCV 2017(Marr Prize)的最佳论文奖,并获得了ICCV 2017最佳学生论文奖。
15 | 2018年,第31届计算机视觉和模式识别大会(Conference on Computer Vision and Pattern Recognition, CVPR)在美国盐湖城召开,何恺明获得本届大会的PAMI年轻学者奖。
16 |
17 | 
18 |
19 | ## 1.算法原理
20 |
21 | 言归正传,如果是图像处理或研究图像去雾领域的作者,建议大家认真阅读这篇英文原文,能在2009年提出该算法真的很惊艳。
22 |
23 | ## 引用及参考中文论文
24 |
25 | 何涛, 等. 基于暗通道先验的单幅图像去雾新算法[J]. 计算机科学, 2021.
26 | 王蓉, 等. 基于改进加权融合暗通道算法的图像去雾研究[J]. 浙江科技学院学报, 2021.
27 | 图像去雾算法的原理、实现、效果(速度可实时)- 挚爱图像处理
28 | 图像去雾之何凯明暗通道先验去雾算法原理及c++代码实现 - Do it !
29 |
30 | ## 英文原文
31 |
32 | https://ieeexplore.ieee.org/document/5567108
33 | Single Image Haze Removal Using Dark Channel Prior
34 | https://ieeexplore.ieee.org/document/5206515
35 | Single image haze removal using dark channel prior
36 |
37 | 
38 |
39 | **暗通道先验(Dark Channel Prior, DCP)去雾算法 依赖大气散射模型进行去雾处理,通过对大量有雾图像和无雾图像进行观察总结,得到其中存在的一些映射关系,然后根据有雾图像的形成过程来进行逆运算,从而恢复清晰图像。**
40 |
41 | 
42 |
43 | 算法实现过程及原理如下,参考何恺明老师和何涛老师的论文。
44 |
45 | ### (1) 大气散射模型
46 |
47 | 在计算机视觉和计算机图形学中,方程所描述的大气散射模型被广泛使用。参数解释如下:
48 |
49 | x是图像的空间坐标
50 | I(x)代表有雾图像(待去雾图像)
51 | J(x)代表无雾图像(待恢复图像)
52 | A代表全球大气光值
53 | t(x)代表透射率
54 | 方程右边第一项为场景直接衰减项,第二项为环境光项。
55 |
56 | 
57 |
58 | ### (2) 暗通道定义
59 |
60 | 在绝大多数非天空的局部区域中,某些像素总会至少有一个颜色通道的值很低。对于一幅图像J(x),其暗通道的数学定义表示如下:
61 |
62 | 
63 |
64 | 其中,Ω(x)表示以x为中心的局部区域,上标c表示RGB三个通道。该公式的意义用代码表达也很简单,首先求出每个像素RGB分量中的最小值,存入一副和原始图像大小相同的灰度图中,然后再对这幅灰度图进行最小值滤波,滤波的半径由窗口大小决定。
65 |
66 | ### (3) 暗通道先验理论
67 |
68 | 暗通道先验理论指出:对于非天空区域的无雾图像J(x)的暗通道趋于0,即:
69 |
70 | 
71 |
72 | 实际生活中造成暗原色中低通道值主要有三个因素:
73 |
74 | a) 汽车、建筑物和城市中玻璃窗户的阴影,或者是树叶、树与岩石等自然景观的投影;
75 | b) 色彩鲜艳的物体或表面,在RGB的三个通道中有些通道的值很低(比如绿色的草地/树/植物,红色或黄色的花朵/叶子,或者蓝色的水面);
76 | c) 颜色较暗的物体或者表面,例如灰暗色的树干和石头。
77 |
78 |
79 |
80 | 
81 |
82 | 总之,自然景物中到处都是阴影或者彩色,这些景物的图像的暗原色总是很灰暗的,而有雾的图像较亮。因此,可以明显的看到暗通道先验理论的普遍性。
83 |
84 |
85 |
86 | 
87 |
88 | ### (4) 公式变形
89 |
90 | 根据大气散射模型,将第一个公式稍作处理,变形为下式:
91 |
92 | 
93 |
94 | 假设每一个窗口的透射率t(x)为常数,记为t’(x),并且A值已给定,对式两边同时进行两次最小值运算,可得:
95 |
96 | 
97 |
98 | 其中,J(x)是要求的无雾图像,根据前述的暗通道先验理论可知:
99 |
100 | 
101 |
102 | 因此可推导出:
103 |
104 |
105 | 
106 |
107 | ### (5) 透射率计算
108 |
109 | 将上式带入可得到透射率t’(x)的预估值,如下所示:
110 |
111 | 
112 |
113 | 现实生活中,即便晴空万里,空气中也会存在一些颗粒,在眺望远处的景物时,人们还是能感觉到雾的存在。另外,雾的存在让人们感受到景深,因此在去雾的同时有必要保留一定程度的雾。可以通过引入一个0到1之 间 的 因 子 w(一 般取0.95)对预估透射率进行修正,如式所示:
114 |
115 | 
116 |
117 | 以上的推导过程均假设大气光值A是已知的,在实际中,可以借助暗通道图从原始雾图中求取。具体步骤如下:
118 |
119 | 先求取暗通道图,在暗通道图中按照亮度的大小提取最亮的前0.1%的像素
120 | 在原始雾图I(x)中找对应位置上具有最高亮度的点的值,作为大气光值A
121 | 此外,由于透射率t偏小时,会造成J偏大,恢复的无雾图像整体向白场过度,因此有必要对透射率设置一个下限值t0(一般取值为0.1),当t值小于t0 时,取t=t0。将以上求得的透射率和大气光值代入公式,最终整理得到图像的恢复公式如下:
122 |
123 |
124 | 
125 |
126 | 这就是暗通道先验去雾算法的原理过程,下面简单补充论文中的处理效果图。
127 | 
128 |
129 | 再次膜拜偶像,极力推荐大家阅读论文。
130 |
--------------------------------------------------------------------------------