医学图像分割_医学图像分割综述 - CSDN
精华内容
参与话题
  • 最近,医学图像分割这一新分割应用领域也广泛受关注,出现了好多新方法、新数据集,今天小编专门整理最新七篇医学图像分割相关应用论文—对抗攻击、跨模态域自适应、迁移学习、one-shot、GAN等。 1、Non-...

    【导读】最近小编推出CVPR2019图卷积网络相关论文、CVPR2019生成对抗网络相关视觉论文、【可解释性】相关论文和代码,CVPR视觉目标跟踪相关论文,CVPR视觉问答相关论文,反响热烈。最近,医学图像分割这一新分割应用领域也广泛受关注,出现了好多新方法、新数据集,今天小编专门整理最新七篇医学图像分割相关应用论文—对抗攻击、跨模态域自适应、迁移学习、one-shot、GAN等。

    在这里插入图片描述

    1、Non-Local Context Encoder: Robust Biomedical Image Segmentation against Adversarial Attacks(非局部上下文编码器: 针对对抗攻击的鲁棒生物医学图像分割)

    AAAI ’19 Oral

    作者:Xiang He, Sibei Yang, Guanbin Li?, Haofeng Li, Huiyou Chang, Yizhou Yu

    摘要:基于深度卷积神经网络(CNNs)的生物医学图像分割技术近年来受到广泛关注。然而,它对对抗性样本的脆弱性不容忽视。本文首次发现所有基于CNN的先进生物医学图像分割模型都对对抗扰动敏感。这限制了这些方法在安全关键的生物医学领域的应用。在本文中,我们发现生物医学图像中的全局空间依赖关系和全局上下文信息可以被用来抵御对抗攻击。为此,提出了一种基于非局部上下文编码(NLCE)的短时和长时程空间依赖关系建模方法,并对全局上下文进行编码,通过信道感知增强特征激活。NLCE模块增强了非局部上下文编码网络(NLCEN)的鲁棒性和准确性,NLCEN利用NLCE模块学习鲁棒性增强的金字塔特征表示,并将不同层次的信息集成在一起。在肺和皮肤病变分割数据集上的实验表明,NLCEN在抵抗对抗攻击方面优于任何其他先进的生物医学图像分割方法。此外,NLCE模块可用于提高其他基于CNN的生物医学图像分割方法的鲁棒性。

    在这里插入图片描述
    网址:

    https://arxiv.org/abs/1904.12181v1

    2、Synergistic Image and Feature Adaptation: Towards Cross-Modality Domain Adaptation for Medical Image Segmentation(协同图像与特征自适应: 面向医学图像分割的跨模态域自适应)

    AAAI ’19 Oral

    作者:Cheng Chen, Qi Dou, Hao Chen, Jing Qin, Pheng-Ann Heng

    摘要:本文提出了一种新的无监督域自适应框架——协同图像与特征自适应(SIFA),有效地解决了domain shift问题。域自适应是近年来深度学习研究的一个重要和热点问题,其目的是解决神经网络应用于新的测试领域时性能下降的问题。我们提出的SIFA是一个优雅的学习图,它从图像和特征两个角度展示了适应性的协同融合。特别是,我们同时跨域转换图像的外观并且增强所提取的特征的域不变性来进行分割任务。在端到端学习过程中,特征编码器层由两个透视图共享,以掌握它们的共同特征。在不使用目标域的任何标注的情况下,我们的统一模型的学习是以对抗性损失为指导,从各个方面使用多个判别器。我们已经通过对心脏结构的跨模态医学图像分割这一具有挑战性的应用广泛地验证了我们的方法。实验结果表明,我们的SIFA模型能够将性能下降幅度从17.2%恢复到73.0%,并显著优于现有的最先进方法。

    在这里插入图片描述
    网址:

    https://arxiv.org/abs/1901.08211v3

    代码链接:

    https://github.com/cchen-cc/SIFA

    3、Data augmentation using learned transformations for one-shot medical image segmentation(利用学习变换进行数据增强,实现one-shot医学图像分割)

    CVPR ’19

    作者:Amy Zhao, Guha Balakrishnan, Frédo Durand, John V. Guttag, Adrian V. Dalca

    摘要:图像分割是医学应用中的一个重要课题。基于卷积神经网络的方法获得了最先进的精度;然而,它们通常依赖于带有大型标记数据集的监督训练。医学图像的标注需要大量的专业知识和时间,而典型的手工调优的数据增强方法无法捕捉这些图像中的复杂变化。我们提出了一种自动数据增强方法来合成标记医学图像。我们在分割磁共振成像(MRI)脑扫描的任务中展示了我们的方法。我们的方法只需要一次分段扫描,并利用半监督方法中的其他未标记扫描。我们从图像中学习转换模型,并使用该模型和带标记的示例来合成其他带标记的示例。每个转换都由空间变形场和强度变化组成,从而能够综合复杂的效果,如解剖学和图像采集过程的变化。我们展示了使用这些新示例训练有监督的分割器,对于one-shot生物医学图像分割的最先进方法有着显着的改进。我们的代码可以在https://github.com/xamyzhao/brainstorm上找到。

    在这里插入图片描述
    网址:

    https://arxiv.org/abs/1902.09383

    代码链接:

    https://github.com/xamyzhao/brainstorm

    4、Autofocus Layer for Semantic Segmentation(用于语义分割的自动聚焦层)

    作者:Yao Qin, Konstantinos Kamnitsas, Siddharth Ancha, Jay Nanavati, Garrison Cottrell, Antonio Criminisi, Aditya Nori

    摘要:为了提高神经网络的多尺度处理能力,提出了一种自聚焦卷积层用于语义分割。自动聚焦层根据处理过的上下文自适应地改变有效接受域的大小,以生成更强大的特征。这是通过将具有不同膨胀率的多个卷积层并行化来实现的,并结合一种注意力机制,该机制学会将注意力集中在由上下文驱动的最优尺度上。通过共享并行卷积的权值,我们使网络的规模不变,只增加了少量的参数。提出的自动聚焦层可以很容易地集成到现有网络中,提高模型的表示能力。我们在骨盆CT多器官分割和MRI脑肿瘤分割的挑战性任务中评估了我们的模型,并取得了非常有前景的性能。

    在这里插入图片描述
    网址:

    https://arxiv.org/abs/1805.08403v3

    5、Few-shot 3D Multi-modal Medical Image Segmentation using Generative Adversarial Learning(利用生成式对抗学习进行Few-shot三维多模态医学图像分割)

    作者:Arnab Kumar Mondal, Jose Dolz, Christian Desrosiers

    摘要:我们解决了在极少数标记示例可供训练的情况下分割三维多模态医学图像的问题。利用最近成功的对抗学习进行半监督分割,我们提出了一种基于生成对抗网络(GAN)的新方法来训练具有标记和未标记图像的分割模型。该方法通过学习识别生成网络中获得的真假patch,来防止过拟合。我们的工作扩展了目前的专注于二维单模态图像对抗性学习方法到更具挑战性的三维体多模态。针对iSEG-2017和MRBrainS 2013数据集的脑MRI分割问题,对该方法进行了评估。与在完全监督下训练的最先进的分割网络相比,性能有了显著的提高。此外,我们的工作提供了对半监督分割的不同GAN架构的综合分析,显示了最近的技术,如特征匹配,以产生比传统对抗训练方法更高的性能。我们的代码可以通过https://github.com/arnab39/FewShot_GAN-Unet3D公开获取。
    在这里插入图片描述

    网址:

    https://arxiv.org/abs/1810.12241v1

    代码链接:

    https://github.com/arnab39/FewShot_GAN-Unet3D

    6、nnU-Net: Breaking the Spell on Successful Medical Image Segmentation(nnU-Net)

    作者:Fabian Isensee, Jens Petersen, Simon A. A. Kohl, Paul F. Jäger, Klaus H. Maier-Hein

    摘要:由于数据集的多样性,语义分割是医学图像分析中的一个热门子领域,每年都有大量的新方法被提出。然而,这个不断增长的系列方法变得越来越难以理解。同时,许多提出的方法不能推广到实验之外,从而阻碍了在新数据集上开发分割算法的过程。这里我们介绍nnU-Net(“no-new-Net”),这个框架可以自适应任何给定的新数据集。虽然到目前为止这个过程完全由人为驱动,但我们首先尝试根据给定数据集的属性自动执行必要的调整,例如预处理,精确的patch大小,batch大小和推理设置。值得注意的是,nnU-Net去掉了通常在文献中提出的架构上的附加功能,只依赖于嵌入在健壮的训练方案中的简单U-Net架构。开箱即用,nnU-Net在六个完善的细分挑战中实现了最先进的性能。源代码可从https://github.com/MIC-DKFZ/nnunet获得。

    在这里插入图片描述
    网址:

    https://arxiv.org/abs/1904.08128v1

    代码链接:

    https://github.com/MIC-DKFZ/nnunet

    7、PnP-AdaNet: Plug-and-Play Adversarial Domain Adaptation Network with a Benchmark at Cross-modality Cardiac Segmentation(PnP-AdaNet:即插即用的对抗性域自适应网络,用于跨模态心脏分割的基准)

    作者:Qi Dou, Cheng Ouyang, Cheng Chen, Hao Chen, Ben Glocker, Xiahai Zhuang, Pheng-Ann Heng

    摘要:深卷积网络在各种医学图像计算任务中表现出了最先进的性能。利用来自不同模式的图像进行相同的分析任务具有临床益处。然而,深度模型对不同分布的测试数据的泛化能力仍然是一个主要的挑战。在本文中,我们提出了PnPAdaNet(即插即用的对抗域自适应网络)来适应不同医学图像模式(如MRI和CT)之间的分割网络。我们建议以一种无监督的方式对齐源域和目标域的特征空间,从而解决重要的domain shift问题。具体地,域自适应模块灵活地替换源网络的早期编码器层,并且在域之间共享更高层。通过对抗性学习,我们构建了两个判别器,其输入分别是多级特征和预测分割掩码。我们在非配对MRI和CT中的心脏结构分割任务验证了我们的域自适应方法。综合消融研究的实验结果证明了我们提出的PnP-AdaNet的优异功效。此外,我们为心脏数据集引入了一个新的基准,用于无监督跨模态域自适应的任务。我们将公开我们的代码和数据集,旨在促进医学成像这一具有挑战性但重要的研究课题的未来研究。

    在这里插入图片描述
    网址:

    https://arxiv.org/abs/1812.07907v1

    代码链接:

    https://github.com/carrenD/Medical-Cross-Modality-Domain-Adaptation

    -END-

    展开全文
  • 医学图像分割综述(上)

    万次阅读 2017-11-28 11:23:45
    医学图像分割是医学图像处理与分析领域的复杂而关键的步骤,其目的是将医学图像中具有某些特殊含义的部分分割出来,并提取相关特征,为临床诊疗和病理学研究提供可靠的依据,辅助医生作出更为准确的诊断。...

           医学图像分割是医学图像处理与分析领域的复杂而关键的步骤,其目的是将医学图像中具有某些特殊含义的部分分割出来,并提取相关特征,为临床诊疗和病理学研究提供可靠的依据,辅助医生作出更为准确的诊断。由于医学图像自身的复杂性,在分割过程中需要解决不均匀及个体差异等一系列问题,所以一般的图像分割方法难以直接应用于医学图像分割。当前,医学图像分割仍在从手动分割或半自动分割向全自动分割发展[1]。

           图像分割的定义

           令R代表整个图像区域,对R的分割可看做将R分成若干个满足以下条件的非空子集(子区域){R1,R2,R3…Rn}。该集合满足以下特性:

              目前国内外广泛应用的医学图像分割方法有很多种,许多学者试应用数学、物理、光学、计算机等领域知识拓展医学图像分割的理论方法。  

              图像分割方法可以分为以下几类

    (1)基于阈值的分割:通过阈值对不同的物体进行分割。

         阈值分割是最常见的并行直接检测区域的分割方法。[2]如果只用选取一个阈值称为单阈值分割,它将图像分为目标和背景;如果需用多个阈值则称为多阈值方法,图像将被分割为多个目标区域和背景,为区分目标,还需要对各个区域进行标记。阈值分割方法基于对灰度图像的一种假设:目标或背景内的相邻像素间的灰度值是相似的,但不同目标或背景的像素在灰度上有差异,反映在图像直方图上就是不同目标和背景对应不同的峰。选取的阈值应位于两个峰之间的谷,从而将各个峰分开。

         阈值分割的优点是实现相对简单,对于不类的物体灰度值或其他特征值相差很大时,能很有效的对图像进行分割。阈值分割通常作为医学图像的预处理,然后应用其他一系列分割方法进行后处理。

         阈值分割的缺点是不适用于多通道图像和特征值相差不大的图像,对于图像中不存在明显的灰度差异或各物体的灰度值范围有较大重叠的图像分割问题难以得到准确的结果。另外,由于它仅仅考虑了图像的灰度信息而不考虑图像的空间信息,阈值分割对噪声和灰度不均匀很敏感。

         阈值选取的几种方法为直方图阈值分割法、类间方差阈值分割法、二维最大熵值分割法和模糊阈值分割法。



    (2)基于边缘的分割:先确定边缘像素,并把它们连接在一起,以构成所需的边界

        基于边缘的分割方法可以说是人们最早研究的方法,基于在区域边缘上的像素灰度值的变化往往比较剧烈,它试图通过检测不同区域间的边缘来解决图像分割问题。边缘检测技术可以按照处理的技术分为串行边缘检测以及并行边缘检测。所谓串行边缘检测是指:要想确定当前像素点是否属于欲检测边缘上的一点,取决于先前像素的验证结果;而在并行边缘检测技术中,一个像素点是否属于检测边缘上的一点取决于当前正在检测的像素点以及该像素点的一些相邻像素点,这样该模型可以同时用于图像中的所有像素点,因而称之为并行边缘检测技术。 根据灰度变化的特点,常见的边缘可分为跃阶型、房顶型和凸缘型。

           边缘检测分为三类:点检测、线检测和边缘检测。点检测是检测图像中孤立的点,线检测主要是哈夫变换,利用图像全局特性而直接检测目标轮廓,即可将边缘像素连接起来组成区域封闭边界的常用方法。边缘检测依据两个具有不同灰度值的相邻区域之间总存在边缘,边缘检测算子很多,如梯度算子,方向算子,拉普拉斯算子,马尔算子,综合正交算子,坎尼算子等。

         


      (3)基于区域的分割:把各像素划归到各个物体或区域中

        图像分割-把图像分解为若干个有意义的子区域,而这种分解-基于物体有平滑均匀的表面,与图像中强度恒定或缓慢变化的区域相对应,即每个子区域都具有一定的均匀性质前面所讨论的边缘、阈值,没有明显使用分割定义中的均匀测度度量区域分割-直接根据事先确定的相似性准则,直接取出若干特征相近或相同象素组成区域。常用的区域分割-区域增长(区域生长)、区域分裂-合并方法等。

        区域生长和分裂合并是两种典型的串行区域分割方法。其特点是将分割过程分解为顺序的多个步骤,其中后续步骤要根据前面步骤的结果进行判断而确定。
        区域生长的基本思想是将具有相似性质的像素集合起来构成区域,该方法需要先选取一个种子点,然后依次将种子像素周围的相似像素合并到种子像素所在的区域中。区域生长算法的研究重点一是特征度量和区域增长规则的设计,二是算法的高效性和准确性。区域增长方式的优点是计算简单。与阈值分割类似,区域增长也很少单独使用,往往是与其他分割方法一起使用,特别适用于分割小的结构如肿瘤和伤疤。区域生长的缺点是它需要人工交互以获得种子点,这样使用者必须在每个需要抽取出的区域中植入一个种子点。同时,区域增长方式也对噪声敏感,导致抽取出的区域有空洞或者在局部体效应的情况下将分开的区域连接起来。为解决这些问题,J.F. Mangin 等提出了一种同伦的区域生长方式,以保证初始区域和最终抽取出的区域的拓扑结构相同。Shu-Yen Wan 等提出的对称区域增长算法有效地弥补了原算法对种子点敏感和占用内存多的弱点,而且对3D连接对象标记和删除空洞的算法效率较高。另外,模糊连接度方法与区域增长相结合也是一个发展方向。

        区域生长方法将图像以像素为基本单位来进行操作。主要方法可以基于区域灰度差,基于区域内灰度分布统计性质,或基于区域形状。

        分裂合并方法利用图像数据的金字塔或四叉树结构的层次概念,将图像划分为一组任意不相交的初始区域,即可以从图像的这种金字塔或四叉树数据结构的任一中间层开始,根据给定的均匀性检测准则,进行分裂和合并这些区域,逐步改善区域划分的性能,知道最后将图片分成数量最少的均匀区域为止。


    (4)基于运动的分割:通过视频物体运动进行分割

        随着多媒体技术的发展,视频图像得到广泛应用,由一系列时间上连续的2-D图像组成。从空间分割的角度来看,视频图像分割主要是希望把其中独立运动的区域(目标)逐帧检测处理。从时间分割的角度来看,主要是把连续的序列分解为时间片断。


    (5)  基于活动轮廓模型的方法
        活动轮廓模型,又称Snake模型,由Kass在1987年提出。由于 Snake模型有着高效的数值方案以及严谨的数学基础,且应用广泛,提出后即成为图像分割领域所研究的热点。

        原始的Snake模型其基本思想是通过能量最小化,将一条带有能量函数的初始曲线朝着待检测的目标轮廓方向逐步变形与运动,最终收敛到目标边界,得到一个光滑并且连续的轮廓。原始Snake模型首先在目标区域附近手动设置一条闭合曲线作为Snake模型的初始轮廓线,初始轮廓线随时间不断演化,越来越逼近目标边界,当演化停止时即获得最终结果。Snake算法的3个主要步骤为:(1)读取数据;(2)数据的预处理,如图像的去噪、求梯度,求外力场等;(3)确定模型的参数与迭代次数,然后开始迭代。

        原始的Snake模型存在难以捕捉目标凹陷边界及对初始轮廓线敏感等不足,因而后续有许多改进的方法。在此也不过多描述。


    (6)模糊聚类算法

        大多数的医学图像具有模糊性,图像质量低、噪声大。模糊聚类法将模糊集理论与聚类算法相结合,模糊集理论对图像的不确定性具备较好描述能力,将此特点结合到分类中,应用到医学图像分割领域。该方法不是以“一刀切”的方式将像素点硬性分到某一区域,而是引入模糊理论中“隶属度”的概念,将像素点分到隶属程度高的区域中去,提高分割的准确率。目前最常用的是模糊C- 均值算法 (FCM),该算法通过两次迭代得到最优边界。


    (7)基于小波变换的方法

        小波变换是对Fourier分析的继承与发展,利用小波变换进行医学图像分割的基本方法是通过小波变换将图像直方图分解成不同级别的系数,用尺度控制并依照小波系数和给定的分割准则来选择阈值。小波变换在较大尺度上由噪音引起的细小突变较少,容易描述医学图像信号的整体行为,可检测出医学图像灰度值变化较大的轮廓,因此可以通过在不同尺度下逐步确定阈值来处理医学图像。

        

       以上7类均为传统分割方法。每一种都有诸多变形与改进。还有基于图论、图谱引导、数学形态学等等研究。但作者的主要任务是用深度算法研究医学图像中的分割任务,因而本文仅作一个简单的概述,下篇将重点讲述神经网络与医学图像的分割。



    参考文献:

    【1】医学图像分割综述:楼琼,11106109

    【2】刘宇, 陈胜. 医学图像分割方法综述[J]. 电子科技, 2017, 30(8):169-172.

    【3】黄文博, 燕杨, 王云吉. 医学图像分割方法综述[J]. 长春师范大学学报, 2013, 32(4):22-25.



    展开全文
  • 医学图像分割 (MICCAI 2019)

    千次阅读 2020-04-29 01:04:28
    一.头部 2.《FocusNet: Imbalanced Large and Small Organ Segmentation with an End-to-End Deep Neural Network for Head and Neck CT Images》 针对 Head and Neck(HaN) CT images 数据集来源:非公共数据集 ...

    一.头部

    1.《FocusNet: Imbalanced Large and Small Organ Segmentation with an End-to-End Deep Neural Network for Head and Neck CT Images》

    针对 Head and Neck(HaN) CT images
    数据集来源:非公共数据集 和 MICCAI Head and Neck Auto Segmentation Challenge 2015 dataset
    创新点:对小器官额外使用一个定位和分割网络,和大器官的分割结果整合,解决器官大小尺度不一问题,提升整体分割效果。
    在这里插入图片描述
    包括主题网络S-Net, 小目标定位网络SOL-net, 小目标分割网络SOS-Net.
    S-Net 为改进的U-Net 如下所示:
    在这里插入图片描述
    Loss 采取Focal loss 和 Dice loss.

    2.《Deep Cascaded Attention Network for Multi-task Brain Tumor Segmentation》

    任务:brain tumor segmentation(3D)
    数据集来源:BRATS 2018 dataset
    具体要求:区分brain tumor的whole tumor (WT), tumor core (TC) 和 enhancing tumor (ET). 这三个区域具有典型层次关系,WT 包含TC, TC又包含ET。
    在这里插入图片描述
    Cascaded attention 模块很好的利用了分割任务的层次关系
    对应的attention map M, 通过下式进行可视化:c代表channel, 即通过L2-norm得到average pixel-wise activation。
    在这里插入图片描述
    每个branch的网络结构如下:
    类U-Net结构,中间多加了Feature Bridge 模块。
    在这里插入图片描述
    Loss 采用multi-class soft Dice Loss和Focal Loss组合形式
    在这里插入图片描述

    3.《X-Net: Brain Stroke Lesion Segmentation Based on Depthwise Separable Convolution and Long-Range Dependencies》

    任务:Brain Stroke Lesion Segmentation
    数据集:Anatomical Tracings of Lesions After Stroke (ATLAS) dataset
    网络结构如下:
    具体实现包括嵌入Depthwise Separable Convolution, 加入FSM模块(类似Non-Local)等
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
    分割效果对比如下:
    在这里插入图片描述

    4.《CLCI-Net: Cross-Level Fusion and Context Inference Networks for Lesion Segmentation of Chronic Stroke》

    数据集同 3.
    网络结构如下:
    在这里插入图片描述
    创新点包括:
    (1)使用Cross-Level feature Fusion (CLF) 策略(dense/密集连接策略)融合multi-scale 特征.
    (2)CLF策略进一步拓展了ASPP。 即在原来ASPP5个分支的基础上又增加了4个分支,对应前面的各级融合后的multic-scale特征。
    (3)用ConvLSTM替换了传统U-Net 在decoder 部分 high-level特征与encoder 传来的 low-level特征 简单concate的融合方式。

    分割效果对比如下:
    在这里插入图片描述

    5.《Refined Segmentation R-CNN: A Two-Stage Convolutional Neural Network for Punctate White Matter Lesion Segmentation in Preterm Infants》

    任务:punctate white matter lesion (PWML) segmentation
    数据集来源:非公共数据集
    在这里插入图片描述
    该病特点:病灶非常小
    Mask-rnn架构-----Two-Stage 方法,先检测再分割
    H-RPN网络抽取特征图生成一系列包含周围信息的ROIs,送入network head, head借助周围信息进行class和box的修正,然后将修正后的ROIs送入LRS-Net 进行病灶分割。
    因为病灶太小,H-RPN网络使用ReLU加强对True Positive 区域的提取;扩大了box的大小从而包含周围信息,降低box预测偏差,也有利于后续修正;同时因为正样本太少,缩小了proposal的数量,避免正负样本过度不平衡。
    LRS-Net 中使用Dense CRF修正分割图。
    不同方法分割效果对比如下:
    在这里插入图片描述

    二.背部

    1.《Multi-resolution Path CNN with Deep Supervision for Intervertebral Disc Localization and Segmentation》

    任务:Intervertebral Disc(IVDs) Localization and Segmentation
    数据集来源:MICCAI 2018 IVDM3Seg Challenge dataset
    网络结构如下:
    Multi-Resolution Path CNN with deep supervision(MRP-DSN)
    每个路径分支独立负责一个resolution的预测,并配有对应resolution的Ground Truth 加以监管,彼此不互相干扰,使得训练更有效,也不易陷入相同的局部极小值,最后融合到一起给出最终预测。
    (具体实现包括Denseblock模块, ASPP模块等)
    在这里插入图片描述
    预测效果如下:
    在这里插入图片描述

    三.腹部

    Colorectal adenocarcinoma

    1.《Deep Segmentation-Emendation Model for Gland Instance Segmentation》

    Gland Instance segmentation

    分割医学图像数据来源:2015 MICCAI Gland Segmentation (GlaS) Challenge datasetcolorectal adenocarcinoma gland (CRAG) dataset
    数据如下所示:
    在这里插入图片描述
    分割网络:
    DSE model 在这里插入图片描述
    DSE model = Segmentation Network + Emendation Network
    Segmentation Network 进行正向预测,得到分割预测
    Emendation Network 进行反向预测,即预测出错的部分
    在train阶段,SN网络逐步学习提升分割能力,EN学习提升检测错误的能力。二者都受相应Ground Truth监管。
    EN网络的Ground Truth由SN网络给出分割预测图和分割的Ground Truth 对比得到,具体定义如下:
    在这里插入图片描述
    通过训练,EN网络逐步具备预测出错部分的能力。

    在test阶段,SN网络给出预测后,EN网络给出出错部分,进行修正,refine后的分割图为最终分割结果。具体的:SN网络给出的预测在EN网络的预测指导下修正错误。即:如果Ei=0, 代表预测正确,无需修正;如果Ei=1,代表预测范围小了,当前点也属于分割区(对应上述第二个if),所以将该点修正为1;如果Ei=2, 代表预测范围大了,当前点不属于分割区,所以将该点修正为0。
    在这里插入图片描述
    预测效果和其他方法对比如下:
    在这里插入图片描述
    Seg-Net的训练采用cross-entropy loss
    Eme-Net的训练因为存在严重类别不平衡问题,采用multi-class Dice loss 和cross-entropy (ce) loss 结合的方式(Dice loss 可以缓解类别不平衡问题)。
    在这里插入图片描述

    本篇最大的创新点就在于提出了一个用于预测(检查)错误部分的网络从而进行修正完善。

    其他

    1.《ET-Net: A Generic Edge-aTtention Guidance Network for Medical Image Segmentation》

    在这里插入图片描述
    测试任务和数据包括:
    1.Optic Disc and Cup Segmentation in Retinal Images
    the REFUGE1 dataset 和 the Drishti-GS dataset
    2.Vessel Segmentation in Retinal Images
    DRIVE dataset
    3.Lung Segmentation in X-Ray Images
    Montgomery County (MC) dataset
    4.Lung Segmentation in CT Images
    The Lung Nodule Analysis (LUNA) competition dataset
    在这里插入图片描述
    两个预测模块的Loss 均采用Lovasz-Softmax loss (对于类别不平衡问题,该loss的表现比cross entropy loss 要好)
    在这里插入图片描述

    2.《Selective Feature Aggregation Network with Area-Boundary Constraints for Polyp Segmentation》

    任务:polyp segmentation
    数据集来源:the EndoScene dataset
    网络结构采用1个encoder, 2个decoder,分别预测area 和 boundary,从而提升整体息肉分割效果。
    在这里插入图片描述
    创新点:
    (1)在U-net 结构上加入Up-Concatenations(图中斜向上的红线)。捕获multi-level 特征。
    (2)encoder 和 decoder 内嵌入SKM(Selective Kernel Module)模块。融合multi-scale 特征。
    在这里插入图片描述
    (3)Loss 的设计。loss 由4部分组成。如下所示:
    在这里插入图片描述La 为 area loss,由 binary cross-entropy loss 和 dice loss组成。

    在这里插入图片描述
    Lb 为boundary loss, 是 binary cross-entropy loss
    在这里插入图片描述
    LC1和LC2为Area-Boundary Constraint Loss。LC1最小化通过area推断出的boundary 和对应Ground Truth的差异,是binary cross-entropy loss;LC2最小化通过area推断出的boundary 和boundary 分支给出的结果之间的差异,以保证内部一致性,是K-L divergence loss
    在这里插入图片描述

    展开全文
  • 医学图像分割 unet实现(一)

    万次阅读 多人点赞 2019-03-13 12:27:56
    这次实验用来学习unet网络实现图像分割(keras, backend: tensorflow)。 数据集DRIVE:为眼部图像,目的是分割出眼部血管。 数据集结构: 上面分别是训练的原始图片images、first_manual、mask 整体流程: 1、前期...

    这次实验用来学习unet网络实现图像分割(keras, backend: tensorflow)。
    数据集DRIVE:为眼部图像,目的是分割出眼部血管。
    数据集结构:在这里插入图片描述
    train_imagesfirst_manual
    在这里插入图片描述
    上面分别是训练的原始图片images、first_manual、mask

    整体流程:

    • 1、前期准备:将所有图片写入h5文件,(为了后期训练网络时减少io时间)
    • 2、训练网络
      • 2.1、获取训练图片和groundtruth图片
        • 2.1.1、 读取hdf5文件,获取img array
        • 2.1.2、 预处理所有的img
        • 2.2.3、 提取patches
      • 2.2、构建model,并训练
      • 2.3、模型保存(包括architecture 和 weights)
    • 3、测试
      • 3.1、获取测试图片
      • 3.2、model加载
      • 3.3、model predict
      • 3.4、测试结果重建,可视化显示分割结果(patches->full image)
      • 3.5、用不同的metrics测评model结果

    文档结构
    在这里插入图片描述
    注意,lib文件夹里的加上__init__.py很重要,加上后,这个文件夹才能被python识别为一个package,从而,可以在run_training.py中写from lib.help_function import *

    第一步
    python prepare_dataset_DRIVE.py
    将图片读取为ndarray格式,并保存至hdf5文件

    # -*- coding: utf-8 -*-
    
    #==========================================================
    #
    #  This prepare the hdf5 datasets of the DRIVE database
    #
    #============================================================
    
    import os
    import h5py
    import numpy as np
    from PIL import Image
    """
    PIL 读取的图片为RGB通道的Image格式,
    cv2 读取的图片为BGR通道的ndarray格式
    skimage 读取的图片为RGB通道的ndarray格式
    """
    
    
    
    def write_hdf5(arr,outfile):
      with h5py.File(outfile,"w") as f:
        f.create_dataset("image", data=arr, dtype=arr.dtype)
    
    
    #------------Path of the images --------------------------------------------------------------
    #train
    original_imgs_train = "./DRIVE/training/images/"
    groundTruth_imgs_train = "./DRIVE/training/1st_manual/"
    borderMasks_imgs_train = "./DRIVE/training/mask/"
    #test
    original_imgs_test = "./DRIVE/test/images/"
    groundTruth_imgs_test = "./DRIVE/test/1st_manual/"
    borderMasks_imgs_test = "./DRIVE/test/mask/"
    #---------------------------------------------------------------------------------------------
    
    # 图像原始数量、通道数、高、宽
    Nimgs = 20
    channels = 3
    height = 584
    width = 565
    dataset_path = "./DRIVE_datasets_training_testing/"
    
    def get_datasets(imgs_dir,groundTruth_dir,borderMasks_dir,train_test="null"):
        imgs = np.empty((Nimgs,height,width,channels))
        groundTruth = np.empty((Nimgs,height,width))
        border_masks = np.empty((Nimgs,height,width))
        for path, subdirs, files in os.walk(imgs_dir): #list all files, directories in the path
            for i in range(len(files)):
                #original
                print("original image: ",files[i])
                img = Image.open(imgs_dir+files[i])
                imgs[i] = np.asarray(img)
                #corresponding ground truth
                groundTruth_name = files[i][0:2] + "_manual1.gif"
                print("ground truth name: ", groundTruth_name)
                g_truth = Image.open(groundTruth_dir + groundTruth_name)
                groundTruth[i] = np.asarray(g_truth)
                #corresponding border masks
                border_masks_name = ""
                if train_test=="train":
                    border_masks_name = files[i][0:2] + "_training_mask.gif"
                elif train_test=="test":
                    border_masks_name = files[i][0:2] + "_test_mask.gif"
                else:
                    print("specify if train or test!!")
                    exit()
                print("border masks name: ", border_masks_name)
                b_mask = Image.open(borderMasks_dir + border_masks_name)
                border_masks[i] = np.asarray(b_mask)
    
        print("imgs max: ", str(np.max(imgs)))
        print("imgs min: ", str(np.min(imgs)))
        assert(np.max(groundTruth)==255 and np.max(border_masks)==255)
        assert(np.min(groundTruth)==0 and np.min(border_masks)==0)
        print("ground truth and border masks are correctly withih pixel value range 0-255 (black-white)")
       
        assert(imgs.shape == (Nimgs,height,width,channels))
        groundTruth = np.reshape(groundTruth,(Nimgs,height,width,1))
        border_masks = np.reshape(border_masks,(Nimgs,height,width,1))
        assert(groundTruth.shape == (Nimgs,height,width,1))
        assert(border_masks.shape == (Nimgs,height,width,1))
        return imgs, groundTruth, border_masks
    
    if not os.path.exists(dataset_path):
        os.makedirs(dataset_path)
    #getting the training datasets
    imgs_train, groundTruth_train, border_masks_train = get_datasets(original_imgs_train,groundTruth_imgs_train,borderMasks_imgs_train,"train")
    print("saving train datasets")
    write_hdf5(imgs_train, dataset_path + "DRIVE_dataset_imgs_train.hdf5")
    write_hdf5(groundTruth_train, dataset_path + "DRIVE_dataset_groundTruth_train.hdf5")
    write_hdf5(border_masks_train,dataset_path + "DRIVE_dataset_borderMasks_train.hdf5")
    
    #getting the testing datasets
    imgs_test, groundTruth_test, border_masks_test = get_datasets(original_imgs_test,groundTruth_imgs_test,borderMasks_imgs_test,"test")
    print("saving test datasets")
    write_hdf5(imgs_test,dataset_path + "DRIVE_dataset_imgs_test.hdf5")
    write_hdf5(groundTruth_test, dataset_path + "DRIVE_dataset_groundTruth_test.hdf5")
    write_hdf5(border_masks_test,dataset_path + "DRIVE_dataset_borderMasks_test.hdf5")
    

    配置文件configuration.txt

    [data paths]
    path_local =  ./DRIVE_datasets_training_testing/
    train_imgs_original = DRIVE_dataset_imgs_train.hdf5
    train_groundTruth = DRIVE_dataset_groundTruth_train.hdf5
    train_border_masks = DRIVE_dataset_borderMasks_train.hdf5
    test_imgs_original = DRIVE_dataset_imgs_test.hdf5
    test_groundTruth = DRIVE_dataset_groundTruth_test.hdf5
    test_border_masks = DRIVE_dataset_borderMasks_test.hdf5
    
    
    
    [experiment name]
    name = test
    
    
    [data attributes]
    #Dimensions of the patches extracted from the full images
    patch_height = 48
    patch_width = 48
    
    
    [training settings]
    #number of total patches:
    N_subimgs = 190000
    #if patches are extracted only inside the field of view:
    inside_FOV = False
    #Number of training epochs
    N_epochs = 150
    batch_size = 32
    #if running with nohup
    nohup = True
    
    
    [testing settings]
    #Choose the model to test: best==epoch with min loss, last==last epoch
    best_last = best
    #number of full images for the test (max 20)
    full_images_to_test = 20
    #How many original-groundTruth-prediction images are visualized in each image
    N_group_visual = 1
    #Compute average in the prediction, improve results but require more patches to be predicted
    average_mode = True
    #Only if average_mode==True. Stride for patch extraction, lower value require more patches to be predicted
    stride_height = 5
    stride_width = 5
    #if running with nohup
    nohup = False
    

    第二步
    python run_training.py

    # -*- coding: utf-8 -*-
    
    ###################################################
    #
    #   run_training.py  Script to launch the training
    #
    ##################################################
    
    import os, sys
    # 注意,py3是configparser,py2是ConfigParser
    import configparser
    
    
    #config file to read from
    config = configparser.RawConfigParser()
    config.readfp(open(r'./configuration.txt'))
    #===========================================
    #name of the experiment
    name_experiment = config.get('experiment name', 'name')
    nohup = config.getboolean('training settings', 'nohup')   #std output on log file?
    
    # 在自己机器上测试时,注释掉
    run_GPU = '' if sys.platform == 'win32' else ' THEANO_FLAGS=device=gpu,floatX=float32 '
    
    #create a folder for the results
    result_dir = name_experiment
    print("\n1. Create directory for the results (if not already existing)")
    if os.path.exists(result_dir):
        print("Dir already existing")
    elif sys.platform=='win32':
        os.system('mkdir ' + result_dir)
    else:
        os.system('mkdir -p ' +result_dir)
    
    print("copy the configuration file in the results folder")
    if sys.platform=='win32':
        os.system('copy configuration.txt .\\' +name_experiment+'\\'+name_experiment+'_configuration.txt')
    else:
        os.system('cp configuration.txt ./' +name_experiment+'/'+name_experiment+'_configuration.txt')
    
    # 注意,在自己机器上测试时,把与run_GPU有关的命令去掉
    # run the experiment
    if nohup:
        print("\n2. Run the training on GPU with nohup")
        #os.system(run_GPU +' nohup python -u ./src/retinaNN_training.py > ' +'./'+name_experiment+'/'+name_experiment+'_training.nohup')
        os.system(run_GPU + 'nohup python -u ./src/retinaNN_training.py > ' +'./'+name_experiment+'/'+name_experiment+'_training.nohup')
    
    else:
        print("\n2. Run the training on GPU (no nohup)")
        os.system(run_GPU + 'python ./src/retinaNN_training.py')
    
    # os.system('python ./src/retinaNN_training.py')
    
    # -*- coding: utf-8 -*-
    # retinaNN_training.py
    import numpy as np
    import configparser
    
    from keras.models import Model
    from keras.layers import Input, concatenate, Conv2D, MaxPooling2D, UpSampling2D, Reshape, core, Dropout
    from keras.optimizers import Adam
    from keras.callbacks import ModelCheckpoint, LearningRateScheduler
    from keras import backend as K
    from keras.utils.vis_utils import plot_model as plot
    from keras.optimizers import SGD
    
    import sys
    sys.path.insert(0, './lib/') 
    from help_functions import *
    
    #function to obtain data for training/testing (validation)
    from extract_patches import get_data_training
    
    
    
    #Define the neural network
    def get_unet(n_ch,patch_height,patch_width):
        inputs = Input(shape=(patch_height,patch_width,n_ch))
        conv1 = Conv2D(32, (3, 3), activation='relu', padding='same')(inputs)
        conv1 = Dropout(0.2)(conv1)
        conv1 = Conv2D(32, (3, 3), activation='relu', padding='same')(conv1)
        pool1 = MaxPooling2D((2, 2))(conv1)
        #
        conv2 = Conv2D(64, (3, 3), activation='relu', padding='same')(pool1)
        conv2 = Dropout(0.2)(conv2)
        conv2 = Conv2D(64, (3, 3), activation='relu', padding='same')(conv2)
        pool2 = MaxPooling2D((2, 2))(conv2)
        #
        conv3 = Conv2D(128, (3, 3), activation='relu', padding='same')(pool2)
        conv3 = Dropout(0.2)(conv3)
        conv3 = Conv2D(128, (3, 3), activation='relu', padding='same')(conv3)
    
        up1 = UpSampling2D(size=(2, 2))(conv3)
        up1 = concatenate([conv2,up1],axis=3)
        conv4 = Conv2D(64, (3, 3), activation='relu', padding='same')(up1)
        conv4 = Dropout(0.2)(conv4)
        conv4 = Conv2D(64, (3, 3), activation='relu', padding='same')(conv4)
        #
        up2 = UpSampling2D(size=(2, 2))(conv4)
        up2 = concatenate([conv1,up2], axis=3)
        conv5 = Conv2D(32, (3, 3), activation='relu', padding='same')(up2)
        conv5 = Dropout(0.2)(conv5)
        conv5 = Conv2D(32, (3, 3), activation='relu', padding='same')(conv5)
        #  注意,写成这种结构,并且用的loss为categorical_crossentropy,
        # 需要对groundtruth数据进行处理,见后面help_function.py里的mask_Unet
        conv6 = Conv2D(2, (1, 1), activation='relu',padding='same')(conv5)
        conv6 = core.Reshape((2,patch_height*patch_width))(conv6)
        conv6 = core.Permute((2,1))(conv6)
        ############
        conv7 = core.Activation('softmax')(conv6)
    
        model = Model(inputs=inputs, outputs=conv7)
    
        # sgd = SGD(lr=0.01, decay=1e-6, momentum=0.3, nesterov=False)
        model.compile(optimizer='sgd', loss='categorical_crossentropy',metrics=['accuracy'])
    
        return model
    
    
    
    
    
    print("read configure")
    #========= Load settings from Config file
    config = configparser.RawConfigParser()
    config.read('configuration.txt')
    #patch to the datasets
    path_data = config.get('data paths', 'path_local')
    #Experiment name
    name_experiment = config.get('experiment name', 'name')
    #training settings
    N_epochs = int(config.get('training settings', 'N_epochs'))
    batch_size = int(config.get('training settings', 'batch_size'))
    
    
    print("load data")
    #============ Load the data and divided in patches
    patches_imgs_train, patches_masks_train = get_data_training(
        DRIVE_train_imgs_original = path_data + config.get('data paths', 'train_imgs_original'),
        DRIVE_train_groudTruth = path_data + config.get('data paths', 'train_groundTruth'),  #masks
        patch_height = int(config.get('data attributes', 'patch_height')),
        patch_width = int(config.get('data attributes', 'patch_width')),
        N_subimgs = int(config.get('training settings', 'N_subimgs')),
        inside_FOV = config.getboolean('training settings', 'inside_FOV') #select the patches only inside the FOV  (default == True)
    )
    
    # 这些可以不写
    print("create sampel")
    #========= Save a sample of what you're feeding to the neural network ==========
    N_sample = min(patches_imgs_train.shape[0],40)
    visualize(group_images(patches_imgs_train[0:N_sample,:,:,:],5),'./'+name_experiment+'/'+"sample_input_imgs")#.show()
    visualize(group_images(patches_masks_train[0:N_sample,:,:,:],5),'./'+name_experiment+'/'+"sample_input_masks")#.show()
    
    print("construct model")
    #=========== Construct and save the model arcitecture =====
    n_ch = patches_imgs_train.shape[3]
    patch_height = patches_imgs_train.shape[1]
    patch_width = patches_imgs_train.shape[2]
    model = get_unet(n_ch, patch_height, patch_width)  #the U-net model
    print("Check: final output of the network:")
    print(model.output_shape)
    plot(model, to_file='./'+name_experiment+'/'+name_experiment + '_model.png')   #check how the model looks like
    json_string = model.to_json()
    open('./'+name_experiment+'/'+name_experiment +'_architecture.json', 'w').write(json_string)
    
    #============  Training ==================================
    checkpointer = ModelCheckpoint(filepath='./'+name_experiment+'/'+name_experiment +'_best_weights.h5', verbose=1, monitor='val_loss', mode='auto', save_best_only=True) #save at each epoch if the validation decreased
    
    
    # def step_decay(epoch):
    #     lrate = 0.01 #the initial learning rate (by default in keras)
    #     if epoch==100:
    #         return 0.005
    #     else:
    #         return lrate
    #
    # lrate_drop = LearningRateScheduler(step_decay)
    
    patches_masks_train = masks_Unet(patches_masks_train)  #reduce memory consumption
    model.fit(patches_imgs_train, patches_masks_train, nb_epoch=N_epochs, batch_size=batch_size, verbose=1, shuffle=True, validation_split=0.1, callbacks=[checkpointer])
    
    
    #========== Save and test the last model ===================
    model.save_weights('./'+name_experiment+'/'+name_experiment +'_last_weights.h5', overwrite=True)
    

    上面出现过的一些函数:

    # extract_patches.py
    import numpy as np
    import random
    import configparser
    
    from help_functions import load_hdf5
    from help_functions import visualize
    from help_functions import group_images
    
    from pre_processing import my_PreProc
    
    
    #To select the same images
    # random.seed(10)
    
    #Load the original data and return the extracted patches for training/testing
    def get_data_training(DRIVE_train_imgs_original,
                          DRIVE_train_groudTruth,
                          patch_height,
                          patch_width,
                          N_subimgs,
                          inside_FOV):
        train_imgs_original = load_hdf5(DRIVE_train_imgs_original)
        train_masks = load_hdf5(DRIVE_train_groudTruth) #masks always the same
        # visualize(group_images(train_imgs_original[0:20,:,:,:],5),'imgs_train')#.show()  #check original imgs train
    
    
        train_imgs = my_PreProc(train_imgs_original)
        train_masks = train_masks/255.
    
        train_imgs = train_imgs[:,9:574,:,:]  #cut bottom and top so now it is 565*565
        train_masks = train_masks[:,9:574,:,:]  #cut bottom and top so now it is 565*565
        data_consistency_check(train_imgs,train_masks)
    
        #check masks are within 0-1
        assert(np.min(train_masks)==0 and np.max(train_masks)==1)
    
        print("\ntrain images/masks shape:")
        print(train_imgs.shape)
        print("train images range (min-max): ", str(np.min(train_imgs)), ' - ', str(np.max(train_imgs)))
        print("train masks are within 0-1\n")
    
        #extract the TRAINING patches from the full images
        patches_imgs_train, patches_masks_train = extract_random(train_imgs,train_masks,patch_height,patch_width,N_subimgs,inside_FOV)
        data_consistency_check(patches_imgs_train, patches_masks_train)
    
        print("\ntrain PATCHES images/masks shape:")
        print(patches_imgs_train.shape)
        print("train PATCHES images range (min-max): ", str(np.min(patches_imgs_train)), ' - ', str(np.max(patches_imgs_train)))
    
        return patches_imgs_train, patches_masks_train#, patches_imgs_test, patches_masks_test
    
    
    #data consinstency check
    def data_consistency_check(imgs,masks):
        assert(len(imgs.shape)==len(masks.shape))
        assert(imgs.shape[0]==masks.shape[0])
        assert(imgs.shape[1]==masks.shape[1])
        assert(imgs.shape[2]==masks.shape[2])
        assert(masks.shape[3]==1)
        assert(imgs.shape[3]==1 or imgs.shape[3]==3)
    
    
    #extract patches randomly in the full training images
    #  -- Inside OR in full image
    def extract_random(full_imgs,full_masks, patch_h,patch_w, N_patches, inside=True):
        if (N_patches%full_imgs.shape[0] != 0):
            print("N_patches: plase enter a multiple of 20")
            exit()
        assert (len(full_imgs.shape)==4 and len(full_masks.shape)==4)  #4D arrays
        assert (full_imgs.shape[3]==1 or full_imgs.shape[3]==3)  #check the channel is 1 or 3
        assert (full_masks.shape[3]==1)   #masks only black and white
        assert (full_imgs.shape[2] == full_masks.shape[2] and full_imgs.shape[1] == full_masks.shape[1])
        patches = np.empty((N_patches,patch_h,patch_w,full_imgs.shape[3]))
        patches_masks = np.empty((N_patches,patch_h,patch_w,full_masks.shape[3]))
        img_h = full_imgs.shape[1]  #height of the full image
        img_w = full_imgs.shape[2] #width of the full image
        # (0,0) in the center of the image
        patch_per_img = int(N_patches/full_imgs.shape[0])  #N_patches equally divided in the full images
        print("patches per full image: ", str(patch_per_img))
        iter_tot = 0   #iter over the total numbe rof patches (N_patches)
        for i in range(full_imgs.shape[0]):  #loop over the full images
            k=0
            while k <patch_per_img:
                x_center = random.randint(0+int(patch_w/2),img_w-int(patch_w/2))
                # print "x_center " +str(x_center)
                y_center = random.randint(0+int(patch_h/2),img_h-int(patch_h/2))
                # print "y_center " +str(y_center)
                #check whether the patch is fully contained in the FOV
                if inside==True:
                    if is_patch_inside_FOV(x_center,y_center,img_w,img_h,patch_h)==False:
                        continue
                patch = full_imgs[i,y_center-int(patch_h/2):y_center+int(patch_h/2),x_center-int(patch_w/2):x_center+int(patch_w/2),:]
                patch_mask = full_masks[i,y_center-int(patch_h/2):y_center+int(patch_h/2),x_center-int(patch_w/2):x_center+int(patch_w/2),:]
                patches[iter_tot]=patch
                patches_masks[iter_tot]=patch_mask
                iter_tot +=1   #total
                k+=1  #per full_img
        return patches, patches_masks
    
    #check if the patch is fully contained in the FOV
    def is_patch_inside_FOV(x,y,img_w,img_h,patch_h):
        x_ = x - int(img_w/2) # origin (0,0) shifted to image center
        y_ = y - int(img_h/2)  # origin (0,0) shifted to image center
        R_inside = 270 - int(patch_h * np.sqrt(2.0) / 2.0) #radius is 270 (from DRIVE db docs), minus the patch diagonal (assumed it is a square #this is the limit to contain the full patch in the FOV
        radius = np.sqrt((x_*x_)+(y_*y_))
        if radius < R_inside:
            return True
        else:
            return False
    

    help_function.py

    # -*- coding: utf-8 -*-
    
    import h5py
    import numpy as np
    from PIL import Image
    from matplotlib import pyplot as plt
    
    def load_hdf5(infile):
      with h5py.File(infile,"r") as f:  #"with" close the file after its nested commands
        return f["image"][()]
    
    def write_hdf5(arr,outfile):
      with h5py.File(outfile,"w") as f:
        f.create_dataset("image", data=arr, dtype=arr.dtype)
    
    #convert RGB image in black and white
    def rgb2gray(rgb):
        assert (len(rgb.shape)==4)  #4D arrays
        assert (rgb.shape[3]==3)
        bn_imgs = rgb[:,:,:,0]*0.299 + rgb[:,:,:,1]*0.587 + rgb[:,:,:,2]*0.114
        bn_imgs = np.reshape(bn_imgs,(rgb.shape[0],rgb.shape[1],rgb.shape[2],1))
        return bn_imgs
    
    #group a set of images row per columns
    def group_images(data,per_row):
        assert data.shape[0]%per_row==0
        assert (data.shape[3]==1 or data.shape[3]==3)
        all_stripe = []
        for i in range(int(data.shape[0]/per_row)):
            stripe = data[i*per_row]
            for k in range(i*per_row+1, i*per_row+per_row):
                stripe = np.concatenate((stripe,data[k]),axis=1)
            all_stripe.append(stripe)
        totimg = all_stripe[0]
        for i in range(1,len(all_stripe)):
            totimg = np.concatenate((totimg,all_stripe[i]),axis=0)
        return totimg
    
    
    #visualize image (as PIL image, NOT as matplotlib!)
    def visualize(data,filename):
        assert (len(data.shape)==3) #height*width*channels
        img = None
        if data.shape[2]==1:  #in case it is black and white
            data = np.reshape(data,(data.shape[0],data.shape[1]))
        if np.max(data)>1:
            img = Image.fromarray(data.astype(np.uint8))   #the image is already 0-255
        else:
            img = Image.fromarray((data*255).astype(np.uint8))  #the image is between 0-1
        img.save(filename + '.png')
        return img
    
    
    #prepare the mask in the right shape for the Unet
    def masks_Unet(masks):
        assert (len(masks.shape)==4)  #4D arrays
        assert (masks.shape[3]==1 )  #check the channel is 1
        im_h = masks.shape[1]
        im_w = masks.shape[2]
        masks = np.reshape(masks,(masks.shape[0],im_h*im_w))
        new_masks = np.empty((masks.shape[0],im_h*im_w,2))
        for i in range(masks.shape[0]):
            for j in range(im_h*im_w):
                if  masks[i,j] == 0:
                    new_masks[i,j,0]=1
                    new_masks[i,j,1]=0
                else:
                    new_masks[i,j,0]=0
                    new_masks[i,j,1]=1
        return new_masks
    
    
    def pred_to_imgs(pred, patch_height, patch_width, mode="original"):
        assert (len(pred.shape)==3)  #3D array: (Npatches,height*width,2)
        assert (pred.shape[2]==2 )  #check the classes are 2
        pred_images = np.empty((pred.shape[0],pred.shape[1]))  #(Npatches,height*width)
        if mode=="original":
            for i in range(pred.shape[0]):
                for pix in range(pred.shape[1]):
                    pred_images[i,pix]=pred[i,pix,1]
        elif mode=="threshold":
            for i in range(pred.shape[0]):
                for pix in range(pred.shape[1]):
                    if pred[i,pix,1]>=0.5:
                        pred_images[i,pix]=1
                    else:
                        pred_images[i,pix]=0
        else:
            print("mode ", str(mode), " not recognized, it can be 'original' or 'threshold'")
            exit()
        pred_images = np.reshape(pred_images,(pred_images.shape[0], patch_height, patch_width,1))
        return pred_images
    

    pre_processing.py

    # -*- coding: utf-8 -*-
    
    ###################################################
    #
    #  pre_processing.py Script to pre-process the original imgs
    #
    ##################################################
    
    
    import numpy as np
    from PIL import Image
    import cv2
    
    from help_functions import *
    
    
    #My pre processing (use for both training and testing!)
    def my_PreProc(data):
        assert(len(data.shape)==4)
        assert (data.shape[3]==3)  #Use the original images
        #black-white conversion
        train_imgs = rgb2gray(data)
        #my preprocessing:
        train_imgs = dataset_normalized(train_imgs)
        train_imgs = clahe_equalized(train_imgs)
        train_imgs = adjust_gamma(train_imgs, 1.2)
        train_imgs = train_imgs/255.  #reduce to 0-1 range
        return train_imgs
    
    
    #============================================================
    #========= PRE PROCESSING FUNCTIONS ========================#
    #============================================================
    
    #==== histogram equalization
    def histo_equalized(imgs):
        assert (len(imgs.shape)==4)  #4D arrays
        assert (imgs.shape[3]==1)  #check the channel is 1
        imgs_equalized = np.empty(imgs.shape)
        for i in range(imgs.shape[0]):
            imgs_equalized[i,:,:,0] = cv2.equalizeHist(np.array(imgs[i,:,:,0], dtype = np.uint8))
        return imgs_equalized
    
    
    # CLAHE (Contrast Limited Adaptive Histogram Equalization)
    #adaptive histogram equalization is used. In this, image is divided into small blocks called "tiles" (tileSize is 8x8 by default in OpenCV). Then each of these blocks are histogram equalized as usual. So in a small area, histogram would confine to a small region (unless there is noise). If noise is there, it will be amplified. To avoid this, contrast limiting is applied. If any histogram bin is above the specified contrast limit (by default 40 in OpenCV), those pixels are clipped and distributed uniformly to other bins before applying histogram equalization. After equalization, to remove artifacts in tile borders, bilinear interpolation is applied
    def clahe_equalized(imgs):
        assert (len(imgs.shape)==4)  #4D arrays
        assert (imgs.shape[3]==1)  #check the channel is 1
        #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(imgs.shape[0]):
            imgs_equalized[i,:,:,0] = clahe.apply(np.array(imgs[i,:,:,0], dtype = np.uint8))
        return imgs_equalized
    
    
    # ===== normalize over the dataset
    def dataset_normalized(imgs):
        assert (len(imgs.shape)==4)  #4D arrays
        assert (imgs.shape[3]==1)  #check the channel is 1
        imgs_normalized = np.empty(imgs.shape)
        imgs_std = np.std(imgs)
        imgs_mean = np.mean(imgs)
        imgs_normalized = (imgs-imgs_mean)/imgs_std
        for i in range(imgs.shape[0]):
            imgs_normalized[i] = ((imgs_normalized[i] - np.min(imgs_normalized[i])) / (np.max(imgs_normalized[i])-np.min(imgs_normalized[i])))*255
        return imgs_normalized
    
    
    def adjust_gamma(imgs, gamma=1.0):
        assert (len(imgs.shape)==4)  #4D arrays
        assert (imgs.shape[3]==1)  #check the channel is 1
        # build a lookup table mapping the pixel values [0, 255] to
        # their adjusted gamma values
        invGamma = 1.0 / gamma
        table = np.array([((i / 255.0) ** invGamma) * 255 for i in np.arange(0, 256)]).astype("uint8")
        # apply gamma correction using the lookup table
        new_imgs = np.empty(imgs.shape)
        for i in range(imgs.shape[0]):
            new_imgs[i,:,:,0] = cv2.LUT(np.array(imgs[i,:,:,0], dtype = np.uint8), table)
        return new_imgs
    
    def pred_to_imgs(pred, patch_height, patch_width, mode="original"):
        assert (len(pred.shape)==3)  #3D array: (Npatches,height*width,2)
        assert (pred.shape[2]==2 )  #check the classes are 2
        pred_images = np.empty((pred.shape[0],pred.shape[1]))  #(Npatches,height*width)
        if mode=="original":
            for i in range(pred.shape[0]):
                for pix in range(pred.shape[1]):
                    pred_images[i,pix]=pred[i,pix,1]
        elif mode=="threshold":
            for i in range(pred.shape[0]):
                for pix in range(pred.shape[1]):
                    if pred[i,pix,1]>=0.5:
                        pred_images[i,pix]=1
                    else:
                        pred_images[i,pix]=0
        else:
            print("mode ", str(mode), " not recognized, it can be 'original' or 'threshold'")
            exit()
        pred_images = np.reshape(pred_images,(pred_images.shape[0], patch_height, patch_width, 1))
        return pred_images
    

    训练的步数少时,出来的效果如下,我还以为是网络写错了:
    在这里插入图片描述
    随着训练步数再增加一点,慢慢有了雏形:
    在这里插入图片描述
    训练步数再增加一点,可见继续训练下去,是能达到一定的效果的,这次只是为了学习,就只跑了一小会:
    在这里插入图片描述

    展开全文
  • 基于深度学习的医学图像分割综述

    千次阅读 2019-06-15 15:17:59
    医学图像分割技术的发展不仅影响到医学图像处理中其他相关技术的发展,如可视化、三维重建等,而且在生物医学图像的分析中也占有极其重要的地位。近年来,由于深度学习算法在医学图像分割中的应用, 医...
  • 医学图像分割

    万次阅读 2018-06-05 20:57:15
    作者: Zeynettin Akkus &amp; Alfiia Galimzianova &amp; Assaf Hoogi &amp; Daniel L. Rubin &amp; Bradley J. Erickson ...这篇综述的目的是提供关于最近基于深度学习的分割方...
  • 今天看到了一篇介绍深度学习在医学影像...图像分割在影像学诊断中大有用处。自动分割能帮助医生确认病变肿瘤的大小,定量评价治疗前后的效果。除此之外,脏器和病灶的识别和甄别也是一项影像科医生的日常工作。CT和...
  • unet做医学图像分割

    万次阅读 热门讨论 2019-01-13 15:37:23
    关于unet网络医学分割的网址 unet,大家可以在该网站中学习有关unet的知识 我将我的版本上传上了github,这是用keras实现的,运行data.py就可以将图片转换成.npy格式,这里我已经生成数据了,直接运行unet.py就...
  • 医学图像分割综述

    万次阅读 2013-10-22 15:21:24
    图像分割的定义: 将一副图像g(x,y),其中0≤x≤Max_x,0≤y≤Max_y,进行分割就是将图像划分为满足如下条件的子区域: 1.基于区域的分割方法: 图像分割通常用到不同对象间特征的不连续性和同一对象...
  • 文章来源:Kamnitsas K, Ledig C, Newcombe V F J, et al. Efficient multi-scale 3D CNN with fully connected CRF for accurate brain lesion segmentation[J]. Medical image ... 医学图像分割领域的最经典的
  • 医学图像开源数据集

    千次阅读 2019-07-03 15:31:32
    数据集:一文道尽医学图像数据集与竞赛: https://www.cnblogs.com/yumoye/p/10512460.html https://zhuanlan.zhihu.com/p/24634505
  • 深度学习医疗图像分割经典之作

    千次阅读 2019-01-24 15:43:58
    之前看到了CSDN上有博主写了一篇关于医疗图像分割的综述,那么就把链接基于深度学习的医学图像分割综述放在这里,方便以后参考。 最近在做一个关于医疗图像分割的项目,然后就开始读论文了,Un...
  • 医学图像分割中常用的度量指标

    千次阅读 2018-06-04 21:16:42
    接下来给大家介绍一下医学图像中常用的几个度量指标,主要参考MICCAI2007 - 首先定义以下标识符:VgtVgtV_{gt}代表的是ground truth的分割结果,VpredVpredV_{pred}代表的是预测的分割结果. - DICE: 这个相信大家最...
  • 图像分割与三维重建

    千次阅读 2015-12-09 12:55:21
    医学图像分割: 根据需要选择一定的特征量或指定特定的测量准则来检测图像中不同区域的一致性,根据检测出来的区域一致性将图像区别成不同区域,从而可以更加方便地进行进一步的分析和理解。 <2>.医学图像分割方法...
  • 全卷积神经网络图像分割(U-net)-keras实现

    万次阅读 多人点赞 2018-11-04 19:10:13
    最近在研究全卷积神经网络在图像分割方面的应用,因为自己是做医学图像处理方面的工作,所以就把一个基于FCN(全卷积神经网络)的神经网络用 keras 实现了,并且用了一个医学图像的数据集进行了图像分割。...
  • (最开始接触医学图像分割时写的综述,写的比较幼稚,传上来的时候格式可能有些乱。需要原文的小伙伴可以加我qq:604395564联系,也欢迎做医学图像处理的小伙伴一起交流学习。自己写的,欢迎转载,但请注明出处哦^_^...
  • 一、医学图像的特点 医学图像边界模糊、梯度复杂,需要较多的高分辨率信息 人体内部结构相对固定,分割目标在人体图像中的分布很具有规律,语义简单明确,低分辨率的信息就可以简单定位 二、unet网络结构适用的...
  • 图像分割方法及性能评价综述

    万次阅读 2014-05-16 16:42:19
    摘 要 对医学图像分割算法的客观评价是推进算法在临床上得到应用的关键。针对目前对医学图像分割方法的研究较多,而对分割算法的评价方法的研究却很少的问题,提出了一种判断和比较医学图像分割算法优劣的评价方法。...
  • ML之GMM:基于GMM算法的图像分割案例

    万次阅读 2019-08-02 15:36:05
    ML之GMM:基于GMM算法的图像分割案例 目录 输出结果 实现代码 输出结果 实现代码
  • 基于卷积神经网络的图像语义分割

    万次阅读 2018-02-14 20:41:25
    论文地址基于卷积神经网络的图像语义分割浙江大学硕士论文图像分割:就是把感兴趣的内容从图像上分割出来,但是一般会对这个目标加上一定的语义信息,即语义标签,把加上语义的图像分割称为图像语义分割。...
1 2 3 4 5 ... 20
收藏数 7,489
精华内容 2,995
关键字:

医学图像分割