DDANet: Dual Decoder Attention Network for Automatic Polyp Segmentation

news2024/11/29 4:46:17

双decoder用于息肉分割。文章的创新点在与使用了双分支的decoder,单encoder的结构。decoder的第二个分支会产生注意力map,在代码中体现为输出通道为1。这个和之前看的confidence map很像。
看一下文章的结构图:
在这里插入图片描述
在decoder中,第二个分支生成注意力图,其实shared encoder的跳连接上下两个是一样的,在代码中可以看到,稍后分析。
encoder,decoder的构成:
在这里插入图片描述
他这里使用的encoder不是原始的resnet,但是使用了resnet的思想,且在两个3x3卷积之后,加入了通道注意力,这个在ESANet中RGB和Depth融合方法中也有用到。
在decoder使用的4倍转置卷积,和encoder的特征进行concat,和RedNet的跳连接结构,和上采样结构都很像。
实验:
医学图像的数据集,不太了解。
------------------------------------------------------分割线-------------------------------------------------------------------------------------------------------------------------
代码:


import torch
import torch.nn as nn
import torchvision.models as models

class SELayer(nn.Module):
    def __init__(self, channel, reduction=16):
        super().__init__()
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        self.fc = nn.Sequential(
            nn.Linear(channel, int(channel / reduction), bias=False),
            nn.ReLU(inplace=True),
            nn.Linear(int(channel / reduction), channel, bias=False),
            nn.Sigmoid()
        )

    def forward(self, x):
        b, c, _, _ = x.size()
        y = self.avg_pool(x).view(b, c)
        y = self.fc(y).view(b, c, 1, 1)
        return x * y.expand_as(x)

class ResidualBlock(nn.Module):
    def __init__(self, in_c, out_c):
        super(ResidualBlock, self).__init__()

        self.conv1 = nn.Conv2d(in_c, out_c, kernel_size=3, padding=1)
        self.bn1 = nn.BatchNorm2d(out_c)

        self.conv2 = nn.Conv2d(out_c, out_c, kernel_size=3, padding=1)
        self.bn2 = nn.BatchNorm2d(out_c)

        self.conv3 = nn.Conv2d(in_c, out_c, kernel_size=1, padding=0)
        self.bn3 = nn.BatchNorm2d(out_c)
        self.se = SELayer(out_c)

        self.relu = nn.ReLU(inplace=True)

    def forward(self, x):
        x1 = self.conv1(x)
        x1 = self.bn1(x1)
        x1 = self.relu(x1)

        x2 = self.conv2(x1)
        x2 = self.bn2(x2)

        x3 = self.conv3(x)
        x3 = self.bn3(x3)
        x3 = self.se(x3)

        x4 = x2 + x3
        x4 = self.relu(x4)

        return x4

class EncoderBlock(nn.Module):
    def __init__(self, in_c, out_c):
        super(EncoderBlock, self).__init__()

        self.r1 = ResidualBlock(in_c, out_c)
        self.r2 = ResidualBlock(out_c, out_c)
        self.pool = nn.MaxPool2d(2, stride=2)

    def forward(self, x):
        x = self.r1(x)
        x = self.r2(x)
        p = self.pool(x)

        return x, p

class DecoderBlock(nn.Module):
    def __init__(self, in_c, out_c):
        super(DecoderBlock, self).__init__()

        self.upsample = nn.ConvTranspose2d(in_c, out_c, kernel_size=4, stride=2, padding=1)
        self.r1 = ResidualBlock(in_c+out_c, out_c)
        self.r2 = ResidualBlock(out_c, out_c)

    def forward(self, x, s):
        x = self.upsample(x)
        x = torch.cat([x, s], axis=1)
        x = self.r1(x)
        x = self.r2(x)

        return x


