Single Image Haze Removal Using Dark Channel Prior(暗通道先验)

news2025/1/17 13:57:17

去雾算法都会依赖于很强的先验以及假设,并结合相应的物理模型,完成去雾过程。本文作者何凯明及其团队通过大量的无雾图像和有雾图像,归纳总结出无雾图像在其对应的暗通道图像上具有极低的强度值(趋近于0),并结合如下公式:
在这里插入图片描述
求出透射率参数以及全球大气光值,进而轻松的求取无雾图像J。

1、暗通道先验是什么?

暗通道先验是基于户外的的无雾图像总结归纳的,发现的一个普遍存在的现象就是:在大多数的非天空的图像块中,至少有一个图像通道的一些像素值是非常低且接近于0的。作者使用J_dark来表示暗通道图像,具体的计算公式如下所示:
在这里插入图片描述
上述公式可以知道,对于无雾图像y,求取其三通道对应像素为的最小值,并使用一个固定大小(15*15)的块去取其中最小的值即为当前像素的暗通道值。

下图很明显的展示了有雾与无雾图像的暗通道图像的差异:对于有雾图像(不考虑天空),暗通道图像偏白,也就是其强度值比较高,而对于无雾图像,暗通道图像整体偏黑,也就是其强度值比较低。

在这里插入图片描述
按照暗通道的定义就可以很清楚的知道:
在这里插入图片描述
上述公式就是本文的精髓暗通道先验,就是这么一个普普通通的经验总结,但是没有人想到。
作者在发现这个先验以后,还去做了很多的验证工作,其中就包括:
(1)随机选取5000张landscape+cityscape场景的数据,在剪裁掉天空部分后,resize到最长边为500的图像,使用15*15的核区域去计算暗通道图像,统计其强度概率分布图、累积概率强度分布图、图像的强度分布图

在这里插入图片描述
得出的结论是:
1、We can see that about 75 percent of the pixels in the dark channels have zero values, and the intensity of 90 percent of the pixels is below 25
2、most dark channels have very low average intensity, showing that only a small portion of outdoor haze-free images deviate from our prior

2、使用暗通道先验进行去雾

2.1 估计透射参数t

假设全球大气光值A是一个常量,利用公式(1)进行归一化操作可得:
在这里插入图片描述
接着假设t在一个固定大小的块中是恒定值,并且在公式(7)的两边同时求取暗通道值:
在这里插入图片描述
对于无雾图像来说J趋近于0的:
在这里插入图片描述
因此可以得出:
在这里插入图片描述
通过上述几个公式联合求解可得:
在这里插入图片描述
这里有一个特殊的点是:作者之前在讲解暗通道先验的时候排除了天空的干扰问题,但是通过公式(11)可以知道,对于有雾的天空而言,I和大气光参数A基本一致,因此:
在这里插入图片描述
对于天空来说t->0,说明对于天空来说,因为距离无限远,反射率基本等于0是完全合理的。因此作者很优雅的处理了暗通道中的非天空数据,不用特意将天空数据裁剪完成后方才进行暗通道计算。

实际环境下,无论天气如何晴朗,都会存在一些颗粒和雾气,这种现象称为aerial perspective,如果我们直接移除雾气,会让去雾后的图像看起来非常不自然,因此需要保留一定的雾气,而这就导致反射率需要加一些补偿值:
在这里插入图片描述

2.2 估计大气光值A

作者依托于Tan的理论:有雾图像中最亮的像素点是最不透明的,这主要针对是阴霾而没有阳光的天气,在这种情况下,大气光是照明的唯一来源,因此每个通道的颜色场景亮度为:
在这里插入图片描述
同时公式(1)可以改写为:
在这里插入图片描述当图像中的像素处于无线远的位置时,最亮点的像素时最haze-opaque的,可近似为A。但是实际经验告诉我们不能忽略阳光,因此在考虑阳光照射的情况下,公式(18),(19)就需要改写为:
在这里插入图片描述在这里插入图片描述
因此,图像中最亮的像素点可以比大气光值A大,可能会出现在白色的小汽车或者白色建筑物上
在这里插入图片描述依据暗通道先验,有雾图像的暗通道图表征其雾浓度,因此可以通过有雾图像的暗通道图来检测物浓度最大的区域,进而去提升大气光值的估计。
最终的方法是:首先获取图像的暗通道图,接着获取暗通道图中top 0.1%的的最亮像素点,然后选取暗通道图最亮像素点所对应的原始图像强度值最大的值作为大气光值。

