HRNet 论文和代码详解

github: https://github.com/HRNet/HRNet-Semantic-Segmentation

Paper: https://arxiv.org/abs/1908.07919

High-Resoultion Net(HRNet)由微软亚洲研究院和中科大提出,发表在CVPR2019

摘要:高分辨率表示对于位置敏感的视觉问题十分重要,比如目标检测、语义分割、姿态估计。为了这些任务位置信息更加精准,很容易想到的做法就是维持高分辨率的feature map,事实上HRNet之前几乎所有的网络都是这么做的,通过下采样得到强语义信息,然后再上采样恢复高分辨率恢复位置信息(如下图所示),然而这种做法,会导致大量的有效信息在不断的上下采样过程中丢失。而HRNet通过并行多个分辨率的分支,加上不断进行不同分支之间的信息交互,同时达到强语义信息和精准位置信息的目的。

模型的主要特点是在整个过程中特征图(Feature Map)始终保持高分辨率,这与之前主流方法思路上有很大的不同。在HRNet之前,2D人体姿态估计算法是采用(Hourglass/CPN/Simple Baseline/MSPN等)将高分辨率特征图下采样至低分辨率,再从低分辨率特征图恢复至高分辨率的思路(单次或重复多次),以此过程实现了多尺度特征提取的一个过程。

HRNet在整个过程中保持特征图的高分辨率,但多尺度特征提取是姿态估计模型一定要实现的过程,那么HRNet是如何实现多尺度特征提取的呢?模型是通过在高分辨率特征图主网络逐渐并行加入低分辨率特征图子网络,不同网络实现多尺度融合与特征提取实现的。

特点与优势:

(1)作者提出的方法是并行连接高分辨率与低分辨率网络,而不是像之前方法那样串行连接。因此,其方法能够保持高分辨率,而不是通过一个低到高的过程恢复分辨率,因此预测的heatmap可能在空间上更精确。

(2)本文提出的模型融合相同深度和相似级别的低分辨率特征图来提高高分辨率的特征图的表示效果,并进行重复的多尺度融合。

缺点:因为特征图分辨率过大,而且数量多,这样肯定会导致巨额的耗时计算,对显存对硬件要求更高了

HRNet结构细节

Backbone设计

我将HRNet整个backbone部分进行了拆解,分成4个stage,每个stage分成蓝色框和橙色框两部分。其中蓝色框部分是每个stage的基本结构,由多个branch组成,HRNet中stage1蓝色框使用的是BottleNeck,stage2&3&4蓝色框使用的是BasicBlock。其中橙色框部分是每个stage的过渡结构,HRNet中stage1橙色框是一个TransitionLayer,stage2&3橙色框是一个FuseLayer和一个TransitionLayer的叠加,stage4橙色框是一个FuseLayer。

解释一下为什么这么设计,FuseLayer是用来进行不同分支的信息交互的,TransitionLayer是用来生成一个下采样两倍分支的输入feature map的,stage1橙色框显然没办法做FuseLayer,因为前一个stage只有一个分支,stage4橙色框后面接neck和head了,显然也不再需要TransitionLayer了。

整个backbone的构建流程可以总结为:make_backbone -> make_stages -> make_branches

有关backbone构建相关的看源码,主要讲一下FuseLayerTransitionLayerNeck的设计

FuseLayer设计

FuseLayer部分以绿色框为例,融合前为pre,融合后为post,静态构建一个二维矩阵,然后将pre和post对应连接的操作一一填入这个二维矩阵中。

以上图为例,图1的pre1和post1的操作为空,pre2和post1的操作为2倍上采,pre3和post1的操作为4倍上采;图2的pre1和post2的操作为3×3卷积下采,pre2和post2的操作为空,pre3和post2的操作为2倍上采;图3的pre1和post3的操作为连续两个3×3卷积下采,pre2和post3的操作为3×3卷积下采,pre3和post的操作为空。

前向计算时用一个二重循环将构建好的二维矩阵一一解开,将对应同一个post的pre转换后进行融合相加。比如post1 = f11(pre1) + f12(pre2) + f13(pre3)

FuseLayer的整体code如下:

def _make_fuse_layers(self):
  fuse_layers = []
  for post_index, out_channel in enumerate(self.out_channels[:len(self.in_channels)]):
      fuse_layer = []
      for pre_index, in_channel in enumerate(self.in_channels):
          if pre_index > post_index:
              fuse_layer.append(nn.Sequential(
                  nn.Conv2d(in_channel, out_channel, 1, 1, 0, bias=False),
                  nn.BatchNorm2d(out_channel, momentum=0.1),
                  nn.Upsample(scale_factor=2**(pre_index-post_index), mode='nearest')))
          elif pre_index < post_index:
              conv3x3s = []
              for cur_index in range(post_index - pre_index):
                  out_channels_conv3x3 = out_channel if cur_index == post_index - pre_index - 1 else in_channel
                  conv3x3 = nn.Sequential(
                      nn.Conv2d(in_channel, out_channels_conv3x3, 3, 2, 1, bias=False),
                      nn.BatchNorm2d(out_channels_conv3x3, momentum=0.1)
                  )
                  if cur_index < post_index - pre_index - 1:
                      conv3x3.add_module('relu_{}'.format(cur_index), nn.ReLU(False))
                  conv3x3s.append(conv3x3)
              fuse_layer.append(nn.Sequential(*conv3x3s))
          else:
              fuse_layer.append(None)
      fuse_layers.append(nn.ModuleList(fuse_layer))
  return nn.ModuleList(fuse_layers)

def forward(self, x):
  x_fuse = []
  for post_index in range(len(self.fuse_layers)):
      y = 0
      for pre_index in range(len(self.fuse_layers)):
          if post_index == pre_index:
              y += x[pre_index]
          else:
              y += self.fuse_layers[post_index][pre_index](x[pre_index])
      x_fuse.append(self.relu(y))

TransitionLayer设计

TransitionLayer以黄色框为例,静态构建一个一维矩阵,然后将pre和post对应连接的操作一一填入这个一维矩阵中。当pre1&post1、pre2&post2、pre3&post3的通道数对应相同时,一维矩阵填入None;通道数不相同时,对应位置填入一个转换卷积。post4比较特殊,这一部分代码和图例不太一致,图例是pre1&pre2&pre3都进行下采然后进行融合相加得到post4,而代码中post4通过pre3下采得到。

TransitionLayer整体code如下

def _make_transition_layers(self):
  num_branches_pre = len(self.in_channels)
  num_branches_post = len(self.out_channels)
  transition_layers = []
  for post_index in range(num_branches_post):
      if post_index < len(self.in_channels):
          if self.in_channels[post_index] != self.out_channels[post_index]:
              transition_layers.append(nn.Sequential(
                  nn.Conv2d(self.in_channels[post_index], self.out_channels[post_index], 3, 1, 1, bias=False),
                  nn.BatchNorm2d(self.out_channels[post_index], momentum=0.1),
                  nn.ReLU(inplace=True)
              ))
          else:
              transition_layers.append(None)
      else:
          conv3x3s = []
          for pre_index in range(post_index + 1 - num_branches_pre):
              in_channels_conv3x3 = self.in_channels[-1]
              out_channels_conv3x3 = self.out_channels[post_index] if pre_index == post_index - \
                  num_branches_pre else in_channels_conv3x3
              conv3x3s.append(nn.Sequential(
                  nn.Conv2d(in_channels_conv3x3, out_channels_conv3x3, 3, 2, 1, bias=False),
                  nn.BatchNorm2d(out_channels_conv3x3, momentum=0.1),
                  nn.ReLU(inplace=True)
              ))
          transition_layers.append(nn.Sequential(*conv3x3s))
  return nn.ModuleList(transition_layers)

def forward(self, x):
  x_trans = []
  for branch_index, transition_layer in enumerate(self.transition_layers):
      if branch_index < len(self.transition_layers) - 1:
          if transition_layer:
              x_trans.append(transition_layer(x[branch_index]))
          else:
              x_trans.append(x[branch_index])
      else:
          x_trans.append(transition_layer(x[-1]))

Neck设计

我把HRNet所描述的make_head过程理解成make_neck(因为一般意义上将最后的fc层理解成head更为清晰,这个在很多开源code中都是这样子拆解的)。下面着重讲解一下HRNet的neck设计。

HRNet的backbone输出有四个分支,paper中给出了几种方式对输出分支进行操作。

(a)图是HRNetV1的操作方式,只使用分辨率最高的feature map。

(b)图是HRNetV2的操作方式,将所有分辨率的feature map(小的特征图进行upsample)进行concate,主要用于语义分割和面部关键点检测。

(c)图是HRNetV2p的操作方式,在HRNetV2的基础上,使用了一个特征金字塔,主要用于目标检测。

而在图像分类任务上,HRNet有另一种特殊的neck设计

HRNet的neck可以分成三个部分,IncreLayer(橙色框),DownsampLayer(蓝色框)和FinalLayer(绿色框)。对每个backbone的输出分支进行升维操作,然后按照分辨率从大到小依次进行下采样同时从上到下逐级融合相加,最后用一个1x1conv升维。

def _make_neck(self, in_channels):
  head_block = Bottleneck
  self.incre_channels = [32, 64, 128, 256]
  self.neck_out_channels = 2048

  incre_modules = []
  downsamp_modules = []
  num_branches = len(self.in_channels)
  for index in range(num_branches):
      incre_module = self._make_layer(head_block, in_channels[index], incre_channels[index], 1, stride=1)
      incre_modules.append(incre_module)
      if index < num_branches - 1:
          downsamp_in_channels = self.incre_channels[index] * incre_module.expansion
          downsamp_out_channels = self.incre_channels[index+1] * incre_module.expansion
          downsamp_module = nn.Sequential(
              nn.Conv2d(in_channels=downsamp_in_channels, out_channels=downsamp_out_channels,
                        kernel_size=3, stride=2, padding=1),
              nn.BatchNorm2d(downsamp_out_channels, momentum=0.1),
              nn.ReLU(inplace=True)
          )
          downsamp_modules.append(downsamp_module)
  incre_modules = nn.ModuleList(incre_modules)
  downsamp_modules = nn.ModuleList(downsamp_modules)
  final_layer = nn.Sequential(
      nn.Conv2d(in_channels=self.out_channels[-1] * 4, out_channels=2048,
                kernel_size=1, stride=1, padding=0),
      nn.BatchNorm2d(2048, momentum=0.1),
      nn.ReLU(inplace=True)
  )
  return incre_modules, downsamp_modules, fine_layer

def forward(self, x):
  y = self.incre_modules[0](x[0])
  for index in range(len(self.downsamp_modules)):
      y = self.incre_modules[index+1](x[index+1]) + self.downsamp_modules[index](y)
  y = self.final_layer(y)
  y = F.avg_pool2d(y, kernel_size=y.size()[2:]).view(y.size(0), -1)

还有几个小细节

  1. BN层的momentom都设置为0.1
  2. stem使用的是两层stried为2的conv3x3
  3. FuseLayer的ReLU的inplace都设置为False

BPR:用于实例分割的边界Patch优化(CVPR2021)

 

Look Closer to Segment Better: Boundary Patch Refinement for Instance Segmentation

代码链接:https://github.com/tinyalpha/BPR

后处理分割结果,效果是即插即用后处理模块当年的sota通过将 BPR 框架应用于 PolyTransform + SegFix 基线,我们在 Cityscapes 排行榜上排名第一。

从目前的排名来说(22.09.23),排名第五,与top1相差不到2个百分点,而 BPR后处理使得PolyTransform + SegFix的效果提升了1.5个百分点。 相比于MASK-RCNN提升了4.2个百分点。

CVPR21上一篇关于实例分割的文章。对于Mask RCNN来说,其最终得到的mask分辨率太低,因此还原到原尺寸的时候,一些boundary信息就显得非常粗糙,导致预测生成的mask效果不尽如人意。而且处于boundary的pixel本身数量相比于整张image来说很少,同时本身难以做分类。现有的一些方法试图提升boundary quality,但预测mask边界这个task本身的复杂度和segmentation很接近了,因此开销较大。

因此本文作者提出了一种crop-and-refine的策略。首先通过经典的实例分割网络(如Mask RCNN)得到coarse mask。随后在mask的boundary出提取出一系列的patch,随后将这些patch送入一个Refinement Network,这个Refinement Network负责做二分类的语义分割,进而对boundary处的patch进行优化,整个后处理的优化网络称为BPR(Boundary Patch Refinement)。该网络可以解决传统Mask RCNN预测的mask的边界粗糙的问题。

本文的核心就是在Mask RCNN一类的网络给出coarse mask后,如何设计Refine Network来对这个粗糙 mask 的边界进行优化,进而得到resolution更高,boundary quality更好的mask。

