当前位置:   article > 正文

opencv-python图像增强二:图像去雾(暗通道去雾)

opencv-python图像增强二:图像去雾(暗通道去雾)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


一、简介:

图像去雾计算机视觉领域中的一个重要问题,其目的是恢复被雾气遮挡的图像中的真实细节。在自然场景中,雾气会导致图像的对比度下降、颜色失真以及细节模糊。去雾算法的主要目标是在保留图像内容的同时,尽可能地恢复被雾气遮挡的信息。本次使用的算法为暗通道去雾,暗通道去雾算法基于暗通道先验理论,该理论指出在自然场景的任意一点(除天空外),其暗通道图像中的像素值在大部分情况下都接近于0。通过提取暗通道图像,并对其进行去雾处理,可以恢复图像中的真实细节。

二、暗通道去雾方案简述:

图像去雾处理中,我们采取以下步骤来提升图像质量:
首先,我们从三通道彩色图片中提取每个像素的最小值,并将其拆解为单通道图像。这一步帮助我们识别出图像中最暗的部分。
接着,我们对这个单通道图像进行最小值滤波操作,这一过程有助于进一步提取图像的暗部信息,使得暗部特征更加明显。
然后,为了去除暗部噪声并模糊图像细节,我们对提取出的暗部图像进行引导滤波处理。这一步骤有助于平滑图像,同时保留重要的结构信息。
接下来,我们计算全局光强值A,这个值用于排除天空等过曝区域,确保去雾效果更加自然。
最后,我们对原图像的每个通道分别减去暗通道,以保留暗部细节并校正颜色与亮度。这个过程可以理解为对雾气影响的校正。经过这一步骤处理后,新的图像暗通道接近零,这意味着图像中的雾气已被有效去除,从而得到清晰、无雾的图片。

三、算法实现步骤

3.1最小值滤波

最小值滤波是一种图像处理技术,其工作原理是在图像中每个像素点的邻域内,找出所有像素值中的最小值,并用这个最小值替换掉原始像素点的值。这个过程对于突出图像中的暗部细节非常有用,尤其是在去除图像噪声和增强图像对比度方面。

代码如下:

# 定义一个函数,用于对灰度图像进行最小值滤波
def zmMinFilterGray(src, r=7):
    # 检查输入图像的形状是否正确
    if len(src.shape) != 2:
        raise ValueError("输入图像必须是灰度图像")
    # 定义结构元素,它是一个由全1组成的矩形核,半径为r
    structure_element = np.ones((2 * r + 1, 2 * r + 1))
    # 使用OpenCV的erode函数对输入图像进行腐蚀操作
    # 腐蚀操作可以理解为用结构元素覆盖图像的每个像素,并保留覆盖区域的最小值
    # 这里使用的是全1的结构元素,因此腐蚀操作相当于最小值滤波
    result = cv2.erode(src, structure_element)
    # 返回腐蚀操作后的结果
    return result
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

3.2 引导滤波

引导滤波(Guided Filter)是一种用于图像滤波的算法,它利用引导图像的内容来对目标图像进行滤波处理,同时保持目标图像的边缘和细节。引导滤波的核心思想是假设引导图像与目标图像在局部区域具有相似的结构,因此可以借助引导图像的特性来对目标图像进行滤波。
代码如下(示例):

def guidedfilter(I, p, r, eps):
    # 获取图像的高度和宽度
    height, width = I.shape

    # 计算引导图像I的局部均值
    m_I = cv2.boxFilter(I, -1, (r, r))
    # 计算输入图像p的局部均值
    m_p = cv2.boxFilter(p, -1, (r, r))
    # 计算引导图像I和输入图像p的局部乘积的均值
    m_Ip = cv2.boxFilter(I * p, -1, (r, r))
    # 计算引导图像I和输入图像p的局部协方差
    cov_Ip = m_Ip - m_I * m_p
    # 计算引导图像I自身的局部协方差,即局部方差
    m_II = cv2.boxFilter(I * I, -1, (r, r))
    var_I = m_II - m_I * m_I
    # 估计线性系数a
    a = cov_Ip / (var_I + eps)
    # 估计线性系数b
    b = m_p - a * m_I
    # 对系数a和b进行全局均值滤波,得到平滑的系数
    m_a = cv2.boxFilter(a, -1, (r, r))
    m_b = cv2.boxFilter(b, -1, (r, r))
    # 计算输出图像,即m_a * I + m_b
    return m_a * I + m_b
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

