念头
在对靶果园喷雾中,对图像的预处理上可以进行去雾及去雨算法,在进行激光图像及实际图像匹配时或许有用
这里参考自
《Single Image Haze Removal Using Dark Channel Prior》一文中图像去雾算法的原理、实现、效果(速度可实时)-博客园
暗通道先验去雾(single image haze removal using dark channel prior)-zhihu
去雾算法
了解到2009年何凯明博士发表的《Single Image Haze Removal Using Dark Channel Prior》,这篇论文是2009年CVPR最佳论文,作者何凯明博士,2007年清华大学毕业,2011年香港中文大学博士毕业。
背景
关于去雾,介绍下背景:
在计算机图形学和计算机视觉中,存在一个很重要的模型,这种模型被广泛的应用于雾图中
其中,I(x)是指观测到的图像,这个是已知值。J(x)是无雾图像。t(x)是transmission from scene to camera透射率。A是全球大气光成分。通过这个公式,我们的目标就是从已有的图像中计算得到无雾图像J(x),透射率t(x)以及估算全球大气光成分A。
这个图像描述了汇入手机成像的部分,分别是由直接传输并且部分散射得到的图像以及从全球大气光成分得到的光最后混合形成我们所看到的最终的图像,即 I(x) 。
对于透射率的计算,有一个已有的公式(Beer-Lambert law),该公式用来表示当大气中物质是同质的时候,透射率t(x)可以被表达成为
其中β是大气散射的参数,d是景深。这个公式表达了t(x)会随着距离的增大而呈现出指数型的衰减。因为如果我们得到了这个透射率,我们就可以根据这一规律得到景物的深度。
暗通道先验
之前的部分已经说明了,暗通道先验(Dark Channel Prior)是基于统计意义上的观测得到的结果。观测结果表明,在大部分无雾图像的无天空区域,像素中至少存在一个颜色通道存在很低非常低的亮度值。这个最低的亮度值几乎等同于0。因此,对于一个观测图像J(x)
,其暗通道
式中Jc(y)表示彩色图像的每个通道,Ω(x)表示以像素x为中心的一个窗口
上面公式的步骤:
1-求出每个像素RGB分量中的最小值,存入一幅和原始图像大小相同的灰度图中
2-对这幅灰度图进行最小值滤波,滤波的半径由窗口大小决定,一般有 窗口大小=2*半径+1
暗通道先验的理论指出:
Jdark➡0
实际生活中造成暗原色中低通道值主要有三个因素:
a)汽车、建筑物和城市中玻璃窗户的阴影,或者是树叶、树与岩石等自然景观的投影;
b)色彩鲜艳的物体或表面,在RGB的三个通道中有些通道的值很低(比如绿色的草地/树/植物,红色或黄色的花朵/叶子,或者蓝色的水面);
c)颜色较暗的物体或者表面,例如灰暗色的树干和石头。总之,自然景物中到处都是阴影或者彩色,这些景物的图像的暗原色总是很灰暗的。
无雾图像及其暗通道图像:
有雾图像及其暗通道图像:
从上面的比较可以看出,暗通道先验理论得到应验,何凯明统计了5000多幅图像的特征都基本符合这个理论。
有了这个理论,我们就可以求公式
上式处理变形为:
上标c表示r,g,b三个通道的意思
假设在每一个窗口内透射率t(x)为常数,定义为
,A值已经给定,对上式两边取最小值(假设t(x)为常数)
重点来了,根据刚才的暗通道先验理论,有
(即刚才得出的Jdark➡0)
因此可以得到
最后将上式代入可得:
这就是透射率t(x)的预估值。
在现实生活中,即使是晴天白云,空气中也存在着一些颗粒,因此,看远处的物体还是能感觉到雾的影响,另外,雾的存在让人类感到景深的存在,因此,有必要在去雾的时候保留一定程度的雾,这时可以在上式引入一个在[0,1]之间的因子,则上式可以修正为:
该文所有的测试结果基于w=0.95
上述推论中都是假设全球大气光A值时已知的,在实际中,我们可以借助于暗通道图从有雾图像中获取该值。具体步骤如下:
1) 从暗通道图中按照亮度的大小取前0.1%的像素位置。
2) 在这些位置中,在原始有雾图像I中寻找对应的具有最高亮度的点的值,作为A值。
以上是本人查到的如何确定全球大气光A值的方法步骤,这里应该跟全球大气光的定义有关,在如下超链接文章中暂时找不到跟大气光A值的求法相关的资料,但是找到了作者提到跟大气光A值的定义相关的参考文献第12-15,可以参考
12-Fattal R. Dehazing using color-lines. ACM T Graphics. 2014; 34(1): 13. doi: 10.1145/2651362
13-Carr P, Hartley P. Improved single image dehazing using geometry. IEEE DICTA: Digital Image Computing: Techniques and Applications; 2009 Dec 1–3; Melbourne, Australia. 2009. p. 103–110. doi: 10.1109/DICTA.2009.25
14-Fattal R. Single image dehazing. ACM T Graphics. 2008; 27(3): 72. doi: 10.1145/1399504.1360671
15-Koschmieder H. Theorie der horizontalen Sichtweite: Kontrast und Sichtweite. Keim & Nemnich; 1924.171–181 p.
有时间再补充下关于大气光A值的定义吧,这里先放着。
上述提到的文献关于图像,散射以及大气光成分的资料参考:(也可免费下载,右键保存即可)
Particle Pollution Estimation Based on Image Analysis
现在I(x),A,t(x)都已经求得了,因此J也可以求得了。
当透射率t(x)的值很小时,会导致J(x)的值偏大,从而使图像整体泛白,一般设置一个阈值t0,当t(x)值小于t0时,令t(x)=t0,博客园的大佬设置了t0=0.01去计算,待会看看效果如何。
最终恢复的公式如下:
直接用上述理论进行恢复前后比较的效果如下:
其中透射率图如果很粗糙,去雾后的图会显得比较不协调
要获得更为精细的透射率图,何博士在文章中提出了了soft matting方法,能得到非常细腻的结果。但是他的一个致命的弱点就是速度特慢,不使用于实际使用。在2011年,何博士又出了一篇论文,其中提到了导向滤波的方式来获得较好的透射率图。该方法的主要过程集中于简单的方框模糊。
各参数对去雾效果的影响
窗口的大小。这个对结果来说是个关键的参数,窗口越大,其包含暗通道的概率越大,暗通道也就越黑。我们不去从理论角度分析,从实践的效果来看,似乎窗口越大,去雾的效果越不明显。
w的值也有关,越小,去雾效果越不明显
归一化时,即每个通道数据除以对应的A值时,A值的选取并不能保证每个像素分量值除以A值后小于1,导致透射率t(x)的值可能小于0(这是不允许的,跟透射率定义有关),如果t(x)小于0,就设置为0,博客园大佬的建议是这么做的,这样就能解决归一化遇到的一些问题。
计算导向滤波图时,可以用原始图像做导向图,也可以用其灰度图,但是用RGB导向图在下一步的计算会花非常多时间。
去雾结果后的操作
在何凯明博士的论文说,去雾后的图像会比原始的暗,因此在处理完需要进行一定的曝光增强之后会得到比较好的效果。
实际应用遇到的困难:
由于项目的图像采集设备采集到的图像信息需要和激光雷达采集到的图像像素距离信息结合来确定果树的目标位置,因此在未实验基础上,理论上是可以对设备采集到的图像进行去雾去雨算法优化图像信息并且加上原项目采用的K-means聚类算法、G-R和G-B算法分割果树树冠再进行形态学滤波去噪,理论上经过这些处理后,得到最终分割的果树树冠的精确率会更高,不仅在实际的对靶喷雾应用中降低艳阳天或是阴天以及雾天,雨天的环境因素,(这里还未考虑到果树对靶喷雾作业是否会在雨天或者雾天进行作业,仅仅是为了优化采集设备采集的图像为目的)
还有待参考的一些去雾及去雨算法一些资源,待更新到最近的论文应用中
去雾算法github文献及代码
需要研究的知识:
导向滤波引导滤波/导向滤波(Guided Filter)
双边滤波
大气光A的定义
(https://download.csdn.net/download/weixin_44167512/11254467?utm_medium=distribute.pc_relevant.none-task-download-2defaultBlogCommendFromMachineLearnPai2default-1.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant.none-task-download-2defaultBlogCommendFromMachineLearnPai2default-1.control)
附上基于opencv-python的去雾代码:
#请使用python3版本
import cv2
import numpy as np
def zmMinFilterGray(src, r=7):
'''最小值滤波,r是滤波器半径'''
if r <= 0:
return src
h, w = src.shape[:2]
I = src
res = np.minimum(I, I[[0] + [x for x in range(h - 1)], :])
res = np.minimum(res, I[[x for x in range(1, h)] + [h - 1], :])
I = res
res = np.minimum(I, I[:, [0] + [x for x in range(w - 1)]])
res = np.minimum(res, I[:, [x for x in range(1, w)] + [w - 1]])
return zmMinFilterGray(res, r - 1)
def guidedfilter(I, p, r, eps):
'''引导滤波,直接参考网上的matlab代码'''
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 getV1(m, r, eps, w, maxV1): # 输入rgb图像,值范围[0,1]
'''计算大气遮罩图像V1和光照值A, V1 = 1-t/A'''
V1 = np.min(m, 2) # 得到暗通道图像
V1 = guidedfilter(V1, zmMinFilterGray(V1, 7), 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)
V1, A = getV1(m, r, eps, w, maxV1) # 得到遮罩图像和大气光照
for k in range(3):
Y[:, :, k] = (m[:, :, k] - V1) / (1 - V1 / 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('origin2.jpg') / 255.0) * 255
cv2.imwrite('defog.jpg', m)
2021/3/16 20:50
关于论文获取,有个比较简单的方式,查看论文获取