给定一个coarse mask(上图a),首先需要决定这个mask的哪些部分要做refine。这里作者提出了一种sliding-window式的方法提取到boundary处的一系列patch(上图b)。具体来说,就是在mask边界处密集assign正方形的bounding box,这些box内部囊括了boundary pixel。随后,由于这些box有的overlap太大导致redundant(冗余),这里采用NMS进行过滤(上图c),以实现速度和精度的trade-off(平衡)。

随后这些survive下来的image patch(上图d)和mask patch(上图e)都resize到同一尺寸,一起喂入Refinement Network。这里作者argue说一定要喂入mask patch,因为一旦拥有mask patch的location和semantic信息,这个refinement network就不再需要学习instance-level semantic(实例类别信息,比如这个image patch属于哪个类别)了。所以,refinement network只需要学习boundary处的hard pixel,并把它们正确分类。

关于Refinement Network,其任务是为每一个提取出来的boundary patch独立地做二分类语义分割,任何的语义分割模型都可以搬过来做这个task。输入的通道数为4(RGB+mask),输出通道数为2(BG or FG),这里作者采用了HRNetV2(CVPR 2019),这种各种level feature不断做融合的网络可以maintain高分辨率的representation。通过合理的增加input size,boundary batch就可以得到比之前方法更高的resolution。

HRNetV2 网络结构

在对每个patch独立地refine以后,需要将它们reassemble(组装)到coarse mask上面。有的相邻的patch可能存在overlap的情况,最终的结果是取平均,以0.5作为阈值判断某个pixel属于前景或是背景。

Experiment

这里的指标是AP (Average precision):指的是PR曲线的面积(AP就是平均精准度,简单来说就是对PR曲线上的Precision值求均值。)对于实例分割的评价指标:使用AP评价指标

实例分割和目标检测mAP计算时除了IOU计算方式(实例分割是mask间的IOU)不同,其他都是一样的.

对于一个二分类任务,二分类器的预测结果可分为以下4类:

二分类器的结果可分为4类

Precision的定义为:

Recall的定义为: 

Precision从预测结果角度出发,描述了二分类器预测出来的正例结果中有多少是真实正例,即该二分类器预测的正例有多少是准确的;Recall从真实结果角度出发,描述了测试集中的真实正例有多少被二分类器挑选了出来,即真实的正例有多少被该二分类器召回。

逐步降低二分类器预测正例的门槛,则每次可以计算得到当前的Precision和Recall。以Recall作为横轴,Precision作为纵轴可以得到Precision-Recall曲线图,简称为P-R图。

详细解释:目标检测/实例分割中 AP 和 mAP 的混淆指标

preview

首先通过实验证明了将mask patch一并作为输入的重要性:

patch size、不同的patch extraction策略,input size对结果的影响:

RefineNet的选取,NMS的阈值:

Cityscape上与其他方法的比较:PolyTransform + SegFix baseline,达到最高的AP。

迁移到其他model上面的结果 and coco数据集上的结果

Mask-RCNN论文

论文:http://cn.arxiv.org/pdf/1703.06870v3

代码:https://github.com/facebookresearch/maskrcnn-benchmark

B站网络详解 FPN

Introduction

我们提出了一个简单、灵活、通用的实例分割框架,称为Mask R-CNN。我们的方法能够有效检测图像中的目标,同时为每个实例生成高质量的分割掩码。Mask R-CNN通过添加一个预测对象掩码的分支,与现有的边框识别分支并行,扩展了之前的Faster R-CNN。Mask R-CNN的训练很简单,只为Faster R-CNN增加了一小部分开销,运行速度为5帧/秒。此外,Mask R-CNN很容易泛化到其他任务,如人体姿态估计。我们展示了Mask R-CNN在COCO挑战赛的实例分割、目标检测和人物关键点检测任务上的最优结果。在不使用花哨技巧的情况下,Mask R-CNN在各项任务上都优于现有的单一模型,包括COCO 2016挑战赛的冠军。我们希望Mask R-CNN能够成为一个坚实的基线,并有助于简化未来实例识别的研究。

Fast/Faster R-CNN和Fully Convolutional Network(FCN)框架极大地推动了计算机视觉领域中目标检测和语义分割等方向的发展。这些方法的概念很直观,具有良好的灵活性和鲁棒性,并且能够快速训练和推理。我们这项工作的目标是为实例分割任务开发一个相对可行的框架。

实例分割具有一定的挑战性,因为它需要正确检测图像中的所有对象,同时还要精确分割每个实例。因此,它结合了目标检测和语义分割等计算机视觉任务中的元素。目标检测旨在对单个物体进行分类,并使用边框对每个物体进行定位。语义分割旨在将每个像素归类到一组固定的类别,而不区分对象实例。鉴于此,人们可能会认为需要一套复杂的方法才能获得良好的结果。然而,我们证明了一个令人惊讶的事实:简单、灵活、快速的系统也可以超越现有的最先进的实例分割模型。

我们的方法称为Mask R-CNN,通过在每个RoI(感兴趣区域,Region of Interest)上添加一个预测分割掩码的分支来扩展Faster R-CNN,并与现有的用于分类和边框回归的分支并行。掩码分支是应用于每个RoI的一个小FCN,以像素到像素的方式预测分割掩码,并且只会增加较小的计算开销。Mask R-CNN是基于Faster R-CNN框架而来的,易于实现和训练,有助于广泛、灵活的架构设计。

原则上,Mask R-CNN是Faster R-CNN的直观扩展,但正确构建掩码分支对于获得好的结果至关重要。最重要的是,Faster R-CNN的设计没有考虑网络输入和输出之间的像素到像素的对齐。这一点在RoIPool(处理实例的核心操作)如何执行粗空间量化来提取特征上表现得最为明显。为了修正错位,我们提出了一个简单的、没有量化的层,称为RoIAlign,它忠实地保留了精确的空间位置。尽管这看起来是一个很小的变化,但是RoIAlign有很大的影响:它将掩码精度提高了10%-50%,在更严格的localization指标下显示出更大的收益。其次,我们发现有必要将掩码和类别预测解耦:我们为每个类别独立预测一个二进制掩码,类别之间没有竞争,并依靠网络的RoI分类分支来预测类别。相比之下,FCN通常执行逐像素的多分类操作,将分割和分类耦合在一起,我们的实验结果表明这种方法的实例分割效果不佳。

在不使用花哨技巧的情况下,Mask R-CNN在COCO实例分割任务上就超越了之前的所有SOTA单模型,包括COCO 2016比赛的冠军。作为副产品,我们的方法在COCO目标检测任务上也表现出色。在消融实验中,我们评估了多个基本实例,这使我们能够证明Mask R-CNN的鲁棒性,并分析其核心因素的影响。

我们的模型可以在GPU上以每帧约200ms的速度运行,在一台8-GPU的机器上进行COCO训练需要1-2天。我们相信,快速的训练和测试,以及框架的灵活性和准确性,将有利于未来实例分割的研究。

最后,我们通过COCO关键点数据集上的人体姿态估计任务展示了Mask R-CNN框架的通用性。通过将每个关键点视为一个独热二进制掩码,只需对Mask R-CNN稍加修改,即可用于检测特定实例的姿态。Mask R-CNN超越了COCO 2016关键点检测比赛的冠军,并且能够以5帧/秒的速度运行。因此,Mask R-CNN可以被更广泛地视为一个实例识别的灵活框架,并且很容易泛化到其他更复杂的任务上。

模型方法

Mask R-CNN方法很简单:Faster R-CNN对每个候选对象有两个输出,一个是类别标签,另一个是边框偏移量。在此基础上,我们添加了第三个分支,用于输出分割掩码。因此,Mask R-CNN是一个自然且直观的想法。但是掩码输出不同于类别和边框输出,需要提取更精细的对象空间布局。接下来,我们介绍了Mask R-CNN的关键元素,包括像素到像素对齐,这是Fast/Faster R-CNN所缺失的部分。

用于实例分割的Mask R-CNN框架

RoIAlign:虚线网格表示特征映射图,实线边框表示RoI(Region of Interest),点表示每个边框中的4个采样点。RoIAlign通过双线性插值从特征映射图上的相邻网格点计算每个采样点的值。

  • Network Architecture: 为了表述清晰,有两种分类方法
  1. 使用了不同的backbone:resnet-50,resnet-101,resnext-50,resnext-101;
  2. 使用了不同的head Architecture:Faster RCNN使用resnet50时,从Block 4导出特征供RPN使用,这种叫做ResNet-50-C4
  3. 作者使用除了使用上述这些结构外,还使用了一种更加高效的backbone:FPN(特征金字塔网络)
Head架构:我们扩展了两个现有的Faster R-CNN Head。
  • Mask R-CNN基本结构:与Faster RCNN采用了相同的two-state结构:首先是通过一阶段网络找出RPN,然后对RPN找到的每个RoI进行分类、定位、并找到binary mask。这与当时其他先找到mask然后在进行分类的网络是不同的。
  • Mask R-CNN的损失函数L = L{_{cls}} + L{_{box}} + L{_{mask}} (当然了,你可以在这里调权以实现更好的效果)
  • Mask的表现形式(Mask Representation):因为没有采用全连接层并且使用了RoIAlign,我们最终是在一个小feature map上做分割。
  • RoIAlign:RoIPool的目的是为了从RPN网络确定的ROI中导出较小的特征图(a small feature map,eg 7×7),ROI的大小各不相同,但是RoIPool后都变成了7×7大小。RPN网络会提出若干RoI的坐标以[x,y,w,h]表示,然后输入RoI Pooling,输出7×7大小的特征图供分类和定位使用。问题就出在RoI Pooling的输出大小是7×7上,如果RON网络输出的RoI大小是8*8的,那么无法保证输入像素和输出像素是一一对应,首先他们包含的信息量不同(有的是1对1,有的是1对2),其次他们的坐标无法和输入对应起来。这对分类没什么影响,但是对分割却影响很大。RoIAlign的输出坐标使用插值算法得到,不再是简单的量化;每个grid中的值也不再使用max,同样使用差值算法。

Implementation Details

使用Fast/Faster相同的超参数,同样适用于Mask RCNN

  • Training:

1、与之前相同,当IoU与Ground Truth的IoU大于0.5时才会被认为有效的RoI,L{_{mask}}只把有效RoI计算进去。

2、采用image-centric training,图像短边resize到800,每个GPU的mini-batch设置为2,每个图像生成N个RoI,在使用ResNet-50-C4 作为backbone时,N=64,在使用FPN作为backbone时,N=512。作者服务器中使用了8块GPU,所以总的minibatch是16, 迭代了160k次,初始lr=0.02,在迭代到120k次时,将lr设定到 lr=0.002,另外学习率的weight_decay=0.0001 momentum = 0.9。如果是resnext,初始lr=0.01,每个GPU的mini-batch是1。

3、RPN的anchors有5种scale,3种ratios。为了方便剥离、如果没有特别指出,则RPN网络是单独训练的且不与Mask R-CNN共享权重。但是在本论文中,RPN和Mask R-CNN使用一个backbone,所以他们的权重是共享的。(Ablation Experiments 为了方便研究整个网络中哪个部分其的作用到底有多大,需要把各部分剥离开)

  • Inference:在测试时,使用ResNet-50-C4作为 backbone情况下proposal number=300,使用FPN作为 backbone时proposal number=1000。然后在这些proposal上运行bbox预测,接着进行非极大值抑制。mask分支只应用在得分最高的100个proposal上。顺序和train是不同的,但这样做可以提高速度和精度。mask 分支对于每个roi可以预测k个类别,但是我们只要背景和前景两种,所以只用k-th mask,k是根据分类分支得到的类型。然后把k-th mask resize成roi大小,同时使用阈值分割(threshold=0.5)二值化

Experiments

Main Results

在下图中可以明显看出,FCIS的分割结果中都会出现一条竖着的线(systematic artifacts),这线主要出现在物体重的部分,作者认为这是FCIS架构的问题,无法解决的。但是在Mask RCNN中没有出现。

Ablation Experiments(剥离实验)

  • Architecture:
    从table 2a中看出,Mask RCNN随着增加网络的深度、采用更先进的网络,都可以提高效果。注意:并不是所有的网络都是这样。
  • Multinomial vs. Independent Masks:(mask分支是否进行类别预测)从table 2b中可以看出,使用sigmoid(二分类)和使用softmax(多类别分类)的AP相差很大,证明了分离类别和mask的预测是很有必要的
  • Class-Specific vs. Class-Agnostic Masks:目前使用的mask rcnn都使用class-specific masks,即每个类别都会预测出一个mxm的mask,然后根据类别选取对应的类别的mask。但是使用Class-Agnostic Masks,即分割网络只输出一个mxm的mask,可以取得相似的成绩29.7vs30.3
  • RoIAlign:tabel 2c证明了RoIAlign的性能
  • Mask Branch:tabel 2e,FCN比MLP性能更好

