2018-07-03 10:15:34 weixin_41783077 阅读数 36953
  • U-Net图像语义分割实战:训练自己的数据集

    U-Net是一种基于深度学习的图像语义分割方法,尤其在医学图像分割中表现优异。 本课程将手把手地教大家使用labelme图像标注工具制作自己的数据集,生成Mask图像,并使用U-Net训练自己的数据集,从而能开展自己的图像分割应用。 本课程有三个项目实践: (1) Kaggle盐体识别比赛 :利用U-Net进行Kaggle盐体识别 (2) Pothole语义分割:对汽车行驶场景中的路坑进行标注和语义分割 (3) Kaggle细胞核分割比赛 :利用U-Net进行Kaggle细胞核分割 本课程使用keras版本的U-Net,在Ubuntu系统上用Jupyter Notebook做项目演示。 包括:数据集标注、数据集格式转换和Mask图像生成、编写U-Net程序文件、训练自己的数据集、测试训练出的网络模型、性能评估。 本课程提供项目的数据集和Python程序文件。

    864 人正在学习 去看看 白勇

(自己写的,需要转载请联系作者,或者标明出处呀,欢迎加微信交流:wx604954)

 

摘要:医学图像分割是决定医学图像在临床诊疗中能否提供可靠依据的关键问题。医学图像分割技术的发展不仅影响到医学图像处理中其他相关技术的发展,如可视化、三维重建等,而且在生物医学图像的分析中也占有极其重要的地位。近年来,由于深度学习算法在医学图像分割中的应用, 医学图像分割技术取得了显著的进展。在本文中,我们讨论了用于医学图像分割最先进的深度学习架构及其优化。在最后一节中,我们讨论了深度学习算法在医学图像分割领域的未来发展及面临的挑战。

 

关键词: 深度学习;卷积神经网络;图像分割;医学图像

 

0引言

随着医学影像学设备的快速发展和普及,成像技术包括磁共振成像(MR)、计算机断层扫描(CT)、超声、正电子发射断层扫描(PET)等,成为医疗机构开展疾病诊断、手术计划制定、预后评估、随访不可或缺的设备。全世界每天都产生大量的医学影像学信息,有报道显示全世界医学影像信息量占全世界信息总量的1/5以上[1]。医学图像处理是分析医学图像的首要步骤,有助于使图像更加直观、清晰,提高诊断效率,因此国内外都十分重视医学影像处理[2]。图像分割是图像处理的重要环节,也是难点,是制约三维重建等技术应用的瓶颈性问题[3]。近年来,由于深度学习方法的迅速发展,基于深度学习的图像分割算法在医学图像分割领域取得了显著的成就。

深度学习是机器学习领域中一系列试图使用多重非线性变换对数据进行多层抽象的算法,不仅学习输入和输出之间的非线性映射,还学习输入数据向量的隐藏结构[4],以用来对新的样本进行智能识别或预测。20世纪80年代末期,用于人工神经网络的反向传播算法的发明掀起了基于统计模型的机器学习热潮[5],20世纪90年代,浅层学习掀起机器学习的第一次浪潮[6],这些浅层结构虽然相比于过去基于人工规则的系统展现了很大的优越性,但对于处理复杂的问题,表现出特征学习能力不足、维数灾难、易陷入局部最优等缺点[7]。针对上述问题,2006年,Hinton在《Science》上首次提出“深度学习”并掀起了机器学习的第二次浪潮[8],多隐层的网络具有优异的特征学习能力,学习到的特征对数据有更本质的刻画。深度学习在医学图像分割方面的突破对于医学领域的发展至关重要[9]。

                                                                                                            

1. 医学图像分割

医学成像有多种图像模态,诸如MR、CT、PET、超声成像等等。成像可以获得反映二维和三维区域人体的生理和物理特性的图像,本文的内容主要侧重于MR和CT成像的特点和临床应用。二维图像中的每个元素称为像素,三维区域中每个元素称为体素,在某些情形下,可以把三维图像表示为一系列的二维切片进行观察,优点是计算复杂度低且需要的内存较小。

1.1 MR图像

磁共振成像(MRI)是无线电成像领域中使用最广泛的技术。作为一种动态且灵活的技术,MRI可以实现多变的图像对比度,该过程的实现是通过使用不同的脉冲序列和改变成像参数对应纵向松弛时间(T1)和横向松弛时间(T2),T1加权和T2加权成像的信号强度与特定组织的特征有关[10]。MR成像中,图像的对比度依赖于相位对比脉冲序列参数,最常见的脉冲序列是T1加权和T2加权自旋回波序列[12]。通过MR成像可以观察大脑、肝脏、胸、腹部和骨盆的结构细节,这有利于诊断检测或治疗[13]。

MRI对软组织有很好的成像能力;有非常高的分辨率;具有较高的信噪比;利用不同的脉冲序列可以得到对比度多变的多通道图像,进而用于不同解剖结构的目标分割和分类[14]。然而,在MRI中存在多种伪影,如部分容积、随机场噪声、强度不均匀性、梯度、运动、环绕、吉布斯振铃、磁化性等[15]。此外,相比于CT图像,MRI的获取需要相当长的时间,且通常条件下很难得到统一的图像质量。

1.2 CT图像

医学CT成像设备使用X射线(一种电磁波)得到人体的结构和功能信息。CT影像是基于X射线吸收剖面的重构图像,由于不同物质和组织吸收X射线能力不同,因此X射线可用于诊断[16]。CT成像作为当前多类疾病实体诊断的金标准,广泛应用于大脑、肝脏、胸部、腹部、骨盆、脊柱等身体部位以及CT血管造影的早期诊断筛查[17]。但是与MR图像相比较,CT图像敏感性和特异性相对较差。

CT成像中的伪影[18]包括:部分容积效应、条形伪影、运动伪影、束硬化伪影、环状伪影、金属伪影等。由于这些伪影的存在给CT图像分割带来了一定的难度,不同组织部位分割精度也不一样[19]。

1.3 医学图像分割的特点

医学图像分割是医学图像处理与分析领域的复杂而关键的步骤,其目的是将医学图像中具有某些特殊含义的部分分割出来,并提取相关特征,为临床诊疗和病理学研究提供可靠的依据,辅助医生做出更为准确的诊断[20]。图像分割过程是把图像分割成多个区域,这些区域内部有类似的性质,如灰度、颜色、纹理、亮度、对比度等。医学图像分割的目标是(以放射治疗为例)[21]:(1)研究解剖结构;(2)识别感兴趣区域(即定位肿瘤、病变和其他异常组织);(3)测量组织体积;(4)观察肿瘤生长或治疗中肿瘤体积的减少,为治疗前的计划和治疗中提供帮助;(5)辐射剂量计算。

从医学图像中自动分割出目标是个艰巨的任务,因为医学图像具有较高的复杂性且缺少简单的线性特征;此外分割结果的准确率还受到部分容积效应、灰度不均匀性、伪影、不同软组织间灰度的接近性等因素的影响[22]。针对通常采用的校正技术来说,可以将MR和CT图像中的伪影分类为[23]:(1)需要适当的滤波算法处理的伪影,如噪声伪影、敏感性伪影、存在非清晰边缘的伪影;(2)需要适当图像修复算法的伪影,如运动伪影;(3)需要特定算法的伪影,如部分容积和灰度不均匀性。图像处理领域尽管在已存在很多算法处理上述问题,但是医学图像分割仍然是个复杂和具有挑战性的问题。从医学图像处理过程的角度来看,基于灰度和基于纹理特征技术的分类是常规的分类方式[24]。此外,用机器学习的工具去优化这些图像分割算法是当前较受关注的技术[25].

