[论文解读-单图像去雨-SPANet] Spatial Attentive Single-Image Deraining with a High Quality Real Rain Dataset

本文主要是介绍[论文解读-单图像去雨-SPANet] Spatial Attentive Single-Image Deraining with a High Quality Real Rain Dataset,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

[论文解读-单图像去雨]Spatial Attentive Single-Image Deraining with a High Quality Real Rain Dataset

目录

  • [论文解读-单图像去雨]Spatial Attentive Single-Image Deraining with a High Quality Real Rain Dataset
    • 目录
    • 1.相关链接
    • 2.发现问题
    • 3.贡献
    • 4.从有雨视频得到无雨的清晰图像
    • 5.网络结构
      • 5.1.主干
      • 5.2.SAM
    • 6.训练细节
      • 6.1.损失函数
      • 6.2.其他细节

1.相关链接

chrome-extension://aajldohlagodeegngemjjgmabejbejli/pdf/viewer.html?file=https://arxiv.org/pdf/1904.01538v2.pdf

https://codeload.github.com/stevewongv/SPANet/zip/master

2.发现问题

现有的合成雨模式单一缺乏多样性,缺少真实雨的数据集,之所以会产生缺乏真实雨数据的情况是因为难以同时获得真实的有雨和无雨的图像对。

3.贡献

  • 提出了一种从真实有雨的连续帧中生成无雨清晰图像的方法,该方法结合了时间先验和人类监督(雨滴不可能长时间覆盖在同一个像素点上,视频中某一个像素点的值应该在真实值附近波动)。
  • 构建了一个包含约29.5K的有雨和无雨图像对的大规模数据集。
  • 提出了SPANet(Spatial Attentive Network),该方法从局部到全局消除雨滴。

4.从有雨视频得到无雨的清晰图像

在这里插入图片描述

上图中红色柱是人类选出的被雨滴覆盖的像素,蓝色是人类选出的没有被雨滴覆盖的像素,显然上图中该点的真实像素值应该在4附近。
我们假设每一帧图像包含 l l l 个像素点,假设选取 N N N 帧连续的有雨图像,我们可以定义每一帧上同一个像素点值的集合 O l = { o 1 l , . . . , o N l } O_l=\{o_{1l},...,o_{Nl}\} Ol={o1l,...,oNl},根据上图我们可以很容易考虑到选择集合中出现次数最多的像素值(此处不是用RGB值计算出现次数,而是用亮度值,作者是把图像转换到YCbCr空间得到亮度通道后再进行统计)作为背景的估计值会比较合理,也就是计算集合 O l O_l Ol 的众数,我们可以通过以下等式来表达:

ϕ l = Φ ( O l ) , (1) \phi_l=\Phi(O_l),\tag 1 ϕl=Φ(Ol),(1)

其中 Φ \Phi Φ 是mode函数,返回集合中出现次数最多的数值。但在式(1)中计算 ϕ l \phi_l ϕl 时并没有考虑邻域信息,当图像中包含稠密雨时,计算得到的图像中会包含许多噪声。所以进一步计算 O l O_l Ol 中的 ϕ l \phi_l ϕl 对应的百分位数范围 ( R l m i n , R l m a x ) (R_l^{min},R_l^{max}) (Rlmin,Rlmax) ,下图中(b)直观展示了此处百分比范围的含义。
R l min ⁡ = 100 % N ∑ i = 1 N { 1 ∣ o i l < ϕ l } R l max ⁡ = 100 % N ∑ i = 1 N { 1 ∣ o i l > ϕ l } (2) \begin{aligned}&R_{l}^{\min }=\frac{100 \%}{N} \sum_{i=1}^{N}\left\{1 \mid o_{i l}<\phi_{l}\right\}\\&R_{l}^{\max }=\frac{100 \%}{N} \sum_{i=1}^{N}\left\{1 \mid o_{i l}>\phi_{l}\right\}\end{aligned}\tag 2 Rlmin=N100%i=1N{1oil<ϕl}Rlmax=N100%i=1N{1oil>ϕl}(2)
图1

