摘要:具体介绍图像去雾算法,通过图像增强后的图像也能利用于指标检测、图像分类或物联网检测等畛域,并且成果更好。

本文分享自华为云社区《图像预处理之图像去雾详解》,作者: eastmount。

一.图像去雾

随着社会的倒退,环境污染逐步加剧,越来越多的城市频繁呈现雾霾,这不仅给人们的身体健康带来危害,还给那些依赖图像信息的计算机视觉零碎造成了不良影响,因为在雾天采集到的图像对比度和饱和度均较低,色彩易产生偏移与失真等。因而,寻找一种简略无效的图像去雾办法,对计算机视觉的后续钻研至关重要。

该局部次要从下列几篇论文摘取对图像去雾算法进行遍及,援用及参考中文论文:

• 魏红伟, 等. 图像去雾算法钻研综述[J]. 软件导刊, 2021.
• 王道累, 等. 图像去雾算法的综述及剖析[J]. 图学学报, 2021.
• OpenCV图像增强万字详解(直方图均衡化、部分直方图均衡化、主动色调均衡化)- Eastmount

图像增强(Image Enhancement)是指依照某种特定的需要,突出图像中有用的信息,去除或者减弱无用的信息。图像增强的目标是使解决后的图像更适宜人眼的视觉个性或易于机器辨认。 在医学成像、遥感成像、人物摄影等畛域,图像增强技术都有着宽泛的利用。图像增强同时能够作为指标辨认、指标跟踪、特色点匹配、图像交融、超分辨率重构等图像处理算法的预处理算法。

近些年来,呈现了泛滥的单幅图像去雾算法,利用比拟宽泛的有:

• 直方图均衡化去雾算法
• Retinex去雾算法
• 暗通道先验去雾算法
• 基于卷积神经网络的DehazeNet去雾算法

其次要能够分为 3 类:基于图像增强的去雾算法、基于图像复原的去雾算法和基于 CNN 的去雾算法。

(1) 基于图像增强的去雾算法

通过图像增强技术突出图像细节,晋升对比度,使之看起来更加清晰,这类算法的适用性较广。具体的算法有:

• Retinex 算法
依据成像原理,打消了反射重量的影响,达到了图像增强去雾的成果
• 直方图均衡化算法
使图像的像素散布更加平均,放大了图像的细节
• 偏微分方程算法
将图像视作一个偏微分方程,通过计算梯度场进步对比度
• 小波变换算法
对图像进行合成,放大有用的局部

此外,在这类算法的根底上呈现了泛滥的基于图像增强原理的改良算法。

(2) 基于图像复原的去雾算法

次要是基于大气散射物理学模型,通过对大量有雾图像和无雾图像进行察看总结,失去其中存在的一些映射关系,而后依据有雾图像的造成过程来进行逆运算,从而复原清晰图像。其中最经典的要属何恺明大佬提出的:

• 暗通道先验去雾算法
通过对大量无雾图像进行特征分析,找到了无雾图像与大气散射模型中某些参数的先验关系。该算法复杂度低,去雾成果好,因而在其根底上呈现了大量基于暗通道先验的改良算法。

(3) 基于CNN的去雾算法

应用 CNN 建设一个端到端的模型,通过有雾图像复原出无雾图像,目前应用神经网络进行去雾的算法次要有两种思路:

  • 应用 CNN 生成大气散射模型的某些参数,而后再依据大气散射模型来复原无雾图像
  • 应用 CNN (例如 GAN)间接依据含糊图像生成无雾的清晰图像

CNN 因其弱小的学习能力在多个畛域失去利用,因而也呈现了采纳 CNN 进行去雾的算法。2016年CAI等首次提出了一种名为DehazeNet的去雾网络,用于预计有雾图像的透射率。DehazeNet 将有雾的含糊图像作为输出,输入其透射率,基于大气散射模型实践复原出无雾的清晰图像。

