当前位置: 首页 > news >正文

经典语义分割(二)医学图像分割模型UNet

经典语义分割(二)医学图像分割模型UNet

  • 我们之前介绍了全卷积神经网络( FCN) ,FCN是基于深度学习的语义分割算法的开山之作。

  • 今天我们介绍另一个语义分割的经典模型—UNet,它兼具轻量化与高性能,通常作为语义分割任务的基线测试模型,至今仍是如此。

  • UNet从本质上来说也属于一种全卷积神经网络模型,它的取名来源于其架构形状:模型整体呈现U形

    • 它原本是为了解决医疗影像语义分割问题的。在2015年的ISBI细胞跟踪挑战赛中,Ronnebreger等人利用UNet网络以较大优势赢得比赛。
      • 由于隐私问题、注释过程的复杂性、专家技能要求以及使用生物医学成像系统拍摄图像的高价格,在生物医学任务中,收集大量的数据很困难。
      • 而UNet能够在小样本数据集上训练并取得优秀成绩,因此各种基于其改进的网络模型广泛运用于医学图像分割任务中。特别是在肺结节、视网膜血管、皮肤病以及颅内肿瘤四大医学分割领域,出现了大量基于U-Net 改进的模型。
    • 下面几点或许能够解释为何UNet在医疗影像上表现突出:
      • UNet的U形网络结构密集融合了浅层特征与深层特征;
      • 医疗影像数据量与UNet模型体量上相匹配,有效避免了过拟合;
      • 医疗影像结构简单且固定,具有较低语义信息。
    • 不过,之后几年的发展,也证实了它是语义分割任务中的全能选手。
  • 论文地址:U-Net: Convolutional Networks for Biomedical Image Segmentation

1 UNet网络

1.1 UNet网络简述

UNet网络结构如下图所示,最主要的两个特点是:U型网络结构和Skip Connection跳层连接。

  • Unet通过跳接的U形网络结构结合了浅层特征与深层特征,用于最后的语义分割图生成。

    • 与FCN不同的是,UNet以拼接方式来结合浅层特征与深层特征;
    • 而FCN则是以相加方式来结合浅层特征与深层特征;
  • U形网络架构能够更充分地融合浅层特征和深层特征,这也是UNet性能优于FCN的主要原因。

    • 浅层特征图更倾向于表达例如点、线、边缘轮廓等基本特征单元;蕴含的空间信息更多。

    • 深层特征图更倾向于表达图像的语义信息;蕴含的空间信息更少,语义特征更多。

在这里插入图片描述

1.2 网络架构详解

UNet的主干分为对称的左右两部分:

  • 左边为特征提取网络(编码器,encoder),原始输入图像通过卷积-最大池化进行四次下采样,获得四层级的特征图;

  • 右边为特征融合网络(解码器,decoder),各层级特征图与经过反卷积获得的特征图通过跳接方式进行特征融合;

  • 最后一层通过与标签计算损失进行语义图预测。

1.2.1 DoubleConv模块

  • 从UNet网络中可以看出,不管是下采样过程还是上采样过程,每一层都会连续进行两次卷积操作,这种操作在UNet网络中重复很多次,可以单独写一个DoubleConv模块

    • 如下图所示,in_channels设为1,out_channels为64。
    • 输入图片大小为572×572,经过步长为1,padding为0的3×3卷积(注意原文没有进行填充),因此得到feature map为570×570,而非572×572,再经过一次卷积得到568×568的feature map。
    import torch.nn as nnclass DoubleConv(nn.Module):"""(convolution => [BN] => ReLU) * 2"""def __init__(self, in_channels, out_channels):super().__init__()self.double_conv = nn.Sequential(nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=0),nn.BatchNorm2d(out_channels),nn.ReLU(inplace=True),nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=0),nn.BatchNorm2d(out_channels),nn.ReLU(inplace=True))def forward(self, x):return self.double_conv(x)
    