通过以上方法,我们可以得到连续帧中每一个像素点的亮度值的众数的百分比范围(换句话说,就是得到了每个像素点对应的 ϕ \phi ϕ 的百分比范围),我们将每个百分比范围作在图上就能得到上图(a)中的多个蓝色条,再取一根线让它穿过尽量多的蓝色条,也就是图中红色虚线,这就是我们得到的百分比值,然后我们用这个值对每一个像素点在时间(帧)维度上做百分比滤波(例如中值滤波就是百分比值为50%的百分比滤波,每个像素点用同样的值做滤波)。
不难想到,这种方法的效果与 N N N 的取值有关,作者通过简单地多次试验,得到结论:稀疏或者正常雨时 N N N 取20-100,稠密雨时 N N N 取200-300。

5.网络结构

图2

5.1.主干

将带雨滴的图片送入上图中(a)所示结构,会先通过一个卷积层(个人认为这里的卷积主要是为了调整通道数,另外也能一定程度上增大感受野),之后是三个标准的残差块,用于特征提取。接着是四个SAB,如上图中(b)所示,SAB中包含三个SARB和一个SAM。SAM生成的attention map用来指导SARB去除雨的条纹。要注意的的是,四个SAB中的SAM的权重是共享的,SARB的权重不共享。紧接着是两个串联的标准残差块和一个卷积层来重构清晰背景图。下面会放上主干部分及SAM部分的代码帮助理解。

5.2.SAM

feature map送入SAM中后,首先是进入注意力模块计算注意力权重(上图中d最上边的分支,输出有四份独立的attention map,attention map在四个分支上没有共享,在两个阶段上是共享的)。在主分支上,首先是对输入的feature map做3x3卷积,然后送入四个方向的IRNN,也就是图中带箭头的小方框。然后将IRNN的输出与注意力权重相乘,之后将四个方向的输出按通道维度相拼接,然后就重复之前从3x3卷积开始的过程,两个阶段结构相似但权重不共享。最后是一个relu激活的卷积层与一个sigmoid激活的卷积层,最后的输出作为主干中用到的attention map。以向右移动的IRNN为例,其计算方法见式(3)(至于为什么是减一不是加一,个人认为是因为在图像处理中,一般是以最左上角像素点为坐标原点,并且向右移动的IRNN处理是从左到右的)。
h i , j ← max ⁡ ( α dir h i , j − 1 + h i , j , 0 ) (3) h_{i, j} \leftarrow \max \left(\alpha_{\text {dir}} h_{i, j-1}+h_{i, j}, 0\right)\tag 3 hi,jmax(αdirhi,j1+hi,j,0)(3)

计算右移的IRNN时是将输入中的每一行看成一个输入,一行中的每一列作为不同时刻的输入。feature map送入IRNN之后先用一个3x3卷积作为输入到影藏层的权重(类似RNN中的U),这一步处理就能得到 h h h ,然后再对 h h h 应用式(3)就能得到四个方向的输出。
用通俗的话来解释式(3):就是对当前的 h i , j h_{i, j} hi,j 加上上一个 h h h h i , j − 1 h_{i, j-1} hi,j1(因为向右)的一部分。式(3)中 α dir \alpha_{\text {dir}} αdir 类似RNN中的W。
IRNN细节可以参考论文《Inside-outside net: Detecting objects in context withskip pooling and recurrent neural networks》。

在这里插入图片描述