class CompNet(nn.Module):
    def __init__(self):
        super(CompNet, self).__init__()

        """ Shared Encoder """
        self.e1 = EncoderBlock(3, 32)
        self.e2 = EncoderBlock(32, 64)
        self.e3 = EncoderBlock(64, 128)
        self.e4 = EncoderBlock(128, 256)

        """ Decoder: Segmentation """
        self.s1 = DecoderBlock(256, 128)
        self.s2 = DecoderBlock(128, 64)
        self.s3 = DecoderBlock(64, 32)
        self.s4 = DecoderBlock(32, 16)

        """ Decoder: Autoencoder """
        self.a1 = DecoderBlock(256, 128)
        self.a2 = DecoderBlock(128, 64)
        self.a3 = DecoderBlock(64, 32)
        self.a4 = DecoderBlock(32, 16)

        """ Autoencoder attention map """
        self.m1 = nn.Sequential(
            nn.Conv2d(128, 1, kernel_size=1, padding=0),
            nn.Sigmoid()
        )
        self.m2 = nn.Sequential(
            nn.Conv2d(64, 1, kernel_size=1, padding=0),
            nn.Sigmoid()
        )
        self.m3 = nn.Sequential(
            nn.Conv2d(32, 1, kernel_size=1, padding=0),
            nn.Sigmoid()
        )
        self.m4 = nn.Sequential(
            nn.Conv2d(16, 1, kernel_size=1, padding=0),
            nn.Sigmoid()
        )

        """ Output """
        self.output1 = nn.Conv2d(16, 1, kernel_size=1, padding=0)
        self.output2 = nn.Conv2d(16, 1, kernel_size=1, padding=0)

    def forward(self, x):
        """ Encoder """
        x1, p1 = self.e1(x)
        x2, p2 = self.e2(p1)
        x3, p3 = self.e3(p2)
        x4, p4 = self.e4(p3)

        """ Decoder 1 """
        s1 = self.s1(p4, x4)
        a1 = self.a1(p4, x4)
        m1 = self.m1(a1)
        x5 = s1 * m1

        """ Decoder 2 """
        s2 = self.s2(x5, x3)
        a2 = self.a2(a1, x3)
        m2 = self.m2(a2)
        x6 = s2 * m2

        """ Decoder 3 """
        s3 = self.s3(x6, x2)
        a3 = self.a3(a2, x2)
        m3 = self.m3(a3)
        x7 = s3 * m3

        """ Decoder 4 """
        s4 = self.s4(x7, x1)
        a4 = self.a4(a3, x1)
        m4 = self.m4(a4)
        x8 = s4 * m4

        """ Output """
        out1 = self.output1(x8)
        out2 = self.output2(a4)

        return out1, out2

if __name__ == "__main__":
    x = torch.rand((1, 3, 512, 512))

    model = CompNet()
    y1, y2 = model.forward(x)

我们直接看forward函数:
1:首先就是输入的x经过四个encoder block
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
每个block中包含的residual block:


class ResidualBlock(nn.Module):
    def __init__(self, in_c, out_c):
        super(ResidualBlock, self).__init__()

        self.conv1 = nn.Conv2d(in_c, out_c, kernel_size=3, padding=1)
        self.bn1 = nn.BatchNorm2d(out_c)

        self.conv2 = nn.Conv2d(out_c, out_c, kernel_size=3, padding=1)
        self.bn2 = nn.BatchNorm2d(out_c)

        self.conv3 = nn.Conv2d(in_c, out_c, kernel_size=1, padding=0)
        self.bn3 = nn.BatchNorm2d(out_c)
        self.se = SELayer(out_c)

        self.relu = nn.ReLU(inplace=True)

    def forward(self, x):
        x1 = self.conv1(x)
        x1 = self.bn1(x1)
        x1 = self.relu(x1)

        x2 = self.conv2(x1)
        x2 = self.bn2(x2)

        x3 = self.conv3(x)
        x3 = self.bn3(x3)
        x3 = self.se(x3)

        x4 = x2 + x3
        x4 = self.relu(x4)

        return x4

以第一个为例:输入通道为3,输出通道为32.
经过两个3x3卷积,然后一个1x1卷积的残差连接,注意,这里经过1x1卷积之后才经过SElayer。和图中画的有些不同。
SELayer:和之前看的通道注意力一样,首先经过平均池化,然后经过两个线性层,最后与原始的x相乘得到最终的结果。
在这里插入图片描述
每个encoder block包含两个残差块,一个2x2最大池化。注意这里返回的是x,用来跳连接的。 x, p对应于x1, p1 。同理x2为p1。encoder结束后,两个输出分别经过两个decoder分支。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在decoder block中首先通过转置卷积进行上采样四倍,然后和跳连接相concat,再经过两个残差块。而另一条分支的处理和这一条一样的。
在这里插入图片描述
不同的是第二条分支产生的结果是一个注意力图。通过一个卷积生成通道为1的attention map,这里医学分割图的最终结果就是单通道,如果是其他的数据集即时多通道,即这里其实相当于之前说的置信度图。
在这里插入图片描述
生成的置信度图与第一条分支产生的结果相乘,这样执行四次。
在这里插入图片描述
最终的输出经过两个通道为1的卷积,即最终的分割图。
在这里插入图片描述
整个网络框架可以简化为:
在这里插入图片描述

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

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