下图是别离对直方图均衡化、暗通道先验去雾、DehazeNet和AOD-Net去雾算法进行测试,试验后果如图所示。由图可知,基于图像增强的直方图均衡化算法的去雾图像对比度明显增强,因为不思考降质起因,在减少对比度的同时也对噪声进行了放大,呈现细节失落与色调偏差景象。基于物理模型的暗通道去雾算法、基于神经网络的 DehazeNet 和 AOD-Net 算法的去雾成果较直方图均衡化算法更佳。

其余去雾算法比照后果如下图所示,比方城市和路线有无图像去雾成果比照。

最初,正如总结王道累老师总结的一样,目前针对有雾图像去雾的算法次要是从基于图像增强、图像复原和 CNN 3 个方向进行的。

• 基于图像增强的办法不思考有雾图像的造成过程,而是间接通过突出图像的细节,进步对比度等形式,从而使有雾图像看上去更加清晰。
• 基于图像复原的办法则是追寻图像降质的物理过程,通过物理模型还原出清晰的图像。
• 基于 CNN 的办法则是利用神经网络弱小的学习能力,寻找有雾图像与图像复原物理模型中某些系数的映射关系或者应用 GAN,依据有雾图像还原出无雾的清晰图像。

上述 3 类去雾算法对于雾天图像都有着显著的去雾成果,只管其在理论生存中曾经失去了宽泛的利用,但下述几点仍有可能是今后图像去雾畛域的钻研重点和难点:

更加实在的雾天图像数据集
采纳神经网络进行去雾的算法在成果上好于图像增强和还原的办法,然而因为在自然界中很难拍摄到一组背景雷同的有雾图像和无雾图像,因而目前训练神经网络所采纳的数据集均是通过合成失去的,尽管可能在肯定水平上拟合自然环境,然而依然存在着一些差距。所以目前急需一种由在实在环境中获取到的具备雷同背景的有雾图像和无雾图像构建的数据集,来进步神经网络去雾算法的鲁棒性和稳定性。
更加简便的去雾算法
目前各类算法可能无效去除单幅图像上的雾霾,但绝对较好的算法都存在着工夫复杂度高的问题,很难利用到视频去雾或者需要较多的简单工作中去。
鲁棒性更强的去雾算法
上述算法都只对图像上存在的平均的薄雾有较好的去雾成果,对于浓雾或者散布不均的团雾则成果较差,因而找到一种适用范围更广的去雾办法将会是一个极具挑战性的课题。

二.ACE去雾算法

1.算法原理

该局部次要介绍参考作者书籍以及相干论文进行叙述,简略介绍ACE算法的原理常识。如果读者想具体理解其原理,举荐浏览英文原文,详见上面的参考文献,都是大佬。
援用及参考中文论文:

• 尹胜楠, 等. 基于疾速ACE算法的视觉里程计图像增强办法[J]. 电子测量与仪器学报, 2021.
• 李景文, 等. 基于暗通道先验改良的主动色调平衡算法[J]. 科学技术与工程, 2019.
• 杨秀璋, 等. 一种改良的简单环境下条形码图像增强和定位算法[J]. 古代计算机, 2020.
• OpenCV—python 主动色调平衡(ACE)- SongpingWang
• OpenCV图像增强万字详解(直方图均衡化、部分直方图均衡化、主动色调均衡化)- Eastmount

英文原文:

• https://www.ipol.im/pub/art/2...
Automatic Color Enhancement (ACE) and its Fast Implementation
• https://www.sciencedirect.com...
A new algorithm for unsupervised global and local color correction(原作者Rizzi大佬)

图像对比度加强的算法在很多场合都有用途,特地是在医学图像中,这是因为在泛滥疾病的诊断中,医学图像的视觉查看时很有必要的。Retinex算法是代表性的图像增强算法,它依据人的视网膜和大脑皮层模仿对物体色彩的波长光线反射能力而造成,对简单环境下的一维条码具备肯定范畴内的动静压缩,对图像边缘有着肯定自适应的加强。

主动色调平衡(Automatic Color Enhancement,ACE) 算法是Rizzi大神在Retinex算法的实践上提出的,它通过计算图像指标像素点和四周像素点的明暗水平及其关系来对最终的像素值进行校对,实现图像的对比度调整,产生相似人体视网膜的色调恒常性和亮度恒常性的平衡,具备很好的图像增强成果。

