根据多篇文章整理,仅供参考相互学习。


上面这位博主对源码的讲解还是挺详细的,感兴趣的也可以去看看.

 Mask R-CNN

Mask R-CNN 是一个两阶段的框架,第一个阶段扫描图像并生成提议(proposals,即有可能包含一个目标的区域),第二阶段分类提议并生成边界框和掩码。Mask R-CNN 扩展自 Faster R-CNN,由同一作者在去年提出。Faster R-CNN 是一个流行的目标检测框架,Mask R-CNN 将其扩展为实例分割框架。

有效地检测图像中的目标,同时还能为每个实例生成一个高质量的分割掩码(segmentation mask)。这个方面被称为 Mask R-CNN,是在 Faster R-CNN 上的扩展——在其已有的用于边界框识别的分支上添加了一个并行的用于预测目标掩码的分支。

何恺明大神的论文Mask R-CNN 获得ICCV最佳论文 ,它是在 Feature Pyramid Network (FPN) 和 ResNet101基础上实现的。

下图阐述了Mask R-CNN的Mask branch:

MaskRCNN后处理 mask rcnn流程_ FCN

 

在Mask R-CNN中的RoI Align之后有一个"head"部分,主要作用是将RoI Align的输出维度扩大,这样在预测Mask时会更加精确。在Mask Branch的训练环节,作者没有采用FCN式的SoftmaxLoss,反而是输出了K个Mask预测图(为每一个类都输出一张),并采用average binary cross-entropy loss训练,当然在训练Mask branch的时候,输出的K个特征图中,也只是对应ground truth类别的那一个特征图对Mask loss有贡献。

Mask R-CNN的训练损失函数可以描述为:

MaskRCNN后处理 mask rcnn流程_ 分割掩码_02

 损失函数:分类误差+检测误差+分割误差,即L=Lcls+Lbox+Lmask 

 Lcls、Lbox:利用全连接预测出每个RoI的所属类别及其矩形框坐标值

 Lmask:

  1. mask分支采用FCN对每个RoI的分割输出维数为K*m*m(其中:m表示RoI Align特征图的大小),即K个类别的m*m的二  值mask;保持m*m的空间布局,pixel-to-pixel操作需要保证RoI特征 映射到原图的对齐性,这也是使用RoIAlign解决对齐问题原因,减少像素级别对齐的误差。
  2. K*m*m二值mask结构解释:最终的FCN输出一个K层的mask,每一层为一类,Log输出,用0.5作为阈值进行二值化,产生背景和前景的分割Mask
  3. 这样,Lmask 使得网络能够输出每一类的 mask,且不会有不同类别 mask 间的竞争. 分类网络分支预测 object 类别标签,以选择输出 mask,对每一个ROI,如果检测得到ROI属于哪一个分 类,就只使用哪一个分支的相对熵误差作为误差值进行计算。(举例说明:分类有3类(猫,狗,人),检测得到当前ROI属于“人”这一类,那么所使用的Lmask为“人”这一分支的mask,即,每个class类别对应一个mask可以有效避免类间竞争(其他class不贡献Loss)
  4. 对每一个像素应用sigmoid,然后取RoI上所有像素的交叉熵的平均值作为Lmask。

在Mask R-CNN中,相较于Faster R-CNN还有些略微的调整,比如positive RoI被定义成了与Ground truth的IoU大于0.5的(Faster R-CNN中是0.7)。

1. Mask R-CNN算法步骤

首先,输入一幅你想处理的图片,然后进行对应的预处理操作,或者预处理后的图片;

然后,将其输入到一个预训练好的神经网络中(ResNeXt等)获得对应的feature map;

接着,对这个feature map中的每一点设定预定个的ROI,从而获得多个候选ROI;

接着,将这些候选的ROI送入RPN网络进行二值分类(前景或背景)和BB回归,过滤掉一部分候选的ROI;

接着,对这些剩下的ROI进行ROIAlign操作(即先将原图和feature map的pixel对应起来,然后将feature map和固定的feature对应起来);

最后,对这些ROI进行分类(N类别分类)、BB回归和MASK生成(在每一个ROI里面进行FCN操作)。

2. Mask R-CNN架构分解

2.1.1 主干架构

主干网络是一个标准的卷积神经网络(通常来说是 ResNet50 和 ResNet101),作为特征提取器。

MaskRCNN后处理 mask rcnn流程_ 分割掩码_03

代码提示:主干网络在 resnet_graph()

2.1.2 FPN

特征金字塔网络(FPN)是对该主干网络的扩展,可以在多个尺度上更好地表征目标。

前面说的CNN其实就是下图中左侧那一组,最下面橙色框框里是一个输入图片,越往上层,视野越大,但是由于卷积和池化,图片的尺寸实际是越来越小的,所以上面用越来越小的方块代表没一层的输出,整体看起来,就像是金字塔一样,这就是Pyramid这个词的来源了。

那FPN就是在原来CNN一个金字塔旁边又增加了一个金字塔。

MaskRCNN后处理 mask rcnn流程_ 分割掩码_04

 

FPN增加一个金字塔是要干什么那?实际是想要在大视野的时候还能看见小东西。所以,如果把前一个金字塔中大视野特征(如左侧金字塔最高层特征)拿到后一个金字塔的底层中,这样就可以在小视野中也能看见大视野中的图像了。这类似于井底之蛙看不见外面的大好景色,但是你可以录视频拿到井底放给他看,他就一样能知道外面的世界有什么了。

在我们的 Mask R-CNN 实现中使用的是 ResNet101+FPN 主干网络。

代码提示:FPN 在 MaskRCNN.build() 中创建,位于构建 ResNet 的部分之后。FPN 引入了额外的复杂度:在 FPN 中第二个金字塔拥有一个包含每一级特征的特征图,而不是标准主干中的单个主干特征图(即第一个金字塔中的最高层)。选用哪一级的特征是由目标的尺寸动态地确定的。

2.2 区域建议网络(RPN)

MaskRCNN后处理 mask rcnn流程_MaskRCNN后处理_05

展示 49 个 anchor box 的简化图示

RPN 是一个轻量的神经网络,它用滑动窗口来扫描图像,并寻找存在目标的区域。RPN 扫描的区域被称为 anchor.

代码提示:RPN 在 rpn_graph() 中创建。anchor 的尺度和长宽比由 config.py 中的 RPN_ANCHOR_SCALES 和 RPN_ANCHOR_RATIOS 控制。

RPN 为每个 anchor 生成两个输出:

    1) anchor 类别:前景或背景(FG/BG)。前景类别意味着可能存在一个目标在 anchor box 中。

    2) 边框精调:前景 anchor(或称正 anchor)可能并没有完美地位于目标的中心。因此,RPN 评估了 delta 输出(x、y、宽、高的变化百分数)以精调 anchor box 来更好地拟合目标。