在这里插入图片描述

1.2.2 Down模块

UNet网络一共有4次下采样过程,模块化代码如下:

  • Down模块先进行一个最大化池化,将高宽减半
  • 然后接一个DoubleConv模块,让通道加倍
  • 至此,UNet网络的左半部分的下采样过程的代码都写好了,接下来是右半部分的上采样过程
class Down(nn.Module):"""Downscaling with maxpool then double conv"""def __init__(self, in_channels, out_channels):super().__init__()self.maxpool_conv = nn.Sequential(nn.MaxPool2d(2),DoubleConv(in_channels, out_channels))def forward(self, x):return self.maxpool_conv(x)

在这里插入图片描述

1.2.3 Up模块

  • Up模块除了常规的上采样操作,还有进行特征的融合。
  • UP模块定义了两种方法:Upsample和ConvTranspose2d,即双线性插值反卷积
  • 在forward前向传播函数中,x1接收的是上采样的数据,x2接收的是特征融合的数据。特征融合方法就是先对小的feature map进行padding,再进行concat。
  • 注意:在下面代码中,上采样后会进行padding,和左边encoder相应的feature map的高宽一致,这点和图中不一样。
class Up(nn.Module):def __init__(self, in_channels, out_channels, bilinear=True):super(Up, self).__init__()if bilinear:self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)self.conv = DoubleConv(in_channels, out_channels, in_channels // 2)else:self.up = nn.ConvTranspose2d(in_channels, in_channels // 2, kernel_size=2, stride=2)self.conv = DoubleConv(in_channels, out_channels)def forward(self, x1: torch.Tensor, x2: torch.Tensor) -> torch.Tensor:x1 = self.up(x1)# [N, C, H, W]diff_y = x2.size()[2] - x1.size()[2]diff_x = x2.size()[3] - x1.size()[3]# padding_left, padding_right, padding_top, padding_bottomx1 = F.pad(x1, [diff_x // 2, diff_x - diff_x // 2,diff_y // 2, diff_y - diff_y // 2])x = torch.cat([x2, x1], dim=1)x = self.conv(x)return x

1.2.4 OutConv模块

  • 用上述的DoubleConv模块、Down模块、Up模块就可以拼出UNet的主体网络结构了。

  • UNet网络的输出需要根据分割数量,整合输出通道。

  • 下图展示的是分类为2的情况

在这里插入图片描述

class OutConv(nn.Module):def __init__(self, in_channels, out_channels):super(OutConv, self).__init__()self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=1)def forward(self, x):return self.conv(x)

1.2.5 UNet网络构建