CT和MR图像的分割主要涉及3个相关问题:变化的噪声、像素灰度分类的不确定性及灰度的非均衡性[26]。图像中单一组织的灰度水平一般是逐渐变化的,且其概率密度服从特定的分布函数,该组织对应的图像区域包含有限的像素(或体素)且满足部分容积平均,然而该区域中的单个像素(或体素)的灰度不与任何一类一致,往往被看作混合组织类[28]。

CT和MR图像分割常用的一些方法有:基于阈值[29]、基于区域[30]、基于形变模型[31]、基于模糊[32]及基于神经网络[34]。

当前,基于深度学习的方法已在图像分割领域取得了显著成就,其分割准确率已超过了传统分割方法。本文在对近几年深度学习和医学图像分割文献研习的基础上,对深度学习方法和常用的图像分割算法进行了深入的研究和比较,总结了各种深度学习方法的优缺点及其在医学图像分割领域的应用,最后展望了深度学习在医学图像分割领域的未来发展。

 

2. 深度学习

2.1 深度学习与图像处理

传统的图像处理技术主要包括特征提取和分类器两部分,特征提取算法的设计复杂性与应用局限性、稳定性,以及特定的特征提取算法与特定的分类器相结合的多样性限制着图像处理技术的发展[36]。神经网络的出现,使端到端的图像处理成为可能,当网络的隐藏层发展到多层时便称之为深度学习,但同时需要用逐层初始化技术解决深层次网络训练难度大的问题,之后深度学习便成为时代的主角[37]。而卷积神经网络(Convolutional Neural Network,CNN)便是深度学习与图像处理技术相结合所产生的经典模型,实现该模型的网络实例在特定的图像问题处理上都卓有成效[38]。

神经网络之所以能和图像领域相结合,并呈现巨大的发展前景,是有生物学依据的。人类视觉信息处理机制的发现,是 19 世纪 19 年代生物学界的重大发现之一,它证明了大脑可视皮层是分级存在的。人的视觉系统是一个反复抽象和迭代的过程。而卷积神经网络,就模拟了这个过程。首先,每一个卷积层,便是将具体信息做抽象的过程,而多个卷积层串联操作,便是将上一层的抽象结果再做抽象处理的过程,称之为迭代[39]。在这个抽象迭代的过程中,不断抽取大脑可感知的高维度特征。如当一幅图像经过视网膜进入视野,首先会将光线像素等信息抽象为图像边缘信息,然后再抽象为目标物体的某一部位,再抽象为物体轮廓形成对整个目标的感知[41]。

2.2 卷积神经网络

卷积神经网络(CNN)是一种多阶段的、全局可训练的人工神经网络模型,可以经过少量预处理,从原始数据中学习到抽象的、本质的、深层的特征[42]。在CNN中,每一层的数据都是大小为h×w×d的三维数组,其中h、w是空间维度,d是特征维数或通道维数。在图像处理领域,h×w代表图像尺寸,d代表颜色通道。CNN高层中的每一点对应于图像中的一块区域,称为局部感受野。

CNN运行于局部输入区域的基本操作包括卷积(Convolution)、激活函数(Rectified Linear Units, ReLu)和池化(Pooling),这些操作仅依赖于相关的空间坐标[43]。记 为某层上坐标 的数据向量,下一层中坐标为 的数据向量为 , 可由以下公式计算:  

(2-2)

式中: 为卷积核的大小,s为步长或降采样系数,  决定层的类型:矩阵相乘对应卷积层或者平均池化层;空间域最大值对应最大池化层;非线性函数对应激活函数层。

(1)卷积

研究表明,自然图像某一部分的统计特性与其他部分统计特性相同,因此CNN网络在图像某一部分学习到的特征也能用在图像其他部分上[44]。在CNN网络对一张图像进行卷积运算时,对于图像上的所有位置可以采用相同的卷积核进行卷积运算。在卷积层,卷积核与前一层得到的特征图进行卷积,卷积操作的计算公式如下:

           (2-3)

式中, 为 的邻域.

(2)激活函数

卷积神经网络中每一层的特征图经常与前一层的多个特征图有关,是前一层特征图的卷积结果经过激活函数后得到的。CNN网络中常用的激活函数有ReLu函数、Sigmoid函数和双曲正切函数,其计算公式如下:

(3)池化

在CNN网络完成卷积特征提取之后,把每个隐藏单元提取到的特征看作成一个矩阵,并将该矩阵上划分成几个不重叠的区域,计算划分后的每个区域内特征的平均值或最大值,然后用计算得到的平均值或最大值进行后续的训练,这个过程就是池化。上述采用均值和最大值计算区域特征的池化方法分别称为均值池化和最大值池化。

2.3卷积神经网络在图像分割领域的应用

卷积神经网络自从在手写字体识别领域获得巨大突破后[45],逐渐开始应用在图像处理领域中。CNN网络在ImageNet比赛中的杰出表现[46]表明其在图像特征提取、图像分类等图像处理领域拥有巨大的潜能。目前,CNN网络已广泛应用于医学图像分割中。

Wu, Xundong等人[47]提出了基于迭代的卷积神经网络的细胞膜分割方法,通过在细胞膜检测概率图上迭代使用卷积神经网络算法,细胞膜分割精度有了一定的提升。曹贵宝等人[48]提出了用卷积神经网络和随机森林分类器相结合的方法实现了对神经细胞图像的分割,用随机森林分类器代替了传统的softmax分类器,实验中分别训练卷积神经网络模型和分类器模型,并用其学习到的特征训练随机森林分类器,最后完成电子显微图像的神经细胞膜的分割。实验结果的分割准确度明显高于传统的分割算法SVM,而且用随机森林做分类器比用传统的softmax做分类器效果也要好一些。

CERNAZANUGLAVAN等人[49]提出使用卷积神经网络分割X射线图像中的骨骼结构,通过使用四种不同的卷积神经网络提取骨骼X射线图像的图像特征,并将提取到的图像特征用softmax分类器进行分类,从而实现骨骼结构的分割。实验通过对比表明,第四种网络卷积神经网络结构的分割准确率更高。

Su, Hai 等人[50]提出了一种快速扫描卷积神经网络的方法,用于乳腺磁共振图像的分割。与传统卷积神经网络结构不同,该文献在卷积层采用卷积核去卷积整张图像或者上一卷积层提取到的特征图,并重新排列最大池化层,从而减少冗余计算。实验表明,该文献提出的基于快速扫描卷积神经网络的分割方法大大提高的图像分割速度,并保证了分割精度的不变,对乳腺癌实时诊断提出了技术支持。

卷积神经网络在进行图像分割操作时,因其具有优良的特征提取能力和良好的特征表达能力,不需要人工提取图像特征或对图像进行过多预处理,因此近年来CNN网络在医学影像(MRI、CT、X射线等)分割领域以及辅助诊断方面取得了巨大成功[51]。

 