使用 RPN 的预测,我们可以选出最好地包含了目标的 anchor,并对其位置和尺寸进行精调。如果有多个 anchor 互相重叠,我们将保留拥有最高前景分数的 anchor,并舍弃余下的(非极大值抑制)。然后我们就得到了最终的区域建议,并将其传递到下一个阶段。

代码提示:ProposalLayer 是一个自定义的 Keras 层,可以读取 RPN 的输出,选取最好的 anchor,并应用边框精调。

2.3  ROI 分类器和边界框回归器

这个阶段是在由 RPN 提出的 ROI 上运行的。

MaskRCNN后处理 mask rcnn流程_ FCN_06

阶段 2 的图示。来源:Fast R-CNN

正如 RPN 一样,它为每个 ROI 生成了两个输出:

    1)  类别:ROI 中的目标的类别。和 RPN 不同(两个类别,前景或背景),这个网络更深并且可以将区域分类为具体的类别(人、车、椅子等)。它还可以生成一个背景类别,然后就可以弃用 ROI 了。

    2)  边框精调:和 RPN 的原理类似,它的目标是进一步精调边框的位置和尺寸以将目标封装。

代码提示:分类器和边框回归器已在 fpn_classifier_graph() 中创建。

2.3.1 ROI 池化

在我们继续之前,需要先解决一些问题。分类器并不能很好地处理多种输入尺寸。它们通常只能处理固定的输入尺寸。但是,由于 RPN 中的边框精调步骤,ROI 框可以有不同的尺寸。因此,我们需要用 ROI 池化来解决这个问题。

MaskRCNN后处理 mask rcnn流程_MaskRCNN后处理_07

图中展示的特征图来自较底层。

ROI 池化是指裁剪出特征图的一部分,然后将其重新调整为固定的尺寸。这个过程实际上和裁剪图片并将其缩放是相似的(在实现细节上有所不同)。

Mask R-CNN 的作者提出了一种方法 ROIAlign,在特征图的不同点采样,并应用双线性插值。在我们的实现中,为简单起见,我们使用 TensorFlow 的 crop_and_resize 函数来实现这个过程。

代码提示:ROI 池化在类 PyramidROIAlign 中实现。

