OpenCV 您所在的位置:网站首页 水下图像去雾化 OpenCV

OpenCV

2023-09-20 08:00| 来源: 网络整理| 查看: 265

文章目录 一、前言二、暗通道去雾原理代码优化参数

一、前言

何恺明的暗通道先验(dark channel prior)去雾算法是CV界去雾领域很有名的算法,关于该算法的论文"Single Image Haze Removal Using Dark Channel Prior"一举获得2009年CVPR最佳论文。作者统计了大量的无雾图像,发现一条规律:每一幅图像的RGB三个颜色通道中,总有一个通道的灰度值很低,几乎趋向于0。基于这个几乎可以视作是定理的先验知识,作者提出暗通道先验的去雾算法。

其暗通道的数学表达式为: J d a r k ( x ) = min ⁡ y ∈ Ω ( x ) [ min ⁡ y ∈ Ω ( x ) J c ( y ) ] J^{dark}(x) = \min_{y \in \Omega (x)}\left [ \min_{y \in \Omega (x)}J^c(y) \right ] Jdark(x)=y∈Ω(x)min​[y∈Ω(x)min​Jc(y)]

式中: J c J^c Jc 图像每个通道, Ω ( x ) \Omega (x) Ω(x) 表示以像素x为中心的窗口。 原理:先取图像中每一个像素的三通道中的灰度值的最小值,得到一幅灰度图像,再在这幅灰度图像中,以每一个像素为中心取一定大小的矩形窗口,取矩形窗口中灰度值最小值代替中心像素灰度值,从而得到输入图像的暗通道图像。 滤波窗口大小:WindowSize = 2*Radius + 1。 暗通道先验理论: J d a r k → 0 J^{dark} \to 0 Jdark→0

二、暗通道去雾原理

去雾模型: I ( x ) = J ( x ) t ( x ) + A [ 1 − t ( x ) ] I(x) = J(x)t(x) + A[1-t(x)] I(x)=J(x)t(x)+A[1−t(x)] 其中: I ( x ) I(x) I(x)为原图,待去雾图像。 J ( x ) J(x) J(x)是要恢复的无雾图像。A是大气光成分, t ( x ) t(x) t(x) 透光率。 对于成像模型,将其归一化,即两边同时除以每个通道的大气光值: I c ( x ) A c = t ( x ) J c ( x ) A c + 1 − t ( x ) \frac{I^c(x)}{A^c} = t(x)\frac{J^c(x)}{A^c}+1- t(x) AcIc(x)​=t(x)AcJc(x)​+1−t(x)

假设大气光A为已知量, t ( x ) t(x) t(x) 透光率为常数,将其定义为 t ~ ( x ) \widetilde{t}(x) t (x) 对上式两边两次最小化运算: min ⁡ y ∈ Ω ( x ) [ min ⁡ c I c ( y ) A c ] = t ~ ( x ) min ⁡ y ∈ Ω ( x ) [ min ⁡ c J c ( y ) A c ] + 1 − t ~ ( x ) \min_{y \in \Omega (x)}\left [ \min_c \frac{I^c(y)}{A^c} \right ] = \widetilde{t}(x) \min_{y \in \Omega (x)}\left [ \min_c \frac{J^c(y)}{A^c} \right ] +1-\widetilde{t}(x) y∈Ω(x)min​[cmin​AcIc(y)​]=t (x)y∈Ω(x)min​[cmin​AcJc(y)​]+1−t (x) 根据 暗通道先验理论: J d a r k → 0 J^{dark} \to 0 Jdark→0 有: J d a r k ( x ) = min ⁡ y ∈ Ω ( x ) [ min ⁡ c J c ( y ) ] = 0 J^{dark}(x) = \min_{y \in \Omega (x)}[ \min_cJ^c(y)] = 0 Jdark(x)=y∈Ω(x)min​[cmin​Jc(y)]=0

推出: min ⁡ y ∈ Ω ( x ) [ min ⁡ c I c ( y ) A c ] = 0 \min_{y \in \Omega (x)}\left [ \min_c \frac{I^c(y)}{A^c} \right ] =0 y∈Ω(x)min​[cmin​AcIc(y)​]=0

代回原式,得到: t ~ ( x ) = 1 − min ⁡ y ∈ Ω ( x ) [ min ⁡ c I c ( y ) A c ] \widetilde{t}(x) = 1- \min_{y \in \Omega (x)}\left [ \min_c \frac{I^c(y)}{A^c} \right ] t (x)=1−y∈Ω(x)min​[cmin​AcIc(y)​]

为了防止去雾太过彻底,恢复出的景物不自然,应引入参数 ω = 0.95 \omega = 0.95 ω=0.95,重新定义传输函数为: t ~ ( x ) = 1 − ω min ⁡ y ∈ Ω ( x ) [ min ⁡ c I c ( y ) A c ] \widetilde{t}(x) = 1- \omega \min_{y \in \Omega (x)}\left [ \min_c \frac{I^c(y)}{A^c} \right ] t (x)=1−ωy∈Ω(x)min​[cmin​AcIc(y)​]