3深度学习在医学图像分割中的应用

3.1FCN

传统的基于卷积神经网络的分割方法的做法通常是:为了对一个像素分类,使用该像素周围的一个图像块作为卷积神经网络的输入用于训练和预测。这种方法有几个缺点:一是存储开销很大。例如对每个像素使用的图像块的大小为15x15,则所需的存储空间为原来图像的225倍。二是计算效率低下。相邻的像素块基本上是重复的,针对每个像素块逐个计算卷积,这种计算也有很大程度上的重复。三是像素块大小的限制了感知区域的大小。通常像素块的大小比整幅图像的大小小很多,只能提取一些局部的特征,从而导致分类的性能受到限制。

针对以上问题, Long Jonathan等人[52]2015年提出全卷积网络(FullyConvolutional Networks, FCN)结构。FCN可以对图像进行像素级的分类,从而解决了语义级别的图像分割问题。与经典的卷积神经网络在卷积层之后使用全连接层得到固定长度的特征向量进行分类(全联接层+softmax 输出)不同,全卷积网络可以接受任意尺寸的输入图像,采用反卷积层对最后一个卷积层的特征图进行上采样, 使它恢复到输入图像相同的尺寸,从而可以对每个像素都产生了一个预测, 同时保留了原始输入图像中的空间信息, 最后在上采样的特征图上进行逐像素分类,完成最终的图像分割。FCN的网络结构如图 1 所示。

图1  FCN网络结构

 

在FCN网络结构的基础上,Korez 等人[53]提出了3D FCN网络结构,并将3D FCN网络分割出的脊椎结构用形变模型算法进行优化,进一步提高了脊柱MR图像的分割准确率。Zhou等人[54]将FCN算法和多数投票算法相结合,在人体躯干CT图像中分割出了19个目标。在网络训练过程中,将经过每一个像素点的矢状面、轴状面、冠状面的CT图像分别输入至2D FCN网络进行训练,并用3D Majority Voting算法对三幅图像的分割结果进行表决,得到最后的分割结果。Moeskops等人[55]采用脑部MRI、心肌MRI和冠状动脉在心脏CT血管造影(CTA)中的冠状动脉图像同时训练一个FCN网络,使得训练好的网络可同时用于这三类目标的分割。

FCN在对图像进行分割时,上采样层将特征恢复到原图像的大小,此过程会导致像素定位不精确,从而影响分割结果的准确性。为了解决这一问题,许多研究者采用MRF算法[56]或CRF算法[58]对FCN输出的分割结果进行改进,进一步优化分割结果。

 

3.2U-net

2015年,OlafRonneberger等人[64]提出了U-net网络结构,U-net是基于FCN的一种语义分割网络,适用于做医学图像的分割。

U-net网络结构与FCN网络结构相似,也是分为下采样阶段和上采样阶段,网络结构中只有卷积层和池化层,没有全连接层,网络中较浅的高分辨率层用来解决像素定位的问题,较深的层用来解决像素分类的问题,从而可以实现图像语义级别的分割。与FCN网络不同的是,U-net的上采样阶段与下采样阶段采用了相同数量层次的卷积操作,且使用skip connection结构将下采样层与上采样层相连,使得下采样层提取到的特征可以直接传递到上采样层,这使得U-net网络的像素定位更加准确,分割精度更高。此外,在训练过程中,U-net只需要一次训练,FCN为达到较精确的FCN-8s结构需要三次训练,故U-net网络的训练效率也高于FCN网络。

U-net网络结构如图2所示, 蓝色箭头代表卷积和激活函数, 灰色箭头代表复制剪切操作, 红色箭头代表下采样, 绿色箭头代表反卷积,conv 1X1代表卷积核为1X1的卷积操作。从图中可以看出,U-net网络没有全连接层,只有卷积和下采样。U-net可以对图像进行端到端的分割,即输入是一幅图像, 输出也是一幅图像。

图2  U-net网络结构

 

U-net网络提出后,在医学图像分割领域表现优秀,许多研究者均采用U-net网络结构做医学图像分割,并在U-net网络结构基础上提出改进。Çiçek等人[65]提出3D U-net网络结构,该结构通过输入3D图像连续的2D切片序列实现3D图像分割。Milletari等人[66]提出了U-net网络结构的一种3D变形结构V-net,V-net结构使用Dice系数损失函数代替传统的交叉熵损失函数,且使用3D卷积核对图像进行卷积,通过1x1x1的卷积核减少通道维数。Drozdzal等人[67]提出在U-net网络结构中不仅可以使用长跳跃连接结构,也可以使用短跳跃连接结构。

在病灶分割任务中,深度学习算法需要完成目标识别、器官分割和组织分割等多项任务,因此分割过程中应结合图像的全局信息和局部信息来实现病灶的准确分割,Kamnitsas等人[68]和Ghafoorian等人[69]都提出使用多尺度卷积来提取图像全局信息和局部信息。此外,Wang等人[70]提出一种伤口图像分析系统,先用U-net网络对伤口图像进行分割,再用SVM分类器对分割出的伤口图像进行分类,判断伤口是否感染,最后用GP回归算法对伤口愈合时间进行预测。Brosch等人[71]使用U-net网络对脑部MRI中的脑白质病灶进行分割,并在U-net网络的第一层卷积和最后一层反卷积之间加入跳跃连接结构,使得该网络结构在训练数据较少的情况下仍得到了很好的分割结果。

 

3.3RNN

循环神经网络(Recurrent Neural Networks,RNN)是为离散序列分析而开发的,已经在众多自然语言处理(Natural Language Processing,NLP)中取得了巨大成功以及广泛应用[72]。传统的神经网络假定不同输入和输出之间是相互独立的,网络中不存在反馈连接,而RNN网络结构中至少包含一个反馈连接(自循环),即假定当前输出不仅与当前输入有关,也与先前输出有关,所以RNN适合于解决时间处理和顺序学习问题[73]。

在实际训练过程中,梯度消失的问题使得传统RNN在实际中很难处理长期依赖[75],而长短期记忆网络(Long ShortTerm Memory,LSTM)作为一种 RNN 的特殊类型,则绕开了这些问题,可以从学习到长期依赖关系。LSTM 由Hochreiter等人[76]提出,并在近期被Alex Graves[77]进行了改良和推广。Cho 等人[78]提出在LSTM网络结构中加入门控复发单元,可对LSTM网络结构进行简化。在很多实际问题中,LSTM 都取得相当巨大的成功,并得到了广泛的使用。

虽然RNN在最初提出时,网络输入为一维向量,但在近几年的应用中,RNNs越来越多地应用于图像处理。在自然图像中,像素级的RNN被用作自回归模型,生成模型最终可以产生类似于训练集样本的新图像。