相关文章

小米手机买什么蓝牙耳机好?适配小米手机的蓝牙耳机推荐

作为一位音乐爱好者,拥有一款好的耳机是非常重要的。无线蓝牙耳机的出现就为喜欢听音乐的伙伴们提供了一个很好的选择,既不会有耳朵的胀痛,也不会影响享受音乐的体验,小米手机近几年也是深受大众欢迎,因此,…

【数据结构Note5】-二叉排序树 BST和平衡二叉树AVL

二叉排序树BST 二叉排序树&#xff0c;又称二叉查找树&#xff08;BST&#xff0c;Binary Search Tree&#xff09; 二叉排序树是左子树节点值<根节点值<右子树节点值的二叉树 所以对二叉排序树进行中序遍历会得到一个递增的序列&#xff08;左子树-根-右子树&#xff09…

nginx实战

目录反向代理去掉前缀场景1&#xff1a;去掉一层前缀场景2&#xff1a;去掉两层前缀返回固定json增加后缀映射情况一情况二情况三情况四正向代理负载均衡正则匹配~ /item/(\d)反向代理 通过Nginx我们可以实现反向代理&#xff0c;这也是我在项目中使用到的一个功能&#xff0c…

20221122非累加的m3u8的ts切片列表的补全步骤

20221122非累加的m3u8的ts切片列表的补全步骤 2022/11/22 19:07 https://apppy87xs3b3950.h5.xiaoeknow.com/v2/course/alive/l_6374b6d0e4b0edc794f61031?type2&app_idapppy87xs3b3950&availabletrue&share_user_idu_61333670aabd8_rizBVXTg2F&share_type5&a…

从零开始的图像语义分割:FCN快速复现教程(Pytorch+CityScapes数据集)

从零开始的图像语义分割&#xff1a;FCN复现教程&#xff08;PytorchCityScapes数据集&#xff09;前言一、图像分割开山之作FCN二、代码及数据集获取1.源项目代码2.CityScapes数据集三、代码复现1.数据预处理2.代码修改3.运行结果总结参考网站前言 摆了两周&#xff0c;突然觉…

【第五部分 | JS WebAPI】6:PC端网页特效与本地存储

目录 | 概述 | PC端网页特效之三大系列 1-1 elementObj . offsetXXX 属性 1-2 elementObj . style 和 offset 的区别 1-3 案例&#xff1a;获取鼠标在某个盒子内的位置 2-1 elementObj . clientXXX 属性 3-1 elementObj . scrollXXX 属性 三大系列总结 | 动画函数封装 …

LeetCode1005. K 次取反后最大化的数组和

1 题目描述 给你一个整数数组 nums 和一个整数 k &#xff0c;按以下方法修改该数组&#xff1a; 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。 重复这个过程恰好 k 次。可以多次选择同一个下标 i 。 以这种方式修改数组后&#xff0c;返回数组 可能的最大和 。 示例 1&a…

弹簧(压簧)力度计算与设计

弹簧&#xff08;压簧&#xff09;力度计算与设计弹簧的种类什么是弹性系数弹簧的材料常用材料与用途弹性系数与哪些因素有关弹簧力度设计与计算弹簧收尾设计弹簧是一种利用弹性来工作的机械零件。一般用弹簧钢制成。利用它的弹性可以控制机件的运动、缓和冲击或震动、储蓄能量…

[附源码]java毕业设计校园疫情防控管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

BMN:Boundary-matching network for temporal action proposal generation

Video Analysis 相关领域解读之Temporal Action Detection(时序行为检测) - 知乎本文投稿于 极视角 公众号&#xff0c;链接为 文章链接. 上一篇 Video Analysis相关领域解读之Action Recognition(行为识别) - 知乎专栏介绍了 Action Recognition 领域的研究进展。Action Recog…