ACE算法包含两个步骤:

• 一是对图像进行色调和空域调整,实现图像的色差校对,失去空域重构图像。
模拟视觉零碎的侧抑制性和区域自适应性,进行色调的空域调整。侧抑制性是一个生理学概念,指在某个神经元受到刺激而产生兴奋时,再刺激相近的神经元,后者所产生的兴奋对前者产生的抑制作用。
• 二是对校对后的图像进行动静扩大。
对图像的动静范畴进行全局调整,并使图像满足灰度世界实践和白斑点假如。算法针对单通道,再延长利用到RGB黑白空间的3通道图像,即对3个通道别离解决再进行整合实现。

(1) 区域自适应滤波

输出图像I(灰度图为例),该步是对单通道图像I中所有点p的区域自适应滤波,失去实现色差校对,空域重构后的两头后果图像,计算公式如下:

式中:Ic§-Ic(j)为p、j两个像素点间灰度差值,表白拟生物学上的侧抑制性;d(p,j)示意间隔度量函数,应用两点间的欧氏间隔,作用上控制点j对p的影响权重,映射出滤波的区域适应性;Sa(x)是亮度体现函数(奇函数),本文算法抉择经典Saturation函数。

不同亮度函数和参数的抉择管制了对比度加强的水平,经典的Saturation函数在饱和前取越大的斜率,后果的对比度加强越显著,如图2所示,极限状况是sign函数模式,而Sign函数因为无差别适度加强放大,导致噪声同样失去放大成果不佳,最终抉择Saturation函数作为绝对亮度体现函数。公式如下:

(2) 色调重整拉伸,对图像动静扩大

将式(1)中失去的两头量拉伸映射到 [0, 255] 中,占满动静范畴 [0, 255](8位灰度图像),计算公式如下,式中:[minR,maxR]是两头量L(x)的全副定义域,该项使图像达到全局白平衡。

下图是条形码图像进行ACE图像增强后的效果图,通过图像增强后的图(b)对比度更强,改善了原图像的明暗水平,加强的同时放弃了图像的真实性。

ACE算法英文介绍如下:

试验比照成果如下图所示,大家在写该主题论文的时候,留神和传统办法比照。

2.代码实现

因为OpenCV中临时没有ACE算法包,上面的代码是借鉴“zmshy2128”老师的文章,批改实现的黑白直方图均衡化解决。前面有机会作者详细分析其代码实现过程。

  • 主动色调平衡(ACE)疾速算法 - zmshy2128老师
# -*- coding: utf-8 -*-# By:Eastmount CSDN 2021-03-12# 惨zmshy2128老师文章并批改成Python3代码import cv2import numpy as npimport mathimport matplotlib.pyplot as plt#线性拉伸解决#去掉最大最小0.5%的像素值 线性拉伸至[0,1]def stretchImage(data, s=0.005, bins = 2000):       ht = np.histogram(data, bins);    d = np.cumsum(ht[0])/float(data.size)    lmin = 0; lmax=bins-1    while lmin<bins:        if d[lmin]>=s:            break        lmin+=1    while lmax>=0:        if d[lmax]<=1-s:            break        lmax-=1    return np.clip((data-ht[1][lmin])/(ht[1][lmax]-ht[1][lmin]), 0,1)#依据半径计算权重参数矩阵g_para = {}def getPara(radius = 5):                            global g_para    m = g_para.get(radius, None)    if m is not None:        return m    size = radius*2+1    m = np.zeros((size, size))    for h in range(-radius, radius+1):        for w in range(-radius, radius+1):            if h==0 and w==0:                continue            m[radius+h, radius+w] = 1.0/math.sqrt(h**2+w**2)    m /= m.sum()    g_para[radius] = m    return m#惯例的ACE实现def zmIce(I, ratio=4, radius=300):                         para = getPara(radius)    height,width = I.shape    zh = []    zw = []    n = 0    while n < radius:        zh.append(0)        zw.append(0)        n += 1    for n in range(height):        zh.append(n)    for n in range(width):        zw.append(n)    n = 0    while n < radius:        zh.append(height-1)        zw.append(width-1)        n += 1    #print(zh)    #print(zw)        Z = I[np.ix_(zh, zw)]    res = np.zeros(I.shape)    for h in range(radius*2+1):        for w in range(radius*2+1):            if para[h][w] == 0:                continue            res += (para[h][w] * np.clip((I-Z[h:h+height, w:w+width])*ratio, -1, 1))    return res#单通道ACE疾速加强实现def zmIceFast(I, ratio, radius):    print(I)    height, width = I.shape[:2]    if min(height, width) <=2:        return np.zeros(I.shape)+0.5    Rs = cv2.resize(I, (int((width+1)/2), int((height+1)/2)))    Rf = zmIceFast(Rs, ratio, radius)             #递归调用    Rf = cv2.resize(Rf, (width, height))    Rs = cv2.resize(Rs, (width, height))     return Rf+zmIce(I,ratio, radius)-zmIce(Rs,ratio,radius)   #rgb三通道别离加强 ratio是对比度加强因子 radius是卷积模板半径          def zmIceColor(I, ratio=4, radius=3):                   res = np.zeros(I.shape)    for k in range(3):        res[:,:,k] = stretchImage(zmIceFast(I[:,:,k], ratio, radius))    return res#主函数if __name__ == '__main__':    img = cv2.imread('car.png')    res = zmIceColor(img/255.0)*255    cv2.imwrite('car-Ice.jpg', res)

