Python图像处理丨详解图像去雾处理方法
摘要:本文主要讲解ACE去雾算法、暗通道先验去雾算法以及雾化生成算法。
本文分享自华为云社区《[Python图像处理] 三十.图像预处理之图像去雾详解(ACE算法和暗通道先验去雾算法)丨【拜托了,物联网!】》,作者: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/2012/g-ace/?utm_source=doi
Automatic Color Enhancement (ACE) and its Fast Implementation - https://www.sciencedirect.com/science/article/abs/pii/S0167865502003239
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 cv2 import numpy as np import math import 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)