class SAM(nn.Module):def __init__(self,in_channels,out_channels,attention=1):super(SAM,self).__init__()self.out_channels = out_channelsself.irnn1 = Spacial_IRNN(self.out_channels)self.irnn2 = Spacial_IRNN(self.out_channels)self.conv_in = conv3x3(in_channels,in_channels)self.conv2 = conv3x3(in_channels*4,in_channels)self.conv3 = conv3x3(in_channels*4,in_channels)self.relu2 = nn.ReLU(True)self.attention = attentionif self.attention:self.attention_layer = Attention(in_channels)self.conv_out = conv1x1(self.out_channels,1)self.sigmod = nn.Sigmoid()def forward(self,x):if self.attention:weight = self.attention_layer(x)out = self.conv_in(x)top_up,top_right,top_down,top_left = self.irnn1(out)# direction attentionif self.attention:top_up.mul(weight[:,0:1,:,:])top_right.mul(weight[:,1:2,:,:])top_down.mul(weight[:,2:3,:,:])top_left.mul(weight[:,3:4,:,:])out = torch.cat([top_up,top_right,top_down,top_left],dim=1)out = self.conv2(out)top_up,top_right,top_down,top_left = self.irnn2(out)# direction attentionif self.attention:top_up.mul(weight[:,0:1,:,:])top_right.mul(weight[:,1:2,:,:])top_down.mul(weight[:,2:3,:,:])top_left.mul(weight[:,3:4,:,:])out = torch.cat([top_up,top_right,top_down,top_left],dim=1)out = self.conv3(out)out = self.relu2(out)mask = self.sigmod(self.conv_out(out))return maskclass SPANet(nn.Module):def __init__(self):super(SPANet,self).__init__()self.conv_in = nn.Sequential(conv3x3(3,32),nn.ReLU(True))self.SAM1 = SAM(32,32,1)self.res_block1 = Bottleneck(32,32)self.res_block2 = Bottleneck(32,32)self.res_block3 = Bottleneck(32,32)self.res_block4 = Bottleneck(32,32)self.res_block5 = Bottleneck(32,32)self.res_block6 = Bottleneck(32,32)self.res_block7 = Bottleneck(32,32)self.res_block8 = Bottleneck(32,32)self.res_block9 = Bottleneck(32,32)self.res_block10 = Bottleneck(32,32)self.res_block11 = Bottleneck(32,32)self.res_block12 = Bottleneck(32,32)self.res_block13 = Bottleneck(32,32)self.res_block14 = Bottleneck(32,32)self.res_block15 = Bottleneck(32,32)self.res_block16 = Bottleneck(32,32)self.res_block17 = Bottleneck(32,32)self.conv_out = nn.Sequential(conv3x3(32,3))def forward(self, x):out = self.conv_in(x)out = F.relu(self.res_block1(out) + out)out = F.relu(self.res_block2(out) + out)out = F.relu(self.res_block3(out) + out)Attention1 = self.SAM1(out) out = F.relu(self.res_block4(out) * Attention1  + out)out = F.relu(self.res_block5(out) * Attention1  + out)out = F.relu(self.res_block6(out) * Attention1  + out)Attention2 = self.SAM1(out) out = F.relu(self.res_block7(out) * Attention2 + out)out = F.relu(self.res_block8(out) * Attention2 + out)out = F.relu(self.res_block9(out) * Attention2 + out)Attention3 = self.SAM1(out) out = F.relu(self.res_block10(out) * Attention3 + out)out = F.relu(self.res_block11(out) * Attention3 + out)out = F.relu(self.res_block12(out) * Attention3 + out)Attention4 = self.SAM1(out) out = F.relu(self.res_block13(out) * Attention4 + out)out = F.relu(self.res_block14(out) * Attention4 + out)out = F.relu(self.res_block15(out) * Attention4 + out)out = F.relu(self.res_block16(out) + out)out = F.relu(self.res_block17(out) + out)out = self.conv_out(out)return Attention1 , out

6.训练细节

6.1.损失函数

L total = L 1 + L S S I M + L A t t (4) \mathcal{L}_{\text {total}}=\mathcal{L}_{1}+\mathcal{L}_{S S I M}+\mathcal{L}_{A t t}\tag 4 Ltotal=L1+LSSIM+LAtt(4)

其中 L 1 \mathcal{L}_{\text {1}} L1表示重构误差。 L S S I M = 1 − SSIM ⁡ ( P , C ) \mathcal{L}_{S S I M}=1-\operatorname{SSIM}(\mathcal{P}, \mathcal{C}) LSSIM=1SSIM(P,C),用于约束结构相似性,其中 P \mathcal{P} P 是预测结果, C \mathcal{C} C 是清晰图像。 L a t t = ∥ A − M ∥ 2 2 \mathcal{L}_{a t t}=\|\mathcal{A}-\mathcal{M}\|_{2}^{2} Latt=AM22 A \mathcal{A} A 是来自第一个SAM的attention map, M \mathcal{M} M 是一个指示雨条纹位置的binary map(因为有无雨图像和有雨图像对,相减再二值化即可得到binary map),binary map中用1表示被雨覆盖的像素点,用0表示没被覆盖的像素点。
个人认为从本质上来说获取attention map是一个分类问题,使用BCE来计算 L a t t \mathcal{L}_{a t t} Latt 会不会显得更合理呢?另外,因为结合了三项指标共同构成损失函数,但我们无法量化各项指标的重要程度,所以我认为可以引入对应的权重作为超参数来权衡各项指标的重要程度。