运行后果如图所示,ACE算法能无效进行图像去雾解决,实现图像的细节加强。

最初是指标检测去雾和女神去雾的成果,哈哈,持续加油!

三.暗通道先验去雾算法

该算法是计算机视觉畛域何恺明大佬于2009年提出的图像去雾经典算法,并获取当年CVPR最佳论文。论文题目为《Single Image Haze Removal Using Dark Channel Prior》。下图是大佬的百科简介,是真的厉害,值得咱们大家学习。

• 2003年5月,何恺明拿到输送清华的资格,是当年执信中学惟一输送上清华大学的学生;高考后果出炉当前,何恺明取得满分900分的问题,成为当年广东省9位满分状元之一。
• 2009年,何恺明成为首获计算机视觉畛域三大国内会议之一CVPR“最佳论文奖”的中国学者。
• 在2015年的ImageNet图像识别大赛中,何恺明和他的团队用“图像识别深度差残学习”零碎,击败谷歌、英特尔、高通等业界团队,荣获第一。
• 何恺明作为第一作者取得了CVPR 2009,CVPR 2016和ICCV 2017(Marr Prize)的最佳论文奖,并取得了ICCV 2017最佳学生论文奖。
• 2018年,第31届计算机视觉和模式识别大会(Conference on Computer Vision and Pattern Recognition, CVPR)在美国盐湖城召开,何恺明取得本届大会的PAMI年老学者奖。

1.算法原理

言归正传,如果是图像处理或钻研图像去雾畛域的作者,倡议大家认真浏览这篇英文原文,能在2009年提出该算法真的很惊艳。

援用及参考中文论文:

• 何涛, 等. 基于暗通道先验的单幅图像去雾新算法[J]. 计算机科学, 2021.
• 王蓉, 等. 基于改良加权交融暗通道算法的图像去雾钻研[J]. 浙江科技学院学报, 2021.
• 图像去雾算法的原理、实现、成果(速度可实时)- 挚爱图像处理
• 图像去雾之何凯明暗通道先验去雾算法原理及c++代码实现 - Do it !

英文原文:

• https://ieeexplore.ieee.org/d...
Single Image Haze Removal Using Dark Channel Prior
• https://ieeexplore.ieee.org/d...
Single image haze removal using dark channel prior

暗通道先验(Dark Channel Prior, DCP)去雾算法 依赖大气散射模型进行去雾解决,通过对大量有雾图像和无雾图像进行察看总结,失去其中存在的一些映射关系,而后依据有雾图像的造成过程来进行逆运算,从而复原清晰图像。