对于医疗应用而言,RNN可用于医学图像分割问题。 Stollenga等人[79]首次在六个方向上使用3D LSTM-RNN对脑部MR图像进行分割,用金字塔方式重新排列MD-LSTM中传统的长方体计算顺序,使得网络可以在GPU上并行训练,大大提高了网络训练效率,并在MRBrainS挑战中取得了很好的分割结果。Andermatt等人[80]使用带有门控单元的3D RNN网络分割脑MR图像中的灰质和白质,结合数据预处理和后处理操作,进一步提高了分割准确率。Chen等人[81]提出了一个基于FCN和RNN的三维图像分割新框架,这是第一个明确利用3D图像各向异性的DL分割框架。使用来自ISBI神经元结构分割挑战的数据集和3D真菌图像进行评估,他们的方法比已知的基于DL的3D分割方法在分割准确率上更胜一筹。Poudel等人[82]提出了循环全卷积网络(RFCN),将解剖学检测和图像分割将结合,形成一个端到端的网络结构,从而大大缩短了计算时间,简化了分割流水线,并为实时应用提供了可能。

 

4未来发展方向

4.1加强领域间合作

随着计算机视觉及人工智能领域的发展,医学图像分割的准确率不断上升,为医学诊断提供了极大的帮助。然而,由于人工智能科学家不了解临床需求,临床医生不了解人工智能具体技术,导致人工智能不能很好地满足临床具体需求[83]。为了促进人工智能在医学领域的应用,应加强临床医生与机器学习科学家的广泛合作。这种合作将解决机器学习研究人员无法获得医学数据的问题,也可以帮助机器学习研究者开发出更符合临床需求的深度学习算法,应用到计算机辅助诊断设备中,从而提高诊断效率和准确率。

4.2医学影像数据共享

深度学习算法的训练需要大量的数据集支持,然而,与自然图像相比,带注释的医学图像相对稀缺,且标注成本较高。由于医学领域的敏感性和特殊性,医学数据的注释需要大量专家手动完成,这需要投入大量的人力物力,然而即使在这种情况下,由于病例的复杂性和图像本身模糊等原因,也不能保证标注的完全准确[84]。为了克服这一问题,需要不同医疗服务者共享数据资源,或建立医学影像知识库,为人工智能研究者们提供更多的数据支持。

4.3深度学习技术的进步

目前,大多数医学图像分割采用的是有监督的深度学习算法,然而,针对某些缺乏大量数据支持的罕见疾病,有监督的深度学习算法便无法发挥其性能。为了克服数据缺乏或数据不可用的问题,对于深度学习算法的研究可以从有监督领域转移到半监督或无监督领域[85]。如何在医疗领域有效地使用半监督和无监督算法,同时又不影响医疗系统的准确性,这是目前很多研究者正在研究的一个问题,但却没有提供被临床接纳的解决方案,这将是一个需要被继续研究的方向。

 

5 总结

深度学习算法在医学图像处理领域具有重要的理论意义和实际应用价值。本文从医学图像分割的基本概念出发,详细阐述了不同医学影像的特点以及分割难点。然后介绍了深度学习的基本原理及适用于进行图像处理的卷积神经网络算法,并详细介绍了FCN、U-net、RNN三种网络结构和它们在医学图像分割领域的应用。最后从加强领域间交流合作、医学影像数据共享、深度学习技术的进步这三个方面对深度学习技术在医学图像处理领域的未来发展进行展望。深度学习算法的发展必然会对医学图像分割的发展起到积极的促进作用。

总之,借助大数据的潮流,结合人工智能自身潜力,争取深度学习算法在医学领域取得更大的进步并早日实现智能医疗的初衷。

 

 

 

参考文献

 

 

2019-09-06 17:19:55 OpenSceneGraph 阅读数 584
  • U-Net图像语义分割实战:训练自己的数据集

    U-Net是一种基于深度学习的图像语义分割方法,尤其在医学图像分割中表现优异。 本课程将手把手地教大家使用labelme图像标注工具制作自己的数据集,生成Mask图像,并使用U-Net训练自己的数据集,从而能开展自己的图像分割应用。 本课程有三个项目实践: (1) Kaggle盐体识别比赛 :利用U-Net进行Kaggle盐体识别 (2) Pothole语义分割:对汽车行驶场景中的路坑进行标注和语义分割 (3) Kaggle细胞核分割比赛 :利用U-Net进行Kaggle细胞核分割 本课程使用keras版本的U-Net,在Ubuntu系统上用Jupyter Notebook做项目演示。 包括:数据集标注、数据集格式转换和Mask图像生成、编写U-Net程序文件、训练自己的数据集、测试训练出的网络模型、性能评估。 本课程提供项目的数据集和Python程序文件。

    864 人正在学习 去看看 白勇

基于深度学习的医学图像分割模型研究_曹祺炜

1.基于改进的3D-FCN+CRF以及MS-CapsNetGAN实现脑肿瘤图像分割

图像语义分割,简单而言就是给定一张图片,对图片上的每一个像素点分类,不同颜色代表不同类别。图像分割的主要步骤:图像预处理、数据准备以及图像特征提取、分类器分类和后期处理。前端使用FCN进行特征粗提取,后端使用CRF/MRF优化前端的输出,最后得到分割图。

  • FCN-全卷积网络

此处的FCN主要使用了三种技术:卷积化(Convolutional),上采样(Upsample),跳跃结构(Skip Layer)。

卷积化即是将普通的分类网络丢弃全连接层,换上对应的卷积层即可。上采样即是反卷积,框架不同名字不同,Caffe和Kera里叫Deconvolution,而tensorflow里叫conv_transpose。忽略连接结构的作用就在于优化结果,因为如果将全卷积之后的结果直接上采样得到的结果是很粗糙的,所以作者将不同池化层的结果进行上采样之后来优化输出。

  • MRF-马尔科夫随机场

随机场是由若干个位置组成的整体,当给每一个位置中按照某种分布随机赋予一个值之后,其全体就叫做随机场。举词性标注的例子:假如我们有一个十个词形成的句子需要做词性标注。这十个词每个词的词性可以在我们已知的词性集合(名词,动词...)中去选择。当我们为每个词选择完词性后,这就形成了一个随机场。

马尔科夫随机场是随机场的特例,它假设随机场中某一个位置的赋值仅仅与和它相邻的位置的赋值有关,和与其不相邻的位置的赋值无关。继续举十个词的句子词性标注的例子:如果我们假设所有词的词性只和它相邻的词的词性有关时,这个随机场就特化成一个马尔科夫随机场。比如第三个词的词性除了与自己本身的位置有关外,只与第二个词和第四个词的词性有关。 

  • CRF-条件随机场

CRF是马尔科夫随机场的特例,它假设马尔科夫随机场中只有和两种变量,一般是给定的,而一般是在给定的条件下我们的输出。这样马尔科夫随机场就特化成了条件随机场。在我们十个词的句子词性标注的例子中,是词,是词性。因此,如果我们假设它是一个马尔科夫随机场,那么它也就是一个CRF。

对于CRF,我们给出准确的数学语言描述:设与是随机变量,是给定时的条件概率分布,若随机变量构成的是一个马尔科夫随机场,则称条件概率分布是条件随机场。

è¿éåå¾çæè¿°
①条件随机场保留了隐含马尔可夫模型的一些特性,比如图中的y1,y2,..y1,y2,..等状态的序列还是一个马尔可夫链。
②在图中,顶点x1,y1x1,y1代表一个个随机变量,顶点之间的弧代表他们之间的依赖关系,采用概率分布P(x1,y1)来描述。
③它的特殊性在于变量之间要遵守马尔可夫假设,即每个状态的转移概率只取决于相邻的状态,这一点,它和贝叶斯网络相同。不同之处在于贝叶斯网络是有向图,而条件随机场是无向图,

