文章目录

  • 写在前面
  • 整体框架
  • 部分解码器
  • 整体注意力
  • 整体代码

写在前面

这是2019CVPR的一篇文章,关于显著目标检测的。提出了一种CPD框架
大概记录一下对这个文章的理解,做了一些图,如有不对,欢迎指正
文章链接:论文下载
代码地址:CPD代码

整体框架

这篇文章的模型是基于VGG16设计的

VGG16分为五个阶段

我们先说一下本文的网络模型的整体结构:

将VGG16这五个阶段得到的特征图,分别表示为fi=f1,f2,f3,f4,f5f_i=f_1,f_2,f_3,f_4,f_5fi​=f1​,f2​,f3​,f4​,f5​,大小为H/2(i−1),W/2(i−1)H/2^{(i-1)} \ \ \ , W/2^{(i-1)}H/2(i−1)   ,W/2(i−1)

作者设计了一种分支网络,利用最后两个卷积块构造了两个分支(注意力分支和检测分支)

这是作者给出的网络结构

上面是注意力分支,下面是检测分支。

在注意力分支作者设计了一个部分解码器partial decoder 来集成后三层卷积块f3,f4,f5f_3,f_4,f_5f3​,f4​,f5​提取的特征

经过这个部分解码器,生成了初始的显著图像SiS_iSi​。

我们把这个部分解码器叫做Da=ga(f3a,f4a,f5a)D_a=g_a (f_3^a,f_4^a,f_5^a)Da​=ga​(f3a​,f4a​,f5a​),解码器输出SiS_iSi​

SiS_iSi​再经过一个整体注意力模块(holistic attention module,HAM)后,得到一个增强的特征ShS_hSh​

作者说,“我们可以通过整合三层特征来获得相对精确的显著图,因此Sh有效地消除了特征f3中的干扰”

ShS_hSh​将用来在下面的检测分支细化特征f3f_3f3​

然后我们将特征图f3f_3f3​与ShS_hSh​按元素相乘,得到检测分支的细化特征f3d=f3⊙Shf_3^d =f_3\odot S_hf3d​=f3​⊙Sh​

同理,将f4f_4f4​与ShS_hSh​按元素相乘,得到f4d=f4⊙Shf_4^d =f_4\odot S_hf4d​=f4​⊙Sh​

将f5f_5f5​与ShS_hSh​按元素相乘,得到f5d=f5⊙Shf_5^d =f_5\odot S_hf5d​=f5​⊙Sh​

我们为检测分支构造另一个部分解码器Dd=gd(f3d,f4d,f5d)D_d = g_d(f_3^d,f_4^d,f_5^d)Dd​=gd​(f3d​,f4d​,f5d​),这个解码器输出最终的显著图像SdS_dSd​

使用ground truth联合训练两个分支,两个分支参数不共享。

这就是网络的整体结构和处理流程

下面我们主要详细说下两个部分解码器(partial decoder)和整体注意力模块(holistic attention module,HAM)

部分解码器

这个部分解码器,主要目的就是快速的集成三个块得到的特征图,这里作者设计了一个上下文模块conext module

主要借鉴于这篇论文

  • Receptive Field Block Net for Accurate and Fast Object Detection

这篇文章是ECCV2018年,主要贡献就是提出了RFB(Receptive Field Block)

出发点是模拟人类视觉的感受野从而加强网络的特征提取能力

做法就是将特征图通过不同卷积分支,最后再合并不同分支的结果,然后再与特征图合并。

网络结构如下图所示:

就是在ReLU之前,通过这个模块,增加感受野

左边的结构是原始的RFB,右边的结构相比RFB把3×3的conv变成了两个1×3和3×1的分支,一是减少了参数量,二是增加了更小的感受野,这样也是在模拟人类视觉系统,捕捉更小的感受野。

而本文作者在原有的RFB基础上增加了一个分支。

我画了个图

代码如下:

class RFB(nn.Module):def __init__(self, in_channel, out_channel):super(RFB, self).__init__()self.relu = nn.ReLU(True)self.branch0 = nn.Sequential(nn.Conv2d(in_channel, out_channel, 1),)self.branch1 = nn.Sequential(nn.Conv2d(in_channel, out_channel, 1),nn.Conv2d(out_channel, out_channel, kernel_size=(1, 3), padding=(0, 1)),nn.Conv2d(out_channel, out_channel, kernel_size=(3, 1), padding=(1, 0)),nn.Conv2d(out_channel, out_channel, 3, padding=3, dilation=3))self.branch2 = nn.Sequential(nn.Conv2d(in_channel, out_channel, 1),nn.Conv2d(out_channel, out_channel, kernel_size=(1, 5), padding=(0, 2)),nn.Conv2d(out_channel, out_channel, kernel_size=(5, 1), padding=(2, 0)),nn.Conv2d(out_channel, out_channel, 3, padding=5, dilation=5))self.branch3 = nn.Sequential(nn.Conv2d(in_channel, out_channel, 1),nn.Conv2d(out_channel, out_channel, kernel_size=(1, 7), padding=(0, 3)),nn.Conv2d(out_channel, out_channel, kernel_size=(7, 1), padding=(3, 0)),nn.Conv2d(out_channel, out_channel, 3, padding=7, dilation=7))self.conv_cat = nn.Conv2d(4*out_channel, out_channel, 3, padding=1)self.conv_res = nn.Conv2d(in_channel, out_channel, 1)for m in self.modules():if isinstance(m, nn.Conv2d):m.weight.data.normal_(std=0.01)m.bias.data.fill_(0)def forward(self, x):x0 = self.branch0(x)x1 = self.branch1(x)x2 = self.branch2(x)x3 = self.branch3(x)x_cat = torch.cat((x0, x1, x2, x3), 1)x_cat = self.conv_cat(x_cat)x = self.relu(x_cat + self.conv_res(x))return x

回到我们的论文中,VGG16后三个卷积块提取的特征,首先全部经过一个RFB,然后再送入我们的部分解码器

这个部分解码器具体做了些什么呢?

首先,VGG16的五个块每次通过池化都会让图像的尺寸减半。

参考文章:https://blog.csdn.net/qq_42012782/article/details/123222042

但是本论文中使用的VGG是修改过的

池化层放在卷积块的前面,并且第一个卷积块不池化。

具体的可以去看代码,此处只要知道

假设输入图片是WxW,第一个块输出尺寸等于原尺寸,第二个块原尺寸的1/2,第三个块是1/4,第四个块是1/8,第五个块是1/16

这个部分解码器,就是将x5,x4,x3拿出来。

  • 将x5记作x_1

  • 将x5与x4相乘,记作x_2。

  • 再将x5,x4,x3三个相乘,记作x_3。

  • 最后将三个特征cat起来

  • 然后经过两个卷积输出一张显著图像。

如果大小不一样,那就先上采样,扩大大小,再经过一个3x3的卷积,改变一下通道数(padding=1,stride=1,大小不变)

大小就一样了,就可以相乘了

做了个示意图,虚线框圈起来的就是部分解码器做的事情

代码如下:

class aggregation(nn.Module):def __init__(self, channel):super(aggregation, self).__init__()self.relu = nn.ReLU(True)self.upsample = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)self.conv_upsample1 = nn.Conv2d(channel, channel, 3, padding=1)self.conv_upsample2 = nn.Conv2d(channel, channel, 3, padding=1)self.conv_upsample3 = nn.Conv2d(channel, channel, 3, padding=1)self.conv_upsample4 = nn.Conv2d(channel, channel, 3, padding=1)self.conv_upsample5 = nn.Conv2d(2*channel, 2*channel, 3, padding=1)self.conv_concat2 = nn.Conv2d(2*channel, 2*channel, 3, padding=1)self.conv_concat3 = nn.Conv2d(3*channel, 3*channel, 3, padding=1)self.conv4 = nn.Conv2d(3*channel, 3*channel, 3, padding=1)self.conv5 = nn.Conv2d(3*channel, 1, 1)for m in self.modules():if isinstance(m, nn.Conv2d):m.weight.data.normal_(std=0.01)m.bias.data.fill_(0)def forward(self, x1, x2, x3):# x1: 1/16 x2: 1/8 x3: 1/4x1_1 = x1x2_1 = self.conv_upsample1(self.upsample(x1)) * x2x3_1 = self.conv_upsample2(self.upsample(self.upsample(x1))) \* self.conv_upsample3(self.upsample(x2)) * x3x2_2 = torch.cat((x2_1, self.conv_upsample4(self.upsample(x1_1))), 1)x2_2 = self.conv_concat2(x2_2)x3_2 = torch.cat((x3_1, self.conv_upsample5(self.upsample(x2_2))), 1)x3_2 = self.conv_concat3(x3_2)x = self.conv4(x3_2)x = self.conv5(x)return x