Bounding Box Detection Results    

  • Mask RCNN精度高于Faster RCNN
  • Faster RCNN使用RoI Align的精度更高
  • Mask RCNN的分割任务得分与定位任务得分相近,说明Mask RCNN已经缩小了这部分差距。

Timing 

  • Inference:195ms一张图片,显卡Nvidia Tesla M40。其实还有速度提升的空间,比如减少proposal的数量等。
  • Training:ResNet-50-FPN on COCO trainval35k takes 32 hours  in our synchronized 8-GPU implementation (0.72s per 16-image mini-batch),and 44 hours with ResNet-101-FPN。

Mask R-CNN for Human Pose Estimation

让Mask R-CNN预测k个masks,每个mask对应一个关键点的类型,比如左肩、右肘,可以理解为one-hot形式。

  • 使用cross entropy loss,可以鼓励网络只检测一个关键点;
  • ResNet-FPN结构
  • 训练了90k次,最开始lr=0.02,在迭代60k次时,lr=0.002,80k次时变为0.0002

MICCAI 2022:基于 MLP 的快速医学图像分割网络 UNeXt

论文地址: https://arxiv.org/abs/2203.04967

github:https://github.com/jeya-maria-jose/UNeXt-pytorch

UnetX 网络结构

Datasets

  1. ISIC 2018 – Link
  2. BUSI – Link

MICCAI 2022:基于 MLP 的快速医学图像分割网络 UNeXt

前言

最近 MICCAI 2022 的论文集开放下载了,地址:https://link.springer.com/book/10.1007/978-3-031-16443-9 ,每个部分的内容如下所示:

Part I: Brain development and atlases; DWI and tractography; functional brain networks; neuroimaging; heart and lung imaging; dermatology;

Part II: Computational (integrative) pathology; computational anatomy and physiology; ophthalmology; fetal imaging;

Part III: Breast imaging; colonoscopy; computer aided diagnosis;

Part IV: Microscopic image analysis; positron emission tomography; ultrasound imaging; video data analysis; image segmentation I;

Part V: Image segmentation II; integration of imaging with non-imaging biomarkers;

Part VI: Image registration; image reconstruction;

Part VII: Image-Guided interventions and surgery; outcome and disease prediction; surgical data science; surgical planning and simulation; machine learning – domain adaptation and generalization;

Part VIII: Machine learning – weakly-supervised learning; machine learning – model interpretation; machine learning – uncertainty; machine learning theory and methodologies.

其中关于分割有两个部分,Image segmentation I 在 Part IV, 而 Image segmentation II 在 Part V。

随着医学图像的解决方案变得越来越适用,我们更需要关注使深度网络轻量级、快速且高效的方法。具有高推理速度的轻量级网络可以被部署在手机等设备上,例如 POCUS(point-of-care ultrasound)被用于检测和诊断皮肤状况。这就是 UNeXt 的动机。

方法概述

之前我们解读过基于 Transformer 的 U-Net 变体,近年来一直是领先的医学图像分割方法,但是参数量往往不乐观,计算复杂,推理缓慢。这篇文章提出了基于卷积多层感知器(MLP)改进 U 型架构的方法,可以用于图像分割。设计了一个 tokenized MLP 块有效地标记和投影卷积特征,使用 MLPs 来建模表示。这个结构被应用到 U 型架构的下两层中(这里我们假设纵向一共五层)。文章中提到,为了进一步提高性能,建议在输入到 MLP 的过程中改变输入的通道,以便专注于学习局部依赖关系特征。还有额外的设计就是跳跃连接了,并不是我们主要关注的地方。最终,UNeXt 将参数数量减少了 72 倍,计算复杂度降低了 68 倍,推理速度提高了 10 倍,同时还获得了更好的分割性能,如下图所示。

UNeXt 架构

UNeXt 的设计如下图所示。纵向来看,一共有两个阶段,普通的卷积和 Tokenized MLP 阶段。其中,编码器和解码器分别设计两个 Tokenized MLP 块。每个编码器将分辨率降低两倍,解码器工作相反,还有跳跃连接结构。每个块的通道数(C1-C5)被设计成超参数为了找到不掉点情况下最小参数量的网络,对于使用 UNeXt 架构的实验,遵循 C1 = 32、C2 = 64、C3 = 128、C4 = 160 和 C5 = 256。

TokMLP 设计思路

关于 Convolutional Stage 我们不做过多介绍了,在这一部分重点专注 Tokenized MLP Stage。从上一部分的图中,可以看到 Shifted MLP 这一操作,其实思路类似于 Swin transformer,引入基于窗口的注意力机制,向全局模型中添加更多的局域性。下图的意思是,Tokenized MLP 块有 2 个 MLP,在一个 MLP 中跨越宽度移动特征,在另一个 MLP 中跨越高度移动特征,也就是说,特征在高度和宽度上依次移位。论文中是这么说的:“我们将特征分成 h 个不同的分区,并根据指定的轴线将它们移到 j=5 的位置”。其实就是创建了随机窗口,这个图可以理解为灰色是特征块的位置,白色是移动之后的 padding。

补充:MLP拥有大量参数,计算成本高且容易过度拟合,而且因为层之间的线性变换总是将前一层的输出作为一个整体,所以MLP在捕获输入特征图中的局部特征结构的能力较弱。通过轴向移动特征信息, Shifted MLP可以得到不同方向的信息流,这有助于捕获局部相关性。该操作使得我们采用纯MLP架构即可取得与CNN相同的感受野。

解释过 Shifted MLP 后,我们再看另一部分:tokenized MLP block。首先,需要把特征转换为 tokens(可以理解为 Patch Embedding 的过程,感觉这个就是个普通卷积,而且作者为了保证conv后的矩阵减半,设置步幅为2,总之,有些编故事的意思了)。为了实现 tokenized 化,使用 kernel size 为 3 的卷积(patch_size=3, stride=2),这样会使得矩阵H和W减半,并将通道的数量改为 E,E 是 embadding 嵌入维度( token 的数量),也是一个超参数。然后把这些 token 送到上面提到的第一个跨越宽度的 MLP 中。

这里会产生了一个疑问,关于 kernel size 为 3 的卷积,使用的是什么样的卷积层?答:这里还是普通的卷积,文章中提到了 DWConv(DepthWise Conv),是后面的特征通过 DW-Conv 传递。使用 DWConv 有两个原因:(1)它有助于对 MLP 特征的位置信息进行编码。MLP 块中的卷积层足以编码位置信息,它实际上比标准的位置编码表现得更好。像 ViT 中的位置编码技术,当测试和训练的分辨率不一样时,需要进行插值,往往会导致性能下降。(2)DWConv 使用的参数数量较少。

这时我们得到了 DW-Conv 传递过来的特征,然后使用 GELU 完成激活。接下来,通过另一个 MLP(跨越height)传递特征,该 MLP 把进一步改变了特征尺寸。在这里还使用一个残差连接,将原始 token 添加为残差。然后我们利用 Layer Norm(LN),将输出特征传递到下一个块。LN 比 BN 更可取,因为它是沿着 token 进行规范化,而不是在 Tokenized MLP 块的整个批处理中进行规范化。上面这些就是一个 tokenized MLP block 的设计思路。

此外,文章中给出了 tokenized MLP block 涉及的计算公式:

其中 T 表示 tokens,H 表示高度,W 表示宽度。值得注意的是,所有这些计算都是在 embedding 维度 H 上进行的,它明显小于特征图的维度 HN×HN,其中 N 取决于 block 大小。在下面的实验部分,文章将 H 设置为 768。

实验部分

实验在 ISIC 和 BUSI 数据集上进行,可以看到,在 GLOPs、性能和推理时间都上表现不错。

下面是可视化和消融实验的部分。可视化图可以发现,UNeXt 处理的更加圆滑和接近真实标签。

消融实验可以发现,从原始的 UNet 开始,然后只是减少过滤器的数量,发现性能下降,但参数并没有减少太多。接下来,仅使用 3 层深度架构,既 UNeXt 的 Conv 阶段。显着减少了参数的数量和复杂性,但性能降低了 4%。加入 tokenized MLP block 后,它显着提高了性能,同时将复杂度和参数量是一个最小值。接下来,我们将 DWConv 添加到 positional embedding,性能又提高了。接下来,在 MLP 中添加  Shifted 操作,表明在标记化之前移位特征可以提高性能,但是不会增加任何参数或复杂性。注意:Shifted MLP 不会增加 GLOPs。

一些理解和总结

在这项工作中,提出了一种新的深度网络架构 UNeXt,用于医疗图像分割,专注于参数量的减小。UNeXt 是一种基于卷积和 MLP 的架构,其中有一个初始的 Conv 阶段,然后是深层空间中的 MLP。具体来说,提出了一个带有移位 MLP 的标记化 MLP 块。在多个数据集上验证了 UNeXt,实现了更快的推理、更低的复杂性和更少的参数数量,同时还实现了最先进的性能。

另外,个人觉得 带有移位 MLP 的标记化 MLP 块这里其实有点讲故事的意思了。

我在读这篇论文的时候,直接注意到了它用的数据集。我认为 UNeXt 可能只适用于这种简单的医学图像分割任务,类似的有 Optic Disc and Cup Seg,对于更复杂的,比如血管,软骨,Liver Tumor,kidney Seg 这些,可能效果达不到这么好,因为运算量被极大的减少了,每个 convolutional 阶段只有一个卷积层。MLP 魔改 U-Net 也算是一个尝试,在 Tokenized MLP block 中加入 DWConv 也是很合理的设计。

代码实现:

class shiftmlp(nn.Module):
    def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.GELU, drop=0., shift_size=5):
        super().__init__()
        out_features = out_features or in_features
        hidden_features = hidden_features or in_features
        self.dim = in_features
        self.fc1 = nn.Linear(in_features, hidden_features)
        self.dwconv = DWConv(hidden_features)
        self.act = act_layer()
        self.fc2 = nn.Linear(hidden_features, out_features)
        self.drop = nn.Dropout(drop)

        self.shift_size = shift_size
        self.pad = shift_size // 2

        
        self.apply(self._init_weights)

    def _init_weights(self, m):
        if isinstance(m, nn.Linear):
            trunc_normal_(m.weight, std=.02)
            if isinstance(m, nn.Linear) and m.bias is not None:
                nn.init.constant_(m.bias, 0)
        elif isinstance(m, nn.LayerNorm):
            nn.init.constant_(m.bias, 0)
            nn.init.constant_(m.weight, 1.0)
        elif isinstance(m, nn.Conv2d):
            fan_out = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
            fan_out //= m.groups
            m.weight.data.normal_(0, math.sqrt(2.0 / fan_out))
            if m.bias is not None:
                m.bias.data.zero_()
    


    def forward(self, x, H, W):
        # pdb.set_trace()
        B, N, C = x.shape

        xn = x.transpose(1, 2).view(B, C, H, W).contiguous()
        #pad,方便后面的torch.chunk
        xn = F.pad(xn, (self.pad, self.pad, self.pad, self.pad) , "constant", 0)
        #按照dim=1维度,分成 self.shift_size(5)个块
        xs = torch.chunk(xn, self.shift_size, 1)
        #torch.roll(x,y,d)将x,沿着d维度,向上/下roll y个值
        x_shift = [torch.roll(x_c, shift, 2) for x_c, shift in zip(xs, range(-self.pad, self.pad+1))]
        x_cat = torch.cat(x_shift, 1)
        #x.narrow(*dimension*, *start*, *length*) → Tensor 表示取变量x的第dimension维,从索引start开始到(start+length-1)范围的值。
        x_cat = torch.narrow(x_cat, 2, self.pad, H)
        x_s = torch.narrow(x_cat, 3, self.pad, W)

        x_s = x_s.reshape(B,C,H*W).contiguous()
        x_shift_r = x_s.transpose(1,2)

        x = self.fc1(x_shift_r)

        x = self.dwconv(x, H, W)
        x = self.act(x) 
        x = self.drop(x)

        xn = x.transpose(1, 2).view(B, C, H, W).contiguous()
        xn = F.pad(xn, (self.pad, self.pad, self.pad, self.pad) , "constant", 0)
        xs = torch.chunk(xn, self.shift_size, 1)
        x_shift = [torch.roll(x_c, shift, 3) for x_c, shift in zip(xs, range(-self.pad, self.pad+1))]
        x_cat = torch.cat(x_shift, 1)
        x_cat = torch.narrow(x_cat, 2, self.pad, H)
        x_s = torch.narrow(x_cat, 3, self.pad, W)
        x_s = x_s.reshape(B,C,H*W).contiguous()
        x_shift_c = x_s.transpose(1,2)

        x = self.fc2(x_shift_c)
        x = self.drop(x)
        return x

