摘要

U-Net是基于卷积神经网络(CNN)体系结构设计而成的,由Olaf Ronneberger,Phillip Fischer和Thomas Brox于2015年首次提出应用于计算机视觉领域完成语义分割任务。(其发表U-Net的作者建议该模型能够很好的应用于生物医学图像分割)。而本文将从U-Net的应用领域、模型原理、具体应用三个方面具体概述U-Net神经网络的实现方式及应用技术。最后,本文基于keras神经网络框架设计并实现U-Net模型在EM神经元数据集上的 分割应用,其实现结果现实,U-Net在EM 堆栈神经元的分割任务上具有较好的实验结果。

1.计算机视觉任务概述

1.1 语义分割解析(Semantic Segmentation)

1.1.1 定义

图像语义分割(Semantic Segmentation)是图像处理和是机器视觉技术中关于图像理解的重要一环,也是 AI 领域中一个重要的分支。语义分割即是对图像中每一个像素点进行分类,确定每个点的类别(如属于背景、人或车等),从而进行区域划分。目前,语义分割已经被广泛应用于自动驾驶、无人机落点判定等场景中。

1.1.2 实例解释

图像分类

举个例子进行描述:

在图像领域,我们常见的任务是对图像的分类任务,该任务是较为粗粒度的来识别和理解图像。即是我们给定一张图像,我们期望模型输出该张图像所属的类别(离散标签),而在图像分类任务中,我们假设图像中只有一个(而不是多个)对象。描述如下图:

但是,语义分割则在图像分类任务上更为细化。其需要经历三个过程:图像定位、目标检测、语义分割(及实例分割)。其中,目标检测( Object detection)不仅需要提供图像中物体的类别,还需要提供物体的位置(bounding box)。语义分割( Semantic segmentation)需要预测出输入图像的每一个像素点属于哪一类的标签。实例分割( instance segmentation)在语义分割的基础上,还需要区分出同一类不同的个体。

图像定位

在与离散标签一起输出时(完成分类任务过程中),我们还期望模型能够准确定位图像中存在该物体的位置。这种定位通常使用边界框来实现,边界框可以通过一些关于图像边界的数值参数来识别。其如下图所示:

目标检测

与图像定位不同的是,目标检测需要判断图像中物体的类别,还判断指出提供图像中物体的具体位置(bounding box)。即是现在图像不再局限于只有一个对象,而是可以包含多个对象。任务是对图像中的所有对象进行分类和定位。这里再次使用边界框的概念进行定位。其如下图所示:

语义分割

语义分割通过对每个像素进行密集的预测、推断标签来实现细粒度的推理,从而使每个像素都被标记为其封闭对象矿石区域的类别。即是需要判断图像每个像素点的类别,进行精确分割。图像语义分割是像素级别的!因为我们要对图像中的每个像素进行预测,所以这个任务通常被称为密集预测。其如下图所示:

实例分割

实例分割比语义分割是更为细致的下游任务,其中与像素级的分类任务处理方式一样,我们希望模型分别对类的每个实例进行分类。例如,图中有 3 个人,严格来说是“Person”类的 3 个实例。所有这3个分别分类(以不同的颜色)。其如下图所示:

目标检测、语义分割、实例分割三个像素级别的分类任务,其区别如下图所示。

1.2 应用领域

1.2.1 自动驾驶汽车

自动驾驶是一项极为复杂的深度学习应用任务,其需要模型在不断变化的环境中进行感知、规划和执行相关任务(既是汽车避免路障等问题)。因为人生安全是最重要的,所以这项任务需要最为精准的模型运行效果。语义分割可提供有关道路上的障碍信息、检测路况标记和交通标志等信息。

1.2.2 生物医学影像诊断(精准医疗)

这就是本文将要实现的具体应用了。其实现的最终模型可以减少放射科医生的分析时间,为相关疾病的诊疗提供辅助和技术支持。

1.2.3 地理传感

