├── .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 | 6 | -------------------------------------------------------------------------------- /.idea/other.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 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 | ![](https://img-blog.csdnimg.cn/0839ce4e3cbf4f53b35cd86063838435.png?x-oss-process%3Dimage%2Fwatermark%2Ctype_ZHJvaWRzYW5zZmFsbGJhY2s%2Cshadow_50%2Ctext_Q1NETiBARWFzdG1vdW50%2Csize_20%2Ccolor_FFFFFF%2Ct_70%2Cg_se%2Cx_16#pic_center) 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 | ![](https://img-blog.csdnimg.cn/3416dd0a435b410ba5ece4c90b2f2821.png?x-oss-process%3Dimage%2Fwatermark%2Ctype_ZHJvaWRzYW5zZmFsbGJhY2s%2Cshadow_50%2Ctext_Q1NETiBARWFzdG1vdW50%2Csize_20%2Ccolor_FFFFFF%2Ct_70%2Cg_se%2Cx_16#pic_center) 38 | 39 | **暗通道先验(Dark Channel Prior, DCP)去雾算法 依赖大气散射模型进行去雾处理,通过对大量有雾图像和无雾图像进行观察总结,得到其中存在的一些映射关系,然后根据有雾图像的形成过程来进行逆运算,从而恢复清晰图像。** 40 | 41 | ![](https://img-blog.csdnimg.cn/e1bde38155b046828fb13ee0309fbb79.png?x-oss-process%3Dimage%2Fwatermark%2Ctype_ZHJvaWRzYW5zZmFsbGJhY2s%2Cshadow_50%2Ctext_Q1NETiBARWFzdG1vdW50%2Csize_20%2Ccolor_FFFFFF%2Ct_70%2Cg_se%2Cx_16#pic_center) 42 | 43 | 算法实现过程及原理如下,参考何恺明老师和何涛老师的论文。 44 | 45 | ### (1) 大气散射模型 46 | 47 | 在计算机视觉和计算机图形学中,方程所描述的大气散射模型被广泛使用。参数解释如下: 48 | 49 | x是图像的空间坐标 50 | I(x)代表有雾图像(待去雾图像) 51 | J(x)代表无雾图像(待恢复图像) 52 | A代表全球大气光值 53 | t(x)代表透射率 54 | 方程右边第一项为场景直接衰减项,第二项为环境光项。 55 | 56 | ![](https://img-blog.csdnimg.cn/acc445eff5a84c27a4026f1e9c505f44.png#pic_center) 57 | 58 | ### (2) 暗通道定义 59 | 60 | 在绝大多数非天空的局部区域中,某些像素总会至少有一个颜色通道的值很低。对于一幅图像J(x),其暗通道的数学定义表示如下: 61 | 62 | ![](https://img-blog.csdnimg.cn/46432e67aecb4d029689a0ce72212ecf.png#pic_center) 63 | 64 | 其中,Ω(x)表示以x为中心的局部区域,上标c表示RGB三个通道。该公式的意义用代码表达也很简单,首先求出每个像素RGB分量中的最小值,存入一副和原始图像大小相同的灰度图中,然后再对这幅灰度图进行最小值滤波,滤波的半径由窗口大小决定。 65 | 66 | ### (3) 暗通道先验理论 67 | 68 | 暗通道先验理论指出:对于非天空区域的无雾图像J(x)的暗通道趋于0,即: 69 | 70 | ![](https://img-blog.csdnimg.cn/4ee0c04294e84a30b265933bb41ff1af.png#pic_center) 71 | 72 | 实际生活中造成暗原色中低通道值主要有三个因素: 73 | 74 | a) 汽车、建筑物和城市中玻璃窗户的阴影,或者是树叶、树与岩石等自然景观的投影; 75 | b) 色彩鲜艳的物体或表面,在RGB的三个通道中有些通道的值很低(比如绿色的草地/树/植物,红色或黄色的花朵/叶子,或者蓝色的水面); 76 | c) 颜色较暗的物体或者表面,例如灰暗色的树干和石头。 77 | 78 | 79 | 80 | ![](https://img-blog.csdnimg.cn/b12dc4e35c6e4c26a16971b6051f8485.png?x-oss-process%3Dimage%2Fwatermark%2Ctype_ZHJvaWRzYW5zZmFsbGJhY2s%2Cshadow_50%2Ctext_Q1NETiBARWFzdG1vdW50%2Csize_20%2Ccolor_FFFFFF%2Ct_70%2Cg_se%2Cx_16#pic_center) 81 | 82 | 总之,自然景物中到处都是阴影或者彩色,这些景物的图像的暗原色总是很灰暗的,而有雾的图像较亮。因此,可以明显的看到暗通道先验理论的普遍性。 83 | 84 | 85 | 86 | ![](https://img-blog.csdnimg.cn/c56ed7ffb3ce462d899491c0c37f2dc3.png?x-oss-process%3Dimage%2Fwatermark%2Ctype_ZHJvaWRzYW5zZmFsbGJhY2s%2Cshadow_50%2Ctext_Q1NETiBARWFzdG1vdW50%2Csize_20%2Ccolor_FFFFFF%2Ct_70%2Cg_se%2Cx_16#pic_center) 87 | 88 | ### (4) 公式变形 89 | 90 | 根据大气散射模型,将第一个公式稍作处理,变形为下式: 91 | 92 | ![](https://img-blog.csdnimg.cn/40090d06199343da95460bd1f8c5d178.png#pic_center) 93 | 94 | 假设每一个窗口的透射率t(x)为常数,记为t’(x),并且A值已给定,对式两边同时进行两次最小值运算,可得: 95 | 96 | ![](https://img-blog.csdnimg.cn/910ee44d94ab4fcbbfdb5a89c9da92d0.png#pic_center) 97 | 98 | 其中,J(x)是要求的无雾图像,根据前述的暗通道先验理论可知: 99 | 100 | ![](https://img-blog.csdnimg.cn/e2c744f21b334fc6bd0e7a949d1280e4.png#pic_center) 101 | 102 | 因此可推导出: 103 | 104 | 105 | ![](https://img-blog.csdnimg.cn/0d02d7f93b7e47ef8de9416726f235dc.png#pic_center) 106 | 107 | ### (5) 透射率计算 108 | 109 | 将上式带入可得到透射率t’(x)的预估值,如下所示: 110 | 111 | ![](https://img-blog.csdnimg.cn/383f3b7436ed4f0f91c7c07c99c0b652.png#pic_center) 112 | 113 | 现实生活中,即便晴空万里,空气中也会存在一些颗粒,在眺望远处的景物时,人们还是能感觉到雾的存在。另外,雾的存在让人们感受到景深,因此在去雾的同时有必要保留一定程度的雾。可以通过引入一个0到1之 间 的 因 子 w(一 般取0.95)对预估透射率进行修正,如式所示: 114 | 115 | ![](https://img-blog.csdnimg.cn/a8fb67d3051a4ab5a0aac526dbf4f4de.png#pic_center) 116 | 117 | 以上的推导过程均假设大气光值A是已知的,在实际中,可以借助暗通道图从原始雾图中求取。具体步骤如下: 118 | 119 | 先求取暗通道图,在暗通道图中按照亮度的大小提取最亮的前0.1%的像素 120 | 在原始雾图I(x)中找对应位置上具有最高亮度的点的值,作为大气光值A 121 | 此外,由于透射率t偏小时,会造成J偏大,恢复的无雾图像整体向白场过度,因此有必要对透射率设置一个下限值t0(一般取值为0.1),当t值小于t0 时,取t=t0。将以上求得的透射率和大气光值代入公式,最终整理得到图像的恢复公式如下: 122 | 123 | 124 | ![](https://img-blog.csdnimg.cn/79825f7520f045619b57b3cd78c04e81.png#pic_center) 125 | 126 | 这就是暗通道先验去雾算法的原理过程,下面简单补充论文中的处理效果图。 127 | ![](https://img-blog.csdnimg.cn/2d9a115f05f24b38a9461dec2d2470bd.png?x-oss-process%3Dimage%2Fwatermark%2Ctype_ZHJvaWRzYW5zZmFsbGJhY2s%2Cshadow_50%2Ctext_Q1NETiBARWFzdG1vdW50%2Csize_20%2Ccolor_FFFFFF%2Ct_70%2Cg_se%2Cx_16#pic_center) 128 | 129 | 再次膜拜偶像,极力推荐大家阅读论文。 130 | --------------------------------------------------------------------------------