6.2.其他细节

硬件配置:

  • E5-2640 V4
  • 4个NVIDIA Titan V

优化器:

  • Adam

batchsize:

  • 16

数据扩增:

  • 随机放缩
  • 随机裁剪

学习率:

  • 以0.005开始
  • 30k个iterations后变0.0005
  • 共40k个iterations

未经授权,禁止转载。
如有错误,欢迎指正。
专注于深度学习、计算机视觉、论文解读及复现,欢迎关注我的公众号。


在这里插入图片描述

这篇关于[论文解读-单图像去雨-SPANet] Spatial Attentive Single-Image Deraining with a High Quality Real Rain Dataset的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

解读静态资源访问static-locations和static-path-pattern

《解读静态资源访问static-locations和static-path-pattern》本文主要介绍了SpringBoot中静态资源的配置和访问方式,包括静态资源的默认前缀、默认地址、目录结构、访... 目录静态资源访问static-locations和static-path-pattern静态资源配置

MySQL中时区参数time_zone解读

《MySQL中时区参数time_zone解读》MySQL时区参数time_zone用于控制系统函数和字段的DEFAULTCURRENT_TIMESTAMP属性,修改时区可能会影响timestamp类型... 目录前言1.时区参数影响2.如何设置3.字段类型选择总结前言mysql 时区参数 time_zon

基于WinForm+Halcon实现图像缩放与交互功能

《基于WinForm+Halcon实现图像缩放与交互功能》本文主要讲述在WinForm中结合Halcon实现图像缩放、平移及实时显示灰度值等交互功能,包括初始化窗口的不同方式,以及通过特定事件添加相应... 目录前言初始化窗口添加图像缩放功能添加图像平移功能添加实时显示灰度值功能示例代码总结最后前言本文将

MySQL中的锁和MVCC机制解读

《MySQL中的锁和MVCC机制解读》MySQL事务、锁和MVCC机制是确保数据库操作原子性、一致性和隔离性的关键,事务必须遵循ACID原则,锁的类型包括表级锁、行级锁和意向锁,MVCC通过非锁定读和... 目录mysql的锁和MVCC机制事务的概念与ACID特性锁的类型及其工作机制锁的粒度与性能影响多版本

Redis过期键删除策略解读

《Redis过期键删除策略解读》Redis通过惰性删除策略和定期删除策略来管理过期键,惰性删除策略在键被访问时检查是否过期并删除,节省CPU开销但可能导致过期键滞留,定期删除策略定期扫描并删除过期键,... 目录1.Redis使用两种不同的策略来删除过期键,分别是惰性删除策略和定期删除策略1.1惰性删除策略

Redis与缓存解读

《Redis与缓存解读》文章介绍了Redis作为缓存层的优势和缺点,并分析了六种缓存更新策略,包括超时剔除、先删缓存再更新数据库、旁路缓存、先更新数据库再删缓存、先更新数据库再更新缓存、读写穿透和异步... 目录缓存缓存优缺点缓存更新策略超时剔除先删缓存再更新数据库旁路缓存(先更新数据库,再删缓存)先更新数

C#反射编程之GetConstructor()方法解读

《C#反射编程之GetConstructor()方法解读》C#中Type类的GetConstructor()方法用于获取指定类型的构造函数,该方法有多个重载版本,可以根据不同的参数获取不同特性的构造函... 目录C# GetConstructor()方法有4个重载以GetConstructor(Type[]

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

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

AI hospital 论文Idea

一、Benchmarking Large Language Models on Communicative Medical Coaching: A Dataset and a Novel System论文地址含代码 大多数现有模型和工具主要迎合以患者为中心的服务。这项工作深入探讨了LLMs在提高医疗专业人员的沟通能力。目标是构建一个模拟实践环境,人类医生(即医学学习者)可以在其中与患者代理进行医学

MCU7.keil中build产生的hex文件解读

1.hex文件大致解读 闲来无事,查看了MCU6.用keil新建项目的hex文件 用FlexHex打开 给我的第一印象是:经过软件的解释之后,发现这些数据排列地十分整齐 :02000F0080FE71:03000000020003F8:0C000300787FE4F6D8FD75810702000F3D:00000001FF 把解释后的数据当作十六进制来观察 1.每一行数据