DehazeNet单图像去雾的端到端系统(学习笔记)

2023-11-21 14:10

本文主要是介绍DehazeNet单图像去雾的端到端系统(学习笔记),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1、图像去雾算法分类:图像去雾分为图像恢复和图像增强,在图像恢复中又分为单图像去雾和多图像去雾,图像去雾中使用的物理模型是大气散射模型,可参考暗通道先验中对大气散射模型的解释。

2、与雾相关的特征

(1)暗通道先验:清晰图像块的RGB颜色空间中有一个通道很暗(数值很低甚至接近于零)。暗通道为局部区域中所有像素颜色的最小值::

暗通道先验可直接用于估计透射率t(x),t(x)正比于1-D(x)。

(2)最大化对比度:根据大气散射模型,图像的对比度因雾度而降低为,基于该观察,局部对比度是s×s局部pitch中像素强度相对于中心像素的方差,局部最大值为r×r区域r中的局部对比度值定义为C(x):

通过最大化局部对比度来增强图像的可见性。

(3)颜色衰减:雾会导致图像饱和度的降低和亮度的增加,整体上表现为颜色的衰减。根据颜色衰减先验,亮度和饱和度的差值记为A(x),景深和A(x)成正比,所以A(x)可以用于估计透射率 :

 (4)色调差异:原始图像I(x)与其半逆图像I_{si}(x)之间的色调差异已用于检测雾度,

对于无雾图像,其半逆图像的三个通道中的像素值不会全部翻转,从而导致I_{si}(x)和I(x)之间的色调变化较大。色调差异特征被定义为:

 

上标“h”表示HSV颜色空间中图像的色调通道,透射率t(x)向H(x)反向传播。

3、DeHazeNet主要贡献

(1)F1层:Maxout激活函数学习与雾相关的特征;

(2)F4层:用BReLu替代ReLu和Sigmoid函数,Sigmoid函数会出现梯度消失问题,导致收敛速度慢,ReLu函数更好的适用于分类问题而非回归问题,BReLu可以保持双边约束和局部线性来进行图像恢复,提高收敛性。

(3)端到端系统:输入是有雾图像,输出是其对应的透射率。

4、网络结构

(1)F1特征提取层,即提取有雾图像特征。根据不同的假设与先验设计不同的滤波器。举的例子中有16个滤波器。其中每四个是上述一种先验特征滤波器。通过maxout unit的激活函数,每四个输出一张图。这里不padding,输入是3*16*16三通道的块。输出的是四个16*12*12,每一个代表一种特征。

(2)F2使用多尺度的平行卷积操作。由于多尺度特征被证明有利于去雾并且在inception的模型中也用到了平行卷积,即同一张图用不同尺度的卷积核进行卷积。分别用16个3*3、16个5*5和16个7*7的卷积核进行卷积,每一种尺度产生16个,并且通过padding每张图大小应该是一致的。总共获得48个48*10*10。

 

(3)F3Maxpooling对局部数据敏感,另外根据假设透射率有局部不变性,所以用一个7*7局部最大值滤波替代maxpooling。输出是48个48*6*6。

 

(4)通过1个4*4的卷积核,产生1*1的标量,并且使用的激活函数为BReLU。因为ReLU抑制了小于0的数,只适用于图像分类等方面,并不适合图像复原。因为最后的透射率图允许高于1或者低于0。所以提出了BReLU,既保持了局部线性,又保持了双边的限制。输出的是一个标量,即输入块中心点的透射率值。

 

5、与传统去雾方法的联系:DehazeNet的第一层特征F1设计用于有雾图像的特征提取。以暗通道先验为例,如果权重W1是一个相反的滤波器(在一个通道的中心有值为−1的稀疏矩阵),而B1是一个单位偏差,那么特征图的最大输出相当于颜色通道的最小输出,这类似于暗通道。同样,当权重为圆形滤波器时,F1与最大对比度相似;当W1包含全通滤波器和相反滤波器时,F1与最大和最小特征图相似,这是颜色空间从RGB到HSV转换的运算,然后提取颜色衰减和色调色差特征。

综上所述,在如上图所示的滤波器学习成功后,第2节中提到的与雾相关的特征可以从DehazeNet的第一层中提取出来。另一方面,Maxout激活函数可以看作是对任意凸函数的分段线性逼近。在本文中,在四个特征映射(k=4)中选择最大值来近似一个任意的凸函数。

