语义分割网络FCN

语义分割是一种像素级的分类,输出是与输入图像大小相同的分割图,输出图像的每个像素对应输入图像每个像素的类别,每一个像素点的灰度值都是代表当前像素点属于该类的概率。

语义分割任务需要解决的是如何把定位和分类这两个问题一起解决,毕竟语义分割就是进行逐个像素点的分类,就是把where和what结合在了一起。这时候就需要物体的一些细节特征。

在传统的CNN网络中,在最后的卷积层之后会连接上若干个全连接层,将卷积层产生的特征图(feature map)映射成为一个固定长度的特征向量。一般的CNN结构适用于图像级别的分类和回归任务,因为它们最后都期望得到输入图像的分类的概率,如ALexNet网络最后输出一个1000维的向量表示输入图像属于每一类的概率.

而FCN是对图像进行像素级的分类(也就是每个像素点都进行分类),从而解决了语义级别的图像分割问题。与经典CNN在卷积层使用全连接层得到固定长度的特征向量进行分类不同,FCN可以接受任意尺寸的输入图像,采用反卷积层对最后一个卷基层的特征图(feature map)进行上采样,使它恢复到输入图像相同的尺寸,从而可以对每一个像素都产生一个预测,同时保留了原始输入图像中的空间信息,上采样的特征图进行像素的分类。

可以看出,FCN在CNN的基础上将全连接层转换为卷积层可以使分类网输出热图。添加层和空间损失产生了端到端密集学习的高效机器。

——————————————————————————————————————————

传统的图像分类会用到全连接层,但是使用全连接层的话会破坏原有的空间信息,而且使得特征不再具备局部信息,这对于图像分割这种像素级别的预测任务来说,影响非常大。

在FCN出现之前,比较常见的做法是采用滑动窗口,每个窗口从原图像上采集一小块区域,也可以叫做patchwise,作为网络的输入。虽然这样会减少全连接层所带来的对空间信息的破坏,但是它有几个比较明显的缺陷:

  • 它计算量非常大,因为一张图需要滑动次数非常多,这样一来,相邻patch之间重合的部分会重复计算;
  • 不同patch之间相互独立,没有利用到全局的空间信息;
  • patch的设置对模型性能的影响很大,patch过小,则感受野太小,非常影响准确率,patch过大,则重复计算的区域就很多;

FCN的思想

FCN将传统CNN中的全连接层去掉,取而代之的是转化为一个个的卷积层,这样可以保持空间的位置关系。如下图所示:

整体的网络结构分为两个部分:全卷积部分和反卷积部分,丢弃了全连接层,在保持了空间信息的同时,也可以接受任意尺寸的输入图像。其中全卷积部分借用了一些经典的CNN网络,如AlexNet,VGG等等,并把最后的全连接层换成卷积,用于提取特征,形成热点图;而反卷积部分则是将小尺寸的热点图上采样得到原尺寸的语义分割图像。

输入图像经过卷积和池化之后,得到的特征图的宽高相对原图缩小了好几倍,所产生图叫做heatmap----热图,热图就是我们最重要的高维特征图图,得到高维特征之后就是最重要的一步也是最后的一步对原图像进行upsampling,也就是上采样,将图像进行放大直到到原图像的大小。

其实FCN的根本在于丢掉全连接层,保留了原本的空间信息,然后利用跳跃连接来融合特征,将定位较准的浅层信息和分类较准的深层信息结合起来。

最后的输出是类别数大小的heatmap,经过上采样恢复为原图大小的图片,为了对每个像素进行分类预测,这样才能形成最后已经进行语义分割的图像。所以在最后通过逐个像素地求其在1000张图像该像素位置的最大概率作为该像素的分类。

算法细节