语义分割问题也可以被视为分类问题(其最终的解决方式),其中每个像素被归类为一系列对象类别中的一个。因此,可用于卫星图像的土地使用制图。例如监测森林砍伐和城市化区域。道路和建筑物检测也是交通管理、城市规划和道路监测的重要研究课题等。

1.2.4 精准农业

农业机器人可以减少田间需要喷洒的除草剂次数和剂量,通过对作物和杂草进行语义分割,实时协助它们触发除草动作。

2. U-Net模型解释

2.1 U-Net模型概述

U-Net的U形结构如图所示。网络是一个经典的全卷积网络(即网络中没有全连接操作)。网络的输入是一张 572*572 的边缘经过镜像操作的图片(input image tile),关于“镜像操作“会在接下来进行详细分析,网络的左侧(红色虚线)是由卷积和Max Pooling构成的一系列降采样操作,论文中将这一部分叫做压缩路径(contracting path)。压缩路径由4个block组成,每个block使用了3个有效卷积和1个Max Pooling降采样,每次降采样之后Feature Map的个数乘2,因此有了图中所示的Feature Map尺寸变化。最终得到了尺寸为 32*32的Feature Map。

网络的右侧部分(绿色虚线)在论文中叫做扩展路径(expansive path)。同样由4个block组成,每个block开始之前通过反卷积将Feature Map的尺寸乘2,同时将其个数减半(最后一层略有不同),然后和左侧对称的压缩路径的Feature Map合并,由于左侧压缩路径和右侧扩展路径的Feature Map的尺寸不一样,U-Net是通过将压缩路径的Feature Map裁剪到和扩展路径相同尺寸的Feature Map进行归一化的(即图1中左侧虚线部分)。扩展路径的卷积操作依旧使用的是有效卷积操作,最终得到的Feature Map的尺寸是 388*388。由于该任务是一个二分类任务,所以网络有两个输出Feature Map。

首先,数据集我们的原始图像的尺寸都是 512*512 的。为了能更好的处理图像的边界像素,U-Net使用了镜像操作(Overlay-tile Strategy)来解决该问题。镜像操作即是给输入图像加入一个对称的边(图2),那么边的宽度是多少呢?一个比较好的策略是通过感受野确定。因为有效卷积是会降低Feature Map分辨率的,但是我们希望 515*512的图像的边界点能够保留到最后一层Feature Map。所以我们需要通过加边的操作增加图像的分辨率,增加的尺寸即是感受野的大小,也就是说每条边界增加感受野的一半作为镜像边。

2.2 U-Net 和 autoencoder 架构的区别

U-Net模型与传统的图像分割方法(自动编码器体系结构)有所区别。传统的经典图像分割方法是最初输入信息的大小随着层数的增加其特征信息在不断减少,至此,自动编码器体系结构的编码器部分完成,开始解码器部分。而U-Net模型学习线性特征表示,其特征大小在逐渐增大,其最后的输出大小等于输入大小。

既是整个Auto-Encoder有两个吸引人的应用:1)利用前半部分做特征提取;2)利用后半部分做图像生成。

然而,U-Net结构和Auto-Encoder的传统结构十分相似,但是它独特的前传结构让网络能够capture到很多空间的信息,U-Net的直接前传可以保留很多空间信息,既其前半部分是降采样,下半部分是升采样。

2.3 U-Net模型代码详解