class shiftedBlock(nn.Module):
    def __init__(self, dim, num_heads, mlp_ratio=4., qkv_bias=False, qk_scale=None, drop=0., attn_drop=0.,
                 drop_path=0., act_layer=nn.GELU, norm_layer=nn.LayerNorm, sr_ratio=1):
        super().__init__()


        self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()
        self.norm2 = norm_layer(dim)
        mlp_hidden_dim = int(dim * mlp_ratio)
        self.mlp = shiftmlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop)
        self.apply(self._init_weights)

    def _init_weights(self, m):
        if isinstance(m, nn.Linear):
            trunc_normal_(m.weight, std=.02)
            if isinstance(m, nn.Linear) and m.bias is not None:
                nn.init.constant_(m.bias, 0)
        elif isinstance(m, nn.LayerNorm):
            nn.init.constant_(m.bias, 0)
            nn.init.constant_(m.weight, 1.0)
        elif isinstance(m, nn.Conv2d):
            fan_out = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
            fan_out //= m.groups
            m.weight.data.normal_(0, math.sqrt(2.0 / fan_out))
            if m.bias is not None:
                m.bias.data.zero_()

    def forward(self, x, H, W):

        x = x + self.drop_path(self.mlp(self.norm2(x), H, W))
        return x

PointRend –图像细颗粒分割

title
https://arxiv.org/abs/1912.08193

论文地址: https://arxiv.org/abs/1912.08193

gitlab: https://github.com/zsef123/PointRend-PyTorch

存在的问题

在目前的语义分割网络中存在的问题主要有过采样和现采样。

1.过采样( oversample ):对于图片中低频区域( 属于同一个物体 ),没必要使用 太多的采样点,却使用太多采样点造成过采样;

2.欠采样( undersample ) :对于图片中高频区域( 靠近物体边界 ),如果这些区域的采样过于稀疏,导致分割出的边界过于平滑,不大真实

文章要解决的问题是在实例分割任务中边缘不够精细的问题。以MaskRCNN举例,由于计算量和显存的原因,对于每一个ROIAlign之后的proposal我们一般只会upsample到28*28的分辨率输出mask。这对于绝大多数物体显然是不够的。如果想得到像素级别的精度,我们不得不付出更大的计算和存储代价。那有什么办法可以在低代价下仍然得到精细的分割结果呢?其实很重要的一点是往往这些不准确的部分是在物体的边缘,这些边缘其实只占了整个物体中非常小的一部分。所以基于这样的一个想法,作者提出可以每次在预测出来的mask中只选择Top N最不确定的位置进行细分预测。每个细分点的特征可以通过Bilinear插值得到,每个位置上的classifier通过一个简单的MLP来实现。这其实是等价于用一个1*1的conv来预测,但是对于中心很确定的点并不计算。整体的示意图如下:

PointRend 解决了什么问题?

这篇论文讲了一个很好听的故事,即:把语义分割以及实例分割问题(统称图像分割问题)当做一个渲染问题来解决。故事虽然这么讲,但本质上这篇论文其实是一个新型上采样方法,针对物体边缘的图像分割进行优化,使其在难以分割的物体边缘部分有更好的表现

作为一个小白,那么问题来了:

1、什么是渲染?

2、为什么要把图像分割问题当做渲染问题呢?

要想知道什么是渲染,可以参考:

计算机中所说的「渲染」是什么意思?

简单来说,渲染就是“绘制”,把3D的物体在2D平面上绘制出来。

为什么要把图像分割问题和渲染问题扯在一起呢?因为讲故事好听啊,论文好写嘛….咳咳…不不,是因为二者有类似的问题要解决:即物体边缘难以处理。

具体来说,在图像渲染中,对于多个3D物体,在边缘要判断对于镜头而言谁先谁后,而且还得抗锯齿;而对于图像分割问题,边缘恢复也一直是个麻烦事儿,因为在典型的语义分割网络中(如FCN、DeepLab),在CNN内部一般都会相对输入图像降采样16倍,然后再想办法上采样回去。更细致地说,对于 DeepLabV3+,模型最后直接是一个4倍的双线性插值上采样,这显然对物体边缘的预测十分不利。虽然 DeepLabV3+当时在2017年就达到了秒天秒地的 89%mIoU on VOC2012 test (使用了300M JFT 数据集预训练),至今无人超越(因为JFT 数据集 Google没有公开 \手动滑稽),但显然这个上采样过程仍然存在较大的提升空间。

参考链接:Uno Whoiam:DeepLab 语义分割模型 v1、v2、v3、v3+ 概要(附 Pytorch 实现)

而在实例分割网络中,Mask R-CNN 这货生成的 Mask 才 28×28,要是把这样的 mask 拉伸到 不说多了比如 256×256,还指望它可以很好地预测边缘?我只能说这是在想Peach。

事实上,在图像分割任务上边缘预测不理想这个情况其实在许多前人的工作中都有提及,比如 Not All Pixels Are Equal: Difficulty-Aware Semantic Segmentation via Deep Layer Cascade 中就详细统计了语义分割中,模型最容易误判的 pixel基本上都在物体边缘(如下图右上红色部分标记) 。

而关于上采样其实也有一些前人的工作,如 Decoders Matter for Semantic Segmentation: Data-Dependent Decoding Enables Flexible Feature Aggregation,在实现上有点像超分辨率网络 ESPCN 里使用的 sub-pixel convolutional layer 的操作,不过多加了一个二阶范数约束:

总的来说,图像分割边缘预测是一个未被很好解决的问题,而何恺明团队的 PointRend 是对此问题的一个新的思路和解法,接下来将介绍 PointRend 是如何 work 的。

文主要贡献

1.提出可嵌入主流网络的PointRend模块,提高了图像分割精度。

2.把图像分割问题看作渲染问题,本质上是一个新型上采样方法,为图像分割提供独特视角。

3.降低了训练所需的算力。输出224×224分辨率图像,PointRend只需0.9B FLOPs。

二、总体思路

PointRend 方法要点总结来说是一个迭代上采样的过程:

while 输出的分辨率 < 图片分辨率:

  1. 对输出结果进行2倍双线性插值上采样得到 coarse prediction_i。(粗分辨率预测)
  2. 挑选出 N 个“难点”,即结果很有可能和周围点不一样的点(例如物体边缘)。
  3. 对于每个难点,获取其“表征向量”,“表征向量”由两个部分组成,其一是低层特征(fine-grained features),通过使用点的坐标,在低层的特征图上进行双线性插值获得(类似 RoI Align),其二是高层特征(coarse prediction),由步骤 1 获得。
  4. 使用 MLP 对“表征向量”计算得到新的预测,更新 coarse prediction_i 得到 coarse prediction_i+1。这个 MLP 其实可以看做一个只对“难点”的“表征向量”进行运算的由多个 conv1x1 组成的小网络。

整个过程可以这么理解:

小明同学做题,现在有已知条件(coarse prediction_0,fine-grained features),想求解答案(coarse prediction_k),发现直接求(双线性插值or其它方法)不够准确,那就一步一步来吧(从coarse prediction_1,coarse prediction_2….求到coarse prediction_k)。好的,现在求coarse prediction_1,诶,发现有好多东西不知道,不能从 coarse prediction_0 直接得到怎么办?那就找出不知道的(“难点”),在 fine-grained features 里面找出对应的线索(ROIAlign-like 双线性插值),然后在结合 coarse prediction_0 得到整体线索(“特征向量”)求解(使用MLP计算),嗯,终于得到 coarse prediction_1了。再用同样的思路反复求解,直到 coarse prediction_k。

示意图如下:

对于一个coarse prediction(4×4大小),将其上采样两倍(8×8大小,这里可以理解为检测头的输出)后,取了一些难分割的点(大多是边缘部分),取这些点的特征向量输入到MLP网络中,进行point prediction,得到每一个点的新类别,最后结果输出(8×8大小,边缘更加精确的结果)。

另外,其PointRend 训练为了节省时间,没有使用上述的迭代过程,而是使用多种组合的采样方法,不赘述,详见paper。

  1. 从PointRend的应用思路中可以看到,这里包含了两个阶段的特征处理,分别是fine-grained features和coarse prediction部分,如果主干网络是ResNet,那么fine-grained features就是ResNet的stage2输出,也就是4倍下采样时的精细分割结果,而coarse prediction就是检测头的预测结果(还未上采样还原成原图的结果)。
  2. 从coarse prediction中挑选N个“难点”,也就是结果很有可能和周围点不一样的点(比如物体边缘的点)。对于每一个难点,获取他的“特征向量”,对于点特征向量(point features),主要由两部分组成,分别是fine-grained features的对应点和coarse prediction的对应点的特征向量,将这个两个特征向量拼接成一个向量。
  3. 接着,通过一个MLP网络对这个“特征向量”进行预测,更新coarse prediction。也就相当于对这个难点进行新的预测,对他进行分类。

看完这个,我们就可以这么理解,将预测难的点(边缘点)提取出来,再提取其特征向量,经过MLP网络,将这个点的归属进行分类,然后提升这些点的分类准确率。这就是PointRend的思想。

一个PointRend模块包括三部分

1.point selection strategy:用于inference和traing的点选择

对于点采样过程,需要对模型的Train过程和Inference过程做区分

该方法的核心思想是灵活自适应地选择图像平面上的点来预测分割标签。直观地说,这些点应该更密集地位于高频区域附近,例如物体边界,类似于射线追踪中的反混叠问题。我们产生了推理训练的想法。

  • inference推理

通过仅在与其邻域有显着不同的位置进行计算,该方法可用于有效地渲染高分辨率图像(例如,通过光线跟踪)。对于所有其他位置,通过对已经计算的输出值(从粗网格开始)进行插值来获得值。

对于每个区域,我们以粗到精的方式迭代地“渲染”输出蒙版。在规则网格上的点上进行最粗糙级别的预测(例如,通过使用标准的粗糙分段预测头)。在每次迭代中,PointRend使用双线性插值对其先前预测的分段进行上采样,然后在此较密集的网格上选择N个最不确定的点(例如,对于二进制掩码,概率最接近0.5的那些)。然后,PointRend为这N个点中的每一个点计算特征,并预测它们的标签。重复该过程,直到将分段上采样到所需的分辨率为止。

  • training

对于Train过程的点采样操作,同样可以遵循Inference中的操作。但是作者发现,这样子采样对于梯度的传播不太友好,于是只能被迫选择其他的点采样策略——干脆就用随机采样的方式来进行采样。

在训练过程中,PointRend还需要选择一些点,以在这些点上构建用于训练point head的逐点(point-wise)特征。原则上,点选择策略可以类似于推理inference中使用的细分策略。但是,细分引入了一系列步骤,这些步骤对于通过反向传播训练神经网络不太友好。取而代之的是,为了训练,我们使用基于随机采样的非迭代策略

采样策略在特征图上选择N个点进行训练。它旨在使用三个原理将选择偏向不确定区域,同时还保留一定程度的均匀覆盖。对于训练和推理选择,N的值可以不同。

(i)过度生成:我们通过从均匀分布中随机采样kN个点(k> 1)来过度生成候选点。(ii)重要抽样:通过对所有kN个点的粗略预测值进行插值并计算任务特定的不确定性估计,我们将重点放在具有粗略预测的点上。从kN个候选中选择最不确定的βN个点(β∈[0,1])。(iii)覆盖范围:从均匀分布中采样剩余的(1-β)N点。我们用不同的设置来说明此过程,并将其与常规的网格选择进行比较,如下图所示。

在训练时,预测和损失函数仅在N个采样点上计算(除粗略分割外),这比通过细分步骤进行反向传播更简单,更有效。这种设计类似于在Faster R-CNN系统中对RPN + Fast R-CNN的并行训练,其推理是顺序的。

2. Point-wise Representation:逐点表示

PointRend通过组合(例如,级联)两种特征类型(细粒度和粗略预测特征)在选定点上构造逐点特征,如下所述。

  • 细粒度特征

为了允许PointRend呈现精细的分割细节,我们从CNN特征图中提取每个采样点的特征向量。 因为一个点是“实值2D坐标”,所以我们按照标准做法对特征图执行双线性插值,以计算特征向量。 可以从单个特征图中提取特征(例如,ResNet中的res2);也可以按照Hypercolumn方法,从多个特征图(例如res2到res5)中提取并连接它们。

  • 粗预测特征

细粒度的特征可以解析细节,但在两个方面也有不足:

首先,它们不包含特定区域的信息,因此,两个实例的边界框重叠的相同点将具有相同的细粒度特征。但是,该点只能位于一个实例之中。 因此,对于实例分割的任务,其中不同的区域可能针对同一点预测不同的标签,因此需要其他区域特定的信息。