2.4  分割掩码

到第2.3 节为止,我们得到的正是一个用于目标检测的 Faster R-CNN。而分割掩码网络正是 Mask R-CNN 的论文引入的附加网络。

MaskRCNN后处理 mask rcnn流程_ FCN_08

掩码分支是一个卷积网络,取 ROI 分类器选择的正区域为输入,并生成它们的掩码。其生成的掩码是低分辨率的:28x28 像素。但它们是由浮点数表示的软掩码,相对于二进制掩码有更多的细节。掩码的小尺寸属性有助于保持掩码分支网络的轻量性。在训练过程中,我们将真实的掩码缩小为 28x28 来计算损失函数,在推断过程中,我们将预测的掩码放大为 ROI 边框的尺寸以给出最终的掩码结果,每个目标有一个掩码。

代码提示:掩码分支网络在 build_fpn_mask_graph() 中。

2.4.1 建立一个颜色填充过滤器

MaskRCNN后处理 mask rcnn流程_ 分割掩码_09

和大多数图像编辑 app 中包含的过滤器不同,我们的过滤器更加智能一些:它能自动找到目标。当你希望把它应用到视频上而不是图像上时,这种技术更加有用。

===========================================================================================

在这里,我将Mask R-CNN的ROIAlign和FCN分别进行讲解,这也是该算法的核心

接下来到了Mask R-CNN,我们来看看RoI Pooling出了什么问题:

问题1:从输入图上的RoI到特征图上的RoI feature,RoI Pooling是直接通过四舍五入取整得到的结果。这样会带来什么坏处呢?就是RoI Pooling过后的得到的输出可能和原图像上的RoI对不上,如下图所示:

MaskRCNN后处理 mask rcnn流程_MaskRCNN后处理_10

 

右图中蓝色部分表示包含了轿车主体的的信息的方格,RoI Pooling Layer的四舍五入取整操作导致其进行了偏移。

问题2:再将每个RoI对应的特征转化为固定大小的维度时,又采用了取整操作。在这里笔者举例讲解一下RoI Pooling的操作:

MaskRCNN后处理 mask rcnn流程_MaskRCNN后处理_11

在从RoI得到对应的特征图时,进行了问题1描述的取整,在得到特征图后,如何得到一个6×6的全连接层的输入呢?RoI Pooling这样做:将RoI对应的特征图分成6×6块,然后直接从每块中找到最大值。在上图中的例子中,比如原图上的的RoI大小是280×480,得到对应的特征图是18×30。将特征图分成6块,每块大小是3×5,然后在每一块中分别选择最大值放入6×6的对应区域中。在将特征图分块的时候,又用到了取整。这种取整操作(在Mask R-CNN中被称为quantization)对RoI分类影响不大,可是对逐像素的预测目标是有害的,因为对每个RoI取得的特征并没有与RoI对齐。因此,Mask R-CNN对RoI Pooling做了改进并提出了RoI Align。

RoI Align的主要创新点是,针对问题1,不再进行取整操作。针对问题2,使用双线性插值来更精确地找到每个块对应的特征。总的来说,RoI Align的作用主要就是剔除了RoI Pooling的取整操作,并且使得为每个RoI取得的特征能够更好地对齐原图上的RoI区域。

ROIPooling和ROIAlign的分析与比较

ROI Pooling和ROIAlign最大的区别是:前者使用了两次量化操作,而后者并没有采用量化操作,使用了线性插值算法

双线性插值是一种比较好的图像缩放算法,它充分的利用了原图中虚拟点(比如20.56这个浮点数,像素位置都是整数值,没有浮点值)四周的四个真实存在的像素值来共同决定目标图中的一个像素值,即可以将20.56这个虚拟的位置点对应的像素值估计出来。

该网络可以在一定程度上面提高检测的精度,当前很多的方法都用到了它。由于FPN网络已经包含了res5,可以更加高效的使用特征,因此这里使用了较少的filters。

FCN

MaskRCNN后处理 mask rcnn流程_MaskRCNN后处理_12

Mask R-CNN在Faster-RCNN上添加一个分支网络:Fully Convolution Networ(FCN)。FCN算法是一个经典的语义分割算法,可以对图片中的目标进行准确的分割。其总体架构如上图所示,它是一个端到端的网络,主要的模快包括卷积和去卷积,即先对图像进行卷积和池化,使其feature map的大小不断减小;然后进行反卷积操作,即进行插值操作,不断的增大其feature map,最后对每一个像素值进行分类。从而实现对输入图像的准确分割。