FCN上采样使用的是反卷积,也叫转置卷积,文章采用了好几种上采样的结果。为了得到更好的分割效果,论文提出几种方式FCN-32s、FCN-16s、FCN-8s,如下图所示:

  • 网络对原图像进行第一次卷积与下采样后原图像缩小为1/2;
  • 之后对图像进行第二次卷积与下采样后图像缩小为1/4;
  • 重复上面过程,接着继续对图像进行第三次相同的操作,图像缩小为原图像的1/8,此时保留这个阶段的特征图;
  • 同样,在第四次后得到为原图像的1/16的特征图并保留;
  • 最后对图像进行第五次操作,缩小为原图像的1/32,然后把原来CNN操作中的全连接变成两次卷积操作,也就是conv6、conv7,但此时图像的大小还是为原图的1/32,最后生成的可以叫做heatmap了,也就是我们最终想要的结果。

最后我们可以得到1/32尺寸的heatmap,1/16尺寸的featuremap和1/8尺寸的featuremap,将1/32尺寸的heatmap进行上采样到原始尺寸,这种模型叫做FCN-32s。这种简单粗暴的方法还原了conv5中的特征,但是其中一些细节是无法恢复的,所以FCN-32s精度很差,不能够很好地还原图像原来的特征。

基于上述原因,所以自然而然的就想到将浅层网络提取的特征和深层特征相融合,这样或许能够更好地恢复其中的细节信息。于是FCN把conv4中的特征对conv7进行2倍上采样之后的特征图进行融合,然后这时候特征图的尺寸为原始图像的1/16,所以再上采样16倍就可以得到原始图像大小的特征图,这种模型叫做FCN-16s。

为了进一步恢复特征细节信息,就重复以上操作。于是乎就把pool3后的特征图对conv7上采样4倍后的特征图和对pool4进行上采样2倍的特征图进行融合,此时的特征图的大小为原始图像的1/8。融合之后再上采样8倍,就可以得到原始图像大小的特征图了,这种模型叫做FCN-8s。

代码实现

  1. backbone部分
class VGG(nn.Module):def __init__(self, pretrained=True):super(VGG, self).__init__()
​# conv1 1/2self.conv1_1 = nn.Conv2d(3, 64, kernel_size=3, padding=1)self.relu1_1 = nn.ReLU(inplace=True)self.conv1_2 = nn.Conv2d(64, 64, kernel_size=3, padding=1)self.relu1_2 = nn.ReLU(inplace=True)self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
​# conv2 1/4self.conv2_1 = nn.Conv2d(64, 128, kernel_size=3, padding=1)self.relu2_1 = nn.ReLU(inplace=True)self.conv2_2 = nn.Conv2d(128, 128, kernel_size=3, padding=1)self.relu2_2 = nn.ReLU(inplace=True)self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
​# conv3 1/8self.conv3_1 = nn.Conv2d(128, 256, kernel_size=3, padding=1)self.relu3_1 = nn.ReLU(inplace=True)self.conv3_2 = nn.Conv2d(256, 256, kernel_size=3, padding=1)self.relu3_2 = nn.ReLU(inplace=True)self.conv3_3 = nn.Conv2d(256, 256, kernel_size=3, padding=1)self.relu3_3 = nn.ReLU(inplace=True)self.pool3 = nn.MaxPool2d(kernel_size=2, stride=2)
​# conv4 1/16self.conv4_1 = nn.Conv2d(256, 512, kernel_size=3, padding=1)self.relu4_1 = nn.ReLU(inplace=True)self.conv4_2 = nn.Conv2d(512, 512, kernel_size=3, padding=1)self.relu4_2 = nn.ReLU(inplace=True)self.conv4_3 = nn.Conv2d(512, 512, kernel_size=3, padding=1)self.relu4_3 = nn.ReLU(inplace=True)self.pool4 = nn.MaxPool2d(kernel_size=2, stride=2)
​# conv5 1/32self.conv5_1 = nn.Conv2d(512, 512, kernel_size=3, padding=1)self.relu5_1 = nn.ReLU(inplace=True)self.conv5_2 = nn.Conv2d(512, 512, kernel_size=3, padding=1)self.relu5_2 = nn.ReLU(inplace=True)self.conv5_3 = nn.Conv2d(512, 512, kernel_size=3, padding=1)self.relu5_3 = nn.ReLU(inplace=True)self.pool5 = nn.MaxPool2d(kernel_size=2, stride=2)# load pretrained params from torchvision.models.vgg16(pretrained=True)if pretrained:pretrained_model = vgg16(pretrained=pretrained)pretrained_params = pretrained_model.state_dict()keys = list(pretrained_params.keys())new_dict = {}for index, key in enumerate(self.state_dict().keys()):new_dict[key] = pretrained_params[keys[index]]self.load_state_dict(new_dict)
​def forward(self, x):x = self.relu1_1(self.conv1_1(x))x = self.relu1_2(self.conv1_2(x))x = self.pool1(x)pool1 = x
​x = self.relu2_1(self.conv2_1(x))x = self.relu2_2(self.conv2_2(x))x = self.pool2(x)pool2 = x
​x = self.relu3_1(self.conv3_1(x))x = self.relu3_2(self.conv3_2(x))x = self.relu3_3(self.conv3_3(x))x = self.pool3(x)pool3 = x
​x = self.relu4_1(self.conv4_1(x))x = self.relu4_2(self.conv4_2(x))x = self.relu4_3(self.conv4_3(x))x = self.pool4(x)pool4 = x
​x = self.relu5_1(self.conv5_1(x))x = self.relu5_2(self.conv5_2(x))x = self.relu5_3(self.conv5_3(x))x = self.pool5(x)pool5 = x
​return pool1, pool2, pool3, pool4, pool5
  1. FCN-8s部分