转铁蛋白修饰纳米载体(纳米颗粒,介孔硅,四氧化三铁,二氧化硅等)

转铁蛋白又名运铁蛋白&#xff08;Transferrin&#xff0c;TRF、Tf&#xff09;&#xff0c;负责运载由消化管吸收的铁和由红细胞降解释放的铁。以三价铁复合物&#xff08;Tf-Fe3&#xff09;的形式进入骨髓中&#xff0c;供成熟红细胞的生成。转铁蛋白主要存在于血浆中&#…

vue 动态表单优秀案例

不同的下拉框 就会显示不同的表单&#xff0c;表单配置是灵活匹配的&#xff0c;还有就是 一定要知道都有哪些类型的数据做到兼容起来。 app.vue <template><a-select v-model:value"FormDataSelect" :options"FormDataSelectList" /><a-fo…

相控阵天线(五):稀疏阵列(概率密度稀疏法、多阶密度加权法、迭代傅里叶(IFT)法)

目录简介稀疏线阵概率密度稀疏法多阶密度加权法迭代傅里叶(IFT)综合法对称分布稀疏阵列建模仿真简介 稀疏阵是在不明显改变阵列波束宽度的情况下去掉一些阵元&#xff0c;可以用满阵列的几分之一的阵元构造一个减低了增益的高方向性阵列&#xff0c;符合大型阵列设计中降低成本…

【C++】哈希算法

目录 1.哈希映射 1.1哈希的概念 1.2哈希冲突 1.3哈希函数 1.31直接定值法 1.32除留余数法 2.解决哈希冲突 2.1闭散列法 2.11线性探测 2.12二次探测 3代码实现 3.1状态&#xff1a; 3.2创建哈希节点类 3.21哈希表扩容&#xff1a; 3.3数据插入 3.4查找与删除 3.…

数据可视化之设计经验分享:轻松三步教你学会制作数据可视化大屏思路

当看到屏幕上一个个炫酷&#xff0c;具有科技感的数据大屏时&#xff0c;很多人都会好奇这是怎么做出来的。自己在制作大屏时明明按着需求做了&#xff0c;可是做出来后总是觉得画面不好看&#xff0c;不够炫&#xff0c;感觉很糟糕。 那要如何才能设计那样的数据可视化大屏呢…

JS 的新一代日期/时间 API Temporal

众所周知&#xff0c;JS的Date是出了名的难用&#xff0c;一直以来我们都在使用momentjs&#xff0c;dayjs等第三方库来处理日期和时间格式&#xff0c;于是 TC39 组织开始了对 Date 的升级改造&#xff0c;他们找到了 moment.js 库的作者&#xff0c;Maggie &#xff0c;由她来…

【深度学习】实验5答案:滴滴出行-交通场景目标检测

DL_class 学堂在线《深度学习》实验课代码报告&#xff08;其中实验1和实验6有配套PPT&#xff09;&#xff0c;授课老师为胡晓林老师。课程链接&#xff1a;https://www.xuetangx.com/training/DP080910033751/619488?channeli.area.manual_search。 持续更新中。 所有代码…

代码随想录刷题| 01背包理论基础 LeetCode 416. 分割等和子集

目录 01背包理论基础 二维dp数组 1、确定dp数组以及下标的含义 2、确定递推公式 3、dp数组如何初始化 4、确定遍历顺序 5、打印dp数组 最终代码 一维dp数组 1、确定dp数组的定义 2、确定递推公式 3、初始化dp数组 4、遍历顺序 5、打印dp数组 最终代码 416. 分割…

一次搞懂SpringBoot核心原理:自动配置、事件驱动、Condition

前言 SpringBoot是Spring的包装&#xff0c;通过自动配置使得SpringBoot可以做到开箱即用&#xff0c;上手成本非常低&#xff0c;但是学习其实现原理的成本大大增加&#xff0c;需要先了解熟悉Spring原理。如果还不清楚Spring原理的&#xff0c;可以先查看博主之前的文章&…

Vue实现简易购物车功能

用Vue写一个列表案例&#xff0c;页面布局什么的dom&#xff0c;不需要自己事先全部排好&#xff0c;而是通过li遍历&#xff0c;把数据遍历出来&#xff1b;先定义好div标签&#xff0c;li根据数组的长度datalist进行遍历&#xff0c;图片的链接要用“&#xff1a;”&#xff…