2.3 图像重建

在得到大气光值A以及相应的透射率参数t以后,可以利用22式进行求解并获取相应的无雾图像。
在这里插入图片描述

2.4 番外篇

由于透射参数t的求解比较粗暴,导致得到的t图比较粗糙,为了进行细腻处理,作者使用soft matting进行图像的refine,下图是对比:
在这里插入图片描述
Soft matting的效果明显比直接求出的t值好很多,但是有一个致命的缺点是速度比较慢,因此在2010年何凯明使用引导滤波(guided filter)进行加速。

3 代码实现

3.1暗通道图求解

暗通道图就是对图像的RGB三通道求取最小值,然后在用一个核去取最小值,当然简便的方法是构造一个核,直接使用腐蚀操作进行实现。

	def cal_Dark_Channel(im, width = 15):
	    im_dark = np.min(im, axis = 2)
	    border = int((width - 1) / 2)
	    im_dark_1 = cv2.copyMakeBorder(im_dark, border, border, border, border, cv2.BORDER_DEFAULT)
	    res = np.zeros(np.shape(im_dark))
	    for i in range(res.shape[0]):
	        for j in range(res.shape[1]):
	            res[i][j] = np.min(im_dark_1[i: i + width, j: j + width]) 
    	return res

3.2 求解大气光值A

	#计算A参数, im为暗通道图像, img为原图
	def cal_Light_A(im, img):
	    
	    s_dict = {}
	    for i in range(im.shape[0]):
	        for j in range(im.shape[1]):
	            s_dict[(i, j)] = im[i][j]
	        
	    s_dict = sorted(s_dict.items(), key = lambda x: x[1])   
	    
	    A = np.zeros((3, ))
	    num = int(im.shape[0] * im.shape[1] * 0.001)
	    
	    for i in range(len(s_dict) - 1, len(s_dict) - num - 1, -1):
	        
	        X_Y = s_dict[i][0]
	        A = np.maximum(A, img[X_Y[0], X_Y[1], :])
	
	    return A

3.3 反射率参数t

	def cal_trans(A, img, w = 0.95):
	    
	    dark = cal_Dark_Channel(img / A)
	    t = np.maximum(1 - w * dark, 0)
	    
    return t

3.4 使用引导滤波进行优化t

	def Guided_filtering(t, img_gray, width, sigma = 0.0001):
	    
	    mean_I = np.zeros(np.shape(img_gray))
	    cv2.boxFilter(img_gray, -1, (width, width), mean_I, (-1, -1), True, cv2.BORDER_DEFAULT)
	    mean_t = np.zeros(np.shape(t))
	    cv2.boxFilter(t, -1, (width, width), mean_t, (-1, -1), True, cv2.BORDER_DEFAULT)
	    corr_I = np.zeros(np.shape(img_gray))
	    cv2.boxFilter(img_gray * img_gray, -1, (width, width), corr_I, (-1, -1), True, cv2.BORDER_DEFAULT)
	    corr_IT = np.zeros(np.shape(t))
	    cv2.boxFilter(img_gray * t, -1, (width, width), corr_IT, (-1, -1), True, cv2.BORDER_DEFAULT)
	    
	    var_I = corr_I - mean_I * mean_I
	    cov_IT = corr_IT - mean_I * mean_t
	
	    a = cov_IT / (var_I + sigma)    
	    b = mean_t - a * mean_I
	    
	    mean_a = np.zeros(np.shape(a))
	    mean_b = np.zeros(np.shape(b))
	    cv2.boxFilter(a, -1, (width, width), mean_a, (-1, -1), True, cv2.BORDER_DEFAULT)
	    cv2.boxFilter(b, -1, (width, width), mean_b, (-1, -1), True, cv2.BORDER_DEFAULT)
	    
	    return mean_a * img_gray + mean_b
	