class FCNs(nn.Module):def __init__(self, num_classes, backbone="vgg"):super(FCNs, self).__init__()self.num_classes = num_classesif backbone == "vgg":self.features = VGG()
​# deconv1 1/16self.deconv1 = nn.ConvTranspose2d(512, 512, kernel_size=3, stride=2, padding=1, output_padding=1)self.bn1 = nn.BatchNorm2d(512)self.relu1 = nn.ReLU()
​# deconv1 1/8self.deconv2 = nn.ConvTranspose2d(512, 256, kernel_size=3, stride=2, padding=1, output_padding=1)self.bn2 = nn.BatchNorm2d(256)self.relu2 = nn.ReLU()
​# deconv1 1/4self.deconv3 = nn.ConvTranspose2d(256, 128, kernel_size=3, stride=2, padding=1, output_padding=1)self.bn3 = nn.BatchNorm2d(128)self.relu3 = nn.ReLU()
​# deconv1 1/2self.deconv4 = nn.ConvTranspose2d(128, 64, kernel_size=3, stride=2, padding=1, output_padding=1)self.bn4 = nn.BatchNorm2d(64)self.relu4 = nn.ReLU()
​# deconv1 1/1self.deconv5 = nn.ConvTranspose2d(64, 32, kernel_size=3, stride=2, padding=1, output_padding=1)self.bn5 = nn.BatchNorm2d(32)self.relu5 = nn.ReLU()
​self.classifier = nn.Conv2d(32, num_classes, kernel_size=1)
​def forward(self, x):features = self.features(x)
​y = self.bn1(self.relu1(self.deconv1(features[4])) + features[3])
​y = self.bn2(self.relu2(self.deconv2(y)) + features[2])
​y = self.bn3(self.relu3(self.deconv3(y)))
​y = self.bn4(self.relu4(self.deconv4(y)))
​y = self.bn5(self.relu5(self.deconv5(y)))
​y = self.classifier(y)
​return y

文末

图中可以看出,FCN-8s的细节特征最为丰富,分割效果良好。同时论文中也尝试了将pool2、pool1的特征图进行融合,但是效果提升不明显,所以最终效果最好的就是FCN-8s。

