概述
文章目录
- 一、前言
- 二、暗通道去雾原理
- 代码优化参数
一、前言
何恺明的暗通道先验(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)minJc(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[cminAcIc(y)]=t
(x)y∈Ω(x)min[cminAcJc(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[cminJc(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[cminAcIc(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[cminAcIc(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[cminAcIc(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] <= 0.999:
break
A = np.mean(m, 2)[V1 >= 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
最后
以上就是高高煎蛋为你收集整理的OpenCV—Python 暗通道图像去雾算法的全部内容,希望文章能够帮你解决OpenCV—Python 暗通道图像去雾算法所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复