2.图像分割结果评判标准:

除对分割结果定性分析之外,对分割结果的定量比较显得更为重要。Dice 相似系数(Dice  Similarity  Coefficient,  DSC)法被广泛应用于评估图像分割算法性能的度量,此外,准确率(Accuracy)、精确度(Precision)、召回率(Recall)、过分割率、欠分割率(Under-segmentation  Error,  UE)、真阳性率(True  Positive  Rate,  TPR)和阳性预测值(positive  predictive  value,  PPV)等指标也经常被用作评估算法的分割结果。 精确率指的是输出结果中正确像素点占总像素点的比例,召回率指的是正确像素点占传统人工分割结果中的比例,精确率与召回率的调和均值用 F1-score 表示,F1-score 值越高,则说明算法的分割效果越好,当 F1-score=1 时,表示试验所得的结果与参考标准一致。

https://blog.csdn.net/woshisunwen/article/details/84308996

3.图像分割方法的分类:基于阈值/边缘/区域/活动轮廓模型/模糊聚类算法/数学形态学/神经网络的图像分割

 基于神经网络的图像分割:

通常,CNN 由卷积,池化和完全连接的神经网络层组成。卷积层利用空间相关性在输入图像中,通过共享滤波器内核权重来计算每个特征映射。池化层允许减少每个输入要素图的尺寸,同时保留最相关的要素响应。每个 CNN 层的输出通常馈送到非线性激活功能。使用非线性激活函数允许我们在输入图像和期望输出之间建模非常复杂的映射。

CNN 的一个缺点是当卷积特征被馈送到网络的完全连接层时,图像的空间信息丢失。然而,空间信息对于语义分割任务尤其重要。因此,Long 等人提出全卷积网络FCN 克服这个限制。在 FCN 中,CNN 的最终密集连接层由转置卷积层代替,以便将学习的上采样应用于网络内的低分辨率特征映射。该操作可以在执行语义分割的同时恢复输入图像的原始空间维度。类似的网络结构已成功应用于医学成像中的语义分割任务和生物医学图像的分割。从共聚焦显微镜等模式扩展到 3D 生物医学成像数据或磁共振成像。在典型的 FCN 架构中,可以利用跳过连接来连接网络的不同级别,以便保留更接近原始图像的图像特征。这有助于网络实现更详细的分割结果。 

https://blog.csdn.net/caoniyadeniniang/article/details/76014526

4.模糊聚类算法FCM(FCM算法是目前比较流行的一种模糊聚类算法)

  • (1)基于模糊关系的分类法:其中包括谱系聚类算法(又称系统聚类法)、基于等价关系的聚类算法、基于相似关系的聚类算法和图论聚类算法等等。它是研究比较早的一种方法,但是由于它不能适用于大数据量的情况,所以在实际中的应用并不广泛。
  • (2)基于目标函数的模糊聚类算法:该方法把聚类分析归结成一个带约束的非线性规划问题,通过优化求解获得数据集的最优模糊划分和聚类。该方法设计简单、解决问题的范围广,还可以转化为优化问题而借助经典数学的非线性规划理论求解,并易于计算机实现。因此,随着计算机的应用和发展,基于目标函数的模糊聚类算法成为新的研究热点。
  • (3)基于神经网络的模糊聚类算法:它是兴起比较晚的一种算法,主要是采用竞争学习算法来指导网络的聚类过程。

5.体素

体素是体积元素(Volume Pixel)的简称,包含体素的立体可以通过立体渲染或者提取给定阈值轮廓的多边形等值面表现出来。一如其名,是数字数据于三维空间分割上的最小单位,体素用于三维成像、科学数据与医学影像等领域。

体素成像是一种在二维图像显示器上显示体数据的方法。这些体数据可能是在三维空间物体采样的结果,例如人脑的磁共振图像。通过体素成像,三维空间采样点的集合可以转换成计算机二维屏幕上有意义的图像。体素成像与传统图形显示方法的思想截然不同,它的最大特点就在于放弃了传统图形学中体由面构造这一约束,在不构造物体表面几何描述的情况下直接对体数据进行显示,采用体绘制光照模型直接从三维数据场中绘制出各种物理量的分带晴况,也就是说,直接研究光线穿过三维体数据场时的变化,得到最终的绘制结果。能够描述物体内部结构是体素成像最大的优势。

6.胶囊网络

解决了CNN的问题:CNN的组件的朝向和空间上的相对关系对它来说不重要,它只在乎有没有特征。还有就是池化层:不仅减少了参数,还可以避免过拟合。但是,它的确抛弃了一些信息,比如位置信息。同一物体,尽管拍摄的角度不同,但你的大脑可以轻易的辨识这是同一对象,CNN却没有这样的能力。所以,CapsNet应运而生。

例如输入一张手写字的图片。首先对这张图片做了常规的卷积操作,得到ReLU Conv1;然后再对ReLU Conv1做卷积操作,并将其调整成适用于CapsNet的向量神经元层PrimaryCaps,而不是以往的标量神经元。

神经网络和胶囊网络区别: 神经网络每个神经元输出的是一个标量,胶囊网络输出是一个向量。所谓胶囊就是一个向量,它可包含任意个值,每个值代表了当前需要识别的物体的一个特征。结合之前对传统CNN的学习,我们知道,卷积层的每个值,都是上一层某一块区域和卷积核完成卷积操作,即线性加权求和的结果,它只有一个值,所以是标量。而我们的胶囊网络,它的每一个值都是向量,也就是说,这个向量不仅可表示物体的特征、还可以包括物体的方向、状态等。

PrimaryCaps到DigitCaps层的传播也就是CapsNet和以往CNN操作的最大区别。DigitCaps中一共10个向量,每个向量中元素的个数为16。对这10个向量求模,求得模值最大的那个向量代表的就是图片概率最大的那个分类。因为胶囊网络中:用向量模的大小衡量某个实体出现的概率,模值越大,概率越大。

 

2019-11-19 22:09:08 weixin_43876801 阅读数 361
  • U-Net图像语义分割实战:训练自己的数据集

    U-Net是一种基于深度学习的图像语义分割方法,尤其在医学图像分割中表现优异。 本课程将手把手地教大家使用labelme图像标注工具制作自己的数据集,生成Mask图像,并使用U-Net训练自己的数据集,从而能开展自己的图像分割应用。 本课程有三个项目实践: (1) Kaggle盐体识别比赛 :利用U-Net进行Kaggle盐体识别 (2) Pothole语义分割:对汽车行驶场景中的路坑进行标注和语义分割 (3) Kaggle细胞核分割比赛 :利用U-Net进行Kaggle细胞核分割 本课程使用keras版本的U-Net,在Ubuntu系统上用Jupyter Notebook做项目演示。 包括:数据集标注、数据集格式转换和Mask图像生成、编写U-Net程序文件、训练自己的数据集、测试训练出的网络模型、性能评估。 本课程提供项目的数据集和Python程序文件。

    864 人正在学习 去看看 白勇

导读