在这部分代码实现过程中一定要注意调整卷积核的大小和padding的大小,这样才可以在最后保证图片的尺寸恢复到和原来一样。

 inputs = Input(input_size)conv1 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(inputs)conv1 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv1)pool1 = MaxPooling2D(pool_size=(2, 2))(conv1)conv2 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool1)conv2 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv2)pool2 = MaxPooling2D(pool_size=(2, 2))(conv2)conv3 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool2)conv3 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv3)pool3 = MaxPooling2D(pool_size=(2, 2))(conv3)conv4 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool3)conv4 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv4)drop4 = Dropout(0.5)(conv4)pool4 = MaxPooling2D(pool_size=(2, 2))(drop4)conv5 = Conv2D(1024, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool4)conv5 = Conv2D(1024, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv5)drop5 = Dropout(0.5)(conv5)up6 = Conv2D(512, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(drop5))merge6 = concatenate([drop4,up6], axis = 3)conv6 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge6)conv6 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv6)up7 = Conv2D(256, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv6))merge7 = concatenate([conv3,up7], axis = 3)conv7 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge7)conv7 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv7)up8 = Conv2D(128, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv7))merge8 = concatenate([conv2,up8], axis = 3)conv8 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge8)conv8 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv8)up9 = Conv2D(64, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv8))merge9 = concatenate([conv1,up9], axis = 3)conv9 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge9)conv9 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv9)conv9 = Conv2D(2, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv9)conv10 = Conv2D(1, 1, activation = 'sigmoid')(conv9)model = Model(input = inputs, output = conv10)from keras.utils.vis_utils import plot_modelplot_model(model, to_file='model1.png',show_shapes=True)

3. 应用实现

3.1 数据集介绍

数据来自于 ISBI 挑战的数据集。该数据集包含30张训练图、30张对应的标签。30张测试图片。数据是来自果蝇一龄幼虫腹神经索 (VNC) 的串行部分透射电子显微镜 (ssTEM)的图像,其分辨率为4x4x50 nm /像素。

3.2 模型设计与实现

实验环境:python3.6.5;tensorflow==1.12;keras==2.2.4等;

上述2.3实现的 U-Net模型其参数过多,在自己电脑上运行过慢,为了快速建立模型并实现应用,我们设计了更小是U-Net模型,其实现后的结构如下所示:

其模型的训练采用如下代码进行实现(添加了数据增强):

ata_gen_args = dict(rotation_range=0.2,width_shift_range=0.05,height_shift_range=0.05,shear_range=0.05,zoom_range=0.05,horizontal_flip=True,fill_mode='nearest')
myGene = trainGenerator(2,'data/membrane/train','image','label',data_gen_args,save_to_dir = None)model = unet()
model_checkpoint = ModelCheckpoint('unet_membrane.hdf5', monitor='loss',verbose=1, save_best_only=True)
model.fit_generator(myGene,steps_per_epoch=3,epochs=5,callbacks=[model_checkpoint],validation_data=())

其实验结果的准确率曲线与模型训练次数的曲线如下所示:

从图中我们发现 U-Net模型在经过简化之后,其在细胞分割任务中已经达到了不错的训练效果结果,其准确率高达78%,为了进一步提高模型准确率,我们需要在模型设计环节设计完整的 U-Net模型,并加大模型的训练次数,这样就能使模型很轻松的达到95%左右。

3.3 结果展示

输入原始图像:

模型输出图像:

4.结论

本文主要介绍了U-net模型在细胞分割中的相关基本概念及一个具体应用,下一步,将进一步探究将每两个相邻的卷积层替换为残差结构,并在收缩路径和扩张路径中间加入并联在一起的位置注意力模块和通道注意力模块以进一步提高U-net模型的应用效果。

