目录
引言
暗通道先验去雾算法是计算机视觉领域何恺明大佬于2009年提出的图像去雾经典算法,并获取当年CVPR最佳论文。论文题目为《Single Image Haze Removal Using Dark Channel Prior》。暗通道去雾算法基于暗通道原理用于消除图像雾霾,通过估计大气光强度和像素点之间的距离关系,来推断出图像中的雾霾信息,并使用这些信息来消除图像中的雾霾,可以显著改善图像的视觉效果,使其更加清晰、明亮。
1.原理介绍
暗通道去雾算法的核心思想是利用大气对光线散射的特性,具体来说,该算法首先假设在无雾情况下,像素点之间的距离越远,大气光强度越低。可以通过分析像素点之间的距离和大气光强度之间的关系,来估计大气光的强度。一旦获得了大气光的强度,就可以使用该信息来消除图像中的雾霾。传统的去雾算法通常会通过查找像素点周围的相似区域,并将这些区域的色彩分布进行插值,以得到清晰、明亮的图像。然而,这种方法往往会引入噪声和伪影,影响图像质量。暗通道去雾算法通过使用大气光强度信息,可以更准确地估计出图像中的实际场景,从而得到更加真实、自然的去雾效果。
透射率:在图像去雾中,透射率表示由于大气散射而引起的光线衰减程度。它描述了光线在穿过大气层时的损失程度,透射率越低表示图像中的物体对光线的吸收和散射越少,即图像越清晰。透射率通常用一个介于 0 和 1 之间的值来表示,其中 0 表示完全不透明,1 表示完全透明;大气光:大气光是指由于大气中的颗粒对光线的散射而产生的明亮背景光。这种背景光会导致图像中的远处物体呈现较亮的外观。在去雾算法中,大气光通常被视为场景中最明亮的像素值。
2.何凯明去雾论文思想的简单描述
2.1 暗通道先验理论内容
在大多数非天空区域,至少有一个通道的像素值是很低的并且接近于0,并且在一个小的区域内最小的像素强度也接近于0.
暗通道先验定义的数学公式:
公式中Jc表示彩色图像的每个通道 ,Ω(x)表示以像素X为中心的一个窗口。首先求出每个像素RGB分量中的最小值,之后利用opencv的腐蚀操作求出以每个像素为中心的一个窗口的最小值,一般有WindowSize = 2 * Radius + 1。
2.2 暗通道先验的理论依据
低强度的像素值通常由于以下几个方面造成:首先是现实世界中物体的阴影,因为阴影本身具有较低的光强度。其次是色彩鲜艳的物体或表面,它们在 RGB 通道中的某些通道上具有较低的值,因此在图像中呈现较暗的颜色,比如灰暗色的树干和石头等。总的来说,自然景物中到处都存在阴影或色彩丰富的物体,这些因素都会导致图像的暗通道具有较低的像素值,呈现出灰暗的色调。
获取暗通道图像算法代码:
import cv2
import numpy as np
def dark_channel_prior(image, kernel_size=15):
# 分割图像通道
b, g, r = cv2.split(image)
# 计算三个通道中的最小值
min_channel = cv2.min(cv2.min(r, g), b)
# 使用指定大小的矩形卷积核进行腐蚀操作
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernel_size, kernel_size))
dark_channel = cv2.erode(min_channel, kernel)
return dark_channel
# 读取图像
image = cv2.imread("4.jpg")
# 计算暗通道
dark_channel = dark_channel_prior(image)
# 显示结果
cv2.imshow('Dark Channel', dark_channel)
cv2.waitKey(0)
cv2.destroyAllWindows()
效果呈现图:
3.去雾算法流程
1.估计图像透射率和大气光:提供了有关图像中光线衰减和背景光的重要信息,帮助去除大气散射并恢复图像的清晰度和真实感。
2.去雾处理:根据透射率和大气光,对图像中的每个像素进行修复,以减轻由于大气散射引起的光线衰减效应。可以在处理过程中对图像进行进一步的优化,以提高图像的质量和真实感。
3.评估去雾效果:使用PSNR(峰值信噪比)图像质量评估指标,评估去雾后图像与原始图像之间的相似度和质量。
去雾效果图展示:
暗通道先验去雾算法代码:
import cv2
import numpy as np
from skimage.metrics import peak_signal_noise_ratio as psnr
# 计算每个通道中的最小值,输入Image图像,输出最小值img_min
def min_channel(img):
dst_channel = np.min(img, 2)
return dst_channel
def min_filter(image, r): #暗通道
# 最小值滤波,输入最小值图像,在2*r+1的矩形窗口内寻找最小
return cv2.erode(image, np.ones((2 * r + 1, 2 * r + 1)))
#引导滤波
#I=img_arr归一化 引导图像
# p=img_mim每个通道最小值 输入图像
def guided_filter(I, p, r, eps): # (引导I 原图p)
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))
q = m_a * I + m_b
return q
def select_bright(Image, img_origin, w, t0, V):
# 计算大气光A和折射图t
# 输入:Image最小值图像,img_origion原图,w是t之前的修正参数,t0阈值,V导向滤波结果
rows, cols = Image.shape
size = rows * cols
# h,w,d=img.shape,size=h*w*d,b=img.reshape(size)
order = [0 for i in range(size)] # order=np.zeros(size)
m = 0 # map0=Image.reshape(1,-1)[0] map1=map0.sort() map2=map1[::-1]
for t in range(0, rows):
for j in range(0, cols):
order[m] = Image[t][j]
m = m + 1
order.sort(reverse=True)
index = int(size * 0.001) # 从暗通道中选取亮度最大的前0.1%
mid = order[index]
A = 0
img_hsv = cv2.cvtColor(img_origin, cv2.COLOR_RGB2HLS)
for i in range(0, rows):
for j in range(0, cols):
if Image[i][j] > mid and img_hsv[i][j][1] > A:
A = img_hsv[i][j][1]
V = V * w
t = 1 - V / A
t = np.maximum(t, t0)
return t, A
def repair(Image, t, A):
rows, cols = Image.shape[:2]
J = np.zeros(Image.shape)
for i in range(0, rows):
for j in range(0, cols):
t[i][j] = t[i][j] - 0.35
J[i][j] = (Image[i][j] - A / 255.0) / t[i][j] + A / 255.0
return J
img = cv2.imread(r'F:\desktop\fig\123.png', 1) # 修改路径即可
# 归一化图像
img_arr = img.astype(float) / 255.0
# 计算暗通道
img_min = min_channel(img_arr)
img_dark = min_filter(img_min, 1)
img_guided = guided_filter(img_min, img_dark, 75, 0.001)
t, A = select_bright(img_dark, img, 0.95, 0.1, img_guided)
# 评估
# 加载原始图像
if img is None:
print("错误:无法加载原始图像。")
exit()
# 进行去雾处理
dehazed_image = repair(img_arr, t, A)
if dehazed_image is None:
print("错误:图像处理失败。")
exit()
# 计算 PSNR
dehazed_image_uint8 = (dehazed_image * 255).astype(np.uint8)
psnr_value = psnr(img, dehazed_image_uint8)
# 输出 PSNR
print("PSNR:", psnr_value)
# 显示原始图像
cv2.imshow('Original', img)
cv2.waitKey(0) # 等待用户按下任意键继续
# 显示暗通道图像
cv2.imshow('Dark Channel', img_dark)
cv2.waitKey(0) # 等待用户按下任意键继续
# 显示去雾后的图像
cv2.imshow('Dehazed Image', dehazed_image)
cv2.waitKey(0) # 等待用户按下任意键继续
# 释放窗口
cv2.destroyAllWindows()
本文由湖南省“双一流”应用特色学科的“模式识别与质量状态监控”实验室出品。