当前位置: 首页 > news >正文

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

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

文章目录

  • 一、简介:
  • 二、暗通道去雾方案简述:
  • 三、算法实现步骤
    • 3.1最小值滤波
    • 3.2 引导滤波
    • 3.3 计算图像全局光强
  • 四:整体代码实现
  • 五:效果


一、简介:

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

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

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

三、算法实现步骤

3.1最小值滤波

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

代码如下:

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

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# 估计线性系数aa = cov_Ip / (var_I + eps)# 估计线性系数bb = 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_breturn m_a * I + m_b

参数说明:
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)

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

四:整体代码实现

import cv2
import numpy as npdef 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_pm_II = cv2.boxFilter(I * I, -1, (r, r))var_I = m_II - m_I * m_Ia = cov_Ip / (var_I + eps)b = m_p - a * m_Im_a = cv2.boxFilter(a, -1, (r, r))m_b = cv2.boxFilter(b, -1, (r, r))return m_a * I + m_bdef 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 = 2000ht = 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:breakA = np.mean(m, 2)[V1 >= ht[1][lmax]].max()V1 = np.minimum(V1 * w, maxV1)               # 对值范围进行限制return V1, Adef 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)

五:效果

图片原图:
图片原图

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

相关文章:

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

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、简介&#xff1a;二、暗通道去雾方案简述&#xff1a;三、算法实现步骤3.1最小值滤波3.2 引导滤波3.3 计算图像全局光强 四&#xff1a;整体代码实现五&#xf…...

自研Vue3低代码海报制作平台第一步:基础拖拽组件开发

学习来源&#xff1a;稀土掘金 - 幽月之格大佬的技术专栏可拖拽、缩放、旋转组件 - 著作&#xff1a;可拖拽、缩放、旋转组件实现细节 非常感谢大佬&#xff01;受益匪浅&#xff01; 前面我们学习了很多vue3的知识&#xff0c;是时候把它们用起来做一个有意思的平台&#xf…...

QT 的 QSettings 读写 INI 文件的示例

在Qt中&#xff0c;QSettings 类提供了一种便捷的方式来存储和访问应用程序的设置&#xff0c;这些设置可以存储在多种格式的文件中&#xff0c;包括INI、Windows注册表&#xff08;仅Windows平台&#xff09;、XML和JSON等。以下是一些使用 QSettings 读写INI文件的示例。 写…...

【零基础学习CAPL语法】——testStep:测试结果输出函数

文章目录 1.函数介绍2.在报告中体现 1.函数介绍 testStep——测试结果输出函数 2.在报告中体现 //testStep() void PrintTxMsg() {testStep("Tx","[%x] [%.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x]",Diag_Req.id,Diag_Req.byte(0),Diag_Req.byte(1),Di…...

8.5.数据库基础技术-规范化

函数依赖 函数依赖&#xff1a;给定一个X,能唯一确定一个Y,就称X决定&#xff08;确定&#xff09;Y,或者说Y依赖于X。 例如&#xff1a;YX*X函数&#xff0c;此时X能确定Y的值&#xff0c;但是Y无法确定X的值&#xff0c;比如x2,y4,但是y4无法确定x2。函数依赖又可扩展以下两…...

于博士Cadence视频教程学习笔记备忘

标签&#xff1a;PCB教程 PCB设计步骤 cadence教程 Allegro教程 以下是我学习该视频教程的笔记&#xff0c;记录下备忘&#xff0c;欢迎大家在此基础上完善&#xff0c;能回传我一份是最好了&#xff0c;先谢过。 备注&#xff1a; 1、未掌握即未进行操作 2、操作软件是15.…...

8.3.数据库基础技术-关系代数

并&#xff1a;结果是两张表中所有记录数合并&#xff0c;相同记录只显示一次。交&#xff1a;结果是两张表中相同的记录。差&#xff1a;S1-S2,结果是S1表中有而S2表中没有的那些记录。 笛卡尔积&#xff1a;S1XS2,产生的结果包括S1和S2的所有属性列&#xff0c;并且S1中每条记…...

【Vue3】vue模板中如何使用enum枚举类型

简言 有的时候&#xff0c;我们想在vue模板中直接使用枚举类型的值&#xff0c;来做一些判断。 ts枚举 枚举允许开发人员定义一组命名常量。使用枚举可以更容易地记录意图&#xff0c;或创建一组不同的情况。TypeScript 提供了基于数字和字符串的枚举。 枚举的定义这里不说了…...

组合求和2

题目描述&#xff1a; Given a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates where the candidate numbers sum to target. Each number in candidates may only be used once in the combinati…...

Apple Maps现在可在Firefox和Mac版Edge浏览器中使用

Apple Maps最初只能在 Windows 版 Safari、Chrome 浏览器和 Edge 浏览器上运行&#xff0c;现在已在其他浏览器上运行&#xff0c;包括 Mac 版 Firefox 和 Edge。经过十多年的等待&#xff0c;Apple Maps于今年 7 月推出了新版地图应用的测试版&#xff0c;但只能在有限的浏览器…...

基于嵌入式Linux的数据库

数据库 数据库是在数据库管理系统和控制之下&#xff0c;存放在存储 介质上的数据集合。 基于嵌入式的数据库 基于嵌入式linux的数据库主要有SQlite&#xff0c; Firebird,Berkeley DB,eXtremeDB Firebird是关系型数据库&#xff0c;功能强大&#xff0c;支持存储过 程&…...