算法实现过程及原理如下,参考何恺明老师和何涛老师的论文。

(1) 大气散射模型

在计算机视觉和计算机图形学中,方程所形容的大气散射模型被宽泛应用。参数解释如下:

• x是图像的空间坐标
• I(x)代表有雾图像(待去雾图像)
• J(x)代表无雾图像(待复原图像)
• A代表寰球大气光值
• t(x)代表透射率

方程左边第一项为场景间接衰减项,第二项为环境光项。

(2) 暗通道定义

在绝大多数非天空的部分区域中,某些像素总会至多有一个色彩通道的值很低。对于一幅图像J(x),其暗通道的数学定义示意如下:

其中,(x)示意以x为核心的部分区域,上标c示意RGB三个通道。该公式的意义用代码表白也很简略,首先求出每个像素RGB重量中的最小值,存入一副和原始图像大小雷同的灰度图中,而后再对这幅灰度图进行最小值滤波,滤波的半径由窗口大小决定。

(3) 暗通道先验实践

暗通道先验实践指出:对于非天空区域的无雾图像J(x)的暗通道趋于0,即:

理论生存中造成暗原色中低通道值次要有三个因素:

a) 汽车、建筑物和城市中玻璃窗户的暗影,或者是树叶、树与岩石等自然景观的投影;
b) 色彩鲜艳的物体或外表,在RGB的三个通道中有些通道的值很低(比方绿色的草地/树/动物,红色或黄色的花朵/叶子,或者蓝色的水面);
c) 色彩较暗的物体或者外表,例如灰暗色的树干和石头。

总之,天然风物中到处都是暗影或者黑白,这些风物的图像的暗原色总是很灰暗的,而有雾的图像较亮。因而,能够显著的看到暗通道先验实践的普遍性。

(4) 公式变形

依据大气散射模型,将第一个公式稍作解决,变形为下式:

假如每一个窗口的透射率t(x)为常数,记为t’(x),并且A值已给定,对式两边同时进行两次最小值运算,可得:

其中,J(x)是要求的无雾图像,依据前述的暗通道先验实践可知:

因而可推导出:

(5) 透射率计算

将上式带入可失去透射率t’(x)的预估值,如下所示:

现实生活中,即使晴空万里,空气中也会存在一些颗粒,在瞭望远处的风物时,人们还是能感觉到雾的存在。另外,雾的存在让人们感触到景深,因而在去雾的同时有必要保留肯定水平的雾。能够通过引入一个0到1之 间 的 因 子 w(一 般取0.95)对预估透射率进行修改,如式所示:

以上的推导过程均假如大气光值A是已知的,在理论中,能够借助暗通道图从原始雾图中求取。具体步骤如下:

  • 先求取暗通道图,在暗通道图中依照亮度的大小提取最亮的前0.1%的像素
  • 在原始雾图I(x)中找对应地位上具备最高亮度的点的值,作为大气光值A

此外,因为透射率t偏小时,会造成J偏大,复原的无雾图像整体向白场适度,因而有必要对透射率设置一个下限值t0(个别取值为0.1),当t值小于t0 时,取t=t0。将以上求得的透射率和大气光值代入公式,最终整顿失去图像的复原公式如下:

这就是暗通道先验去雾算法的原理过程,上面简略补充论文中的解决效果图。

再次膜拜偶像,极力推荐大家浏览论文。

2.算法实现

实现代码援用木老师的,感觉比我写得好,参考如下:

  • openCV+python实现图像去雾 - 木盏老师
