手把手教你暗通道先验去雾算法

news2024/11/14 14:29:19

0,流程

暗通道先验去雾算法(Dark Channel Prior, DCP)是一种基于图像的去雾技术,由Kaiming He等人在2009年提出。这种算法利用了大气散射模型,通过估计大气光和图像的传输图来去除雾的影响。以下是暗通道先验去雾算法的基本步骤:

  • 暗通道提取

    • 对于一幅图像,首先提取其暗通道。暗通道是指图像中相对较暗的区域,这些区域在雾的影响下仍然保持一定的可见度。
    • 暗通道可以通过对图像的每个像素点的RGB三个通道进行最小值操作来获得。
  • 大气光估计

    • 利用暗通道,估计大气光的值。大气光是指由于大气散射而进入相机的光。
    • 可以通过对暗通道的像素值进行阈值处理来估计大气光的值。例如,可以选择暗通道中大于某个阈值的像素点的像素值作为大气光的估计值。
  • 传输图估计

    • 传输图是指图像中每个像素点的光透过雾的传输率。传输图的估计需要结合大气光和暗通道。
    • 传输图可以通过以下公式估计: 

其中,I(x) 是原始图像的像素值,Lmax​ 是大气光的估计值。

  • 图像恢复

    • 利用传输图和大气光,恢复原始图像。恢复过程可以通过以下公式进行:

​ 其中,I′(x)是恢复后的图像,I(x)是原始图像,L是大气光的估计值,t(x)是传输图。

  • 细节增强

    • 为了增强恢复图像的细节,可以对恢复后的图像进行一些细节增强处理,比如使用双边滤波等。

1,暗通道先验理论内容

 在大多数非天空区域,至少有一个通道的像素值是很低的并且接近于0,并且在一个小的区域内最小的像素强度也接近于0.

暗通道先验定义的数学公式:

公式中Jc表示彩色图像的每个通道 ,Ω(x)表示以像素X为中心的一个窗口。首先求出每个像素RGB分量中的最小值,之后利用opencv的腐蚀操作求出以每个像素为中心的一个窗口的最小值,一般有WindowSize = 2 * Radius + 1。

1.1 暗通道先验的理论依据


        低强度的像素值通常由于以下几个方面造成:首先是现实世界中物体的阴影,因为阴影本身具有较低的光强度。其次是色彩鲜艳的物体或表面,它们在 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(峰值信噪比)图像质量评估指标,评估去雾后图像与原始图像之间的相似度和质量。

去雾效果图展示:

 暗通道先验去雾算法代码:

pip install scikit-image

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:\whl_package\img_1.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()

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

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

相关文章

PLC网关:开启工业4.0时代的智能工厂之路

PLC即可编程逻辑控制器,是工业自动化领域的核心设备,广泛应用于各个工业领域。从PLC问世至今,一直表现出强大的生命力和高速增长态势,2020年全球PLC市场的销售量已经达到了百亿RMB级别。 随着行业智能化、数字化推广,…

Docker从入门到实践教程(电子版)

前言 Docker 是个伟大的项目,它彻底释放了虚拟化的威力,极大降低了云计算资源供应的成本,同时让应用的 分发、测试、部署和分发都变得前所未有的高效和轻松! 本电子书既适用于具备基础 Linux 知识的 Docker 初学者,也…

hot100-5-普通数组

53最大子数组和 56合并区间 238除自身以外数组的乘积 用前缀乘积和后缀乘积 41缺失的第一个正数 189轮转数组

文本编辑三剑客(awk)

awk作为和sed、grep同级的文本处理命令,也又强大的文本分析功能,同样,它的原理并不困难,但操作很多且很杂,可以通过不同的需求进行自定义搭配。 awk工作原理 awk和另外两个命令的工作原理又不相同,当用户…

关于使用Postman在请求https网址没有响应,但是用浏览器有响应的问题解决