import torch
import torch.nn as nn
import torch.nn.functional as F
# pip install torchinfo
from torchinfo import summaryclass UNet(nn.Module):def __init__(self, n_channels, n_classes, bilinear=False):super(UNet, self).__init__()self.n_channels = n_channelsself.n_classes = n_classesself.bilinear = bilinearself.inc = DoubleConv(n_channels, 64)self.down1 = Down(64, 128)self.down2 = Down(128, 256)self.down3 = Down(256, 512)self.down4 = Down(512, 1024)self.up1 = Up(1024, 512, bilinear)self.up2 = Up(512, 256, bilinear)self.up3 = Up(256, 128, bilinear)self.up4 = Up(128, 64, bilinear)self.outc = OutConv(64, n_classes)def forward(self, x):x1 = self.inc(x)x2 = self.down1(x1)x3 = self.down2(x2)x4 = self.down3(x3)x5 = self.down4(x4)x = self.up1(x5, x4)x = self.up2(x, x3)x = self.up3(x, x2)x = self.up4(x, x1)logits = self.outc(x)return logitsif __name__ == '__main__':net = UNet(n_channels=1, n_classes=1)summary(model=net, input_size=(1, 1, 572, 572))
===============================================================================================
Layer (type:depth-idx)                        Output Shape              Param #
===============================================================================================
UNet                                          [1, 1, 564, 564]          --
├─DoubleConv: 1-1                             [1, 64, 568, 568]         --
│    └─Sequential: 2-1                        [1, 64, 568, 568]         --
│    │    └─Conv2d: 3-1                       [1, 64, 570, 570]         640
│    │    └─BatchNorm2d: 3-2                  [1, 64, 570, 570]         128
│    │    └─ReLU: 3-3                         [1, 64, 570, 570]         --
│    │    └─Conv2d: 3-4                       [1, 64, 568, 568]         36,928
│    │    └─BatchNorm2d: 3-5                  [1, 64, 568, 568]         128
│    │    └─ReLU: 3-6                         [1, 64, 568, 568]         --
├─Down: 1-2                                   [1, 128, 280, 280]        --
│    └─Sequential: 2-2                        [1, 128, 280, 280]        --
│    │    └─MaxPool2d: 3-7                    [1, 64, 284, 284]         --
│    │    └─DoubleConv: 3-8                   [1, 128, 280, 280]        221,952
├─Down: 1-3                                   [1, 256, 136, 136]        --
│    └─Sequential: 2-3                        [1, 256, 136, 136]        --
│    │    └─MaxPool2d: 3-9                    [1, 128, 140, 140]        --
│    │    └─DoubleConv: 3-10                  [1, 256, 136, 136]        886,272
├─Down: 1-4                                   [1, 512, 64, 64]          --
│    └─Sequential: 2-4                        [1, 512, 64, 64]          --
│    │    └─MaxPool2d: 3-11                   [1, 256, 68, 68]          --
│    │    └─DoubleConv: 3-12                  [1, 512, 64, 64]          3,542,016
├─Down: 1-5                                   [1, 1024, 28, 28]         --
│    └─Sequential: 2-5                        [1, 1024, 28, 28]         --
│    │    └─MaxPool2d: 3-13                   [1, 512, 32, 32]          --
│    │    └─DoubleConv: 3-14                  [1, 1024, 28, 28]         14,161,920
├─Up: 1-6                                     [1, 512, 60, 60]          --
│    └─ConvTranspose2d: 2-6                   [1, 512, 56, 56]          2,097,664
│    └─DoubleConv: 2-7                        [1, 512, 60, 60]          --
│    │    └─Sequential: 3-15                  [1, 512, 60, 60]          7,080,960
├─Up: 1-7                                     [1, 256, 132, 132]        --
│    └─ConvTranspose2d: 2-8                   [1, 256, 120, 120]        524,544
│    └─DoubleConv: 2-9                        [1, 256, 132, 132]        --
│    │    └─Sequential: 3-16                  [1, 256, 132, 132]        1,771,008
├─Up: 1-8                                     [1, 128, 276, 276]        --
│    └─ConvTranspose2d: 2-10                  [1, 128, 264, 264]        131,200
│    └─DoubleConv: 2-11                       [1, 128, 276, 276]        --
│    │    └─Sequential: 3-17                  [1, 128, 276, 276]        443,136
├─Up: 1-9                                     [1, 64, 564, 564]         --
│    └─ConvTranspose2d: 2-12                  [1, 64, 552, 552]         32,832
│    └─DoubleConv: 2-13                       [1, 64, 564, 564]         --
│    │    └─Sequential: 3-18                  [1, 64, 564, 564]         110,976
├─OutConv: 1-10                               [1, 1, 564, 564]          --
│    └─Conv2d: 2-14                           [1, 1, 564, 564]          65
===============================================================================================
Total params: 31,042,369
Trainable params: 31,042,369
Non-trainable params: 0
Total mult-adds (G): 233.39
===============================================================================================
Input size (MB): 1.31
Forward/backward pass size (MB): 2683.30
Params size (MB): 124.17
Estimated Total Size (MB): 2808.78
===============================================================================================

2 针对UNet模型结构的改进