上述推论假设大气 A A A为已知量。实际中,可借助于暗通道图从有雾图中获取该值。具体步骤大致为:从暗通道图中按照亮度大小提取最亮的额前0.1%像素。然后在原始尤物图像 I I I 中寻找对应位置最高两点的值,作为 A A A值。至此,可以进行无雾图像恢复了。 考虑到当透射图 t t t 值很小时,会导致J值偏大,使整张图向白场过度,故设置一个阈值 t 0 t_0 t0​,当 t t t < t 0 t_0 t0​时,令 t t t = t 0 t_0 t0​(示例程序 t 0 = 0.1 t_0 = 0.1 t0​=0.1) 最终公式: J ( x ) = I ( x ) − A max ⁡ [ t ( x ) , t 0 ] + A J(x) = \frac{I(x) - A }{\max [ t(x),t_0]} + A J(x)=max[t(x),t0​]I(x)−A​+A

导向滤波方法 ​ 对于求解得到的传输函数,应用滤波器对其进行细化,文章中介绍的方法是软抠图的方法,此方法过程复杂,速度缓慢,因此采用导向滤波对传输函数进行滤波。导向滤波的原理此处不再赘述,详情:https://blog.csdn.net/wsp_1138886114/article/details/84228939

import cv2 import numpy as np def zmMinFilterGray(src, r=7): '''最小值滤波,r是滤波器半径''' return cv2.erode(src, np.ones((2 * r + 1, 2 * r + 1))) def guidedfilter(I, p, r, eps): height, width = I.shape m_I = cv2.boxFilter(I, -1, (r, r)) m_p = cv2.boxFilter(p, -1, (r, r)) m_Ip = cv2.boxFilter(I * p, -1, (r, r)) cov_Ip = m_Ip - m_I * m_p m_II = cv2.boxFilter(I * I, -1, (r, r)) var_I = m_II - m_I * m_I a = cov_Ip / (var_I + eps) b = m_p - a * m_I m_a = cv2.boxFilter(a, -1, (r, r)) m_b = cv2.boxFilter(b, -1, (r, r)) return m_a * I + m_b def Defog(m, r, eps, w, maxV1): # 输入rgb图像,值范围[0,1] '''计算大气遮罩图像V1和光照值A, V1 = 1-t/A''' V1 = np.min(m, 2) # 得到暗通道图像 Dark_Channel = zmMinFilterGray(V1, 7) cv2.imshow('20190708_Dark',Dark_Channel) # 查看暗通道 cv2.waitKey(0) cv2.destroyAllWindows() V1 = guidedfilter(V1, Dark_Channel, r, eps) # 使用引导滤波优化 bins = 2000 ht = np.histogram(V1, bins) # 计算大气光照A d = np.cumsum(ht[0]) / float(V1.size) for lmax in range(bins - 1, 0, -1): if d[lmax] = ht[1][lmax]].max() V1 = np.minimum(V1 * w, maxV1) # 对值范围进行限制 return V1, A def deHaze(m, r=81, eps=0.001, w=0.95, maxV1=0.80, bGamma=False): Y = np.zeros(m.shape) Mask_img, A = Defog(m, r, eps, w, maxV1) # 得到遮罩图像和大气光照 for k in range(3): Y[:,:,k] = (m[:,:,k] - Mask_img)/(1-Mask_img/A) # 颜色校正 Y = np.clip(Y, 0, 1) if bGamma: Y = Y ** (np.log(0.5) / np.log(Y.mean())) # gamma校正,默认不进行该操作 return Y if __name__ == '__main__': m = deHaze(cv2.imread('20190708.png') / 255.0) * 255 cv2.imwrite('20190708_02.png', m)

在这里插入图片描述

代码优化参数 以上代码优化,可以使用快速导向滤波减少时间复杂度,从而减少运行时间暗通道最小值滤波半径r。 这个半径对于去雾效果是有影响的。一定情况下,半径越大去雾的效果越不明显,建议的范围一般是5-25之间,一般选择5,7,9等就会取得不错的效果。w的影响自然也是很大的。 这个值是我们设置的保留雾的程度(c++代码中w是去除雾的程度,一般设置为0.95就可以了)。这个基本不用修改。导向滤波中均值滤波半径。 这个半径建议取值不小于求暗通道时最小值滤波半径的4倍。因为前面最小值后暗通道时一块一块的,为了使得透射率图更加精细,这个r不能过小(很容易理解,如果这个r和和最小值滤波的一样的话,那么在进行滤波的时候包含的块信息就很少,还是容易出现一块一块的形状)。

关于去雾的其他方法参考:https://blog.csdn.net/wsp_1138886114/article/details/83793331

鸣谢 https://blog.csdn.net/baimafujinji/article/details/27206237 https://blog.csdn.net/baimafujinji/article/details/30060161 https://www.cnblogs.com/rust/p/10457915.html



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有