Advertisement

《Gated Context Aggregation Network for Image Dehazing and Deraining》阅读笔记

阅读量:

一、论文

摘要:图像去雾的目的是从朦胧的图像中恢复未损坏的内容。 我们没有利用传统的低级或手工图像先验作为恢复约束条件(例如,暗通道和增加的对比度),而是提出了一种端到端门控上下文聚合网络来直接恢复最终的无雾图像。 在该网络中,我们采用了最新的平滑扩张技术,以帮助消除由广泛使用的扩张卷积和可忽略的额外参数引起的网格化伪影,并利用门控子网融合不同级别的特征。 大量的实验表明,我们的方法在数量和质量上都可以大大超过以前的最新方法。 另外,为了证明所提方法的通用性,我们将其进一步应用于图像去水任务,这也实现了最新的性能。 代码已在https://github.com/cddlyf/GCANet提供

介绍

由于大气中存在混浊的介质(例如,黄昏,烟雾和其他颗粒),在这种大气现象中拍摄的图像会受到可见质量的下降,例如对比度和饱和度损失。 以这些退化的图像作为输入,许多最初基于干净的捕获环境设计的基于视觉的系统可能会因性能急剧下降而很容易受到困扰。 鉴于此,已经对图像去雾进行了广泛的研究,以从损坏的输入中恢复干净的图像,以用作前述系统的预处理步骤。

在该文献中,雾化处理通常用物理破坏模型表示

其中,I(x)和J(x)分别是退化的模糊图像和目标无雾场景的辐射度。 A是全球大气光,t(x)是介质透射图,该图取决于未知的深度信息。 先前的大多数除雾方法都首先估计透射图t(x)或大气光A,然后尝试恢复最终的干净图像J(x)。 但是第一步是一个非常具有挑战性的问题,因为在实际情况下,透射图t(x)和大气光A常常是未知的。

为了补偿在破坏过程中丢失的信息,许多传统方法[2、16、17、29、30、46]利用一些图像先验和视觉提示来估计透射图和大气光。 例如,[16]通过使用退化图像的对比度通常会急剧降低的先验来最大化目标图像的局部对比度。 [17]基于室外无雾图像的图像块通常具有低强度值的假设提出了暗通道先验。 [2]基于这样的假设:无雾图像的颜色可以很好地被数百种不同的颜色近似,并提出了一种基于局部非先验的除雾算法。 但是,这些先验并不总是成立,因此在某些实际情况下它们可能无法很好地工作。

随着深度学习的最新进展,通过利用大规模训练数据集,提出了许多基于CNN的方法[1、3、31、22、32、42]。 与如上所述的传统方法相比,基于CNN的方法尝试直接回归中间透射图或最终的干净图像,并获得卓越的性能和鲁棒性。 [3]提出了一种端到端网络来估计中间传输图。 [22]重新制定了大气散射模型,以通过轻量级的CNN预测最终的清洁图像。 文献[32]从原始模糊图像中创建了三个不同的派生输入图像,并融合了来自这些派生输入的去雾结果。文献[42]将等式(1)中的物理模型结合到网络设计中,并使用两个子网分别回归透射图和大气光。

在本文中,我们提出了一种用于图像去雾的新的端到端门控上下文聚合网络(称为“ GCANet”)。 由于膨胀卷积被广泛用于聚集上下文信息以提高其有效性而又不牺牲空间分辨率[41、25、36、15、9],因此我们也采用它来通过覆盖更多相邻像素来帮助获得更准确的恢复结果。 但是,原始的扩张卷积会产生所谓的“网格伪影” [36,15],因为当扩张率大于1时,输出中的相邻单元是根据输入中完全独立的集合计算出来的。 最近,[37]以合成的方式分析了膨胀卷积,并提出使膨胀的卷积平滑,这可以大大减少这种网格化伪影。 因此,我们也将这种想法纳入了我们的上下文聚合网络。 如[42,27]所示,融合不同级别的功能通常对低级和高级任务都是有益的。 受此启发,我们进一步提出了门控子网,以确定不同级别的重要性,并根据其相应的重要性权重对其进行融合。 [32]在他们的网络中也使用了门控融合模块,但是他们直接融合了不同派生输入图像的去雾结果,而不是中间特征。