3.5 图像恢复

	def harz_Rec(A, img, t, t0 = 0.1):
	    
	    img_o = np.zeros(np.shape(img))
	    
	    img_o[:, :, 0] = (img[:, :, 0] - A[0]) / (np.maximum(t, t0)) + A[0]
	    img_o[:, :, 1] = (img[:, :, 1] - A[1]) / (np.maximum(t, t0)) + A[1]
	    img_o[:, :, 2] = (img[:, :, 2] - A[2]) / (np.maximum(t, t0)) + A[2]
	
    return img_o

参考文献

走出寂静岭!暗通道先验的python实现
https://github.com/vaeahc/Dark_Channel_Prior/blob/master/Dark_Channel_Prior.py

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1178031.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

虚拟机没有桥接模式--物理机WiFi不见了--注册表修复

我们知道虚拟机有三种模式: vmnet0 桥接模式;vmnet1 仅主机模式;vmnet8 NAT模式 我自己以前一直用的NAT模式,今天突然要用到桥接模式,发现无法切换... 我下面这个是后面弄好了的,最开始是没有显示桥接模式…

运放电压跟随器为什么要加电阻

这个是运放构成的电压跟随器,他的特点是输出电压等于输入电压,它常常用来对信号进行隔离,缓冲和提高带载能力。 有时候我们还会在电压跟随器上加这两个电阻,其中R1主要是起保护作用,Rf主要是为了消除偏置电流对输出电压…

stable-diffusion-webui安装Wav2Lip

常见错误 1.错误:Torch is not able to use GPU; add --skip-torch-cuda-test to COMMANDLINE_ARGS variable to disable this check 修改代码: launch_utils.py 删除三个地方:

LangChain+LLM实战---文本分块(Chunking)方法

RAG是一个考验技术的工作 基于大模型的企业应用中很大一部分需求就是RAG——检索增强生成。 这个流程依然无法描述RAG的复杂性 RAG涉及的内容其实广泛,包括Embedding、分词分块、检索召回(相似度匹配)、chat系统、ReAct和Prompt优化等&…

Optional——优雅判空

初始化 Optional提供了三个初始化方法: SpringBootTest public class OptionalTest {Testpublic void testOptional() {Optional.empty();Optional.ofNullable(null);Optional.of(null);} }empty返回一个空的Optional对象。 of遇到空会报错,但是使用Op…

Python | 安装、环境配置及包的安装

Python | 安装、环境配置及包的安装 一、前言二、python安装及编辑器配置2.1 python安装2.2 python调试2.3 python编辑器 | PyCharm2.3.1 PyCharm下载2.3.2 PyCharm安装2.3.3 PyCharm启动界面2.3.4 PyCharm初步设置2.3.5 PyCharm环境配置(含Python Interpreter配置)2.3.5.1 New…

2003-2022年飞机航线信息数据

2003-2022年飞机航线信息数据 时间:2003-2022年指标:起点城市、起点城市所属地级市、起点城市所属省份、起点机场、终点城市、终点城市所属地级市、终点城市所属省份、终点机场、航空公司、航班、机型、出发时间、到达时间、准点率、班次_周一、班次_周…

pip安装apex报错ERROR: Could not build wheels for cryptacular.......

问题:在训练模型的时候需要安装apex包,遂即使用以下命令 pip install apex但是报错了,报错信息如下: WARNING: Building wheel for cryptacular failed: [Errno 2] No such file or directory: C:\\Users\\XXX\\AppData\\Local\…

Corel VideoStudio 会声会影2024剪辑中间的视频怎么删 剪辑中音乐太长怎么办

我很喜欢视频剪辑软件Corel VideoStudio 会声会影2024,因为它使用起来很有趣。它很容易使用,但仍然给你很多功能和力量。视频剪辑软件Corel VideoStudio 会声会影2023让我与世界分享我的想法!“这个产品的功能非常多,我几乎没有触…

解决找不到msvcp120.dll,无法继续执行代码的办法,msvcp120.dll丢失的解决办法

在使用电脑的过程中出现了“找不到msvcp120.dll,无法继续执行代码”,通常出现这种错误的原因是因为电脑中的msvcp120.dll文件丢失,但是文件丢失就会导致电脑出现软件不能打开的情况,也可能会导致电脑出现其他的问题,所以今天就给大…