2.1 和transformers结合

在这里插入图片描述

2.2 概率设计

在这里插入图片描述

2.1.3 丰富表示增强

在这里插入图片描述

2.4 主干设计增强

在这里插入图片描述

2.5 跳过连接增强

在这里插入图片描述

2.6 bottleneck增强

在这里插入图片描述

以上改进总结来自这篇综述,感兴趣的可以参考:Medical Image Segmentation Review: The success of U-Net

相关文章:

经典语义分割(二)医学图像分割模型UNet

经典语义分割(二)医学图像分割模型UNet 我们之前介绍了全卷积神经网络( FCN) ,FCN是基于深度学习的语义分割算法的开山之作。 今天我们介绍另一个语义分割的经典模型—UNet,它兼具轻量化与高性能,通常作为语义分割任务的基线测试模型&#x…...

三天学会阿里分布式事务框架Seata-seata事务日志mysql持久化配置

锋哥原创的分布式事务框架Seata视频教程: 实战阿里分布式事务框架Seata视频教程(无废话,通俗易懂版)_哔哩哔哩_bilibili实战阿里分布式事务框架Seata视频教程(无废话,通俗易懂版)共计10条视频&…...

C语言-简单实现单片机中的malloc示例

概述 在实际项目中,有些单片机资源紧缺,需要mallloc内存,库又没有自带malloc函数时,此时,就需要手动编写,在此做个笔录。(已在项目上使用),还可进入对齐管理机制。 直接…...

外包干了2年,技术退步明显

先说一下自己的情况,研究生,19年进入广州某软件公司,干了接近4年的功能测试,今年年初,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试&#xf…...

计算机网络面经-HTTPS加密过程

前言 在上篇文章HTTPS详解一中,我已经为大家介绍了 HTTPS 的详细原理和通信流程,但总感觉少了点什么,应该是少了对安全层的针对性介绍,那么这篇文章就算是对HTTPS 详解一的补充吧。还记得这张图吧。 HTTPS 和 HTTP的区别 显然&am…...

2024年最佳硬盘!为台式电脑、NAS等产品量身定做的顶级机械硬盘

机械硬盘(HDD)可能看起来像是古老的技术,但它们仍然在许多地方提供“足够好”的性能,并且它们很容易以同等的价格提供最多的存储空间。 尽管最好的SSD将为你的操作系统和引导驱动器提供最好的体验,并提供比HDD更好的应…...

串的匹配算法——BF算法(朴素查找算法)

串的模式匹配:在主串str的pos位置查找子串sub,找到返回下标,没有找到返回-1。 1.BF算法思想 相等则继续比较,不相等则回退;回退是i退到刚才位置的下一个(i-j1);j退到0;利用子串是否…...

数据处理分类、数据仓库产生原因

个人看书学习心得及日常复习思考记录,个人随笔。 数据处理分类 操作型数据处理(基础) 操作型数据处理主要完成数据的收集、整理、存储、查询和增删改操作等,主要由一般工作人员和基层管理人员完成。 联机事务处理系统&#xff…...

【力扣100】 118.杨辉三角

添加链接描述 思路: 递推公式是[n,x][n-1,x-1][n-1,x] class Solution:def generate(self, numRows: int) -> List[List[int]]:if numRows1:return [[1]]if numRows2:return [[1],[1,1]]res[[1],[1,1]]for i in range(2,numRows): # i代表的是层数的下标&…...

好物周刊#44:现代终端工具

https://github.com/cunyu1943 村雨遥的好物周刊,记录每周看到的有价值的信息,主要针对计算机领域,每周五发布。 一、项目 1. Github-Hosts 通过修改 Hosts 解决国内 Github 经常抽风访问不到,每日更新。 2. 餐饮点餐商城 针对…...

每日五道java面试题之springMVC篇(一)