整体注意力

我们可以拿上面注意力分支得到的显著图SiS_iSi​直接与下面的特征图相乘,然后送入检测分支。但是这样的做法有利有弊,如果我们获得的SiS_iSi​是准确的,那么我们这样做可以很好的抑制干扰,但是如果我们的SiS_iSi​是错误的,那么这种做法就会得到不好的结果。

所以,本文作者提出了“整体注意力模块”Holistic Attention Module

这个模块的目的是扩大初始显著图SiS_iSi​中显著目标的面积
Sh=MAX(fmin_max(Convg(Si,k)),Si)S_h=MAX(f_{min\_max}(Conv_g(S_i,k)),S_i ) Sh​=MAX(fmin_max​(Convg​(Si​,k)),Si​)
先拿SiS_iSi​做一个高斯核为k,偏执为0的卷积运算。Convg(Si,k)Conv_g(S_i,k)Convg​(Si​,k)

fmin_max()f_{min\_max}()fmin_max​()是一个归一化函数,使值模糊映射在范围[0,1]内

MAX()MAX()MAX()是一个极大值函数,由于卷积运算会模糊Si,它倾向于增加Si显著区域的权重系数

这边代码上做的是:

  • 先将Si做一个卷积运算(高斯核为gkern(31, 4))
  • 然后做一个归一化fmin_max()f_{min\_max}()fmin_max​()
  • 然后去一个最大值
  • 最后与x3特征相乘,作为新的x3,参与检测分支

代码如下:

class HA(nn.Module):# holistic attention moduledef __init__(self):super(HA, self).__init__()gaussian_kernel = np.float32(gkern(31, 4))gaussian_kernel = gaussian_kernel[np.newaxis, np.newaxis, ...]self.gaussian_kernel = Parameter(torch.from_numpy(gaussian_kernel))def forward(self, attention, x):soft_attention = F.conv2d(attention, self.gaussian_kernel, padding=15)soft_attention = min_max_norm(soft_attention)x = torch.mul(x, soft_attention.max(attention))return x

然后下面检测分支,拿到这个x3,以及x4,x5,同样的做一个部分解码器,这个解码器与注意力分支的那个解码器操作一致。

最终输出结果SdS_dSd​

整体代码

整个网络的流程代码如下:

class CPD_VGG(nn.Module):def __init__(self, channel=32):super(CPD_VGG, self).__init__()self.vgg = B2_VGG()self.rfb3_1 = RFB(256, channel)self.rfb4_1 = RFB(512, channel)self.rfb5_1 = RFB(512, channel)self.agg1 = aggregation(channel)self.rfb3_2 = RFB(256, channel)self.rfb4_2 = RFB(512, channel)self.rfb5_2 = RFB(512, channel)self.agg2 = aggregation(channel)self.HA = HA()self.upsample = nn.Upsample(scale_factor=4, mode='bilinear', align_corners=False)def forward(self, x):x1 = self.vgg.conv1(x)x2 = self.vgg.conv2(x1)x3 = self.vgg.conv3(x2)x3_1 = x3x4_1 = self.vgg.conv4_1(x3_1)x5_1 = self.vgg.conv5_1(x4_1)x3_1 = self.rfb3_1(x3_1)x4_1 = self.rfb4_1(x4_1)x5_1 = self.rfb5_1(x5_1)attention = self.agg1(x5_1, x4_1, x3_1)x3_2 = self.HA(attention.sigmoid(), x3)x4_2 = self.vgg.conv4_2(x3_2)x5_2 = self.vgg.conv5_2(x4_2)x3_2 = self.rfb3_2(x3_2)x4_2 = self.rfb4_2(x4_2)x5_2 = self.rfb5_2(x5_2)detection = self.agg2(x5_2, x4_2, x3_2)return self.upsample(attention), self.upsample(detection)