C# 使用LINQ找出一个子字符串在另一个字符串中出现的所有位置

一、实现步骤 遍历主字符串&#xff0c;使用IndexOf方法查找子字符串的位置。如果找到了子字符串&#xff0c;记录其位置&#xff0c;并且从该位置的后面继续查找。重复上述步骤直到遍历完整个字符串。 二、简单代码示例 using System; using System.Collections.Generic; usi…...

YOLOv8添加MobileViTv3模块(代码+free)

目录 一、理由 二、方法 &#xff08;1&#xff09;导入MobileViTv3模块 &#xff08;2&#xff09;在ultralytics/nn/tasks.py的函数parse_model中修改 &#xff08;3&#xff09;在yaml配置文件中写入 &#xff08;4&#xff09;开始训练&#xff0c;先把其他梯度关闭&…...

从概念到落地:全面解析DApp项目开发的核心要素与未来趋势

随着区块链技术的迅猛发展&#xff0c;去中心化应用程序&#xff08;DApp&#xff09;逐渐成为Web3时代的重要组成部分。DApp通过智能合约和分布式账本技术&#xff0c;提供了无需信任中介的解决方案&#xff0c;这种去中心化的特性使其在金融、游戏、社交等多个领域得到了广泛…...

仓颉编程入门 -- 泛型概述 , 如何定义泛型函数

泛型概述 , 如何定义泛型函数 1 . 泛型的定义 在仓颉编程语言中&#xff0c;泛型机制允许我们定义参数化类型&#xff0c;这些类型在声明时不具体指定其操作的数据类型&#xff0c;而是作为类型形参保留&#xff0c;待使用时通过类型实参来明确。这种灵活性在函数和类型声明中…...

SOC估算方法之(OCV-SOC+安时积分法)

一、引言 此方法主要参考电动汽车用磷酸铁锂电池SOC估算方法这篇论文 总结&#xff1a; 开路电压的测量需要将电池静止相当长的一段时间才能达到平衡状态进行测量。 安时积分法存在初始SOC的估算和累积的误差。 所以上述两种方法都存在一定的缺陷&#xff0c;因此下面主要讲…...

指针(下)

文章目录 指针(下)野指针、空指针野指针空指针 二级指针**main**函数的原型说明 常量指针与指针常量常量指针指针常量常量指针常量 动态内存分配常用函数**malloc****calloc****realloc****free** **void**与**void***的区别扩展&#xff1a;形式参数和实际参数的对应关系 指针…...

C# 浅谈IEnumerable

一、IEnumerable 简介 IEnumerable 是一个接口&#xff0c;它定义了对集合进行迭代所需的方法。IEnumerable 接口主要用于允许开发者使用foreach循环来遍历集合中的元素。这个接口定义了一个名为 GetEnumerator 的方法&#xff0c;该方法返回一个实现了 IEnumerator 接口的对象…...

mmdebstrap:创建 Debian 系统 chroot 环境的利器 ️

文章目录 mmdebstrap 的一般性参数说明 &#x1f4dc;mmdebstrap 的常见用法示例 &#x1f308;使用 mmdebstrap 的注意事项 ⚠️ &#x1f308;你好呀&#xff01;我是 山顶风景独好 &#x1f388;欢迎踏入我的博客世界&#xff0c;能与您在此邂逅&#xff0c;真是缘分使然&am…...

【Linux SQLite数据库】一、SQLite交叉编译与移植

SQLite 是一个用 C 语言编写的开源、轻量级、快速、独立且高可靠性的 SQL 数据库引擎&#xff0c;它提供了功能齐全的数据库解决方案。SQLite 几乎可以在所有的手机和计算机上运行&#xff0c;它被嵌入到无数人每天都在使用的众多应用程序中。此外&#xff0c;SQLite 还具有稳定…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:

在 HarmonyOS 应用开发中&#xff0c;手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力&#xff0c;既支持点击、长按、拖拽等基础单一手势的精细控制&#xff0c;也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档&#xff0c…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练

前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1)&#xff1a;从基础到实战的深度解析-CSDN博客&#xff0c;但实际面试中&#xff0c;企业更关注候选人对复杂场景的应对能力&#xff08;如多设备并发扫描、低功耗与高发现率的平衡&#xff09;和前沿技术的…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

在Ubuntu中设置开机自动运行(sudo)指令的指南

在Ubuntu系统中&#xff0c;有时需要在系统启动时自动执行某些命令&#xff0c;特别是需要 sudo权限的指令。为了实现这一功能&#xff0c;可以使用多种方法&#xff0c;包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法&#xff0c;并提供…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

Springboot社区养老保险系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;社区养老保险系统小程序被用户普遍使用&#xff0c;为方…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP

编辑-虚拟网络编辑器-更改设置 选择桥接模式&#xff0c;然后找到相应的网卡&#xff08;可以查看自己本机的网络连接&#xff09; windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置&#xff0c;选择刚才配置的桥接模式 静态ip设置&#xff1a; 我用的ubuntu24桌…...

日常一水C

多态 言简意赅&#xff1a;就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过&#xff0c;当子类和父类的函数名相同时&#xff0c;会隐藏父类的同名函数转而调用子类的同名函数&#xff0c;如果要调用父类的同名函数&#xff0c;那么就需要对父类进行引用&#…...