为了验证所提出的GCANet的有效性,我们将其与最近的除雾基准数据集RESIDE [23]上的现有技术进行了比较。 实验表明,我们的GCANet在质量和数量上都大大优于以前的所有方法。 此外,我们进行了全面的消融研究,以了解每个组件的重要性。 为了显示所提出的GCANet的通用性,我们还将其应用于图像脱水任务,该任务也可以获得比以前最先进的图像脱水方法更高的性能。
总而言之,我们的贡献可分为三部分:

  • 我们提出了一种新的用于图像去雾的端到端门控上下文聚合网络GCANet,其中使用平滑的扩张卷积避免网格化伪影,并使用门控子网融合不同级别的特征。
  • 实验表明,在质量和数量上,GCANet都比以前所有最新的图像去雾方法获得更好的性能。 我们还提供全面的消融研究,以验证每个组件的重要性和必要性。
  • 我们进一步将我们提出的GCANet应用于图像去雨任务,它也优于以前的最新图像去雨方法并证明了其通用性

相关工作

单图像去雾是等式(1)中定义的物理破坏过程的逆恢复过程,由于未知的透射图和全局大气光,这是一个病态严重的问题。 在过去的几十年中,提出了许多不同的图像去雾方法来解决这一具有挑战性的问题,可以将它们大致分为传统的基于先验的方法和基于现代学习的方法。 这两种类型之间最显着的区别在于,图像先验是在前一种类型中手工制作的,而在后一种类型中是自动学习的。

在传统的基于先验的方法中,许多不同的图像统计先验被用作额外的约束条件,以补偿损坏过程中的信息丢失。 例如,[11]通过估计场景的反照率提出了一种基于物理的方法。[17,38,39]在更可靠地计算中间传输图之前发现并改善了有效暗信道。 [34]通过假设清晰图像的局部对比度高于朦胧图像的局部对比度,使用马尔可夫随机场来最大化图像的局部对比度。 基于观察到的小图像块通常在RGB颜色空间中呈现一维分布的观点,[12]最近提出了一种用于图像去雾的色线方法,[2]提出了在表征干净图像之前的非局部路径 。 但是,这些专门制作的先验先验在某些情况下适用,但处理所有情况并不总是很稳健。

最近,通过利用大规模数据集和GPU强大的并行性,提出了一种基于学习的图像去雾方法。 在这些类型的方法中,图像先验是通过神经网络从训练数据集中自动学习的,并保存在网络权重中。 它们的主要区别通常在于学习目标和详细的网络结构。 [3,31]分别提出了端到端CNN网络和多尺度网络来预测中间传输图。但是,传输图估计中的不正确总是会导致低质量的模糊结果。 [22]将传输图和大气光编码为一个变量,然后使用轻量级网络对其进行预测。 [42]通过遵循等式(1)中定义的物理模型,设计了两个不同的子网来预测透射图和大气光。 我们提出了一种端到端的门控上下文聚合网络,用于图像去雾,但是与这些方法不同,我们提出的GCANet旨在直接回归模糊图像和目标干净图像之间的残差。 此外,我们的网络结构绝对不同于以前的网络结构,后者虽然很轻巧,但可以比所有以前的方法获得更好的结果。

二、网络结构

图1.提出的GCANet的总体网络结构,遵循基本的自动编码器结构。 它由三个卷积块作为编码器部分,一个解卷积块和两个卷积块作为解码器部分。 在它们之间插入几个平滑的空洞残差块,以聚合上下文信息,而不会造成网格化假象。 为了融合不同级别的功能,利用了额外的门融合子网。 在期间,GCANet将以端到端的方式预测目标干净图像和模糊输入图像之间的残留。

在本节中,我们将介绍提出的门控上下文聚合网络GCANet的体系结构。 如图1所示,给定一个模糊的输入图像,我们首先通过编码器部分将其编码为特征图,然后通过聚合更多上下文信息并融合不同级别的特征而无需下采样来增强它们。 具体而言,利用了平滑的扩张卷积和额外的门子网。 最终将增强后的特征图解码回原始图像空间,以获取目标雾度残留物。 通过将其添加到输入的模糊图像上,我们将获得最终的无雾图像。