其次,取决于用于细粒度特征的特征图,这些特征可能只包含相对较低级别的信息(例如,我们将对res2使用DeepLabV3)。 因此,需要有更多具有上下文和语义信息的特征。

基于这两点考虑,第二种特征类型是来自网络的粗分割预测,例如表示k类预测的区域(box)中每个点的k维向量。通过设计,粗分辨率能够提了更加全局的上下文信息,而通道则传递语义类别。这些粗略的预测与现有架构的输出相似,并且在训练过程中以与现有模型相同的方式进行监督。例如,在mask R-CNN中,粗预测可以是一个轻量级的7×7分辨率Mask头的输出。

点特征向量(point features),主要由两部分组成,分别是fine-grained features的对应点和coarse prediction的对应点的特征向量,将这个两个特征向量拼接成一个向量

3. point head

给定每个选定点的逐点特征表示,PointRend使用简单的多层感知器(MLP)进行逐点分割预测。这个MLP在所有点(和所有区域)上共享权重,类似于图卷积或PointNet。由于MLP会预测每个点的分割标签,因此可以通过特定任务的分割loss进行训练。

三、效果如何?

实验结果

  • 网络设计

实验使用ResNet-50+ FPN 的Mask-Rcnn作backbone。 Mask-RCNN中的默认head是region-wise FCN,用“ 4×conv”表示,作为用来与本文网络进行比较的基准网络。

为了计算粗略预测,我们用重量更轻的设计替换4×conv Mask头,该设计类似于Mask R-CNN的box head产生7×7Mask预测。具体来说,对于每个边界框,我们使用双线性插值从FPN的P2层提取14×14特征图。这些特征是在边界框内的规则网格上计算的(此操作可以看作是RoIAlign的简单版本)。接下来,我们使用具有256个输出通道步幅为2的 2×2卷积层,后跟ReLU, 将空间大小减小到7×7。最后,类似于Mask R-CNN的box head,用两个带1024宽的隐藏层的MLP为K类分别产生7×7的Mask预测。ReLU用于MLP的隐藏层,并且Sigmoid激活函数应用于输出。

PointRend:在每个选定点上,使用双线性插值从粗预测头的输出中提取K维特征向量,PointRend还从FPN的P2级别插值256维特征向量,步长为4。这些粗预测和细粒度特征向量是串联在一起的,我们使用具有256个通道的3个隐藏层的MLP在选定点进行K类别预测。在MLP的每个层中,我们用K个粗预测特征补充到256个输出通道中,作为下一层输入向量。在MLP中使用ReLU,并将Sigmoid激活函数应用于输出。

不得不说这个针对物体边缘进行优化的上采样方法的确在感官上和数据上都有很不错的效果:

语义分割结果:

实例分割结果(基于MaskR-CNN):

PointRend的一些代码和实现

摘自: https://chowdera.com/2022/194/202207120607167479.html

代码详解: https://www.361shipin.com/blog/1536592971120508928

  • 作者提出可以在预测出来的mask中只选择Top N最不确定的位置进行细分预测。

具体为先根据粗糙预测出来的mask,将mask按类别预测分数排序,选出分数高的前2 类别的mask,计算出在2个类别mask上均有较高得分的Top K个像素点作为K 个不确定点【1个像素点只能对应1个类别,如果它对应2个类别的分数都很高,说明它很可能是边界点,也是不确定的】