6、训练细节:

(1)使用深度学习的方法去雾需要有ground truth,由于自然场景中有雾图像和无雾图像无法同时存在,所以使用合成数据集,故此算法对自然场景中的有雾图像效果不太好。

(2)合成数据过程:给定一个无雾图像J(x)、大气光α和一个随机透射率t∈(0,1),合成一个模糊图像为I(x)=J(x)t+α(1−t)。为了减少变量学习中的不确定性,将大气光α设置为1。

(3)损失函数:MSE损失函数,随机梯度下降

 

(4)图像去雾:网络训练完成之后,得到初始透射率,再通过引导滤波进行细化,然后根据大气散射模型复原图像。

7、待改进的地方

(1)把大气光α当成了全局常量,所以这个算法在雾度均匀的情况下比较好,在不均匀雾度下效果不太好;

(2)有雾图像和无雾图像之间可以直接进行端到端映射,而不用估计透射率。

8、python代码实现

import torch
import torch.nn as nn
from torch.utils.data.dataset import Dataset
from PIL import Image
import torchvision
from torchvision import transforms
import torch.utils.data as data
#import torchsnooper
import cv2BATCH_SIZE = 128
EPOCH = 10# BRelu used for GPU. Need to add that reference in pytorch source file.
class BRelu(nn.Hardtanh):def __init__(self, inplace=False):super(BRelu, self).__init__(0., 1., inplace)def extra_repr(self):inplace_str = 'inplace=True' if self.inplace else ''return inplace_strclass DehazeNet(nn.Module):def __init__(self, input=16, groups=4):super(DehazeNet, self).__init__()self.input = inputself.groups = groupsself.conv1 = nn.Conv2d(in_channels=3, out_channels=self.input, kernel_size=5)self.conv2 = nn.Conv2d(in_channels=4, out_channels=16, kernel_size=3, padding=1)self.conv3 = nn.Conv2d(in_channels=4, out_channels=16, kernel_size=5, padding=2)self.conv4 = nn.Conv2d(in_channels=4, out_channels=16, kernel_size=7, padding=3)self.maxpool = nn.MaxPool2d(kernel_size=7, stride=1)self.conv5 = nn.Conv2d(in_channels=48, out_channels=1, kernel_size=6)#self.brelu = nn.BReLU()for name, m in self.named_modules():# lambda : 定义简单的函数    lambda x: 表达式# map(func, iter)  iter 依次调用 func# any : 有一个是true就返回trueif isinstance(m, nn.Conv2d):# 初始化 weight 和 biasnn.init.normal(m.weight, mean=0,std=0.001)if m.bias is not None:nn.init.constant_(m.bias, 0)def Maxout(self, x, groups):x = x.reshape(x.shape[0], groups, x.shape[1]//groups, x.shape[2], x.shape[3])x, y = torch.max(x, dim=2, keepdim=True)out = x.reshape(x.shape[0],-1, x.shape[3], x.shape[4])return out#BRelu used to CPU. It can't work on GPU.def BRelu(self, x):x = torch.max(x, torch.zeros(x.shape[0],x.shape[1],x.shape[2],x.shape[3]))x = torch.min(x, torch.ones(x.shape[0],x.shape[1],x.shape[2],x.shape[3]))return xdef forward(self, x):out = self.conv1(x)out = self.Maxout(out, self.groups)out1 = self.conv2(out)out2 = self.conv3(out)out3 = self.conv4(out)y = torch.cat((out1,out2,out3), dim=1)#print(y.shape[0],y.shape[1],y.shape[2],y.shape[3],)y = self.maxpool(y)#print(y.shape[0],y.shape[1],y.shape[2],y.shape[3],)y = self.conv5(y)# y = self.relu(y)# y = self.BRelu(y)#y = torch.min(y, torch.ones(y.shape[0],y.shape[1],y.shape[2],y.shape[3]))y = self.BRelu(y)y = y.reshape(y.shape[0],-1)return yloader = torchvision.transforms.Compose([transforms.ToTensor(),transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
augmentation = torchvision.transforms.Compose([transforms.RandomHorizontalFlip(0.5),transforms.RandomVerticalFlip(0.5),transforms.RandomRotation(30),transforms.ToTensor(),transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])class FogData(Dataset):# root:图像存放地址根路径# augment:是否需要图像增强def __init__(self, root, labels, augment=True):# 初始化 可以定义图片地址 标签 是否变换 变换函数self.image_files = rootself.labels = torch.cuda.FloatTensor(labels)self.augment = augment   # 是否需要图像增强# self.transform = transformdef __getitem__(self, index):# 读取图像数据并返回if self.augment:img = Image.open(self.image_files[index])img = augmentation(img)img = img.cuda()return img, self.labels[index]else:img = Image.open(self.image_files[index])img = loader(img)img = img.cuda()return img, self.labels[index]def __len__(self):# 返回图像的数量return len(self.image_files)path_train = []
file = open('path_train.txt', mode='r')
content = file.readlines()
for i in range(len(content)):path_train.append(content[i][:-1])label_train = []
file = open('label_train.txt', mode='r')
content = file.readlines()
for i in range(len(content)):label_train.append(float(content[i][:-1]))#print(float(content[i][:-1]))train_data = FogData(path_train, label_train, False)
train_loader = data.DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True, )net = DehazeNet()
net.load_state_dict(torch.load(r'defog4_noaug.pth', map_location='cpu'))#@torchsnooper.snoop()
def train():lr = 0.00001optimizer = torch.optim.Adam(net.parameters(), lr=0.0000005)loss_func = nn.MSELoss().cuda()for epoch in range(EPOCH):total_loss = 0for i, (x, y) in enumerate(train_loader):# 输入训练数据# 清空上一次梯度optimizer.zero_grad()output = net(x)# 计算误差loss = loss_func(output, y)total_loss = total_loss+loss# 误差反向传递loss.backward()# 优化器参数更新optimizer.step()if i % 10 == 5:print('Epoch', epoch, '|step ', i, 'loss: %.4f' % loss.item(), )print('Epoch', epoch, 'total_loss', total_loss.item())torch.save(net.state_dict(), r'defog4_noaug.pth')#train()def defog(pic_dir):img = Image.open(pic_dir)img1 = loader(img)img2 = transforms.ToTensor()(img)c, h, w = img1.shapepatch_size = 16num_w = int(w / patch_size)num_h = int(h / patch_size)t_list = []for i in range(0, num_w):for j in range(0, num_h):patch = img1[:, 0 + j * patch_size:patch_size + j * patch_size,0 + i * patch_size:patch_size + i * patch_size]patch = torch.unsqueeze(patch, dim=0)t = net(patch)t_list.append([i,j,t])t_list = sorted(t_list, key=lambda t_list:t_list[2])a_list = t_list[:len(t_list)//100]a0 = 0for k in range(0,len(a_list)):patch = img2[:, 0 + a_list[k][1] * patch_size:patch_size + a_list[k][1] * patch_size,0 + a_list[k][0] * patch_size:patch_size + a_list[k][0] * patch_size]a = torch.max(patch)if a0 < a.item():a0 = a.item()for k in range(0,len(t_list)):img2[:, 0 + t_list[k][1] * patch_size:patch_size + t_list[k][1] * patch_size,0 + t_list[k][0] * patch_size:patch_size + t_list[k][0] * patch_size] = (img2[:,0 + t_list[k][1] * patch_size:patch_size + t_list[k][1] * patch_size,0 + t_list[k][0] * patch_size:patch_size + t_list[k][0] * patch_size] - a0*(1-t_list[k][2]))/t_list[k][2]defog_img = transforms.ToPILImage()(img2)defog_img.save('./test21-1.jpg')defog('./21-1.jpg')

这篇关于DehazeNet单图像去雾的端到端系统(学习笔记)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/402817

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

基于人工智能的图像分类系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 图像分类是计算机视觉中的一个重要任务,目标是自动识别图像中的对象类别。通过卷积神经网络(CNN)等深度学习技术,我们可以构建高效的图像分类系统,广泛应用于自动驾驶、医疗影像诊断、监控分析等领域。本文将介绍如何构建一个基于人工智能的图像分类系统,包括环境

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

线性代数|机器学习-P36在图中找聚类

文章目录 1. 常见图结构2. 谱聚类 感觉后面几节课的内容跨越太大,需要补充太多的知识点,教授讲得内容跨越较大,一般一节课的内容是书本上的一章节内容,所以看视频比较吃力,需要先预习课本内容后才能够很好的理解教授讲解的知识点。 1. 常见图结构 假设我们有如下图结构: Adjacency Matrix:行和列表示的是节点的位置,A[i,j]表示的第 i 个节点和第 j 个