随着人工智能,尤其是深度学习技术的迅速发展,基于深度学习的分割方法已在图像分割领域取得了良好的效果,相比传统的机器学习和计算机视觉方法,深度神经网络在分割精度和速度等方面都具有一定的优势。其分割准确度已经超过了传统的分割算法。可以说,深度学习算法在图像处理中具有重要的理论研究意义以及实用价值。然而,目前的深度学习算法主要依靠训练数据,需要大量的手工标记数据用于训练。由于医学图像数据量较大,而且人工标记非常的耗时费力,可能还会有主观意识产生的误差,限制了深度学习方法在医学图像分割领域的应用[1]。
半监督方法通过引入领域知识降低了算法对人工标记数据的需求量,很大程度上缓解了数据标注的难度,是解决数据标记问题的一种重要途径。

半监督

半监督学习(Semi-Supervised Learning,SSL)是模式识别和机器学习领域研究的重点问题,是监督学习与无监督学习相结合的一种学习方法。半监督学习使用大量的未标记数据,以及同时使用标记数据,来进行模式识别工作。当使用半监督学习时,将会要求尽量少的人员来从事工作,同时又能够带来比较高的准确性,因此,半监督学习目前正越来越受到人们的重视[2]。
在这里插入图片描述
详见参考这篇博客:https://www.cnblogs.com/kamekin/p/9683162.html

论文1

title:
Transfer Learning from Partial Annotations forWhole Brain Segmentation

下载地址:https://link_springer.xilesou.top/chapter/10.1007/978-3-030-33391-1_23

具体介绍详见下篇博客
https://blog.csdn.net/weixin_43876801/article/details/103158165

论文2

title:
Removing segmentation inconsistencies with semi-supervised non-adjacency constraint

下载地址:
https://sciencedirect.xilesou.top/science/article/pii/S1361841519300866

具体介绍详见下篇博客
https://blog.csdn.net/weixin_43876801/article/details/103155799

参考:
[1]基于深度学习的医学图像分割技术
[2]百度百科 半监督

2019-03-24 22:14:55 normol 阅读数 10948
  • U-Net图像语义分割实战:训练自己的数据集

    U-Net是一种基于深度学习的图像语义分割方法,尤其在医学图像分割中表现优异。 本课程将手把手地教大家使用labelme图像标注工具制作自己的数据集,生成Mask图像,并使用U-Net训练自己的数据集,从而能开展自己的图像分割应用。 本课程有三个项目实践: (1) Kaggle盐体识别比赛 :利用U-Net进行Kaggle盐体识别 (2) Pothole语义分割:对汽车行驶场景中的路坑进行标注和语义分割 (3) Kaggle细胞核分割比赛 :利用U-Net进行Kaggle细胞核分割 本课程使用keras版本的U-Net,在Ubuntu系统上用Jupyter Notebook做项目演示。 包括:数据集标注、数据集格式转换和Mask图像生成、编写U-Net程序文件、训练自己的数据集、测试训练出的网络模型、性能评估。 本课程提供项目的数据集和Python程序文件。

    864 人正在学习 去看看 白勇