# -*- coding: utf-8 -*-"""Created on Sat Sep 11 00:16:07 2021@author: xiuzhang参考资料:https://blog.csdn.net/leviopku/article/details/83898619"""import sysimport cv2import mathimport numpy as np def DarkChannel(im,sz):    b,g,r = cv2.split(im)    dc = cv2.min(cv2.min(r,g),b)    kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(sz,sz))    dark = cv2.erode(dc,kernel)    return dark def AtmLight(im,dark):    [h,w] = im.shape[:2]    imsz = h*w    numpx = int(max(math.floor(imsz/1000),1))    darkvec = dark.reshape(imsz,1)    imvec = im.reshape(imsz,3)     indices = darkvec.argsort()    indices = indices[imsz-numpx::]     atmsum = np.zeros([1,3])    for ind in range(1,numpx):       atmsum = atmsum + imvec[indices[ind]]     A = atmsum / numpx;    return A def TransmissionEstimate(im,A,sz):    omega = 0.95    im3 = np.empty(im.shape,im.dtype)     for ind in range(0,3):        im3[:,:,ind] = im[:,:,ind]/A[0,ind]    transmission = 1 - omega*DarkChannel(im3,sz)    return transmission def Guidedfilter(im,p,r,eps):    mean_I = cv2.boxFilter(im,cv2.CV_64F,(r,r))    mean_p = cv2.boxFilter(p, cv2.CV_64F,(r,r))    mean_Ip = cv2.boxFilter(im*p,cv2.CV_64F,(r,r))    cov_Ip = mean_Ip - mean_I*mean_p     mean_II = cv2.boxFilter(im*im,cv2.CV_64F,(r,r))    var_I   = mean_II - mean_I*mean_I     a = cov_Ip/(var_I + eps)    b = mean_p - a*mean_I     mean_a = cv2.boxFilter(a,cv2.CV_64F,(r,r))    mean_b = cv2.boxFilter(b,cv2.CV_64F,(r,r))     q = mean_a*im + mean_b    return q def TransmissionRefine(im,et):    gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)    gray = np.float64(gray)/255    r = 60    eps = 0.0001    t = Guidedfilter(gray,et,r,eps)     return t def Recover(im,t,A,tx = 0.1):    res = np.empty(im.shape,im.dtype)    t = cv2.max(t,tx)     for ind in range(0,3):        res[:,:,ind] = (im[:,:,ind]-A[0,ind])/t + A[0,ind]     return res if __name__ == '__main__':        fn = 'car-02.png'    src = cv2.imread(fn)    I = src.astype('float64')/255        dark = DarkChannel(I,15)    A = AtmLight(I,dark)    te = TransmissionEstimate(I,A,15)    t = TransmissionRefine(src,te)    J = Recover(I,t,A,0.1)        arr = np.hstack((I, J))    cv2.imshow("contrast", arr)    cv2.imwrite("car-02-dehaze.png", J*255 )    cv2.imwrite("car-02-contrast.png", arr*255)    cv2.waitKey();

实现成果如下图所示:

如果想和后续指标汽车检测联合,同样能够先去雾再进行检测,如下图所示:

四.图像噪声和雾生成

图像处理总少不了噪声增加或生成,上面补充两个简略的椒盐噪声和雾气模仿生成的代码。这与本文的试验严密相干,能为咱们提供更多的GAN生成样本。前面人工智能系列文章,GAN咱们看看能不能学习实在雾化场景的图像,值得期待,哈哈!

1.加盐噪声

原图是一张风光图像:

代码如下:

# -*- coding:utf-8 -*-import cv2import numpy as np#读取图片img = cv2.imread("fj.png", cv2.IMREAD_UNCHANGED)rows, cols, chn = img.shape#加噪声for i in range(50000):        x = np.random.randint(0, rows)     y = np.random.randint(0, cols)        img[x,y,:] = 210cv2.imshow("noise", img)           #期待显示cv2.waitKey(0)cv2.destroyAllWindows()cv2.imwrite('fj-res.png',img)

输入后果如下图所示:

2.雾的模仿生成

代码如下:

import numpy as npimport cv2 as cvimport osimport random file = ['fj.png']output = 'fj-wu.png'for file_img in file:    #关上图像    img = cv.imread(file_img)    mask_img = cv.imread(file_img)        #雾的色彩    mask_img[:, :] = (166, 178, 180)         #外面参数可调,次要调整雾的浓度    image = cv.addWeighted(img,                           round(random.uniform(0.03, 0.28), 2),                           mask_img, 1, 0)     #保留的文件夹    cv.imwrite(output, image)

输入后果如下图所示,成果还不错。

点击关注,第一工夫理解华为云陈腐技术~