def sampling_points(mask, N, k=3, beta=0.75, training=True):
    """
    主要思想:根据粗糙的预测结果,找出不确定的像素点
    :param mask: 粗糙的预测结果(out)   eg.[2, 19, 48, 48]
    :param N: 不确定点个数(train:N = 图片的尺寸/16, test: N = 8096)    eg. N=48
    :param k: 超参
    :param beta: 超参
    :param training:
    :return: 不确定点的位置坐标  eg.[2, 48, 2]
    """
    assert mask.dim() == 4, "Dim must be N(Batch)CHW"   #this mask is out(coarse)
    device = mask.device
    B, _, H, W = mask.shape   #first: mask[1, 19, 48, 48]
    mask, _ = mask.sort(1, descending=True) #_ : [1, 19, 48, 48],按照每一类的总体得分排序
    if not training:
        H_step, W_step = 1 / H, 1 / W
        N = min(H * W, N)
        uncertainty_map = -1 * (mask[:, 0] - mask[:, 1])
        #mask[:, 0]表示每个像素最有可能的分类,mask[:, 1]表示每个像素次有可能的分类,当一个像素
        #即是最有可能的又是次有可能的,则证明它不好预测,对应的uncertainty_map就相对较大
        _, idx = uncertainty_map.view(B, -1).topk(N, dim=1) #id选出最不好预测的N个点
        points = torch.zeros(B, N, 2, dtype=torch.float, device=device)
        points[:, :, 0] = W_step / 2.0 + (idx  % W).to(torch.float) * W_step    #点的横坐标
        points[:, :, 1] = H_step / 2.0 + (idx // W).to(torch.float) * H_step    #点的纵坐标
        return idx, points  #idx:48 || points:[1, 48, 2]
  • 得到不确定点的位置以后,可以通过Bilinear插值得到对应的特征,对每个不确定点的使用一个MLP来进行单独进行细分预测【训练与预测有所区别】。

具体为:通过刚刚得到的不确定点所在图片的相对位置坐标来找到对应的特征点,将此点对应的特征向量与此点的粗糙预测结果合并,然后通过一个MLP进行细分预测。

##训练阶段
def forward(self, x, res2, out):
        """
        主要思路:
        通过 out(粗糙预测)计算出top N 个不稳定的像素点,针对每个不稳定像素点得到在res2(fine)
        和out(coarse)中对应的特征,组合N个不稳定像素点对应的fine和coarse得到rend,
        再通过mlp得到更准确的预测
        :param x: 表示输入图片的特征     eg.[2, 3, 768, 768]
        :param res2: 表示xception的第一层特征输出     eg.[2, 256, 192, 192]
        :param out: 表示经过级联空洞卷积提取的特征的粗糙预测    eg.[2, 19, 48, 48]
        :return: rend:更准确的预测,points:不确定像素点的位置
        """
        """
        1. Fine-grained features are interpolated from res2 for DeeplabV3
        2. During training we sample as many points as there are on a stride 16 feature map of the input
        3. To measure prediction uncertainty
           we use the same strategy during training and inference: the difference between the most
           confident and second most confident class probabilities.
        """
        if not self.training:
            return self.inference(x, res2, out)
		#获得不确定点的坐标
        points = sampling_points(out, x.shape[-1] // 16, self.k, self.beta) #out:[2, 19, 48, 48] || x:[2, 3, 768, 768] || points:[2, 48, 2]
		#根据不确定点的坐标,得到对应的粗糙预测
        coarse = point_sample(out, points, align_corners=False) #[2, 19, 48]
        #根据不确定点的坐标,得到对应的特征向量
        fine = point_sample(res2, points, align_corners=False)  #[2, 256, 48]
		#将粗糙预测与对应的特征向量合并
        feature_representation = torch.cat([coarse, fine], dim=1)   #[2, 275, 48]
		#使用MLP进行细分预测
        rend = self.mlp(feature_representation) #[2, 19, 48]
        return {"rend": rend, "points": points}
##推理阶段
@torch.no_grad()
    def inference(self, x, res2, out):
        """
        输入:
        x:[1, 3, 768, 768],表示输入图片的特征
        res2:[1, 256, 192, 192],表示xception的第一层特征输出
        out:[1, 19, 48, 48],表示经过级联空洞卷积提取的特征的粗糙预测
        输出:
        out:[1,19,768,768],表示最终图片的预测
        主要思路:
        通过 out计算出top N = 8096 个不稳定的像素点,针对每个不稳定像素点得到在res2(fine)
        和out(coarse)中对应的特征,组合8096个不稳定像素点对应的fine和coarse得到rend,
        再通过mlp得到更准确的预测,迭代至rend的尺寸大小等于输入图片的尺寸大小
        """
        """
        During inference, subdivision uses N=8096
        (i.e., the number of points in the stride 16 map of a 1024×2048 image)
        """
        num_points = 8096
                while out.shape[-1] != x.shape[-1]: #out:[1, 19, 48, 48], x:[1, 3, 768, 768]
        	#每一次预测均会扩大2倍像素,直至与原图像素大小一致
            out = F.interpolate(out, scale_factor=2, mode="bilinear", align_corners=True)   #out[1, 19, 48, 48]
            points_idx, points = sampling_points(out, num_points, training=self.training)   #points_idx:8096 || points:[1, 8096, 2]
            coarse = point_sample(out, points, align_corners=False) #coarse:[1, 19, 8096]   表示8096个不稳定像素点根据高级特征得出的对应的类别
            fine = point_sample(res2, points, align_corners=False)  #fine:[1, 256, 8096]    表示8096个不稳定像素点根据低级特征得出的对应类别
            feature_representation = torch.cat([coarse, fine], dim=1)   #[1, 275, 8096] 表示8096个不稳定像素点合并fine和coarse的特征
            rend = self.mlp(feature_representation) #[1, 19, 8096]
            B, C, H, W = out.shape  #first:[1, 19, 128, 256]
            points_idx = points_idx.unsqueeze(1).expand(-1, C, -1)  #[1, 19, 8096]
            out = (out.reshape(B, C, -1)

PyTorch医学图像分割开源库

github: https://github.com/MontaEllis/Pytorch-Medical-Segmentation

基于PyTorch的专注于医学图像分割的开源库,其支持模型丰富,方便易用。其可算为torchio的一个实例,作者将其综合起来,包含众多经典算法,实用性比较强。

该库特点:

  1. 支持2D和3D医学图像分割,可以修改hparam.py文件来确定是2D分割还是3D分割以及是否可以进行多分类。
  2. 支持绝大数主流分割模型,几乎提供了所有的2D和3D分割的算法。
  3. 兼容几乎所有的医学数据格式(例如 nii.gz, nii, mhd, nrrd, …),修改hparam.py的fold\_arch即可。

作者提供了训练和测试推断的代码,简单配置后训练和推断都仅需要一行命令。

已包含的分割模型:

医学分割图像数据集汇总

(更多数据集请看医学影像数据集集锦:https://github.com/linhandev/dataset

数据集数据集大小 说明 链接
Kvasir-SEG1000 张(对)Kvasir-seg是 胃肠道息肉图像和相应分割面罩的开放式数据集,由医生手动注释,然后由经验丰富的胃肠病学家进行验证。Kvasir-SEG 数据集(大小 46.2 MB)包含来自 Kvasir Dataset v2 的 1000 个息肉图像及其对应的地面实况。Kvasir-SEG 中包含的图像的分辨率从 332×487 到 1920×1072 像素不等。https://datasets.s
imula.no/kvasir-seg/
CVC-ClinicDB600张CVC-ClinicDB 是从结肠镜检查视频中提取的帧数据库。CVC-ClinicDB 数据库由两种不同类型的图像组成:原始图像和息肉掩膜  https://polyp.grand-cha
llenge.org/CVCClinicDB/
CVC-ColonDB300张结肠镜检查视频的注释视频序列。它包含 15 个简短的结肠镜检查序列,来自 15 项不同的研究。在每个序列中显示一个息肉。 
Synapse multi-organ CT50从正在进行的结直肠癌化疗试验和回顾性腹疝研究的组合中随机选择了 50 份腹部 CT 扫描。50 次扫描是在门静脉造影阶段捕获的,具有可变的体积大小 (512 x 512 x 85 – 512 x 512 x 198) 和视场(约 280 x 280 x 280 mm 3 – 500 x 500 x 650 mm 3) . 平面内分辨率从 0.54 x 0.54 mm 2到 0.98 x 0.98 mm 2不等,而切片厚度范围从 2.5 mm 到 5.0 mm。标准注册数据由NiftyReg生成。https://www.syn
apse.org/#!Synapse:syn3193805/wiki/217789
MoNuSeg22,000张数据集是通过仔细注释几名患有不同器官肿瘤并在多家医院被诊断出的患者的组织图像获得的。该数据集是通过从TCGA存档下载以 40 倍放大倍率捕获的 H&E 染色组织图像创建的。H&E 染色是增强组织切片对比度的常规方案,通常用于肿瘤评估(分级、分期等)。考虑到多个器官和患者的细胞核外观的多样性,以及多家医院采用的丰富染色方案,训练数据集将能够开发出强大且可推广的细胞核分割技术,开箱即用。https://monuseg.gr
and-challenge.org/Data/
胰腺分割数据集 美国国立卫生研究院临床中心对 53 名男性和 27 名女性受试者进行了 82 次腹部对比增强 3D CT 扫描(门静脉静脉注射对比剂后约 70 秒)。17 名受试者是在肾切除术前扫描的健康肾脏捐赠者。其余 65 名患者由放射科医师从既无重大腹部病变也无胰腺癌病变的患者中选出。受试者的年龄范围为 18 至 76 岁,平均年龄为 46.8 ± 16.7。CT 扫描具有 512×512 像素的分辨率,具有不同的像素大小和 1.5 – 2.5 mm 之间的切片厚度,在 Philips 和 Siemens MDCT 扫描仪(120 kVp 管电压)上获得。 http://academictorre
nts.com/details/80ecfefc
abede760cdbdf63e38986501f7becd49
MICCAI胰腺分割数据集282目标:肝脏和肿瘤 模式:门静脉期 CT 大小: 420 3D 卷(282 培训 +139 测试) 来源:纪念斯隆凯特琳癌症中心 挑战:标签不平衡与大(背景)、中(胰腺)和小(肿瘤)结构https://drive.google.com
/drive/folders/1HqEgzS8BV2
c7xYNrZdEAnrHk7osJJ–2
LiTS肝脏分割数据集131+70LiTS数据集包含131组训练扫描和70组测试扫描,其中70组测试数据标签不公开。LiTS训练集中包含3DIRCADB中的所有数据,所以不要合并这两个数据集。Medical Segmentation Decathlon中肝脏分割的数据集就是LiTS。https://sliver07.gran
d-challenge.org/
covid19-ct-scans20数据来自Ieee8023,对20组扫描进行了左右肺和感染区的标注。https://www.kaggle.co
m/andrewmvd/covid19-ct-scans
Medical Segmentation Decathlon2,633医学分割十项全能是医学图像分割数据集的集合。它总共包含 2,633 张三维图像,这些图像是从多个感兴趣的解剖结构、多种模式和多个来源收集的。具体来说,它包含以下身体器官或部位的数据:大脑、心脏、肝脏、海马体、前列腺、肺、胰腺、肝血管、脾脏和结肠。http://medicald
ecathlon.com/
GlaS165GlaS 

结肠组织学图像挑战中的腺体分割
本次挑战中使用的数据集包含 165 张图像,这些图像来自 T3 或 T42 期结直肠腺癌的 16 个 H&E 染色组织切片。
每个切片属于不同的患者,切片是在实验室的不同场合处理的。
因此,该数据集在染色分布和组织结构方面表现出很高的受试者间变异性。
使用像素分辨率为 0.465µm 的 Zeiss MIRAX MIDI 幻灯片扫描仪将这些组织切片数字化为全幻灯片图像 (WSI)。
https://warwick.ac.uk/fac/cross_fac/tia/data/glascontest/
2018 Data Science Bowl该数据集包含大量分割的核图像。
这些图像是在各种条件下获得的,并且在细胞类型、放大倍率和成像方式(明场与荧光)方面有所不同。
该数据集旨在挑战算法泛化这些变化的能力。
出自UNet++: A Nested U-Net Architecture for Medical Image Segmentation
https://www.kaggle.com/c/data-science-bowl-2018/overview
ACDC150该数据集由 150 个检查(全部来自不同的患者)组成,分为 5 个均匀分布的亚组(4 个病理组和 1 个健康受试者组),如下所述。
此外,每位患者都附带以下附加信息:体重、身高以及舒张期和收缩期瞬间。
https://acdc.creatis.insa-lyon.fr/description/databases.html

参考:Medical Image Segmentation: https://paperswithcode.com/task/medical-image-segmentation

医学图像分割综述 Medical Image Segmentation Using Deep Learning:A Survey

摘自:MFEI

Abstract

  • 深度学习已经广泛的应用于医疗影像分割领域,大量的论文记录了深度学习在该领域的成功
  • 本文中提出了关于深度学习医疗影像分割的综合专题调查
  • 本文主要有两项贡献
    • 与传统文献做对比
    • 本文关注的是监督和弱监督学习方法,不包括无监督方法。对于监督学习方法,我们从三个方面分析了文献:骨干网络的选择、网络块的设计和损失函数的改进。对于弱监督学习方法,我们分别根据数据增强、迁移学习和交互式分割来研究文献。

1 INTRODUCTION

图1 An overview of deep learning methods on medical image segmentation

  • 早期的医学图像分割方法往往依赖于边缘检测、模板匹配技术、统计形状模型、主动轮廓和机器学习等,虽然有大量的方法被报道并在某些情况下取得了成功,但由于特征表示和困难,图像分割仍然是计算机视觉领域中最具挑战性的课题之一,特别是从医学图像中提取鉴别特征比正常RGB图像更困难,因为普通RGB图像往往存在模糊、噪声、低对比度等问题。
  • 由于深度学习的快速发展,医学图像分割不再需要手工制作的特征,卷积神经网络成功的实现了图像的分层和特征表示,从而成为图像处理和计算机视觉中最热门的研究课题。由于用于特征学习的cnn对图像噪声、模糊、对比度等不敏感,它们为医学图像提供了良好的分割结果。
  • 目前图像分割任务有两类,语义分割和实例分割。语义分割是一种像素级分类,它为图像中的每个像素分配一个相应的类别。与语义分割相比,实例分割不仅需要实现像素级的分类,还需要根据特定的类别来区分实例。
  • 很少有应用于医疗影像分割的实力分割,因为每个器官和组织是很不同的。本文综述了深度学习技术在医疗图像分割方面的研究进展。
  • 监督学习的优点是可以基于精心标记的数据来训练模型,但很难获得大量的医学图像标记数据。无监督学习不需要标记数据,但学习的难度增加了。弱监督学习是在监督学习和无监督学习之间,因为它只需要一小部分标记的数据,大多数数据是未标记的。
  • 通过对以上调查的研究,研究者可以学习医学图像分割的最新技术,然后为计算机辅助诊断和智能医疗做出更重要的贡献。然而这些调查存在两个问题。
    • 1)大多按时间顺序总结了医学图像分割的发展,因此忽略了医学图像分割深度学习的技术分支。
    • 2)这些调查只介绍了相关的技术发展,而没有关注医学图像分割的任务特征,如少镜头学习、不平衡学习等,这限制了基于任务驱动的医学图像分割的改进。

为了解决这两个问题我们提出了一个新的Survey,在这项工作中我们的主要贡献如下:

  • 深度学习医疗影像分割技术从粗到细的分支,如图1所示
  • 对于监督学习的方法,我们从三个方面分析了文献:
    • 骨干网络的选择
    • 网络块的设计
    • 损失函数的改进

回顾了来自处理少镜头数据或类不平衡数据的三个方面的文献:数据增强、迁移学习和交互分割。

  • 收集了目前常见的公共医学图像分割数据集,最后我们讨论了这一领域的未来研究趋势和发展方向

2 SUPERVISED LEARNING

2 An overview of network architectures based on supervised learning.

A. Backbone Networks

研究人员提出了编码器-解码器架构,这是最流行的端到端体系结构之一,如FCN,U-Net,Deeplab等。这些结构中编码器通常用于提取图像特征,而解码器通常用于将提取的特征恢复到原始图像大小,并输出最终的分割结果。虽然端到端结构对于医学图像分割是实用的,但它降低了模型的可解释性。

  • U-Net


图3 U-Net architecture
U-Net解决了一般的CNN网络用于医学影响分割的问题,因为它采用了完美的对称结构和跳过连接。与普通的图像分割不同,医学图像通常包含噪声,边界模糊。因此仅依靠图像的低级特征,很难检测到医学图像中的物体或识别物体。同时,由于缺乏图像的细节信息,仅依靠图像的语义特征也不可能获得准确的边界。而U-Net通过跳跃连接结合低分辨率和高分辨率的特征图,有效地融合了低层次和高级层次的图像特征,是医学图像分割任务的完美解决方案。

  • 3D Net


图4 V-Net architecture
在实践中,由于CT和MRI图像等大多数医学数据都以三维体积数据的形式存在,因此使用三维卷积核可以更好地挖掘数据的高维空间相关性。基于这一想法,C¸ ic¸ek等人[34]将U-Net架构扩展到3D数据的应用中,并提出了直接处理3D医疗数据的3DU-Net。由于计算资源的限制,三维U-Net只包含3个下采样,不能有效地提取深层图像特征,导致对医学图像的分割精度有限。
此外,米列塔利等人提出了类似的结构,V-Net,如图4所示。众所周知,残差连接可以避免梯度的消失,加速网络的收敛速度,很容易设计出更深层次的网络结构,可以提供更好的特征表示。与3DU-Net相比,V-Net采用残差连接设计跟深层次的网络(4次下采样)从而获得更好的性能。
然而,由于大量的参数,这些3D网络也遇到了高计算成本和GPU内存使用的问题。

  • Recurrent Neural Network (RNN)


图5 Recurrent residual convolution unit
RNN最初被设计用于处理序列问题。长短期记忆(LSTM)网络[39]是最流行的rnn之一。通过引入自循环,它可以长时间保持梯度流动。在医学图像分割中,RNN已经被用来建模图像序列的时间依赖性。Alom等人[40]提出了一种结合ResUNet与RNN的医学图像分割方法。该方法实现了递归残差卷积层的特征积累,改进了图像分割任务的特征表示。图5为递归残差卷积单元。
显然,RNN可以通过考虑上下文信息关系来捕获图像的局部和全局空间特征。然而,在医学图像分割中,获取完整和有效的时间信息需要良好的医学图像质量(例如,较小的切片厚度和像素间距)。因此,RNN的设计对于提高医学图像分割的性能并不常见。

  • Skip Connection
    虽然skip connection可以融合低分辨率和高分辨率的信息,从而提高特征表示能力,但由于低分辨率和高分辨率特征之间的语义差距较大,导致特征映射模糊。为了改进skip connection,Ibtehaz等人[43]提出了包含Residual Path(ResPath)的MultiResUNet,这使得编码器特征在与解码器中的相应特征融合之前执行一些额外的卷积操作。Seo等人[44]提出mUNet,Chen等[45]提出FED-Net。mU-Net和FED-Net都在跳跃连接中添加了卷积操作,以提高医学图像分割的性能。
  • Cascade of 2D and 3D
    对于图像分割任务,级联模型通常训练两个或两个以上的模型来提高分割精度。该方法在医学图像分割中尤为流行。级联模型大致可分为三种框架类型
    • 粗-细分割
      • 它使用两个二维网络的级联进行分割,其中第一个网络进行粗分割,然后使用另一个网络模型基于之前的粗分割结果实现精细分割。
  • 检测分割
    • 首先使用R-CNN或者YOLO等网络模型进行目标位置识别,然后使用另一个网络基于之前的粗糙分割结果进行进一步的分割
  • 混合分割
    • 由于大多数医学图像是三维数据,二维卷积神经网络不能学习三维时间信息,而三维卷积神经网络往往需要较高的计算成本。所以一些伪三维的分割方法被提出。Oda等[58]提出了一种三平面的方法,从医学CT体积中有效地分割腹动脉区域。Vu等人[59]将相邻切片的叠加作为中心切片预测的输入,然后将得到的二维特征图输入标准的二维网络进行模型训练。虽然这些伪三维方法可以从三维体数据中分割对象,但由于利用了局部时间信息,它们只能获得有限的精度提高。
    • 与伪三维网络相比,混合级联二维三维网络更受欢迎。Li等人[60]提出了一种混合密集连接的U-Net(H-DenseUNet)用于肝脏和肝肿瘤的分割。该方法首先采用一个简单的Resnet获得一个粗糙的肝脏分割结果,利用二维DenseUNet有效地提取二维图像特征,然后利用三维数据集提取三维图像特征,最后设计一个混合特征融合层,共同优化二维和三维特征。
  • Others
    • GAN已经广泛应用于计算机视觉的多个领域。生成对抗的思想也被用于图像分割。但由于医学图像通常显示低对比度,不同组织之间或组织之间的边界和病变模糊,医学图像数据标签稀疏。Luc等[65]首先将生成对抗网络应用于图像分割,将生成网络用于分割模型,将对抗网络训练为分类器。
    • 结合有关器官形状和位置的先验知识可能对提高医学图像分割效果至关重要,在医学图像分割效果中,由于成像技术的限制,图像被损坏,因此包含了伪影。然而,关于如何将先验知识整合到CNN模型中的工作很少。Oktay等人[68]提出了一种新的通用方法,将形状和标签结构的先验知识结合到解剖约束神经网络(ACNN)中,用于医学图像分析任务。通过这种方式,神经网络的训练过程可以被约束和引导,以做出更解剖学和有意义的预测,特别是在输入图像数据信息不足或足够一致的情况下(例如,缺少对象边界)。上述研究表明由于在神经网络的训练过程中采用了先验知识约束,改进后的模型具有更高的分割精度,且具有更强的鲁棒性。

B. Network Function Block

  • Dense Connection


图6 Dense connection architecture
密集连接通常用于构造一种特殊的卷积神经网络。对于密集连接网络,每一层的输入来自前向传播过程中所有层的输出。受密集连接的启发,Guan等[70]提出了一种改进的U-Net,将它的每个子块替换为密集连接形式,如图6所示。虽然密集的连接有助于获得更丰富的图像特征,但它往往在一定程度上降低了特征表示的鲁棒性,增加了参数的数量。


图7 UNet++
Zhou等人[71]将所有U-Net层(从1层到4层)连接在一起,如图7所示。这种结构的优点是,它允许网络自动学习不同层的特征的重要性。并且对跳跃连接进行了重新设计,可以将具有不同语义尺度的特征聚合在解码器中,从而形成了一个高度灵活的特征融合方案。缺点是由于密集连接的使用,参数的数量增加了。因此,将一种剪枝方法集成到模型优化中,以减少参数的数量。

  • Inception


图8 Inception architecture
对于CNNs来说,深层网络往往比浅层网络具有更好的性能,但也会有梯度消失、难收敛、内存使用要求大等问题。Inception结构克服了这些问题,它在不增加网络深度的情况下并行合并卷积核,具有更好的性能。该结构能够利用多尺度卷积核提取更丰富的图像特征,并进行特征融合以获得更好的特征表示。
图8显示了inception的架构,它包含四个级联分支,随着无卷积次数的逐渐增加,从1到1、3和5,每个分支的接受域分别为3、7、9和19。因此,该网络可以从不同的尺度中提取特征。由于该架构比较复杂,导致模型修改困难

  • Depth Separability
    为了提高网络模型的泛化能力,减少对内存使用的需求,许多研究者将重点研究了复杂医学三维体数据的轻量级网络。
    Howard et.al[76]提出了移动网络将普通卷积分解为深度可分卷积和点态卷积。普通卷积运算的数量通常为DK×DK×M×N,其中M为输入特征映射的维数,N为输出特征映射的维数,DK为卷积核的大小。然而,信道卷积操作的次数为DK×DK×1×M,点卷积为1×1×M×N。与普通卷积相比,深度可分离卷积的计算代价是普通卷积的计算代价(1/N+1/D2K)倍。
    深度可分卷积是减少模型参数数量的一种有效方法,但它可能会导致医学图像分割精度的损失,因此需要采用其他方法(如深度监督)[78]来提高分割精度。
  • Attention Mechanism
    对于神经网络,attention block可以根据不同的重要性选择性地改变输入或给输入变量分配不同的权值。近年来,大多数结合深度学习和视觉注意机制的研究都集中在利用mask形成注意机制上。mask的原理是设计一个新的层,通过训练和学习从图像中识别出关键特征,然后让网络只关注图像中的有趣区域。
    • Local Spatial Attention


图9 The attention block in the attention U-Net
普通的pooling相当于信息合并,这很容易导致关键信息丢失。针对这个问题,设计了一个称为spatial transformer的块,通过执行空间变换来提取图像的关键信息。受此启发,Oktay等人[83]提出了attention U-Net。改进后的U-Net在融合来自编码器和相应的解码器的特征之前,使用一个注意块来改变编码器的输出。注意块输出门控信号来控制不同空间位置的像素的特征重要性。图9显示了该体系结构。这个块通过1×1卷积结合Relu和sigmoid函数,生成一个权重映射,通过与编码器的特征相乘来进行修正。

  • Channel Attention


图10 The channel attention in the SE-Net
通道注意力模块可以实现特征重新校准,利用学习到的全局信息,选择性地强调有用特征,抑制无用特征。
Hu等人[84]提出了SE-Net,将通道关注引入了图像分析领域,该方法通过三个步骤实现了对信道的注意力加权;图10显示了该体系结构。首先是压缩操作,对输入特征进行全局平均池化,得到1×1×通道特征图。第二种是激励操作,将信道特征相互作用以减少信道数,然后将减少后的信道特征重构回信道数。最后利用sigmoid函数生成[0,1]的特征权值映射,将尺度放回原始输入特征。

  • Mixture Attention
    空间注意机制和通道注意机制是改进特征表示的两种常用策略。然而,空间注意忽略了不同通道信息的差异,并平等地对待每个通道。相反,通道注意力直接汇集全局信息,而忽略每个通道中的局部信息,这是一个相对粗糙的操作。因此,结合两种注意机制的优势,研究者设计了许多基于mixed domain attention block的模型。
    Wang等人[86]在U-Net的收缩路径和扩展路径之间的中心瓶颈中嵌入了一个注意块,并提出了网格网。此外,他们还比较了通道注意、空间注意和两种注意的不同组合在医学图像分割中的表现。他们的结论是,以通道为中心的注意力是提高图像分割性能的最有效的方法。
    虽然上述的注意机制提高了最终的分割性能,但它们只执行局部卷积的操作。该操作侧重于相邻卷积核的区域,但忽略了全局信息。此外,降采样的操作会导致空间信息的丢失,这尤其不利于医学图像的分割。
  • Non-local Attention


图11 The global aggregation block in the Non-Local U-Net
最近,Wang等人[87]提出了一种Non-local U-Net来克服局部卷积的缺点。Non-local U-Net在上采样和下采样部分均采用自注意机制和全局聚合块提取全图像信息,提高最终分割精度,图11显示了global aggregation block 。Non-local block是一种通用块,可以很容易地嵌入到不同的卷积神经网络中,以提高其性能。
该注意机制对提高图像分割精度是有效的。事实上,空间注意寻找有趣的目标区域,而通道注意寻找有趣的特征。混合注意机制可以同时利用空间和渠道。然而,与非局部注意相比,传统的注意机制缺乏利用不同目标与特征之间关联的能力,因此基于非局部注意的cnn在图像分割任务中通常比正常的cnn具有更好的性能。

  • Multi-scale Information Fusion
    物体之间的大尺度范围是医学图像分割的挑战之一。例如,中晚期的肿瘤可能比早期的肿瘤要大得多。感知场的大小大致决定了我们可以使用多少上下文信息。一般的卷积或池化只使用单个内核,例如,一个3×3内核用于卷积,一个2×2内核用于池化。
    • Pyramid Pooling:多尺度池化的并行操作可以有效地改善网络的上下文信息,从而提取出更丰富的语义信息。He et al.[88]首先提出了spatial pyramid pooling(SPP)来实现多尺度特征提取。SPP将图像从细空间划分为粗空间,然后收集局部特征,提取多尺度特征。受SPP的启发,设计了一个多尺度信息提取块,并将其命名为multi-kernel pooling(RMP)[75],它使用四个不同大小的池内核对全局上下文信息进行编码。然而,RMP中的上采样操作不能由于池化而恢复细节信息的丢失,这通常会扩大接受域,但降低了图像的分辨率。
    • Atrous Spatial Pyramid Pooling:为了减少池化操作造成的详细信息损失,研究人员提出了atrous convolution而不是池化操作。与普通卷积相比,atrous convolution可以在不增加参数数量的情况下有效地扩大接受域。


图12 The gridding effect (the way of treating images as a chessboard causes the loss of information continuity).
然而,ASPP在图像分割方面存在两个严重的问题。第一个问题是局部信息的丢失,如图12所示,其中我们假设卷积核为3×3,三次迭代的膨胀率为2。第二个问题是,这些信息在很大的距离上可能是无关的。

  • Non-local and ASPP:


图13 The combination of ASPP and Non-local architecture
atrous convolution可以有效地扩大接受域,收集更丰富的语义信息,但由于网格效应,导致了细节信息的丢失。因此,有必要添加约束或建立像素关联来提高无效卷积性能。最近,Yang等人提出了[92]的ASPP和非局部组合块用于人体部位的分割,如图13所示。ASPP使用多个不同规模的并行无性卷积来捕获更丰富的信息,而非本地操作捕获了广泛的依赖关系。该组合具有ASPP和非局部化的优点,在医学图像分割方面具有良好的应用前景。


C. Loss Function

除了通过设计网络主干和函数块来提高分割速度和精度外,设计新的损失函数也可以改进分割精度

  • Cross Entropy Loss
    对于图像分割任务,交叉熵是最流行的损失函数之一。该函数将预测的类别向量和实际的分割结果向量进行像素级的比较。
  • Weighted Cross Entropy Loss
    交叉熵损失对图像平均处理每个像素,输出一个平均值,忽略类不平衡,导致损失函数依赖于包含最大像素数的类的问题。因此,交叉熵损失在小目标分割中的性能往往较低。为了解决类的不平衡的问题,Long等人[32]提出了加权交叉熵损失(WCE)来抵消类的不平衡。对于二值分割的情况,将加权交叉熵损失定义为


其中,β用于调整正样本和负样本的比例,它是一个经验值。如果是β>1,则假阴性的数量将会减少;事实上,交叉熵是加权交叉熵的一个特例,当β=1时,假阳性的数量就会减少。当β=1时。为了同时调整阳性和阴性样本的权重 的权重,我们可以使用平衡交叉熵 (BCE)损失函数,其定义为

  • Dice Loss
    Dice是一个流行的医学影像分割性能评价指标。这个指标本质上是分割结果与相应的真实值之间重叠的度量。Dice的值为0-1之间,计算公式为


其中A为预测分割结果,B为真实分割结果。

  • Tversky Loss
    Dice loss的正则化版本,以控制假阳性和假阴性对损失函数的贡献,TL被定义为


其中,p∈0, 1和0≤pˆ≤1。p和pˆ分别为地面真实值和预测分割。如果β=为0.5,则TL相当于Dice

  • Generalized Dice Loss
    Dice loss虽然一定程度上解决了分类失衡的问题,但却不利于严重的分类不平衡。例如小目标存在一些像素的预测误差,这很容易导致Dice的值发生很大的变化。Sudre等人提出了Generalized Dice Loss (GDL)


GDL优于Dice损失,因为不同的区域对损失有相似的贡献,并且GDL在训练过程中更稳定和鲁棒。

  • Boundary Loss
    为了解决类别不平衡的问题,Kervadec等人[95]提出了一种新的用于脑损伤分割的边界损失。该损失函数旨在最小化分割边界和标记边界之间的距离。作者在两个没有标签的不平衡数据集上进行了实验。结果表明,Dice los和Boundary los的组合优于单一组合。复合损失的定义为


其中第一部分是一个标准的Dice los,它被定义为


第二部分是Boundary los,它被定义为

  • Exponential Logarithmic Loss
    在(9)中,加权Dice los实际上是得到的Dice值除以每个标签的和,对不同尺度的对象达到平衡。因此,Wong等人结合focal loss [96] 和dice loss,提出了用于脑分割的指数对数损失(EXP损失),以解决严重的类不平衡问题。通过引入指数形式,可以进一步控制损失函数的非线性,以提高分割精度。EXP损失函数的定义为


其中,两个新的参数权重分别用ωdice和ωcross表示。Ldice是指数对数骰子损失,而交叉损失是交叉熵损失


其中x是像素位置,i是标签,l是位置x处的地面真值。pi(x)是从softmax输出的概率值。
在(17)中,fk是标签k出现的频率,该参数可以减少更频繁出现的标签的影响。γDice和γcross都用于增强损失函数的非线性。


3 WEAKLY SUPERVISED LEARNING

图14 The weakly supervised learning methods for medical image segmentation.

A. Data Augmentation

在缺乏大量标记数据集的情况下,数据增强是解决这一问题的有效解决方案,然而一般的数据扩展方法产生的图像与原始图像高度相关。与常用的数据增强方法相比,GAN是目前最流行的数据增强策略,因为GAN克服了对原始数据的依赖问题。

  • Traditional Methods
    一般的数据增强方法包括提高图像质量,如噪声抑制,亮度、饱和度、对比度等图像强度的变化,以及旋转、失真、缩放等图像布局的变化。传统数据增强中最常用的方法是参数变换(旋转、平移、剪切、位移、翻转等)。由于这种转换是虚拟的,没有计算成本,并且对医学图像的标注很困难,所以总是在每次训练之前进行。
  • Conditional Generative Adversarial Nets(cGAN)


图15 The cGAN architecture
原始GAN生成器可以学习数据的分布,但生成的图片是随机的,这意味着生成器的生成过程是一种非引导的状态。相比之下,cGAN在原始GAN中添加了一个条件,以指导G的生成过程。图15显示了cGAN的体系结构。
Guibas等人[107]提出了一个由GAN和cGAN组成的网络架构。将随机变量输入GAN,生成眼底血管标签的合成图像,然后将生成的标签图输入条件GAN,生成真实的视网膜眼底图像。最后,作者通过检查分类器是否能够区分合成图像和真实图像来验证合成图像的真实性
虽然cGAN生成的图像存在许多缺陷,如边界模糊和低分辨率,但cGAN为后来用于图像样式转换的CycleGAN和StarGAN提供了一个基本的思路。


B. Transfer Learning

通过利用模型的训练参数来初始化一个新的模型,迁移学习可以实现对有限标签数据的快速模型训练。一种方法是在ImageNet上微调预先训练好的模型,而另一种方法是对跨领域的数据进行迁移训练。

  • Pre-trained Model
    转移学习通常用于解决数据有限的问题在医学图像分析,一些研究人员发现,使用预先训练的网络自然图像如ImageNet编码器在U-Net-like网络,然后对医疗数据进行微调可以进一步提高医学图像的分割效果。
    在ImageNet上进行预训练的模型可以学习到医学图像和自然图像都需要的一些共同的基础特征,因此再训练过程是不必要的,而执行微调对训练模型是有用的。然而,当将预训练好的自然场景图像模型应用于医学图像分析任务时,领域自适应可能是一个问题。此外,由于预先训练好的模型往往依赖于二维图像数据集,因此流行的迁移学习方法很难适用于三维医学图像分析。如果带有注释的医疗数据集的数量足够大,那么就有可能这样做
  • Domain Adaptation


图16 The Cycle GAN architecture
如果训练目标域的标签不可用,而我们只能访问其他域的标签,那么流行的方法是将源域上训练好的分类器转移到没有标记数据的目标域。CycleGAN是一种循环结构,主要由两个生成器和两个鉴别器组成。图16为CycleGAN的体系结构。


C. Interactive Segmentation

手工绘制医学图像分割标签通常是繁琐而耗时的,特别是对于绘制三维体数据。交互式分割允许临床医生交互式地纠正由模型生成的初始分割图像,以获得更准确的分割。有效的交互式分割的关键是,临床医生可以使用交互式方法,如鼠标点击和轮廓框,来改进来自模型的初始分割结果。然后,该模型可以更新参数,生成新的分割图像,从临床医生那里获得新的反馈。

例:Wang等人[121]提出了利用两个神经网络级联的DeepIGeoS,对二维和三维医学图像进行交互分割。第一个CNN被称为P-Net,它输出一个粗糙的分割结果。在此基础上,用户提供交互点或短线来标记错误的分割区域,然后使用它们作为第二个CNNR-Net的输入,获得校正的结果。对二维胎儿MRI图像和三维脑肿瘤图像进行了实验,实验结果表明,与传统的图形切割、随机游走、ITK-Snap等交互式分割方法相比,DeepIGeoS大大减少了用户交互的需求,减少了用户时间。


D. Others Works

半监督学习可以使用一小部分已标记数据和任意数量的未标记数据来训练模型,它的损失函数通常由两个损失函数的和组成。第一个是仅与标记数据相关的监督损失函数。第二个是无监督损失函数或正则化项,与标记和未标记数据相关。

弱监督分割方法从边框或图像级标签或少量标注的图像数据中学习图像分割,而不是使用大量的像素级标注,以获得高质量的分割结果。事实上,少量的注释数据和大量的未注释数据更符合真实的临床情况。然而,在实践中,弱监督学习的性能很少能为医学图像分割任务提供可接受的结果,特别是对三维医学图像。因此,这是一个值得在未来探索的方向。


4 CURRENTLY POPULAR DIRECTION

A. Network Architecture Search

到目前为止,NAS[130]在提高图像分类精度方面取得了重大进展。NAS可以被认为是自动机器学习的一个子域,与超参数优化和元学习有很强的重叠。

大多数深度学习医疗影像分割依赖于U-Net网络,并根据不同的任务对网络结构进行一些改变,但在实际应用中,非网络结构因素可能对提高分割效果也有重要意义。

Isensee等人[136]认为,对网络结构进行过多的人工调整会导致对给定数据集的过拟合,因此提出了一种医学图像分割框架no-new-unet(nnU-Net),以适应任何新的数据集。nnUnet会根据给定数据集的属性自动调整所有超参数,而不需要手动干预。因此,nnU-Net只依赖于普通的2DUNet、3DUNet、UNet级联和一个鲁棒的训练方案。它侧重于预处理(重采样和归一化)、训练(损失、优化器设置、数据增强)、推理(基于补丁的策略、测试时间增强集成、模型集成等)的阶段,以及后处理(例如,增强的单通域)。在实际应用中,网络结构设计的改进通常依赖于没有足够的可解释性理论支持的经验,此外,更复杂的网络模型表明过拟合的风险更高。

为了对高分辨率的二维图像(如CT、MRI和组织病理学图像)进行实时图像分割,压缩神经网络模型的研究已成为医学图像分割的一个流行方向。NAS的应用可以有效地减少模型参数的数量,实现了较高的分割性能。尽管NAS的性能令人惊叹,但我们无法解释为什么特定架构的性能良好。因此,更好地理解对性能有重要影响的机制,以及探索这些特性是否可以推广到不同的任务,对于未来的研究也很重要。

B. Graph Convolutional Neural Network

GCN是研究非欧几里得域的强大工具之一。图是一种由节点和边组成的数据结构。早期的图神经网络(GNNs)主要处理严格的图形问题,如分子结构的分类。在实践中,欧几里得空间(如图像)或序列(如文本),以及许多常见的场景可以转换为图,可以使用GCN技术建模。

Gao等人设计了一种新的基于GCN的图池(gUnPool)和图解池(gUnpool)操作,并提出了一种编码-解码器模型,即graph U-Net。graph U-Net通过添加少量的参数,比流行的unet获得了更好的性能。与传统的深度卷积神经网络相比,当深度值超过4时,增加网络的深度并不能提高graph U-Net的性能。然而,当深度值小于或等于4时,图U-Net比流行的U-Net表现出更强的特征编码能力。

基于GCN的方法比传统的和最近的基于深度学习的方法提供了更好的性能和更强的鲁棒性。由于图结构具有较高的数据表示效率和较强的特征编码能力,因此其在医学图像分割中的结果很有前景。

C. Interpretable Shape Attentive Neural Network

目前,许多深度学习算法倾向于通过使用近似适合输入数据的“记忆”模型来做出判断。因此,这些算法不能被充分地解释,并为每个具体的预测提供令人信服的证据。因此,研究深度神经网络的可解释性是目前的一个热点。

Sun等人[142]提出了SAU-Net,重点关注模型的可解释性和鲁棒性。该架构试图通过使用二次形状流来解决医学图像中边缘分割精度较差的问题。特别是,形状流和规则的纹理流可以并行地捕获丰富的与形状相关的信息。此外,解码器还使用了空间注意机制和通道注意机制来解释模型在U-Net各分辨率下的学习能力。最后,通过提取学习到的形状和空间注意图,我们可以用15个方法来解释每个解码器块的高度激活区域。学习到的形状图可以用来推断由模型学习到的有趣类别的正确形状。SAU-Net能够通过门控形状流学习对象的鲁棒形状特征,并且通过使用注意力的内置显着性映射比以前的工作更容易解释。

Wickstrøm等人[143]探索了卷积神经网络中结直肠息肉语义分割的不确定性和可解释性,作者开发了用于解释网络梯度的引导反向传播[144]的中心思想。通过反向传播,得到输入中每个像素对应的梯度,使网络所考虑的特征能够可视化。在反向传播过程中,由于图像中梯度值大且正的像素需要得到高度的重视,而应抑制梯度值大且梯度值负的像素。如果这些负梯度包含在重要像素的可视化中,它们可能会导致描述性特征的噪声可视化。为了避免产生有噪声的可视化,引导反向传播过程改变了神经网络的反向传播,使每一层的负梯度设置为零,从而只允许正梯度向后流过网络并突出这些像素。

目前,医学图像分析的解释主要是采用注意力和类激活图(CAM)等可视化方法。因此,对医学图像分割深度学习可解释性的研究将是未来的热门方向。

D. Multi-modality Data Fusion

多模态数据融合可以提供更丰富的目标特征,有助于提高目标检测和分割结果,因此在医学图像分析中得到了广泛的应用。

虽然众所周知,多模态融合网络通常显示更好的性能比单模式网络分割任务,多模型融合导致一些新的问题,如如何设计多模式网络有效地结合不同的模式,如何利用不同模式之间的潜在关系,如何将多个信息集成到分割网络提高分割性能等。此外,将多模态数据融合集成到一个有效的单参数网络中,有助于简化部署,提高临床实践中模型的可用性。


5 DISCUSSION AND OUTLOOK

A. Medical Image Segmentation Datasets

B. Popular evaluation metrics

为了有效地衡量医学图像分割模型的性能,人们提出了大量的指标来评价分割的有效性。对图像分割性能的评价依赖于像素质量、区域质量和表面距离质量。

目前比较流行的指标有像素质量指标包括像素精度(PA)。区域质量指标包括Dice score、体积重叠误差(VOE)和相对体积差(RVD)。表面距离质量度量包括平均对称表面距离(ASD)和最大对称表面距离(MSD)。

  • PA
    像素精度只是找到正确分类的像素的比率,除以像素总数。对于K个+1类(K个前景类和背景),像素精度定义为:


其中,pij是第i类预测为属于第j类的像素数。

  • Dice score
    它是一种常用的图像分割度量方法(在医学图像分析中更常用),它可以定义为预测地图和地面真实地图重叠面积的两倍,除以两幅图像的像素总数。对Dice score的定义为:
  • VOE
    它是Jaccard index的补充,其定义为:
  • RVD
    它是一种非对称度量,定义为:
  • ASD
    表面距离度量是参考和预测病变的表面距离的相关度量。
    设S(A)表示a的表面体素集合。任意体素v到S(A)的最短距离定义为:


ASD is defined as:

  • MSD
    它也被称为对称豪斯多夫距离,与ASD相似,但取的最大距离而不是平均值:

本文所有图片公式均来自论文原文

图像分割框架

Unet框架

GitHub

介绍

nnuet

https://github.com/

MIC-DKFZ/nnUNet

十项全能冠军,自动构建分割任务

虽然基于UNet的系列编解码分割网络在各类医学图像分割上取得了长足的进展,并且部分基于相关模型的应用设计已经广泛用于临床分析中。但医学影像本身的复杂性和差异性也极度影响着分割模型的泛化性和通用性,主要体现在以下几个方面:

1)各类模态的医学影像之间差异大,如研究队列的大小、图像尺寸和维度、分辨率和体素(voxel)强度等。

2)分割的语义标签的极度不平衡。相较于影像中的正常组织,病变区域一般都只占极少部分,这就造成了正常组织的体素标签与病灶组织的体素标签之间极度的类不平常。