一、问题描述 使用postman调用正式环境的公共接口,无需鉴权,但是产生了返回状态码200,但是data中却无数据,如下 {"code": "200","message": "操作成功","data": {"qr_c…

【图解网络】学习记录

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 TCP/IP 网络模型有哪几层?键入网址到网页显示,期间发生了什么?Linux 系统是如何收发网络包的?NAPIHTTP 是什么&#…

Flask Bootstrap #3 - BootStrap快速入门

Reference https://www.bilibili.com/video/BV1AQe9eqEj7/?p2&spm_id_frompageDriver&vd_source3d4b12fb4a4bfbc98942d43612ae2fb9 1 BootStrap BootStrap,别人写好的一堆css样式,我们可以直接拿过来用,我们只要掌握两点就可以开…

【TabBar嵌套Navigation案例-自定义tabBar-封装 Objective-C语言】

一、再来说一个自定义tabbar的问题啊, 1.首先呢,这个tabbar,看起来好像效果是实现了,没有什么问题,但是说,从业务逻辑上来讲的话,这样做,是不太好的, 1)首先,我们去创建啊,我们自定义的一个UIView, UIView里边,有很多子控件,那么这个子控件,应该是写在你这个V…

kubernetes管理GUI工具Lens

从github上可以知道,lens的前端是用electron做的客户端工具,打开安装路径你会发现kubectl.exe,没错,就是你经常用的kubectl命令行的客户端工具。kubectl本来就能输出json的数据类型,集成前端更方便了。看到这里你是不是发现&#…

MoE技术揭秘——混合专家模型的计算奥秘

MoE技术揭秘——混合专家模型的计算 MoE技术的类比 你可以把MoE技术比作一个“智能团队”,团队中的每个成员(专家)都有自己擅长的领域。当面对一个问题时,只有擅长此问题的成员才会参与解答,这样既提高了效率&#xff…

如何开发属于自己直播平台的主播美颜SDK?

本篇文章,笔者将从需求分析、技术选型、开发流程等方面进行详细讲解。 一、需求分析 在开发美颜SDK之前,首先需要进行详细的需求分析。主要包括以下几个方面: 1.美颜功能的具体需求:确定美颜效果,包括磨皮、美白、瘦…

懂个锤子Vue 项目工程化扩展:

Vue项目工程化扩展📶: 前言:当然既然学习框架的了,HTMLCSSJS三件套必须的就不说了: JavaScript 快速入门 紧跟前文,目标学习Vue2.0——3.0: 懂个锤子Vue、WebPack5.0、WebPack高级进阶 涉及的…

4418 4412 的 bl1 , bl2 的理解

之前一直 对 三星的 bl1 , bl2 不是很理解。 网上的资料 我自己的 测试: 我在 4418 的源码中没有找到这个 2ndboot 文件夹。

仅花3小时搭好一套仓库管理系统,老板看了直呼哇塞

公司仓库管理长期效率低下,让团队头疼不已。没想到,一位同事凭借自学的零代码系统搭建技能,仅用时3小时就搭建出了一套完善的仓库管理系统,不仅操作简便,还大幅提升了工作效率。老板亲自验收后连连夸赞直接给了1w奖金&…

Unity Yaml

资料 UnityYaml 项目设置 选择用于存储序列化资产的格式 Project Settings->Asset Serialization->Mode Force Text 序列化为文本文件,例如场景文件 保存为yaml格式 方便查看;版本控制时文本文件比二进制文件更容易合并Force Binary 序列化为二进制文件&a…

图片格式怎么转换?这几种图片格式转换方法简单又高效

图片已成为我们日常生活与工作中不可或缺的一部分。然而,不同平台和应用往往对图片格式有着特定的要求,这就使得图片格式的转换成为了一项必备技能。下面给大家分享5种能够简单高效的转换图片格式方法,快来一起学习下吧。 方法一:…

隆尧县“隆品佳尧”区域公用品牌发布推介会暨地标之都七月选品会成功举办

在国家乡村振兴战略与农业现代化建设的大背景下,隆尧县凭借其得天独厚的地理优势和丰富的自然资源,正在成为区域经济与品牌建设的一颗新星。为了进一步推动隆尧县的农业发展和乡村建设,由隆尧县商务局指导、隆尧县电子商务公共服务中心主办的…

GD 32独立看门狗

前言 ... 独立看门狗简介 独立看门狗定时器(FWDGT)有独立的时钟源(IRC40K)。因此就算是主时钟失效了,它仍然能保持工作状态,这非常适合于需要独立环境且对计时精度要求不高的场合。 当内部向下计数器的计数值达到0,独立看门狗会产生一个复位…

迎接新时代:通过Web Distribution分发您的iOS应用

背 景 《数字市场法案》(Digital Markets Act,DMA)是欧盟委员会于2020年11月提出的旨在规范大型科技公司行为的立法提案,于2022年3月8日由欧盟议会和欧盟理事会正式通过,并于2022年11月1日正式生效,主要条…

面试题:MySQL 索引

1. 谈一下你对于MySQL索引的理解?(为什么MySQL要选择B+树来存储索引) MySQL的索引选择B+树作为数据结构来进行存储,使用B+树的本质原因在于可以减少IO次数,提高查询的效率,简单来说就是可以保证在树的高度不变的情况下存储更多的数据: IO效率的提高:在MySQL数据库中,…