平滑的空洞卷积 :现代图像分类网络[21、33、18]通常通过连续的池化和子采样层集成多尺度上下文信息,这些信息会降低分辨率直到获得全局预测。 然而,对于像分割这样的密集预测任务而言,矛盾在于所需的多尺度上下文推理以及在下采样期间丢失的空间分辨率信息。 为了解决这个问题,[41]提出了一个新的扩张卷积层,该层支持接收场的指数扩展而不会降低分辨率或覆盖范围。 在一维情况下,给定一维输入f,内核大小为k的常规卷积层w的输出为:

其中一个输出点覆盖总共k个输入点,因此接收场为k。 但是对于膨胀卷积,可以将其视为“带膨胀滤波器的卷积”,可以表示为:

其中r是扩张率,当r = 1时,扩张卷积将退化为规则卷积。要直观地理解扩张卷积,我们可以将其视为在w的两个相邻权重之间插入r-1个零。 这样,扩张的卷积可以将原始接收场从k增加到r ∗(k-1)+1,而不会降低分辨率。

尽管膨胀卷积有效,但仍会产生所谓的网格化伪影,这在以前的论文中也已注意到[36,15]。 为了更清楚地理解这个问题,最近的一项工作[37]以合成的方式分析了膨胀的卷积。 网格伪像的图示如图2所示,其中一个r = 2的扩张卷积层的情况; 分析k = 3。 考虑到下一层的四个相邻像素,它们及其上一层中的从属单元分别用四种不同的颜色标记。 我们可以很容易地发现这四个邻居像素与上一层中完全不同的一组先前单元有关。 换句话说,在扩展的卷积中,输入单元或输出单元之间没有依赖性。 这就是为什么它将潜在地引起不一致的原因,即网格伪像。

为了缓解这种情况,[37]提议通过添加额外的内核大小(2r-1)的卷积层,在卷积之前在输入单元之间或卷积之后在输出单元之间添加交互。 在本文中,我们选择默认情况下添加输入单位的依赖关系。 需要注意的是,[37]采用了一个可分离和共享的卷积作为额外的卷积层,而不是普通的卷积层。“可分离”是指[8]中可分离的卷积思想,而“共享”是指所有通道均共享卷积权重。 这样,这个特殊的卷积层就具有恒定的参数大小(2r-1)^2,这与特征通道号无关。 图2是平滑的扩张卷积的示意图。

图2.插图[37]中的膨胀卷积和拟议的平滑膨胀卷积的网格化伪影:下一层i的四个不同点用不同的颜色表示,可以看出它们与完全不同的单元集有关 可能会导致网格化伪影。 相比之下,平滑的扩张卷积在扩张卷积之前在输入单元之间增加了额外的可分离和共享卷积层。

门控融合子网 : 如[27,42]所示,将不同级别的功能融合通常对于低级和高级任务都是有益的。 为了实现这个想法,[27]使用特征金字塔在所有尺度上融合了高级语义特征图,[42]则利用了密集连接的网络。 在本文中,我们通过合并额外的门控融合子网G采用不同的方式。具体来说,我们首先从不同级别Fl中提取特征图; 调频; Fh,并将其馈入门控融合子网。 门控融合子网的输出是三个不同的重要权重(M1,Mm,Mh),分别对应于每个特征级别。 最后,这三个特征图F1; 调频; 来自不同级别的Fh与回归的重要权重线性组合。

组合的特征图Fo将被进一步馈送到解码器中以获得目标雾度残留物。 在本文中,我们的门控融合子网仅由一个内核大小为3x3的卷积层组成,其输入是Fl的级联。 调频; Fh,输出通道号为3。