3)不同影像数据之间的专家标注差异大,并且一些图像的标注结果会存在模棱两可的情况。

4)一些数据集在图像几何和形状等属性上差异明显,切片不对齐和各向异性的问题也非常严重。

提出一种鲁棒的基于2D UNet3D UNet的自适应框架nnUMet。作者在各种任务上拿这个框架和目前的STOA方法进行了比较,且该方法不需要手动调参。最终nnUNet得到了最高的平均dice

作者提出一种nnUNetno-new-Net)框架,基于原始的UNet(很小的修改),不去采用哪些新的结构,如相残差连接、dense连接、注意力机制等花里胡哨的东西。相反的,把重心放在:预处理(resamplingnormalization)、训练(lossoptimizer设置、数据增广)、推理(patch-based策略、test-time-augmentations集成和模型集成等)、后处理(如增强单连通域等)。并且在10种数据集上进行测试,都能够达到很好的效果,而算法不能够针对某种数据集进行人为的调整,只能自动的去适应。

 

mmsegmentation

https://github.com/open-mmlab/mmsegmentation

mmSegmentatiopenmmlab项目下开源的图像语义分割框架,目前支持pytorch,由于其拥有pipeline加速,完善的数据增强体系,完善的模型库,作为大数据语义分割训练及测试的代码框架是再好不过了。

Efficient-Segmentation-Networks

https://github.com/xiaoyufenfei/

Efficient-Segmentation-Networks

该项目旨在为使用 PyTorch 的实时语义分割模型提供易于使用、可修改的参考实现

 

Pytorch Medical Segmentation

https://github.com/MontaEllis/

Pytorch-Medical-Segmentation

基于PyTorch的专注于医学图像分割的开源库,其支持模型丰富,方便易用。其可算为torchio的一个实例,作者将其综合起来,包含众多经典算法,实用性比较强。

支持2D3D医学图像分割,可以修改hparam.py文件来确定是2D分割还是3D分割以及是否可以进行多分类。

支持绝大数主流分割模型,几乎提供了所有的2D3D分割的算法。

兼容几乎所有的医学数据格式(例如 nii.gz, nii, mhd, nrrd, …),修改hparam.pyfold_arch即可。

作者提供了训练和测试推断的代码,简单配置后训练和推断都仅需要一行命令。