FCN仍有一些缺点,比如:

  • 得到的效果还不够好,对细节不够敏感;
  • 没有考虑像素与像素之间的关系,缺乏空间一致性等。

其实FCN最大的贡献其实是提供了用于分割的一种全新思路,相比于传统做法它更加高效,因为避免了由于使用像素块而带来的重复存储和计算卷积的问题。

之后的分割算法基本上都是基于全卷积的方式来进行改进、优化的。

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

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

相关文章

嘉里大荣物流与极智嘉再度携手,合作助力物流服务高效升级

近日,全球仓储机器人引领者极智嘉(Geek)与3PL知名企业嘉里大荣物流联合宣布,双方再度携手,6周内共建全新自动化订单履行中心,赋能国际时尚运动品牌New Balance加速B2B和B2C订单交付,为其客户提供更高效便捷的物流服务。…

江科大STM32学习笔记(上)

STM32F103xx 前言外设篇GPIO输出GPIO位结构GPIO模式外设的GPIO配置查看实战1: 如何进行基本的GPIO输入输出 OLED显示屏及调试Keil的调试模式演示 EXTI外部中断NVIC基本结构EXTI结构代码实战2:如何使用中断和对射式红外传感器&旋转编码器 TIM&…

【flink番外篇】1、flink的23种常用算子介绍及详细示例(1)- map、flatmap和filter

Flink 系列文章 1、Flink 专栏等系列综合文章链接 文章目录 Flink 系列文章一、Flink的23种算子说明及示例1、maven依赖2、java bean3、map4、flatmap5、Filter 本文主要介绍Flink 的3种常用的operator(map、flatmap和filter)及以具体可运行示例进行说明…

Mysql的索引详解

1.索引的分类 1.按照功能来分,可以分为主键索引、唯一索引、普通索引、全文索引 2.按照索引字段个数来分,可以分为单列索引、联合索引 3.按照物理实现方式来分,可以聚簇索引、非聚簇索引 2.适合添加索引的场景 1.具有唯一性约束的字段。 2…

医药行业:轻松学会超低温冰箱技能

超低温冰箱在医疗、科研和生物领域中扮演着至关重要的角色,用于存储和保护对温度极为敏感的样品和药品。 然而,由于这些冰箱内的温度波动可能导致样品的损坏,因此对超低温冰箱的监控变得至关重要。 客户案例 医疗研究机构 上海某医疗研究机…

YOLOv7独家原创改进:创新自研CPMS注意力,多尺度通道注意力具+多尺度深度可分离卷积空间注意力,全面升级CBAM

💡💡💡本文自研创新改进:自研CPMS, 多尺度通道注意力具+多尺度深度可分离卷积空间注意力,全面升级CBAM 1)作为注意力CPMS使用; 推荐指数:五星 CPMS | 亲测在多个数据集能够实现涨点,对标CBAM。 收录 YOLOv7原创自研 https://blog.csdn.net/m0_63774211/ca…

外包干了4年,技术退步太明显了。。。。。

先说一下自己的情况,本科生生,18年通过校招进入武汉某软件公司,干了接近4年的功能测试,今年国庆,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测…

Docker容器(一)概述

一、虚拟化概述 1.1引⼊虚拟化技术的必要性 服务器只有5%的时间是在⼯作的;在其它时间服务器都处于“休眠”状态. 虚拟化前 每台主机⼀个操作系统; 软硬件紧密结合; 在同⼀个主机上运⾏多个应⽤程序通常会遭遇冲突; 系统的资源利⽤率低; 硬件成本⾼昂⽽且不够灵活…

金蝶云星空单据体明细权限和表单插件操作事件的先后顺序