网络结构 :遵循[20、10、9]中类似的网络设计原理,我们的整体网络结构也被设计为简单的自动编码器,其中在编码器和解码器之间插入了七个残差块,以增强其学习能力。 具体而言,首先使用三个卷积层将输入的朦胧图像编码为特征图作为编码器部分,其中只有最后一个卷积层将特征图下采样1/2一次。 对称地,使用步幅为1/2的一个反卷积层将特征图上采样到解码器部分中的原始分辨率,然后接下来的两个卷积层将特征图转换回图像空间,以获得最终的目标雾度残差。 对于中间残差块,我们称它们为“平滑扩张的卷积块”,因为我们已用上述平滑扩张的卷积层替换了所有原始的常规卷积层。 将这七个残余块的扩张率分别设置为(2; 2; 2; 4; 4; 4; 1)。 为了在性能和时间之间取得良好的折衷,我们将所有中间卷积层的通道号设置为64。请注意,除了最后一个卷积层以及平滑膨胀卷积层中每个额外的可分离和共享的卷积层,我们将 每个卷积层之后的实例归一化层[35]和ReLU层。 在实验部分,我们将显示实例归一化比批量归一化更适合图像去雾任务。

如[10,9]所示,除输入图像外,预先计算输入图像的边缘并将其作为辅助信息馈入网络对于网络学习非常有帮助。 因此,默认情况下,我们也采用这种简单的想法,并将预先计算的边缘与沿通道维度的输入模糊图像连接起来,作为GCANet的最终输入。

损失函数在先前基于学习的图像去雾方法[3、31、22、24、42、44]中,采用了简单的均方误差损失。 按照相同的策略,默认情况下,我们也使用这种简单的损失。 但是与这些方法不同,我们的学习目标是无雾图像和输入模糊图像之间的残差:

其中r和r ^分别是地面实况和预测的霾残留量。 在期间,我们将r ^添加到输入的模糊图像上,以获得最终的预测无雾图像。需要强调的是,设计更好的损耗函数不是本文的重点,但是我们提出的GCANet应该能够推广到设计更好的损耗。 例如,[24,42,44]发现知觉损失[20],而GAN损失可以改善最终的除雾效果。 但是,即使仅具有上述简单的损失,我们的方法仍然可以达到最新的性能。

三、总结

在本文中,我们提出了一种用于图像去雾的端到端门控上下文聚合网络。 为了从扩张卷积中消除网格化伪影,使用了最新的平滑扩张技术。 而且,利用门控子网来融合不同级别的功能。 尽管所提出的方法很简单,但它在很大程度上优于以前的最新图像去雾方法。 我们还将提出的网络应用于图像排水任务,该任务还可以获得最新的性能。 将来,我们将尝试在[6,19]中使用更多的损失,并考虑扩展到[5]之类的视频去雾处理。

四、代码