目录: 第一题. 什么是Spring MVC?简单介绍下你对Spring MVC的理解?第二题. Spring MVC的优点第三题. Spring MVC的主要组件?第四题. 什么是DispatcherServlet?第五题. 什么是Spring MVC框架的控制器? 第一题. 什么是S…...

【GStreamer】basic-tutorial-4:媒体播放状态、跳转seek操作

【目录】郭老二博文之:图像视频汇总 1、示例注释 #include <gst/gst.h>typedef struct _CustomData {GstElement *playbin; /* 本例只有一个元素*/gboolean playing; /* 是否处于播放状态? */gboolean terminate;...

IPSEC VPN 网关模式实验

要求&#xff1a;FW1与FW3建立IPSEC通道&#xff0c;保证10.0.2.0/24网段能访问192.168.1.0/24网段 因为FW1与FW3都处于边界&#xff0c;所以使用网关部署模式来建立IPSEC VPN FW1 这里选择主模式跟隧道模式 FW3与FW1配置类似&#xff0c;与FW1的源目地址反过来&#xff0c;…...

想在Vue中使用v-for来循环遍历一组对象,但只循环三次

想在Vue中使用v-for来循环遍历一组对象&#xff0c;但只想循环三次&#xff0c;你可以通过一些方法来达到这个目的。下面是一些建议的方法&#xff1a; 1. 使用数组的切片方法 如果你的对象是在一个数组中&#xff0c;你可以使用数组的slice()方法来只取数组的前三个元素。 v…...

Blazor系统教程(.net8)

Blazor系统教程 1.认识 Blazor 简单来讲&#xff0c;Blazor旨在使用C#来替代JavaScript的Web应用程序的UI框架。其主要优势有&#xff1a; 使用C#编写代码&#xff0c;这可提高应用开发和维护的效率利用现有的NET库生态系统受益于NET的性能、可靠性和安全性与新式托管平台(如…...

Day15:技术架构、Maven、Spring Initializer、Spring全家桶、Spring IoC

侧重于服务端&#xff08;后端&#xff09;&#xff0c;不在意前端&#xff0c;了解一些前端即可&#xff09; 技术架构 &#xff08;把Spring设计的更简单好用了就是Spring Boot&#xff09; 开发环境&#xff08;Maven&#xff09; Maven maven通过brew安装的目录为&#x…...

[c/c++] const

const 和 #define 的区别 ? const 和指针一块出现的时候&#xff0c;到底谁不能修改 &#xff1f; const 和 volatile 能同时修饰一个变量吗 ? const 在 c 中的作用 ? 1 const 和 #define 的区别 const 和 #define 的相同点&#xff1a; (1) 常数 const 和 #define 定…...

生成商品条码

php生成商品条码&#xff0c;编码格式为&#xff1a;EAN13 下载第三方包&#xff1a;composer require codeitnowin/barcode 生成条码代码&#xff1a; $filename \Str::random(40) . .png;$barcode new BarcodeGenerator();$barcode->setText($barCode);$barcode->s…...

langchain学习笔记(十一)

关于langchain中的memory&#xff0c;即对话历史&#xff08;message history&#xff09; 1、 Add message history (memory) | &#x1f99c;️&#x1f517; Langchain RunnableWithMessageHistory&#xff0c;可用于任何的chain中添加对话历史&#xff0c;将以下之一作为…...

LabVIEW高温摩擦磨损测试系统

LabVIEW高温摩擦磨损测试系统 介绍了一个基于LabVIEW的高温摩擦磨损测试系统的软件开发项目。该系统实现高温条件下材料摩擦磨损特性的自动化测试&#xff0c;通过精确控制和数据采集&#xff0c;为材料性能研究提供重要数据支持。 项目背景 随着材料科学的发展&#xff0c;…...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动&#xff08;如演唱会、马拉松赛事、高考中考等&#xff09;期间&#xff0c;城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例&#xff0c;暖城商圈曾因观众集中离场导致周边…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...