【学习草稿】

【数据分析】 1、相关性分析 对变量之间相关关系的分析&#xff0c;即相关性分析。其中比较常用的是线性相关分析&#xff0c;用来衡量它的指标是线性相关系数&#xff0c;又叫皮尔逊相关系数&#xff0c;通常用r表示&#xff0c;取值范围是[-1,1]。 r的绝对值<0.3 ,低度线性…

spring报错 @EnableAsync annotation metadata was not injected

报错 报错 internalAsyncAnnotationProcessor 这个spring内部的后处理器 创建失败&#xff0c;进而导致 EnableAsync 注解元数据没有注入容器 分析问题 查了 博客 是配置类放到原始项目路径下导致的问题。 博主的路径虽然正确&#xff0c;但发现是相似的问题&#xff0c;最…

linux 驱动——将模块编译进内核

文章目录 新增 C 文件修改 Makefile 文件修改 Kconfig 文件模块使能内核启动日志参考 linux 驱动——字符设备驱动 linux 驱动——字符设备驱动(自动生成设备节点文件) linux 驱动——将模块编译进内核 前面两节介绍的驱动都是以模块的形式&#xff0c;需要手动加载&#xff0…

【kubernetes】pod的生命周期

文章目录 1、概述2、pod的生命期3、pod阶段4、容器状态5、容器重启策略6、pod状况6.1 Pod就绪态6.2 Pod就绪态的状态6.3 Pod网络就绪 7、容器探针7.1 检查机制7.2 探测结果7.3 探测类型 8、Pod的终止8.1 强制终止Pod8.2 Pod的垃圾收集 1、概述 pod遵循预定义的生命周期&#x…

matlab中的mapminmax函数初步理解和应用

matlab中的mapminmax函数初步认识 一、mapminmax 顾名思义&#xff1a;映射最大最小 二、语法及举例 2.1 语法1 [Y,PS] mapminmax(X) 将矩阵X映射形成矩阵Y, Y中每行中的最小值对应-1&#xff0c;最大值对应1。PS是一个包含映射信息的结构体。 举例&#xff1a; clc cle…

4.第一个Java程序的讲解—Hello World

本文将写一个程序输出 Hello World &#xff0c;然后逐句讲解 ~ 文章目录 一、输出 Hello World二、代码讲解2.1 package com.goole.demo;2.1.1 .idea、out、src2.1.2 解释 2.2 public class Main2.2.1 解释2.2.2 创建新类 2.3 public static void main(String[] args)2.3.1 解…

测试用例的设计方法(全):判定表驱动分析方法

目录 判定表驱动分析方法 一. 方法简介 二. 实战演习 判定表驱动分析方法 一. 方法简介 1.定义&#xff1a;判定表是分析和表达多逻辑条件下执行不同操作的情况的工具。 2.判定表的优点 能够将复杂的问题按照各种可能的情况全部列举出来&#xff0c;简明并避免遗漏。因此…

找不到d3dx9_43.dll如何修复?d3dx9_43.dll丢失的解决办法分享

在电脑使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“找不到d3dx9_43.dll”。这个错误通常出现在运行某些游戏或应用程序时&#xff0c;它会导致程序无法正常运行。那么&#xff0c;如何解决找不到d3dx9_43.dll的问题呢&#xff1f;下面我将分享…

贰[2],QT异常处理

1&#xff0c;异常&#xff1a;QT编译警告 warning LNK4042: 对象被多次指定&#xff1b;已忽略多余的指定 处理办法&#xff0c;检查.pri文件&#xff0c;是否关联了多个相同的文件(头文件.h/源文件.cpp) 2&#xff0c;异常&#xff1a;C4819: 该文件包含不能在当前代码页(936…

FreeRTOS_任务通知

目录 1. 任务通知简介 2. 发送任务通知 2.1 函数 xTaskNotify() 2.2 函数 xTaskNotifyFromISR() 2.3 函数 xTaskNotifyGive() 2.4 函数 vTaskNotifyGiveFromISR() 2.5 函数 xTaskNotifyAndQuery() 2.6 函数 xTaskNotifyAndQueryFromISR() 3. 任务通知通用发送函数 3.…