基于U-Net的的图像分割代码详解及应用实现相关推荐

  1. 基于C语言的JPEG编码代码详解

    一.基于C语言的JPEG编码代码详解 #include <stdio.h> #include <stdlib.h> #include <string.h>#prag ...

  2. [h5]一个基于HTML5实现的视频播放器代码详解

    什么是 HTML5? HTML5 是最新的 HTML 标准. HTML5 是专门为承载丰富的 web 内容而设计的,并且无需额外插件. HTML5 拥有新的语义.图形以及多媒体元素. HTML5 提供 ...

  3. 基于全卷积神经网络的图像分割方法详解(二)

    最近这段时间刚好忙完学业作业,抽空来总结一下前段时间的工作. 前段时间一直忙于用深度学习做医学图像分割,采用的方法是FCN,也就是全卷积神经网络.全卷积神经网络是基于卷积神经网络的改进,使得输入和输出 ...

  4. STAPLE目标跟踪算法 基于C++ /Opencv实现步骤以及代码详解

    Staple Algorithm C++ implementation of staple algorithm for object tracking. 由于最近在做DSST和STAPLE算法跟踪效果 ...

  5. 基于模糊C均值聚类(FCM)的图像分割原理+python代码详解

    一.模糊 "模糊":一个元素可以不同程度的属于某几个子集,也就是说元素对于集合的隶属度可以在[0,1]上取连续值. 二.步骤 2.1步骤 翻译一下: S1:初始化参数:加权指数m, ...

  6. 基于python的随机森林回归实现_PYTHON | 随机森林实战(代码+详解)

    大家好,我是菜鸟君,之前跟大家聊过R语言的随机森林建模,指路 R语言 | 随机森林建模实战(代码+详解),作为刚过完1024节日的码农算法工程师来说,怎么可能只会用一种语言呢?今天就来说说Python ...

  7. 基于神经网络的依存句法分析总结及代码详解

    上一篇文章CS224n之句法分析总结,介绍了句法分析以及具体的依存分析中的arc-standard算法.arc-standard系统是transition systems中最流行的一个系统之一.而本文 ...

  8. 基于RK3399ESP8285自动售货柜项目—MP08开发板端代码详解

    基于RK3399&ESP8285自动售货柜项目-②MP08开发板端代码详解 本系列文章将详细讲解该基于RK3399及ESP8285自动售货柜的完整实现方法,从硬件连接到网络通信再到软件实现,本 ...

  9. 基于spring security实现vue2前后端分离的双token刷新机制(完整代码详解,含金量拉满!)

    目录 一.前言: 核心功能概要: 通过加密算法创建一个用户: 二.后端 代码详解: 1.代码整体结构: 2.所需依赖: 3.UserDetailServiceImpl拦截用户登陆: 4.所需工具类 4 ...

最新文章

  1. linux 下 php 安装 Gearman
  2. [转]PetShop的系统架构设计
  3. 神策数据入选“2019 全球企服科技创新 Top50”
  4. 机器人学习--pitch yaw roll
  5. IntelliJ IDEA——连接Apache Hive时 “Database” Plugin错误[enabling autocommit is not supported.]解决方案
  6. /UI2/CL_NWBC_RUNTIME35 - GET_ROLES
  7. JavaMail--发送html邮件
  8. android 恢复app 到前台,android手机把app disable了,怎么恢复
  9. 他拿着菜刀走进教室,把400个混混送进了一流大学
  10. 如何控制Yahoo! Slurp蜘蛛的抓取频度_国外博客资源站_百度空间
  11. UI中国转的产品知识
  12. php formdata 解析,系统学习前端之FormData详解
  13. 中班音乐会跳舞的机器人_中班音乐律动机器人
  14. 关于最小二乘法快速计算公式汇总
  15. windows 下 tomcat 开机自启动
  16. php submit执行函数,jQuery.submit() 函数详解
  17. 使用 Nginx 反向代理域名
  18. bs前端--弹出子窗体
  19. 程序员如何一句话教老板做事?“我先下班了,你可要加把劲啊”
  20. 前一阵子闹得沸沸扬扬的IP归属地,到底是怎么实现的?

热门文章

  1. Matplotlib 绘图 (二)
  2. window10安装虚拟机及相关软件
  3. 机器学习核心总结-概念、线性回归、损失函数、泛化及数据集划分、特征工程、逻辑回归和分类
  4. android pc 模拟器哪个好,安卓模拟器电脑版哪个好?
  5. 用妙记多 Mojidoc 实践康奈尔笔记法
  6. js实现点击隐藏图片
  7. 互联网公司背后的“深套路”:恶意举报对手,假招聘骗取信息
  8. Latex输入分段函数
  9. android gamed,先游DGame
  10. 一次 Young GC 的优化实践(FinalReference 相关)