复制代码
 import torch

    
 import torch.nn as nn
    
 import torch.nn.functional as F
    
  
    
  
    
 class ShareSepConv(nn.Module):
    
     def __init__(self, kernel_size):
    
     super(ShareSepConv, self).__init__()
    
     assert kernel_size % 2 == 1, 'kernel size should be odd'
    
     self.padding = (kernel_size - 1)//2
    
     weight_tensor = torch.zeros(1, 1, kernel_size, kernel_size)
    
     weight_tensor[0, 0, (kernel_size-1)//2, (kernel_size-1)//2] = 1
    
     self.weight = nn.Parameter(weight_tensor)
    
     self.kernel_size = kernel_size
    
  
    
     def forward(self, x):
    
     inc = x.size(1)
    
     expand_weight = self.weight.expand(inc, 1, self.kernel_size, self.kernel_size).contiguous()
    
     return F.conv2d(x, expand_weight,
    
                     None, 1, self.padding, 1, inc)
    
  
    
  
    
 class SmoothDilatedResidualBlock(nn.Module):
    
     def __init__(self, channel_num, dilation=1, group=1):
    
     super(SmoothDilatedResidualBlock, self).__init__()
    
     self.pre_conv1 = ShareSepConv(dilation*2-1)
    
     self.conv1 = nn.Conv2d(channel_num, channel_num, 3, 1, padding=dilation, dilation=dilation, groups=group, bias=False)
    
     self.norm1 = nn.InstanceNorm2d(channel_num, affine=True)
    
     self.pre_conv2 = ShareSepConv(dilation*2-1)
    
     self.conv2 = nn.Conv2d(channel_num, channel_num, 3, 1, padding=dilation, dilation=dilation, groups=group, bias=False)
    
     self.norm2 = nn.InstanceNorm2d(channel_num, affine=True)
    
  
    
     def forward(self, x):
    
     y = F.relu(self.norm1(self.conv1(self.pre_conv1(x))))
    
     y = self.norm2(self.conv2(self.pre_conv2(y)))
    
     return F.relu(x+y)
    
  
    
  
    
 class ResidualBlock(nn.Module):
    
     def __init__(self, channel_num, dilation=1, group=1):
    
     super(ResidualBlock, self).__init__()
    
     self.conv1 = nn.Conv2d(channel_num, channel_num, 3, 1, padding=dilation, dilation=dilation, groups=group, bias=False)
    
     self.norm1 = nn.InstanceNorm2d(channel_num, affine=True)
    
     self.conv2 = nn.Conv2d(channel_num, channel_num, 3, 1, padding=dilation, dilation=dilation, groups=group, bias=False)
    
     self.norm2 = nn.InstanceNorm2d(channel_num, affine=True)
    
  
    
     def forward(self, x):
    
     y = F.relu(self.norm1(self.conv1(x)))
    
     y = self.norm2(self.conv2(y))
    
     return F.relu(x+y)
    
  
    
  
    
 class GCANet(nn.Module):
    
     def __init__(self, in_c=4, out_c=3, only_residual=True):
    
     super(GCANet, self).__init__()
    
     self.conv1 = nn.Conv2d(in_c, 64, 3, 1, 1, bias=False)
    
     self.norm1 = nn.InstanceNorm2d(64, affine=True)
    
     self.conv2 = nn.Conv2d(64, 64, 3, 1, 1, bias=False)
    
     self.norm2 = nn.InstanceNorm2d(64, affine=True)
    
     self.conv3 = nn.Conv2d(64, 64, 3, 2, 1, bias=False)
    
     self.norm3 = nn.InstanceNorm2d(64, affine=True)
    
  
    
     self.res1 = SmoothDilatedResidualBlock(64, dilation=2)
    
     self.res2 = SmoothDilatedResidualBlock(64, dilation=2)
    
     self.res3 = SmoothDilatedResidualBlock(64, dilation=2)
    
     self.res4 = SmoothDilatedResidualBlock(64, dilation=4)
    
     self.res5 = SmoothDilatedResidualBlock(64, dilation=4)
    
     self.res6 = SmoothDilatedResidualBlock(64, dilation=4)
    
     self.res7 = ResidualBlock(64, dilation=1)
    
  
    
     self.gate = nn.Conv2d(64 * 3, 3, 3, 1, 1, bias=True)
    
  
    
     self.deconv3 = nn.ConvTranspose2d(64, 64, 4, 2, 1)
    
     self.norm4 = nn.InstanceNorm2d(64, affine=True)
    
     self.deconv2 = nn.Conv2d(64, 64, 3, 1, 1)
    
     self.norm5 = nn.InstanceNorm2d(64, affine=True)
    
     self.deconv1 = nn.Conv2d(64, out_c, 1)
    
     self.only_residual = only_residual
    
  
    
     def forward(self, x):
    
     y = F.relu(self.norm1(self.conv1(x)))
    
     y = F.relu(self.norm2(self.conv2(y)))
    
     y1 = F.relu(self.norm3(self.conv3(y)))
    
  
    
     y = self.res1(y1)
    
     y = self.res2(y)
    
     y = self.res3(y)
    
     y2 = self.res4(y)
    
     y = self.res5(y2)
    
     y = self.res6(y)
    
     y3 = self.res7(y)
    
  
    
     gates = self.gate(torch.cat((y1, y2, y3), dim=1))
    
     gated_y = y1 * gates[:, [0], :, :] + y2 * gates[:, [1], :, :] + y3 * gates[:, [2], :, :]
    
     y = F.relu(self.norm4(self.deconv3(gated_y)))
    
     y = F.relu(self.norm5(self.deconv2(y)))
    
     if self.only_residual:
    
         y = self.deconv1(y)
    
     else:
    
         y = F.relu(self.deconv1(y))
    
  
    
     return y
    
    
    
    
    AI生成项目python
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-07-13/HeJsBqxPDp4wrmyOzZnk9dhVSAto.png)

全部评论 (0)

还没有任何评论哟~