【文献阅读】Cascaded Partial Decoder for Fast and Accurate Salient Object Detection相关推荐

  1. [论文阅读] Cascaded Partial Decoder for Fast and Accurate Salient Object Detection

    论文地址:https://arxiv.org/abs/1904.08739 代码:https://github.com/wuzhe71/CPD 发表于:CVPR'19 Abstract 现有的最先进的 ...

  2. Cascaded Partial Decoder for Fast and Accurate Salient Object Detection

    CVPR2019发布,在看参考文献时发现的一篇显著性目标检测文章 有些博客讲的还是蛮详细的,放在下面了 Cascaded Partial Decoder for Fast and Accurate S ...

  3. [CVPR2019]Cascaded Partial Decoder for Fast and Accurate Salient Object Detection

    论文地址:Cascaded Partial Decoder for Fast and Accurate Salient Object Detection 代码地址:https://github.com ...

  4. 显著目标检测之Cascaded Partial Decoder for Fast and Accurate Salient Object Detection(CPD)

    文章目录 摘要 简介 目前显著目标检测的主要方法及其存在的问题 设计CPD的基本思路 主要工作总结 具体方法分析 拟议框架的机制 整体注意力模块(Holistic Attention Module) ...

  5. Complementary Trilateral Decoder for Fast and Accurate Salient Object Detection(速读啊)内含与u-shape的对比

    今天早早起来了 吃完饭就开始干活了 十点开始读论文 所以速读适合没有很长事假你的情况下,你只需要读懂大意就可以了 QAQ,bhys,以后一定精读,好好找找里面的专业名词整理下来呜呜呜 这次策略跟以前差 ...

  6. 文献阅读20期:Transformer Transforms Salient Object Detection and Camouflaged Object Detection

    [ 文献阅读 ] Transformer Transforms Salient Object Detection and Camouflaged Object Detection [1] 表现SOTA ...

  7. 文献翻译2:AOPG: Anchor-free Oriented Proposal Generator for Object Detection

    文献翻译2:AOPG: Anchor-free Oriented Proposal Generator for Object Detection AOPG: Anchor-free Oriented ...

  8. Fast and Accurate CNN Object Detector with Scale Dependent Pooling and Cascad Rejection Classifiers

    论文的题目是<Exploit All the Layers: Fast and Accurate CNN Object Detector with Scale Dependent Pooling ...

  9. 论文阅读笔记三十三:Feature Pyramid Networks for Object Detection(FPN CVPR 2017)

    论文源址:https://arxiv.org/abs/1612.03144 代码:https://github.com/jwyang/fpn.pytorch 摘要 特征金字塔是用于不同尺寸目标检测中的 ...

最新文章

  1. Hibernate的一级缓存
  2. 【深度学习的数学】用神经网络进行图像分类时,为什么输出层的神经单元数量要跟分类数相同?可以采用二进制的表示方式么?
  3. Andorid之taskAffinity 和 FLAG_ACTIVITY_NEW_TASK
  4. oracle的知识,oracle的基本知识
  5. Adobe Illustrator CS6 绿色简体中文版下载地址
  6. pg订阅oracle修改,PostgreSQL修改数据库timezone
  7. dbcc dbreindex server sql_SQL Server性能的提高,可通过DBCC DBREINDEX重建索引
  8. 计算机网络专业学python_「非计算机专业」小白如何学好Python?
  9. C语言:某班有N名同学,每个学生的信息包括学号、姓名、三门课的成绩,从键盘输入名学生的信息,打印出N名学生三门课的平均成绩,以及最高分学生的信息(包括学号,姓名,三门课的成绩,平均分)
  10. 庖丁解牛:控件事件和数据回发概述
  11. 聚类算法(K-Means和DBSCAN)
  12. 计算机原理内存详解,【讲给小白的计算机原理】什么是内存,内存的作用?
  13. 身份证读取设备开发解决方案:3、单片机读取身份证信息的demo
  14. 下面哪个对象能代表当前的HTML文件,GD:JAVA考试选择题
  15. java 象棋人机开源码,中国象棋人机博弈程序(扁平化棋局) C语言实现
  16. 中国五笔 恶意软件清理助手
  17. IT程序猿攻城狮学习资料汇总
  18. dragonfly数据库
  19. CodeForces 760B Frodo and pillows
  20. MATLAB写入文件的操作

热门文章

  1. 虚拟魔方——使用python对普通三阶魔方进行建模
  2. SICK激光传感器Tim561使用说明
  3. HTML简单内容小结(1)
  4. 超详细JDK1.8所有版本下载地址
  5. 培训讲师和PPT技巧总结
  6. Neve 1073 话放 变压器
  7. Pocket Yoga for mac(掌上瑜伽教练软件)
  8. 冷热循环一体机膨胀阀故障排查方法
  9. 【redis】-redis单线程模型讲解
  10. 相机的 高清到底是一个什么东西