参数说明:
I: 输入图像,可以是灰度图像或彩色图像。
p: 引导图像,用于指导滤波器。引导图像应与输入图像具有相同的尺寸和类型。
r: 引导滤波器的半径,以像素为单位。较大的半径可以覆盖更多的像素,从而更好地平滑噪声,但可能会模糊边缘。较小的半径则可以更好地保留边缘细节,但去噪效果可能不够理想。
eps: 引导滤波器的正则化参数。较大的值可以更好地平滑噪声,但可能会模糊图像的细节。较小的值则可以更好地保留细节,但去噪效果可能不够理想。

3.3 计算图像全局光强

在暗通道去雾算法中,计算全局光强A的原因如下:
1.恢复无雾图像:去雾的目标是从带雾图像中恢复出无雾图像。为了做到这一点,需要估计场景中光线的原始强度,即在没有大气散射影响下的光强。全局光强A代表了场景中最亮区域的原始光线强度。
2.指导去雾过程:全局光强A是去雾算法中的一个关键参数,它用于指导如何从带雾图像中恢复出无雾图像的颜色和亮度。通过计算A,可以更准确地估计每个像素点在无雾状态下的光线强度。
3.避免颜色失真:如果全局光强A的估计不准确,去雾后的图像可能会出现颜色失真或过曝现象。因此,准确计算A对于保持图像的自然颜色和细节至关重要。

代码如下(示例):

# 定义一个变量,用于设置直方图的桶数
bins = 2000
# 计算暗通道图像V1的直方图
ht = np.histogram(V1, bins)
# 计算累积分布函数d,用于找到累积概率接近0.999的亮度值
d = np.cumsum(ht[0]) / float(V1.size)
# 遍历直方图的每个桶,从最后一个桶开始向前搜索
for lmax in range(bins - 1, 0, -1):
    # 如果累积分布函数d的值大于或等于0.999,则找到合适的亮度值
    if d[lmax] <= 0.999:
        break
# 计算大气光照系数A,即暗通道图像V1中累积概率大于或等于0.999的像素点的最大值
A = np.mean(m, 2)[V1 >= ht[1][lmax]].max()
# 对值范围进行限制,确保V1的值在合理的范围内
V1 = np.minimum(V1 * w, maxV1)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

这段代码简单来说就是将图像亮度分为2000个等级,用累积分布函数的概率找到图像中过亮区域像素值所在的范围,计算原图三通道像素值取出其均值所代表的新图像,按照范围取出新图像中的所代表区域的最大值,用来代表天空等区域的像素值,至此暗通道去雾所需要的关键算法就整理完毕了,之后需要的就是将这些算法集成起来构筑为一个完整的暗通道去雾算法。

四:整体代码实现

import cv2
import numpy as np

def zmMinFilterGray(src, r=7):
    return cv2.erode(src, np.ones((2 * r + 1, 2 * r + 1)))

def guidedfilter(I, p, r, eps):
    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)
    max_values = np.max(m, axis=2)
    # 得到暗通道图像
    Dark_Channel = zmMinFilterGray(V1, 7)
    cv2.imshow('V1', V1)
    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)
    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):
        print((m[:,:,k] - Mask_img))
        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(r'F:\traditional_vison\R-C.jfif') / 255.0) * 255).astype(np.uint8)
    cv2.imshow("ccccc",m)
    cv2.waitKey(0)
    # cv2.imwrite('20190708_02.png', m)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

五:效果

图片原图:
图片原图

暗通道图:在这里插入图片描述
增强后效果图:
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/运维做开发/article/detail/975309
推荐阅读
相关标签
  

闽ICP备14008679号