首页
壁纸
留言板
友链
更多
统计归档
Search
1
TensorBoard:训练日志及网络结构可视化工具
12,591 阅读
2
主板开机跳线接线图【F_PANEL接线图】
7,202 阅读
3
Linux使用V2Ray 原生客户端
6,331 阅读
4
移动光猫获取超级密码&开启公网ipv6
5,053 阅读
5
NVIDIA 显卡限制功率
3,162 阅读
好物分享
实用教程
linux使用
wincmd
学习笔记
mysql
java学习
nginx
综合面试题
大数据
网络知识
linux
放码过来
python
javascript
java
opencv
蓝桥杯
leetcode
深度学习
开源模型
相关知识
数据集和工具
模型轻量化
语音识别
计算机视觉
杂七杂八
硬件科普
主机安全
嵌入式设备
其它
bug处理
登录
/
注册
Search
标签搜索
好物分享
学习笔记
linux
MySQL
nvidia
typero
内网穿透
webdav
vps
java
cudann
gcc
cuda
树莓派
CNN
图像去雾
ssh安全
nps
暗通道先验
阿里云
jupiter
累计撰写
354
篇文章
累计收到
72
条评论
首页
栏目
好物分享
实用教程
linux使用
wincmd
学习笔记
mysql
java学习
nginx
综合面试题
大数据
网络知识
linux
放码过来
python
javascript
java
opencv
蓝桥杯
leetcode
深度学习
开源模型
相关知识
数据集和工具
模型轻量化
语音识别
计算机视觉
杂七杂八
硬件科普
主机安全
嵌入式设备
其它
bug处理
页面
壁纸
留言板
友链
统计归档
搜索到
1
篇与
的结果
2021-12-06
暗通道先验去雾原理及实现
1.原理介绍1.1 大气散射模型为了表示雾对图像成像的影响,研究者提出了一个物理模型--大气散射模型,用其来表示雾霾等恶劣天气条件对图像造成的影响。该模型由McCartney首先提出,目前已经成为计算机视觉领域中朦胧图像生成过程的经典描述,该模型的数学表达式如下:$$ I(x)=J(x)t(x)+A(1-t(x)) $$其中:$I(x)$、$J(x)$分别表示有雾图像和对应的的无雾清晰图像,$A$表示全球大气光,$t(x)$是透射矩阵,描述的是光通过传输介质后没有被散射的部分,$t(x)$的数学表达式为:(其中,β指的是光的散射系数,d(x)指的是目标与摄象机之间的距离。)$$ t(x)=e^{-\beta d(x)} $$$J(x)t(x)$叫做直接衰减项,描述的是正常的清晰图像在透射媒介后发生衰减后保留下来的部分。可以看出,清晰图像会随着成像设备与物体的距离即$d(x)$的增加发生指数性衰减。$A(1-t(x))$部分称为大气遮罩层,表示的是全局大气光对成像的影响。因此,根据大气散射模型可知,要想恢复出清晰图像,需要解决两个问题:(1)准确的对全局大气光A进行估计求解,(2)准确的对透射矩阵t(x)进行求解。由上面的大气散射模型我们可以很容易得出除雾公式如下:$$ J(x)=\frac{I(x)-A(1-t(x))}{t(x)}=\frac{I(x)+t(x)}{t(x)}+A $$1.2 暗通道先验理论介绍暗通道先验理论为何凯明博士2009年在CVPR(IEEE Conference On Computer Version and Pattern Recogintion/IEEE计算视觉和模式识别会议)上所提出。该理论基于对大量无雾的户外图像的观察和统计,得出了大部分的户外无雾图像的每个局部区域都存在至少一个颜色通道的强度值很低,换言之,这个区域的各个像素点的颜色通道强度的最小值是个很小的数,其值约等于0,即在其暗通道图中表现为黑色。何凯明博士在其发表的暗通道先验除雾的论文中提出,对于任意的输入图像J,其暗通道Jdark可以用数学公式表达如下所示:$$ J^{dark}(x)=\min_{c\in{r,g,b}}[\min_{y\in{\Omega(x)}}(J^c(y))] $$式中$Ω(x)$则是一个正方形区域,该区域的中心是像素$x$,$J^c(x)$代表图像$J$的在某像素点的颜色强度。由公式可知,暗通道实际上图像的最小灰度图经过最小值滤波得到的。分别对无雾图像和有雾图像进行暗通道图的求解效果如图所示:1.3通过暗通道先验对大气光A和透射率t(x)进行估计由大气散射模型可知,若要对图像进行除雾,等价于现在的已经知道的$I(x)$,要求解出$J(x)$,显然这个方程有无数个解。因此就需要用到暗通道先验理论对透射率$t(x)$和大气光$A$进行估计了。首先完成的是对大气光$A$的估计,根据对何凯明博士发表的论文的理解,本文对大气光求解的具体步骤如下:将暗通道图和原图转化为[图片像素数量*通道数]的向量。求出暗通道图对于的向量中亮度前1/1000的向量索引。根据索引在原图转化得到的向量中寻找具有最高亮度的点的值,这就是A的估计值了。在完成大气$A$的求解后,便可以对$t(x)$的求解进行推导了。参考相关资料对$t(x)$的求解过程进行推导如下:首先将大气散射模型稍作变形得到如下式子:$$ \frac{I^c(x)}{A^c}=t(x)\frac{J^c(x)}{A^c}+1-t(x) $$为了对$t(x)$进行求解,先假设在每一个窗口内透射率$t(x)$为常数$\hat t(x)$,因为上面已经得到了$A$的估计值,所以在这里只需将$A$当做一个常量即可,对上式两边进行两次最小值运算,可以得到下式(i):$$ \min_{y \in \Omega(x)}(\min_{c \in \{r,g,b\}}(\frac{I^c(x)}{A^c})) =\hat t(x)\min_{y \in \Omega(x)}(\min_{c \in \{r,g,b\}}(\frac{J^c(x)}{A^c})) +1-\hat t(x) $$根据暗通道先验理论有:$$ J^{dark}(x)=min_{c\in{r,g,b}}[min_{y\in{\Omega(x)}}(J^c(y))] =0 $$因此,可以得出:$$ \min_{y \in \Omega(x)}(\min_{c \in \{r,g,b\}}(\frac{J^c(x)}{A^c})) =0 $$代入式(i)可得式(ii):$$ \hat t(x) = 1 - \min_{y \in \Omega(x)}(\min_{c \in \{r,g,b\}}(\frac{J^c(x)}{A^c})) $$到这就得到了透射图$t(x)$的估计结果。但是如果直接使用这一结果进行除雾效果有时产生的结果并不理想。因为空中总是会存在某些漂浮的小颗粒的,这使得我们看远处的东西会或多或少的受到雾的影响,此外,雾的存在还能让人更好的感受到物体的远近,因此,在去雾时需要考虑对一部分的雾进行保留。对雾的保留可以通过在式(ii)中引入一个在[0,1]之间的因子来实现,变化后得到的新的公式如下所示:$$ \hat t(x) = 1 - \omega\min_{y \in \Omega(x)}(min_{c \in \{r,g,b\}}(\frac{J^c(x)}{A^c})) $$根据何凯明博士的论文及进行了几次测试,最终本文选定的$\omega$的值为0.95。本文对复现实验中对$t(x)$进行求解过程及效果图如下图所示:1.4 根据大气光A和透射率t(x)的估计结果进行图像去雾到这里,我们就可以根据雾天图片退化模型进行清晰图像的恢复了。又因为当像素点x对应的透射图的$t(x)$很小时,会导致根据公式$$ J(x)=\frac{I(x)+t(x)}{t(x)}+A $$求解出的$J(x)$的值偏大,从而导致最终的得到的图片某些地方过曝,所以需要对其进行限制,本文选择限制的最大取值为$t_0=0.1$。从而的到最终使用的图像去雾公式为:$$ J(x)=\frac{I(x)+t(x)}{max(t(x),t_0)}+A $$2.代码实现2.0绘图辅助函数import matplotlib.pyplot as plt #显示单个图 def show_img_by_plt(img,title): plt.figure(figsize=(20,10)) #初始化画布 plt.axis('off') # 关掉坐标轴 plt.imshow(img) #显示图片 plt.title(title,y=-0.1) # 设置图像title plt.show() #show #通过subplot同时显示2个图 def show_double_img_by_subplot(img1,title1,img2,title2): plt.figure(figsize=(20,10)) #初始化画布 #第1个位置创建一个小图 plt.subplot(1,2,1)#表示将整个图像窗口分为1行2列, 当前位置为1 plt.axis('off') # 关掉坐标轴 plt.title(title1,y=-0.1) plt.imshow(img1) #第2个位置创建一个小图 plt.subplot(1,2,2)#表示将整个图像窗口分为1行2列, 当前位置为2 plt.axis('off') # 关掉坐标轴 plt.title(title2,y=-0.1) plt.imshow(img2) plt.show() #show #通过subplot同时显示2个图 #同时显示三个图 def show_three_img_by_subplot(img1,title1,img2,title2,img3,title3): plt.figure(figsize=(16,8)) #初始化画布 #第1个位置创建一个小图 plt.subplot(1,3,1)#表示将整个图像窗口分为1行2列, 当前位置为1 plt.axis('off') # 关掉坐标轴 plt.title(title1,y=-0.1) plt.imshow(img1) #第2个位置创建一个小图 plt.subplot(1,3,2)#表示将整个图像窗口分为1行2列, 当前位置为2 plt.axis('off') # 关掉坐标轴 plt.title(title2,y=-0.1) plt.imshow(img2) #第3个位置创建一个小图 plt.subplot(1,3,3)#表示将整个图像窗口分为1行2列, 当前位置为2 plt.axis('off') # 关掉坐标轴 plt.title(title3,y=-0.1) plt.imshow(img3) plt.show() #show2.1 获取图片暗通道图# 核心函数 #暗通道实际上是在rgb三个通道中取最小值组成灰度图,然后再进行一个最小值滤波得到的。 def get_dark_channel_img(original_img, r=15): #原图rgb三个通道中取最小值组成灰度图 temp_img = np.min(original_img,2) #再进行一个最小值滤波 #最小值滤波用腐蚀来替代了,其实腐蚀就是最小值滤波,最大值滤波是膨胀 kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (r,r)) """ cv2.getStructuringElement( ) 返回指定形状和尺寸的结构元素。 这个函数的第一个参数表示内核的形状,有三种形状可以选择。 矩形:MORPH_RECT; 交叉形:MORPH_CROSS; 椭圆形:MORPH_ELLIPSE; (r,r)表示kernel的size """ dst_img = cv2.erode(temp_img, kernel) """ dst = cv.dilate(temp_img, kerne) 对图片进行腐蚀 参数说明: dst_img 输出与输入相同大小和类型的图像. kernel 用于侵蚀的结构元素可以使用getStructuringElement来创建结构元素。 """ return dst_img #返回最终暗通道图#调用测试 import imageio import numpy as np import cv2 img_src="./image/get_dark_channel_img_clear.jpg" original_img = imageio.imread(img_src) dark_channel_img=get_dark_channel_img(original_img) show_double_img_by_subplot(original_img,"original_img",dark_channel_img,"dark_channel_img")2.2根据暗通道图和原图估计大气光#核心函数 """ 1.从暗通道图按照亮度的大小取前0.1%的像素。 2.在这些位置中,在原始有雾图像I中寻找对应的具有最高亮度的点的值,作为A值。 """ def estimate_A(original_img,dark_channel_img): #计算图片的像素总数 img_h,img_w,img_c_num = original_img.shape img_size = img_h*img_w #计算暗通道图的像素值较大的0.1%的像素的数量 num_px = int(max(math.floor(img_size/1000),1)) #将暗通道图变为列向量 dark_vec = dark_channel_img.reshape(img_size,1); #将图片变为列向量 img_vec = original_img.reshape(img_size,3); #将暗通道图对应的列向量进行排序并返回从小到大的数组索引--argsort函数返回的是数组值从小到大的索引值 indices = np.argsort(dark_vec, axis=0) #取暗通道图像素较大的0.1%的像素的索引 indices = indices[img_size-num_px::] #将原图对应的 暗通道图的像素值排前0.1% 的像素点的像素值进行求平均 A = np.max(img_vec[indices]) return A#调用测试 import imageio import numpy as np import cv2 import math # img_src=str(input("请输入原图的地址:")) img_src="./image/haze_image.jpg" original_img = imageio.imread(img_src)/255 dark_channel_img=get_dark_channel_img(original_img) A = estimate_A(original_img,dark_channel_img)2.3根据原图和大气光A进行投射图t(x)的估计#核心函数 #根据论文中透射图的估计公式进行书写的 def estimate_transmission(original_img,A, omega = 0.95): min_min_Ic_div_Ac = np.zeros(original_img.shape,"float64"); for index in range(0,3): min_min_Ic_div_Ac[:,:,index] = original_img[:,:,index]/A transmission = 1 - omega*get_dark_channel_img(min_min_Ic_div_Ac); return transmission#调用测试 import imageio import numpy as np import cv2 # img_src=str(input("请输入原图的地址:")) img_src="./image/haze_image.jpg" original_img = imageio.imread(img_src) dark_channel_img=get_dark_channel_img(original_img) A = estimate_A(original_img,dark_channel_img) transmission = estimate_transmission(original_img,A) show_double_img_by_subplot(original_img,"original_img",transmission,"transmission")2.4根据估计好的大气光和透射图进行图片恢复#核心函数 def recover_haze_by_dark_channel_prior(haze_img,t_max = 0.1): dark_channel_img=get_dark_channel_img(haze_img) A = estimate_A(haze_img,dark_channel_img) transmission = estimate_transmission(haze_img,A) clear_img = np.empty(haze_img.shape,haze_img.dtype); for index in range(0,3): clear_img[:,:,index] = (haze_img[:,:,index]-A[index])/cv2.max(transmission,t_max) + A[index] return clear_img#调用测试 import imageio import numpy as np # haze_img_src=str(input("请输入有雾图片的地址:")) haze_img_src="./image/haze_image1.jpg" haze_img = imageio.imread(haze_img_src) clear_img = recover_haze_by_dark_channel_prior(haze_img) show_double_img_by_subplot(haze_img,"haze_img",clear_img,"clear_img") transmission = estimate_transmission(haze_img,A) show_three_img_by_subplot(haze_img,"haze_img",clear_img,"clear_img",transmission,"transmission")3.暗通道先验图片去雾汇总import imageio import numpy as np import cv2 import math def get_dark_channel_img(original_img, r=6): #原图rgb三个通道中取最小值组成灰度图 temp_img = np.min(original_img,2) #再进行一个最小值滤波 #最小值滤波用腐蚀来替代了,其实腐蚀就是最小值滤波,最大值滤波是膨胀 kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (r,r)) dst_img = cv2.erode(temp_img, kernel) return dst_img #返回最终暗通道图 def estimate_A(original_img,dark_channel_img): #计算图片的像素总数 img_h,img_w,img_c_num = original_img.shape img_size = img_h*img_w #计算暗通道图的像素值较大的0.1%的像素的数量 num_px = int(max(math.floor(img_size/1000),1)) #将暗通道图变为列向量 dark_vec = dark_channel_img.reshape(img_size,1); #将图片变为列向量 img_vec = original_img.reshape(img_size,3); #将暗通道图对应的列向量进行排序并返回从小到大的数组索引--argsort函数返回的是数组值从小到大的索引值 indices = np.argsort(dark_vec, axis=0) #取暗通道图像素较大的0.1%的像素的索引 indices = indices[img_size-num_px::] #将原图对应的 暗通道图的像素值排前0.1% 的像素点的像素值进行求平均 atm_sum = np.zeros([1,3]) for index in range(1,num_px): atm_sum = atm_sum + img_vec[indices[index]] A = atm_sum / num_px; return A[0] def estimate_transmission(original_img,A, omega = 0.95): min_min_Ic_div_Ac = np.zeros(original_img.shape,"float64"); for index in range(0,3): min_min_Ic_div_Ac[:,:,index] = original_img[:,:,index]/A[index] transmission = 1 - omega*get_dark_channel_img(min_min_Ic_div_Ac); return transmission def recover_haze_by_dark_channel_prior(haze_img,t_max = 0.1): dark_channel_img=get_dark_channel_img(haze_img) A = estimate_A(haze_img,dark_channel_img) transmission = estimate_transmission(haze_img,A) clear_img = np.empty(haze_img.shape,haze_img.dtype); for index in range(0,3): clear_img[:,:,index] = (haze_img[:,:,index]-A[index])/cv2.max(transmission,t_max) + A[index] return clear_img haze_img_src="./image/haze_image.jpg" haze_img = imageio.imread(haze_img_src) clear_img = recover_haze_by_dark_channel_prior(haze_img) show_double_img_by_subplot(haze_img,"haze_img",clear_img,"clear_img")应用与视频from moviepy.editor import VideoFileClip def process_image(image): return deHaze(image/255.0)*255 output_file = './vedio/clear_dark_channel_short.mp4' test_clip = VideoFileClip("./vedio/haze_short.mp4") new_clip = test_clip.fl_image(process_image) new_clip.write_videofile(output_file, audio=False)参考资料暗通道先验原理——DCP去雾算法He K , Sun J , Fellow, et al. Single Image Haze Removal Using Dark Channel Prior[J]. IEEE Transactions on Pattern Analysis & Machine Intelligence, 2011, 33(12):2341-2353.
2021年12月06日
1,562 阅读
1 评论
0 点赞