文章目录 金蝶云星空单据体明细权限和表单插件操作事件的先后顺序顺序说明结论 金蝶云星空单据体明细权限和表单插件操作事件的先后顺序 顺序说明 先分录菜单单击事件EntryBarItemClick 再验权 后表单操作执行事件BeforeDoOperation 结论 如果是需要鉴权通过才允许操作的逻辑…

万界星空科技仓库管理wms系统

企业在管理库存时,尤其是生产制造企业,使用传统方式比如纸笔、Excel 管理库存,由于工具和信息化存在局限,导致在管理库存时出现如下问题: 1、通过纸笔记录出入库申请,人为手动计算易出错,数据易…

全球与中国HDPE管道市场:增长趋势、竞争格局与前景展望

快速成长的人口、快速的经济成长和工业发展增加了对可靠供水系统的需求。工业需要为制造流程、冷却系统和卫生目的提供可靠的水供应。随着国家的发展,它们更加重视基础设施,包括供水系统,以支持工业成长。HDPE管道广泛应用于饮用水和灌溉的配…

医院不良事件报告系统源码带鱼骨图分析

医院不良事件上报系统通过 “事前的人员知识培训管理和制度落地促进”、“事中的事件上报和跟进处理”、 以及 “事后的原因分析和工作持续优化”,结合预存上百套已正在使用的模板,帮助医院从对护理事件、药品事件、医疗器械事件、医院感染事件、输血事件…

CentOS系列:【Linux】CentOS7操作系统安装nginx实战(多种方法,超详细)

CentOS7操作系统安装nginx实战(多种方法,超详细) 一. 实验环境二. 使用yum安装nginx2.1 添加yum源2.1.1 使用官网提供的源地址(方法一)1. 找到官网的源2. 使用rpm -ivh 进行安装3. 安装完成之后查看源: 2.1…

三十七、XA模式

目录 一、XA模式原理 在XA模式中,一个事务通常被分为两个阶段: 二、Seata的XA模式 RM一阶段的工作: TC一阶段的工作: RM二阶段的工作: 1、优点: 2、缺点: 三、实现XA模式 1、修改yml文…

数据结构实验任务六 :基于 Dijsktra 算法的最短路径求解

本次代码为实验六:基于 Dijsktra 算法的最短路径求解实现。本实验的重点在于对于Dijsktra算法的理解。有关Dijsktra的资料可以参考有关博文: 图论:Dijkstra算法——最详细的分析,图文并茂,一次看懂!-CSDN博客 以下附上…

Vision Transformer

Vision Transformer详解-CSDN博客 视频:11.1 Vision Transformer(vit)网络详解_哔哩哔哩_bilibili Vision Transformer学习笔记_linear projection of flattened patches-CSDN博客 一、embedding 层 对于标准的Transformer模块,要求输入的是token (向量…

win10使用copilot(尝试中)

一、 Microsoft account | Sign In or Create Your Account Today – Microsoft 一路next全部点好【1】 二、 查看当前win10的版本,cmd输入命令winver 三、 修改区域为美国 四、更新和安全 Reference 【1】完美|在 Win10 强行开启 Win11 的独有功能…

蓝桥杯每日一题2023.12.4

题目描述 竞赛中心 - 蓝桥云课 (lanqiao.cn) 题目分析 本题使用树型DP,蓝桥杯官网出现了一个点的错误,但实际答案是正确的 状态表示:f[u]:在以u为根的子树中包含u的所有联通块的权值的最大值 假设s1,s2,…sk 是u的…

腾讯云双十二优惠活动有哪些?详细攻略来了!

腾讯云作为全球领先的云计算服务提供商,一直在为各行各业的用户提供优质、高效、稳定的云计算服务。双十二即将来临,腾讯云也为大家准备了一系列丰富多彩的优惠活动。那么,这些优惠活动究竟有哪些?详细攻略在此,一起来…

jsp高校教师调课管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 高校教师调课管理系统是一套完善的java web信息管理系统,对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发,数据库为Mysq…