在之前的一篇博客里:医学图像分割 unet实现(一),是学习并复现别人的实验。这篇将记录下自己毕设第一阶段的实验。毕设题目为:基于深度学习的肝脏肿瘤分割。
经过几番调整,最终确定:第一阶段分割出腹部图像中的肝脏,作为第二阶段的ROI(region of interest),第二阶段利用ROI对腹部图像进行裁剪,裁剪后的非ROI区域变成黑色,作为该阶段输入,分割出肝脏中的肿瘤(更新2019-4-2,已做完实验,医学图像分割 基于深度学习的肝脏肿瘤分割 实战(二)。第三阶段用随机场的后处理方法进行优化。
此外,觉得当时的那篇文章写得很不用心,没费多大功夫,复制粘贴为主。我记录的主要原因之一就是觉得这方面入门实战的文章很少,希望让情况类似的同学少走一点弯路。可现在自己写的东西过几天再去看,都会一头雾水。没有从主干思路到细节的分层讲解,没有关键点的介绍。这篇文章开始,将尽我所能,写得利于接受一点。
(不过,自己才开始学习一个多月,python和框架都是临时学的,并不能保证博客里的东西是对的,有错误的话,望指出)

正文:

目标

分割出CT腹部图像的肝脏区域。

原始数据介绍

实验用到的数据为3Dircadb,即腹部的CT图。一个病人为一个文件夹。
例如3Dircadb1.1(一共20位),该文件夹下会使用到的子文件夹为PATIENT_DICOM(病人原始腹部3D CT图),MASKS_DICOM(该文件夹下具有不同部位的分割结果,比如liver和liver tumor等等)。如下图所示:
在这里插入图片描述
PATIENT_DICOM利用软件展示效果如下:一个dcm文件包含129张切片。
在这里插入图片描述
MASKS_DICOM下的liver分割图效果如下:
在这里插入图片描述

整体思路

1、数据提取

数据读取:
从原始dcm格式读入成我们需要的数组格式

#part1
import numpy as np
import pydicom
import os
import matplotlib.pyplot as plt
import cv2
from keras.preprocessing.image import ImageDataGenerator
from HDF5DatasetWriter import HDF5DatasetWriter
from HDF5DatasetGenerator import HDF5DatasetGenerator

for i in range(1,18): # 前17个人作为测试集
   full_images = [] # 后面用来存储目标切片的列表
   full_livers = [] #功能同上
   # 注意不同的系统,文件分割符的区别
   label_path = '~/3Dircadb/3Dircadb1.%d/MASKS_DICOM/liver'%i
   data_path = '~/3Dircadb/3Dircadb1.%d/PATIENT_DICOM'%i
   liver_slices = [pydicom.dcmread(label_path + '/' + s) for s in os.listdir(label_path)]
   # 注意需要排序,即使文件夹中显示的是有序的,读进来后就是随机的了
   liver_slices.sort(key = lambda x: int(x.InstanceNumber))
   # s.pixel_array 获取dicom格式中的像素值
   livers = np.stack([s.pixel_array for s in liver_slices])
   image_slices = [pydicom.dcmread(data_path + '/' + s) for s in os.listdir(data_path)]
   image_slices.sort(key = lambda x: int(x.InstanceNumber))
   
   """ 省略进行的预处理操作,具体见part2"""
   
   full_images.append(images)
   full_livers.append(livers)
   
   full_images = np.vstack(full_images)
   full_images = np.expand_dims(full_images,axis=-1)
   full_livers = np.vstack(full_livers)
   full_livers = np.expand_dims(full_livers,axis=-1)

2、数据的预处理

1、将ct值转化为标准的hu值
至于为什么要将值进行转化,这儿就不详细说明,具体可以参考医学图像预处理(三)——windowing(ct对比增强),这篇博文中有一样的get_pixels_hu函数
2、窗口化操作
医学图像预处理(三)——windowing(ct对比增强)
3、直方图均衡化

def clahe_equalized(imgs,start,end):
   assert (len(imgs.shape)==3)  #3D arrays
   #create a CLAHE object (Arguments are optional).
   clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
   imgs_equalized = np.empty(imgs.shape)
   for i in range(start, end+1):
       imgs_equalized[i,:,:] = clahe.apply(np.array(imgs[i,:,:], dtype = np.uint8))
   return imgs_equalized

4、归一化
5、仅提取腹部所有切片中包含了肝脏的那些切片,其余的不要
医学图像预处理(四)—— 提取包含目标的切片

#part2
    # 接part1
   images = get_pixels_hu(image_slices)
   
   images = transform_ctdata(images,500,150)
   
   start,end = getRangImageDepth(livers)
   images = clahe_equalized(images,start,end)
   
   images /= 255.
   # 仅提取腹部所有切片中包含了肝脏的那些切片,其余的不要
  
   total = (end - 4) - (start+4) +1
   print("%d person, total slices %d"%(i,total))
   # 首和尾目标区域都太小,舍弃
   images = images[start+5:end-5]
   print("%d person, images.shape:(%d,)"%(i,images.shape[0]))
   
   livers[livers>0] = 1
   
   livers = livers[start+5:end-5]

3、数据增强

利用keras的数据增强接口,可以实现分割问题的数据增强。一般的增强是分类问题,这种情况,只需要对image变形,label保持不变。但分割问题,就需要image和mask进行同样的变形处理。具体怎么实现,参考下面代码,注意种子设定成一样的。

# 可以在part1之前设定好(即循环外)
seed=1
data_gen_args = dict(rotation_range=3,
                    width_shift_range=0.01,
                    height_shift_range=0.01,
                    shear_range=0.01,
                    zoom_range=0.01,
                    fill_mode='nearest')

image_datagen = ImageDataGenerator(**data_gen_args)
mask_datagen = ImageDataGenerator(**data_gen_args)
#part3 接part2
    image_datagen.fit(full_images, augment=True, seed=seed)
    mask_datagen.fit(full_livers, augment=True, seed=seed)
    image_generator = image_datagen.flow(full_images,seed=seed)
    mask_generator = mask_datagen.flow(full_livers,seed=seed)

    train_generator = zip(image_generator, mask_generator)
    x=[]
    y=[]
    i = 0
    for x_batch, y_batch in train_generator:
        i += 1
        x.append(x_batch)
        y.append(y_batch)
        if i>=2: # 因为我不需要太多的数据
            break
    x = np.vstack(x)
    y = np.vstack(y)

4、数据存储

一般而言,数据量较大的话,都会先将原始数据库的东西转化为np或者h5格式的文件,我感觉这样有两个好处,一是真正输入网络训练的时候io量会大大减少(特别是h5很适用于大的数据库),二是数据分享或者上传至服务器时也方便一点。

实验中会出现两个类,分别是写h5和读h5文件的辅助类
这读文件的类写成了generator,这样可以结合训练网络时,keras的fit_generator来使用,降低内存开销。

class HDF5DatasetWriter:
	"""用来写数据到h5文件"""
class HDF5DatasetGenerator:
    """用来读h5文件的数据"""

它们具体的实现在python h5文件的读写,因为篇幅问题,所以这儿不详述

h5文件操作需要的包import h5py

# 可以在part1之前设定好(即循环外)
# 这儿的数量需要提前写好,感觉很不方便,但不知道怎么改,我是先跑了之前的程序,计算了一共有多少
# 张图片后再写的,但这样明显不是好的解决方案
dataset = HDF5DatasetWriter(image_dims=(2782, 512, 512, 1),
                            mask_dims=(2782, 512, 512, 1),
                            outputPath="data_train/train_liver.h5")
#part4 接part3
    dataset.add(full_images, full_livers)
    dataset.add(x, y)
    # end of lop
dataset.close()

测试数据存储的全部过程

测试数据与上面训练数据的处理过程几乎一样,但测试数据不要进行数据增强

full_images2 = []
full_livers2 = []
for i in range(18,21):#后3个人作为测试样本
    label_path = '~/3Dircadb/3Dircadb1.%d/MASKS_DICOM/liver'%i
    data_path = '~/3Dircadb/3Dircadb1.%d/PATIENT_DICOM'%i
    liver_slices = [pydicom.dcmread(label_path + '/' + s) for s in os.listdir(label_path)]
    liver_slices.sort(key = lambda x: int(x.InstanceNumber))
    livers = np.stack([s.pixel_array for s in liver_slices])
    start,end = getRangImageDepth(livers)
    total = (end - 4) - (start+4) +1
    print("%d person, total slices %d"%(i,total))
    
    image_slices = [pydicom.dcmread(data_path + '/' + s) for s in os.listdir(data_path)]
    image_slices.sort(key = lambda x: int(x.InstanceNumber))
    
    images = get_pixels_hu(image_slices)
    images = transform_ctdata(images,500,150)
    images = clahe_equalized(images,start,end)
    images /= 255.
    images = images[start+5:end-5]
    print("%d person, images.shape:(%d,)"%(i,images.shape[0]))
    livers[livers>0] = 1
    livers = livers[start+5:end-5]

    full_images2.append(images)
    full_livers2.append(livers)
    
full_images2 = np.vstack(full_images2)
full_images2 = np.expand_dims(full_images2,axis=-1)
full_livers2 = np.vstack(full_livers2)
full_livers2 = np.expand_dims(full_livers2,axis=-1)

dataset = HDF5DatasetWriter(image_dims=(full_images2.shape[0], full_images2.shape[1], full_images2.shape[2], 1),
                            mask_dims=(full_images2.shape[0], full_images2.shape[1], full_images2.shape[2], 1),
                            outputPath="data_train/val_liver.h5")


dataset.add(full_images2, full_livers2)

print("total images in val ",dataset.close())

5、构建网络

这一部分不多说,是直接用的别人写好的Unet。
唯一进行的更改,就是Crop的那部分。因为,如果输入图片的高或者宽不能等于2^n(n>=m),m为网络收缩路径的层数。那么出现的问题就是,下采样的时候进行了取整操作,但是,后续在扩张路径又会有上采样,且上采样的结果会和收缩路径的特征图进行拼接,即long skip connection。若没有达到上面提到的要求,会导致拼接的两个特征图size不同,报错。

# partA
import os
import sys
import numpy as np
import random
import math
import tensorflow as tf
from HDF5DatasetGenerator import HDF5DatasetGenerator
from keras.models import Model
from keras.layers import Input, concatenate, Conv2D, MaxPooling2D, Conv2DTranspose,Cropping2D
from keras.optimizers import Adam
from keras.callbacks import ModelCheckpoint
from keras import backend as K
from skimage import io


K.set_image_data_format('channels_last')
    
def dice_coef(y_true, y_pred):
    smooth = 1.
    y_true_f = K.flatten(y_true)
    y_pred_f = K.flatten(y_pred)
    intersection = K.sum(y_true_f * y_pred_f)
    return (2. * intersection + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)


def dice_coef_loss(y_true, y_pred):
    return -dice_coef(y_true, y_pred)

def get_crop_shape(target, refer):
        # width, the 3rd dimension
        print(target.shape)
        print(refer._keras_shape)
        cw = (target._keras_shape[2] - refer._keras_shape[2])
        assert (cw >= 0)
        if cw % 2 != 0:
            cw1, cw2 = int(cw/2), int(cw/2) + 1
        else:
            cw1, cw2 = int(cw/2), int(cw/2)
        # height, the 2nd dimension
        ch = (target._keras_shape[1] - refer._keras_shape[1])
        assert (ch >= 0)
        if ch % 2 != 0:
            ch1, ch2 = int(ch/2), int(ch/2) + 1
        else:
            ch1, ch2 = int(ch/2), int(ch/2)

        return (ch1, ch2), (cw1, cw2)

def get_unet():
    inputs = Input((IMG_HEIGHT, IMG_WIDTH , 1))
    conv1 = Conv2D(32, (3, 3), activation='relu', padding='same')(inputs)
    conv1 = Conv2D(32, (3, 3), activation='relu', padding='same')(conv1)
    pool1 = MaxPooling2D(pool_size=(2, 2))(conv1)

    conv2 = Conv2D(64, (3, 3), activation='relu', padding='same')(pool1)
    conv2 = Conv2D(64, (3, 3), activation='relu', padding='same')(conv2)
    pool2 = MaxPooling2D(pool_size=(2, 2))(conv2)

    conv3 = Conv2D(128, (3, 3), activation='relu', padding='same')(pool2)
    conv3 = Conv2D(128, (3, 3), activation='relu', padding='same')(conv3)
    pool3 = MaxPooling2D(pool_size=(2, 2))(conv3)

    conv4 = Conv2D(256, (3, 3), activation='relu', padding='same')(pool3)
    conv4 = Conv2D(256, (3, 3), activation='relu', padding='same')(conv4)
    pool4 = MaxPooling2D(pool_size=(2, 2))(conv4)

    conv5 = Conv2D(512, (3, 3), activation='relu', padding='same')(pool4)
    conv5 = Conv2D(512, (3, 3), activation='relu', padding='same')(conv5)

    up_conv5 = Conv2DTranspose(256, (2, 2), strides=(2, 2), padding='same')(conv5)
    
    ch, cw = get_crop_shape(conv4, up_conv5)
    
    crop_conv4 = Cropping2D(cropping=(ch,cw), data_format="channels_last")(conv4)
    up6 = concatenate([up_conv5, crop_conv4], axis=3)
    conv6 = Conv2D(256, (3, 3), activation='relu', padding='same')(up6)
    conv6 = Conv2D(256, (3, 3), activation='relu', padding='same')(conv6)
    
    up_conv6 = Conv2DTranspose(128, (2, 2), strides=(2, 2), padding='same')(conv6)

    ch, cw = get_crop_shape(conv3, up_conv6)
    crop_conv3 = Cropping2D(cropping=(ch,cw), data_format="channels_last")(conv3)
    
    up7 = concatenate([up_conv6, crop_conv3], axis=3)
    conv7 = Conv2D(128, (3, 3), activation='relu', padding='same')(up7)
    conv7 = Conv2D(128, (3, 3), activation='relu', padding='same')(conv7)
    
    up_conv7 = Conv2DTranspose(64, (2, 2), strides=(2, 2), padding='same')(conv7)
    ch, cw = get_crop_shape(conv2, up_conv7)
    crop_conv2 = Cropping2D(cropping=(ch,cw), data_format="channels_last")(conv2)

    up8 = concatenate([up_conv7, crop_conv2], axis=3)
    conv8 = Conv2D(64, (3, 3), activation='relu', padding='same')(up8)
    conv8 = Conv2D(64, (3, 3), activation='relu', padding='same')(conv8)
    
    up_conv8 = Conv2DTranspose(64, (2, 2), strides=(2, 2), padding='same')(conv8)
    ch, cw = get_crop_shape(conv1, up_conv8)
    crop_conv1 = Cropping2D(cropping=(ch,cw), data_format="channels_last")(conv1)


    up9 = concatenate([up_conv8, crop_conv1], axis=3)
    conv9 = Conv2D(32, (3, 3), activation='relu', padding='same')(up9)
    conv9 = Conv2D(32, (3, 3), activation='relu', padding='same')(conv9)

    conv10 = Conv2D(1, (1, 1), activation='sigmoid')(conv9)

    model = Model(inputs=[inputs], outputs=[conv10])

    model.compile(optimizer=Adam(lr=1e-5), loss=dice_coef_loss, metrics=[dice_coef])

    return model

6、进行训练并测试

这其中主要包括训练文件与测试文件的读取,Checkpoint回掉函数的设定,fit_generator的使用,模型预测,并对预测结果进行保存。

# partB 接partA
IMG_WIDTH = 512
IMG_HEIGHT = 512
IMG_CHANNELS = 1
TOTAL = 2782 # 总共的训练数据
TOTAL_VAL = 152 # 总共的validation数据
# part1部分储存的数据文件
outputPath = './data_train/train_liver.h5' # 训练文件
val_outputPath = './data_train/val_liver.h5'
#checkpoint_path = 'model.ckpt'
BATCH_SIZE = 8 # 根据服务器的GPU显存进行调整

class UnetModel:
    def train_and_predict(self):
        
        reader = HDF5DatasetGenerator(dbPath=outputPath,batchSize=BATCH_SIZE)
        train_iter = reader.generator()
        
        test_reader = HDF5DatasetGenerator(dbPath=val_outputPath,batchSize=BATCH_SIZE)
        test_iter = test_reader.generator()
        fixed_test_images, fixed_test_masks = test_iter.__next__()
#   
        
        model = get_unet()
        model_checkpoint = ModelCheckpoint('weights.h5', monitor='val_loss', save_best_only=True)
        # 注:感觉validation的方式写的不对,应该不是这样弄的
        model.fit_generator(train_iter,steps_per_epoch=int(TOTAL/BATCH_SIZE),verbose=1,epochs=500,shuffle=True,
                            validation_data=(fixed_test_images, fixed_test_masks),callbacks=[model_checkpoint])
#        
        reader.close()
        test_reader.close()
        
        
        print('-'*30)
        print('Loading and preprocessing test data...')
        print('-'*30)
        
        print('-'*30)
        print('Loading saved weights...')
        print('-'*30)
        model.load_weights('weights.h5')
    
        print('-'*30)
        print('Predicting masks on test data...')
        print('-'*30)
        
        
        imgs_mask_test = model.predict(fixed_test_images, verbose=1)
        np.save('imgs_mask_test.npy', imgs_mask_test)
    
        print('-' * 30)
        print('Saving predicted masks to files...')
        print('-' * 30)
        pred_dir = 'preds'
        if not os.path.exists(pred_dir):
            os.mkdir(pred_dir)
        i = 0
        
        
        for image in imgs_mask_test:
            image = (image[:, :, 0] * 255.).astype(np.uint8)
            gt = (fixed_test_masks[i,:,:,0] * 255.).astype(np.uint8)
            ini = (fixed_test_images[i,:,:,0] *255.).astype(np.uint8)
            io.imsave(os.path.join(pred_dir, str(i) + '_ini.png'), ini)
            io.imsave(os.path.join(pred_dir, str(i) + '_pred.png'), image)
            io.imsave(os.path.join(pred_dir, str(i) + '_gt.png'), gt)
            i += 1

unet = UnetModel()
unet.train_and_predict()

模型跑的过程如图。
在这里插入图片描述
预测结果可视化展示
在这里插入图片描述

没有更多推荐了,返回首页