网站建设合同封面,西安关键词优化平台,长沙网络推广袁飞seo,市场营销计划图像分割技术及经典实例分割网络Mask R-CNN#xff08;含Python源码定义#xff09; 文章目录图像分割技术及经典实例分割网络Mask R-CNN#xff08;含Python源码定义#xff09;1. 图像分割技术概述2. FCN与语义分割2.1 FCN简介2.2 反卷积2.2 FCN与语义分割的关系3. Mask …图像分割技术及经典实例分割网络Mask R-CNN含Python源码定义 文章目录图像分割技术及经典实例分割网络Mask R-CNN含Python源码定义1. 图像分割技术概述2. FCN与语义分割2.1 FCN简介2.2 反卷积2.2 FCN与语义分割的关系3. Mask R-CNN3.1 实例分割的难点3.2 FPN特征金字塔3.2 Mask R-CNN3.3 RoiAlign3.4 分割掩膜4. 工程实践1. 图像分割技术概述
图像分割技术是可以浅显的理解为精细化的目标检测过程由于之前的目标检测算法只能使用标定框框定规则区域从而进行分类标出目标的大题区域但是在譬如自动驾驶领域仅仅只有一个规则的区域去框定目标还是不够的比如碰到车道线那么仅仅用一个矩形区域框定车道线并不能准确地指导车辆的下一步动向所以我们需要一个能够追溯细节的新应用领域来将检测到的目标精细化。
图像分割即为图片的每个对象创建一个像素级的掩膜这样可以追溯到目标轮廓的更多细节。下图为细菌的图像分割例子为每个细菌做了图像分割。 图像分类有两类 语义分割语义分割关注类别忽略个体实例。 实例分割实力分割关注个体实例。
为了使得概念更加直观下图说明了分类、目标检测、语义分割、实例分割的不同任务直观示意图。 2. FCN与语义分割
2.1 FCN简介
FCN即Fully Convolutional Networks全卷积网络FCN将传统卷积网络后面的全连接层换成了卷积层这样网络输出不再是类别而是heatmap同时为了解决因为卷积和池化对图像尺寸的影响提出使用上采样的方式恢复尺寸。
传统分类使用的网络结构一般通过在最后连接全连接层它会将原来二维的图像信息“压扁”成一维的分类从而丢失了空间信息最后训练输出一个标量这就是我们的分类标签。FCN网络和一般的网络的最大不同是FCN产生的输出和输入的维度保持一致即改变原本的CNN网络末端的全连接层将其调整为卷积层这样原本的分类网络最终输出一个热度图类型的图像。 其实在笔者的上一篇博文就提过全卷积全卷积网络最大的特点是可以适应任意尺寸的输入因为其不受最终分类数量的限制。
2.2 反卷积
反卷积又称转置卷积它并不是正向卷积的完全逆过程。反卷积是一种特殊的正向卷积先按照一 定的比例通过补0来扩大输入图像的尺寸接着旋转卷积核再进行正向卷积。 详细参考此文章《反卷积(Transposed Convolution)详细推导》 反卷积的处理步骤概括
将上一层的卷积核反转上下左右方向进行反转。将上一层卷积的结果作为输入做补0扩充操作即往每一个元素后面补0。这一步是根据步长来的对于每个元素沿着步长方向补步长减一个0。例如步长为1就不用补0了在扩充后的输入基础上再对整体补0。以原始输入的shape作为输出shape按照卷积padding规则计算pading的补0的位置及个数得到补0的位置及个数。将补0后的卷积结果作为真正的输入反转后的卷积核为filter进行步长为1的卷积操作。 计算padding按规则补0时统一按照padding‘SAME’、步长为1×11\times 11×1的方式来计算 卷积输出通道公式 oi−k2×ps1o\frac{i-k2\times p}{s}1 osi−k2×p1 反卷积输入输出通道关系计算公式 i(o−1)×sk−2×pi(o-1)\times sk-2\times p i(o−1)×sk−2×p 注意
通过反卷积操作并不能还原出卷积之前的图片只能还原出卷积之前图片的尺寸。卷积和反卷积并没有什么关系操作的过程也都是不可逆的。
反卷积的应用场景
反卷积/转置卷积在语义分割领域应用很广如果说pooling层用于特征降维那么在多个pooling 层后就需要用转置卷积来进行分辨率的恢复。如果up-sampling采用双线性插值进行分辨率的提升这种提升是非学习的。采用反卷积来完成上采样的工作就可以通过学习的方式得到更高的精度 反卷积的局限性
矩阵稀疏有大量的0元素因此大量的信息是无用的反卷积所用的转置矩阵计算是非常消耗计算资源的。
2.2 FCN与语义分割的关系
FCN对图像进行像素级的分类从而解决了语义级别的图像分割semantic segmentation问题。FCN可以接受任意尺寸的输入图像采用反卷积层对最后一个卷积层的feature map进行上采样, 使它恢复到输入图像相同的尺寸从而可以对每个像素都产生了一个预测, 同时保留了原始输入图像中的空间信息, 最后在上采样的特征图上进行逐像素分类。最后逐个像素计算softmax分类的损失, 相当于每一个像素对应一个训练样本。
对全卷积网络的末端再进行upsampling上采样即可得到和原图大小一样的输出这就是 热度图了。这里上采样采用了deconvolutional反卷积的方法。
反最大池化、反平均池化
池化操作中最常见的最大池化和平均池化因此最常见的反池化操作有反最大池化和反平均池化。 反最大池化需要记录池化时最大值的位置反平均池化不需要此过程。 语义分割的一种实现-DeconvNet 3. Mask R-CNN
3.1 实例分割的难点
实例分割instance segmentation的难点在于需要同时检测出目标的位置并且对目标进行分割所以这就需要融合目标检测框出目标的位置以及语义分割对像素进行分类分割出目标方法。 3.2 FPN特征金字塔
FPN即Feature Pyramid Networks特征金字塔其提出的背景为目标检测任务和语义分割任务里面常常需要检测小目标。但是当小目标比较小时可能在原图里面只有几十个像素点。对于深度卷积网络从一个特征层卷积到另一个特征层无论步长是1还是2还是更多卷积核都要遍布整个图片进行卷积大的目标所占的像素点比小目标多所以大的目标被经过卷积核的次数远比小的目标多所以在下一个特征层里会更多的反应大目标的特点。特别是在步长大于等于2的情况下大目标的特点更容易得到保留小目标的特征点容易被跳过。因此经过很多层的卷积之后小目标的特点会越来越少。
例如此图中的猫对于周边的落叶来说明显差距很大。 特征图(feature map)用蓝色轮廓表示较粗的轮廓表示语义上更强的特征图。
对于(a)使用图像金字塔构建特征金字塔。特征是根据每个不同大小比例的图像独立计算的每计算一次特征都需要resize一下图片大小耗时速度很慢。对于(b)检测系统都在采用的为了更快地检测而使用的单尺度特征检测。对于©由卷积计算的金字塔特征层次来进行目标位置预测,但底层feature map特征表达能力不足。对于(d)特征金字塔网络(FPN)和b,c一样快但更准确。 FPN的提出是为了实现更好的feature maps融合一般的网络都是直接使用最后一层的feature maps虽然最后一层的feature maps 语义强但是位置和分辨率都比较低容易检测不到比较小的物体。FPN的功能就是融合了底层到高层的feature maps 从而充分的利用了提取到的各个阶段的特征ResNet中的C2-C5。 3.2 Mask R-CNN
Mask R-CNN可算作是Faster R-CNN的升级版。Faster R-CNN广泛用于目标检测。对于给定图像它会给图中每个对象加上类别标签与边界框坐标。Mask R-CNN框架是以Faster R-CNN为基础而架的。因此针对给定图像 Mask R-CNN不仅会给每个对象添加类标签与边界框坐标还会返回其对象掩膜。 Mask R-CNN的网络结构 在Mask R-CNN的设计中网络在进行目标检测的同时进行了实例分割。
Mask R-CNN的大体框架与Faster-RCNN框架相似可以说在基础特征网络之后又加入了全连接的分割子网由原来的两个任务分类回归变为了三个任务分类回归分割。Mask R-CNN 是一个两阶段的框架第一个阶段扫描图像并生成候选区域proposals即有可能包含一个目标的区域第二阶段分类候选区域并生成边界框和掩码。 Faster R-CNN请参考笔者的上一篇博文《CV学习笔记-Faster-RCNN》 Mask R-CNN的不同
使用ResNet网络作为backbone将 Roi Pooling 层替换成了 RoiAlign添加并列的 Mask 层引入FPN 和 FCN Mask R-CNN的处理流程
输入图像进行预处理将预处理后的图像信息输入到预训练好的神经网络中例如ResNet获得feature map对这个feature map中的每一点设定预定个的ROI从而获得多个候选ROI将这些候选的ROI送入RPN网络进行二值分类positive或negative和BB回归过滤掉一部分候选的ROI截止到目前Mask和Faster完全相同对这些剩下的ROI进行ROIAlign操作ROIAlign为Mask R-CNN创新点1比ROIPooling有长足进步最后对这些ROI进行分类N类别分类、BB回归和MASK生成在每一个ROI里面进行FCN操作引入FCN生成Mask是创新点2使得此网络可以进行分割型任务。
Mask R-CNN使用Resnet101作为backbone对应为CNN提取主干特征网络部分 ResNet的相关内容请参照笔者之前的博客《CV学习笔记-ResNet》 特征金字塔FPN的构建是为了实现特征多尺度的融合在Mask R-CNN当中我们取出在主干特征提取网络中长宽压缩了两次C2、三次C3、四次C4、五次C5的结果来进行特征金字塔结构的构造。
具体构造过程如下 P2-P5是将来用于预测物体的bboxbox-regressionmask的。P2-P6是用于训练RPN的即P6只用于RPN网络中。 3.3 RoiAlign
Mask-RCNN中提出了一个新的思想就是RoIAlign其实RoIAlign就是在RoI pooling上稍微改动过来的但是为什么在模型中不继续使用RoI pooling呢 Roi pooling请参考笔者博客CV学习笔记-Faster-RCNN 3.3节 在RoI pooling中出现了两次的取整虽然在feature maps上取整看起来只是小数级别的数但是当 把feature map还原到原图上时就会出现很大的偏差比如第一次的取整是舍去了0.78 665/3220.78还原到原图时是20*32640第一次取整就存在了25个像素点的偏差在第二 次的取整后的偏差更加的大。对于分类和物体检测来说可能这不是一个很大的误差但是对于实 例分割而言这是一个非常大的偏差因为mask出现没对齐的话在视觉上是很明显的。而RoIAlign 的提出就是为了解决这个不对齐问题。 简单概括就是RoiAlign取消了取整的简单取舍使用双线性插值的做法得到固定四个点坐标的像素值从而使得不连续的操作变得连续起来返回到原图的时候误差将会更小。 它充分的利用了原图中虚拟点比如20.56这个浮点数。像素位置都是整数值没有浮点值四周 的四个真实存在的像素值来共同决定目标图中的一个像素值即可以将20.56这个虚拟的位置点对 应的像素值估计出来。 示例
蓝色的虚线框表示卷积后获得的feature map黑色实线框表示ROI feature。
最后需要输出的大小是2x2那么我们就利用双线性插值来估计这些蓝点虚拟坐标点又称双线性插值的网格点处所对应的像素值最后得到相应的输出。
然后在每一个橘红色的区域里面进行max pooling或者average pooling操作获得最终2x2的输出结果。我们的整个过程中没有用到量化操作没有引入误差即原图中的像素和feature map中的像素是完全对齐的没有偏差这不仅会提高检测的精度同时也会有利于实例分割。 RoiAlign的输出将作为分割掩膜的依据。
3.4 分割掩膜
获得感兴趣区域ROI后给已有框架加上一个掩膜分支每个囊括特定对象的区域都会被赋予一个掩膜。每个区域都会被赋予一个m×mm\times mm×m掩膜并按比例放大以便推断。 mask语义分割信息的获取 在之前的步骤中我们获得了预测框我们把这个预测框作为mask模型的区域截取部分利用这个预测框对mask模型中用到的公用特征层进行截取。截取后利用mask模型再对像素点进行分类获得语义分割结果。
mask分支采用FCN对每个RoI产生一个K×m×mK\times m \times mK×m×m的输出即K个分辨率为m×mm\times mm×m的二值的掩膜K为分类物体的种类数目。 K×m×mK\times m \times mK×m×m二值mask结构解释最终的FCN输出一个K层的mask每一层为一类。用0.5作为阈值进 行二值化产生背景和前景的分割Mask。 对于预测的二值掩膜输出我们对每个像素点应用sigmoid函数或softmax等整体损失定义 为交叉熵。引入预测K个输出的机制允许每个类都生成独立的掩膜避免类间竞争。这样做解 耦了掩膜和种类预测。 Mask R-CNN的损失函数为 LLclsLboxLmaskLL_{cls}L_{box}L_{mask} LLclsLboxLmask Lmask 使得网络能够输出每一类的 mask且不会有不同类别 mask 间的竞争
分类网络分支预测 object 类别标签以选择输出 mask。对每一个ROI如果检测得到的ROI属于哪一个分类就只使用哪一个分支的交叉熵误差作为误差值进行计算。举例说明分类有3类猫狗人检测得到当前ROI属于“人”这一类那么所使用的Lmask为“人”这一分支的mask即每个class类别对应一个mask可以有效避免类间竞争其他class不贡献Loss对每一个像素应用sigmoid然后取RoI上所有像素的交叉熵的平均值作为Lmask。
网络输出如何将14×1414\times 1414×14或者28×2828\times 2828×28大小的mask映射到原图是个问题
其实一个后处理将模型预测的mask通过resize得到与proposal中目标相同大小的mask即可。
4. 工程实践
代码工程代码稍大所以摘取网络定义的核心源码环节展示
from keras.layers import Input,ZeroPadding2D,Conv2D,MaxPooling2D,BatchNormalization,Activation,UpSampling2D,Add,Lambda,Concatenate
from keras.layers import Reshape,TimeDistributed,Dense,Conv2DTranspose
from keras.models import Model
import keras.backend as K
from nets.resnet import get_resnet
from nets.layers import ProposalLayer,PyramidROIAlign,DetectionLayer,DetectionTargetLayer
from nets.mrcnn_training import *
from utils.anchors import get_anchors
from utils.utils import norm_boxes_graph,parse_image_meta_graph
import tensorflow as tf
import numpy as np
TimeDistributed:
对FPN网络输出的多层卷积特征进行共享参数。
TimeDistributed的意义在于使不同层的特征图共享权重。#------------------------------------#
# 五个不同大小的特征层会传入到
# RPN当中获得建议框
#------------------------------------#
def rpn_graph(feature_map, anchors_per_location):shared Conv2D(512, (3, 3), paddingsame, activationrelu,namerpn_conv_shared)(feature_map)x Conv2D(2 * anchors_per_location, (1, 1), paddingvalid,activationlinear, namerpn_class_raw)(shared)# batch_size,num_anchors,2# 代表这个先验框对应的类rpn_class_logits Reshape([-1,2])(x)rpn_probs Activation(softmax, namerpn_class_xxx)(rpn_class_logits)x Conv2D(anchors_per_location * 4, (1, 1), paddingvalid,activationlinear, namerpn_bbox_pred)(shared)# batch_size,num_anchors,4# 这个先验框的调整参数rpn_bbox Reshape([-1,4])(x)return [rpn_class_logits, rpn_probs, rpn_bbox]#------------------------------------#
# 建立建议框网络模型
# RPN模型
#------------------------------------#
def build_rpn_model(anchors_per_location, depth):input_feature_map Input(shape[None, None, depth],nameinput_rpn_feature_map)outputs rpn_graph(input_feature_map, anchors_per_location)return Model([input_feature_map], outputs, namerpn_model)#------------------------------------#
# 建立classifier模型
# 这个模型的预测结果会调整建议框
# 获得最终的预测框
#------------------------------------#
def fpn_classifier_graph(rois, feature_maps, image_meta,pool_size, num_classes, train_bnTrue,fc_layers_size1024):# ROI Pooling利用建议框在特征层上进行截取# Shape: [batch, num_rois, POOL_SIZE, POOL_SIZE, channels]x PyramidROIAlign([pool_size, pool_size],nameroi_align_classifier)([rois, image_meta] feature_maps)# Shape: [batch, num_rois, 1, 1, fc_layers_size]相当于两次全连接x TimeDistributed(Conv2D(fc_layers_size, (pool_size, pool_size), paddingvalid),namemrcnn_class_conv1)(x)x TimeDistributed(BatchNormalization(), namemrcnn_class_bn1)(x, trainingtrain_bn)x Activation(relu)(x)# Shape: [batch, num_rois, 1, 1, fc_layers_size]x TimeDistributed(Conv2D(fc_layers_size, (1, 1)),namemrcnn_class_conv2)(x)x TimeDistributed(BatchNormalization(), namemrcnn_class_bn2)(x, trainingtrain_bn)x Activation(relu)(x)# Shape: [batch, num_rois, fc_layers_size]shared Lambda(lambda x: K.squeeze(K.squeeze(x, 3), 2),namepool_squeeze)(x)# Classifier head# 这个的预测结果代表这个先验框内部的物体的种类mrcnn_class_logits TimeDistributed(Dense(num_classes),namemrcnn_class_logits)(shared)mrcnn_probs TimeDistributed(Activation(softmax),namemrcnn_class)(mrcnn_class_logits)# BBox head# 这个的预测结果会对先验框进行调整# [batch, num_rois, NUM_CLASSES * (dy, dx, log(dh), log(dw))]x TimeDistributed(Dense(num_classes * 4, activationlinear),namemrcnn_bbox_fc)(shared)# Reshape to [batch, num_rois, NUM_CLASSES, (dy, dx, log(dh), log(dw))]mrcnn_bbox Reshape((-1, num_classes, 4), namemrcnn_bbox)(x)return mrcnn_class_logits, mrcnn_probs, mrcnn_bboxdef build_fpn_mask_graph(rois, feature_maps, image_meta,pool_size, num_classes, train_bnTrue):# ROI Align利用建议框在特征层上进行截取# Shape: [batch, num_rois, MASK_POOL_SIZE, MASK_POOL_SIZE, channels]x PyramidROIAlign([pool_size, pool_size],nameroi_align_mask)([rois, image_meta] feature_maps)# Shape: [batch, num_rois, MASK_POOL_SIZE, MASK_POOL_SIZE, channels]x TimeDistributed(Conv2D(256, (3, 3), paddingsame),namemrcnn_mask_conv1)(x)x TimeDistributed(BatchNormalization(),namemrcnn_mask_bn1)(x, trainingtrain_bn)x Activation(relu)(x)# Shape: [batch, num_rois, MASK_POOL_SIZE, MASK_POOL_SIZE, channels]x TimeDistributed(Conv2D(256, (3, 3), paddingsame),namemrcnn_mask_conv2)(x)x TimeDistributed(BatchNormalization(),namemrcnn_mask_bn2)(x, trainingtrain_bn)x Activation(relu)(x)# Shape: [batch, num_rois, MASK_POOL_SIZE, MASK_POOL_SIZE, channels]x TimeDistributed(Conv2D(256, (3, 3), paddingsame),namemrcnn_mask_conv3)(x)x TimeDistributed(BatchNormalization(),namemrcnn_mask_bn3)(x, trainingtrain_bn)x Activation(relu)(x)# Shape: [batch, num_rois, MASK_POOL_SIZE, MASK_POOL_SIZE, channels]x TimeDistributed(Conv2D(256, (3, 3), paddingsame),namemrcnn_mask_conv4)(x)x TimeDistributed(BatchNormalization(),namemrcnn_mask_bn4)(x, trainingtrain_bn)x Activation(relu)(x)# Shape: [batch, num_rois, 2xMASK_POOL_SIZE, 2xMASK_POOL_SIZE, channels]x TimeDistributed(Conv2DTranspose(256, (2, 2), strides2, activationrelu),namemrcnn_mask_deconv)(x)# 反卷积后再次进行一个1x1卷积调整通道使其最终数量为numclasses代表分的类x TimeDistributed(Conv2D(num_classes, (1, 1), strides1, activationsigmoid),namemrcnn_mask)(x)return xdef get_predict_model(config):h, w config.IMAGE_SHAPE[:2]if h / 2**6 ! int(h / 2**6) or w / 2**6 ! int(w / 2**6):raise Exception(Image size must be dividable by 2 at least 6 times to avoid fractions when downscaling and upscaling.For example, use 256, 320, 384, 448, 512, ... etc. )# 输入进来的图片必须是2的6次方以上的倍数input_image Input(shape[None, None, config.IMAGE_SHAPE[2]], nameinput_image)# meta包含了一些必要信息input_image_meta Input(shape[config.IMAGE_META_SIZE],nameinput_image_meta)# 输入进来的先验框input_anchors Input(shape[None, 4], nameinput_anchors)# 获得Resnet里的压缩程度不同的一些层_, C2, C3, C4, C5 get_resnet(input_image, stage5True, train_bnconfig.TRAIN_BN)# 组合成特征金字塔的结构# P5长宽共压缩了5次# Height/32,Width/32,256P5 Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (1, 1), namefpn_c5p5)(C5)# P4长宽共压缩了4次# Height/16,Width/16,256P4 Add(namefpn_p4add)([UpSampling2D(size(2, 2), namefpn_p5upsampled)(P5),Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (1, 1), namefpn_c4p4)(C4)])# P4长宽共压缩了3次# Height/8,Width/8,256P3 Add(namefpn_p3add)([UpSampling2D(size(2, 2), namefpn_p4upsampled)(P4),Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (1, 1), namefpn_c3p3)(C3)])# P4长宽共压缩了2次# Height/4,Width/4,256P2 Add(namefpn_p2add)([UpSampling2D(size(2, 2), namefpn_p3upsampled)(P3),Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (1, 1), namefpn_c2p2)(C2)])# 各自进行一次256通道的卷积此时P2、P3、P4、P5通道数相同# Height/4,Width/4,256P2 Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (3, 3), paddingSAME, namefpn_p2)(P2)# Height/8,Width/8,256P3 Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (3, 3), paddingSAME, namefpn_p3)(P3)# Height/16,Width/16,256P4 Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (3, 3), paddingSAME, namefpn_p4)(P4)# Height/32,Width/32,256P5 Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (3, 3), paddingSAME, namefpn_p5)(P5)# 在建议框网络里面还有一个P6用于获取建议框# Height/64,Width/64,256P6 MaxPooling2D(pool_size(1, 1), strides2, namefpn_p6)(P5)# P2, P3, P4, P5, P6可以用于获取建议框rpn_feature_maps [P2, P3, P4, P5, P6]# P2, P3, P4, P5用于获取mask信息mrcnn_feature_maps [P2, P3, P4, P5]anchors input_anchors# 建立RPN模型rpn build_rpn_model(len(config.RPN_ANCHOR_RATIOS), config.TOP_DOWN_PYRAMID_SIZE)rpn_class_logits, rpn_class, rpn_bbox [],[],[]# 获得RPN网络的预测结果进行格式调整把五个特征层的结果进行堆叠for p in rpn_feature_maps:logits,classes,bbox rpn([p])rpn_class_logits.append(logits)rpn_class.append(classes)rpn_bbox.append(bbox)rpn_class_logits Concatenate(axis1,namerpn_class_logits)(rpn_class_logits)rpn_class Concatenate(axis1,namerpn_class)(rpn_class)rpn_bbox Concatenate(axis1,namerpn_bbox)(rpn_bbox)# 此时获得的rpn_class_logits、rpn_class、rpn_bbox的维度是# rpn_class_logits : Batch_size, num_anchors, 2# rpn_class : Batch_size, num_anchors, 2# rpn_bbox : Batch_size, num_anchors, 4proposal_count config.POST_NMS_ROIS_INFERENCE# Batch_size, proposal_count, 4# 对先验框进行解码rpn_rois ProposalLayer(proposal_countproposal_count,nms_thresholdconfig.RPN_NMS_THRESHOLD,nameROI,configconfig)([rpn_class, rpn_bbox, anchors])# 获得classifier的结果mrcnn_class_logits, mrcnn_class, mrcnn_bbox \fpn_classifier_graph(rpn_rois, mrcnn_feature_maps, input_image_meta,config.POOL_SIZE, config.NUM_CLASSES,train_bnconfig.TRAIN_BN,fc_layers_sizeconfig.FPN_CLASSIF_FC_LAYERS_SIZE)detections DetectionLayer(config, namemrcnn_detection)([rpn_rois, mrcnn_class, mrcnn_bbox, input_image_meta])detection_boxes Lambda(lambda x: x[..., :4])(detections)# 获得mask的结果mrcnn_mask build_fpn_mask_graph(detection_boxes, mrcnn_feature_maps,input_image_meta,config.MASK_POOL_SIZE,config.NUM_CLASSES,train_bnconfig.TRAIN_BN)# 作为输出model Model([input_image, input_image_meta, input_anchors],[detections, mrcnn_class, mrcnn_bbox,mrcnn_mask, rpn_rois, rpn_class, rpn_bbox],namemask_rcnn)return modeldef get_train_model(config):h, w config.IMAGE_SHAPE[:2]if h / 2**6 ! int(h / 2**6) or w / 2**6 ! int(w / 2**6):raise Exception(Image size must be dividable by 2 at least 6 times to avoid fractions when downscaling and upscaling.For example, use 256, 320, 384, 448, 512, ... etc. )# 输入进来的图片必须是2的6次方以上的倍数input_image Input(shape[None, None, config.IMAGE_SHAPE[2]], nameinput_image)# meta包含了一些必要信息input_image_meta Input(shape[config.IMAGE_META_SIZE],nameinput_image_meta)# RPN建议框网络的真实框信息input_rpn_match Input(shape[None, 1], nameinput_rpn_match, dtypetf.int32)input_rpn_bbox Input(shape[None, 4], nameinput_rpn_bbox, dtypetf.float32)# 种类信息input_gt_class_ids Input(shape[None], nameinput_gt_class_ids, dtypetf.int32)# 框的位置信息input_gt_boxes Input(shape[None, 4], nameinput_gt_boxes, dtypetf.float32)# 标准化到0-1之间gt_boxes Lambda(lambda x: norm_boxes_graph(x, K.shape(input_image)[1:3]))(input_gt_boxes)# mask语义分析信息# [batch, height, width, MAX_GT_INSTANCES]if config.USE_MINI_MASK:input_gt_masks Input(shape[config.MINI_MASK_SHAPE[0],config.MINI_MASK_SHAPE[1], None],nameinput_gt_masks, dtypebool)else:input_gt_masks Input(shape[config.IMAGE_SHAPE[0], config.IMAGE_SHAPE[1], None],nameinput_gt_masks, dtypebool)# 获得Resnet里的压缩程度不同的一些层_, C2, C3, C4, C5 get_resnet(input_image, stage5True, train_bnconfig.TRAIN_BN)# 组合成特征金字塔的结构# P5长宽共压缩了5次# Height/32,Width/32,256P5 Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (1, 1), namefpn_c5p5)(C5)# P4长宽共压缩了4次# Height/16,Width/16,256P4 Add(namefpn_p4add)([UpSampling2D(size(2, 2), namefpn_p5upsampled)(P5),Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (1, 1), namefpn_c4p4)(C4)])# P4长宽共压缩了3次# Height/8,Width/8,256P3 Add(namefpn_p3add)([UpSampling2D(size(2, 2), namefpn_p4upsampled)(P4),Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (1, 1), namefpn_c3p3)(C3)])# P4长宽共压缩了2次# Height/4,Width/4,256P2 Add(namefpn_p2add)([UpSampling2D(size(2, 2), namefpn_p3upsampled)(P3),Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (1, 1), namefpn_c2p2)(C2)])# 各自进行一次256通道的卷积此时P2、P3、P4、P5通道数相同# Height/4,Width/4,256P2 Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (3, 3), paddingSAME, namefpn_p2)(P2)# Height/8,Width/8,256P3 Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (3, 3), paddingSAME, namefpn_p3)(P3)# Height/16,Width/16,256P4 Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (3, 3), paddingSAME, namefpn_p4)(P4)# Height/32,Width/32,256P5 Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (3, 3), paddingSAME, namefpn_p5)(P5)# 在建议框网络里面还有一个P6用于获取建议框# Height/64,Width/64,256P6 MaxPooling2D(pool_size(1, 1), strides2, namefpn_p6)(P5)# P2, P3, P4, P5, P6可以用于获取建议框rpn_feature_maps [P2, P3, P4, P5, P6]# P2, P3, P4, P5用于获取mask信息mrcnn_feature_maps [P2, P3, P4, P5]anchors get_anchors(config,config.IMAGE_SHAPE)# 拓展anchors的shape第一个维度拓展为batch_sizeanchors np.broadcast_to(anchors, (config.BATCH_SIZE,) anchors.shape)# 将anchors转化成tensor的形式anchors Lambda(lambda x: tf.Variable(anchors), nameanchors)(input_image)# 建立RPN模型rpn build_rpn_model(len(config.RPN_ANCHOR_RATIOS), config.TOP_DOWN_PYRAMID_SIZE)rpn_class_logits, rpn_class, rpn_bbox [],[],[]# 获得RPN网络的预测结果进行格式调整把五个特征层的结果进行堆叠for p in rpn_feature_maps:logits,classes,bbox rpn([p])rpn_class_logits.append(logits)rpn_class.append(classes)rpn_bbox.append(bbox)rpn_class_logits Concatenate(axis1,namerpn_class_logits)(rpn_class_logits)rpn_class Concatenate(axis1,namerpn_class)(rpn_class)rpn_bbox Concatenate(axis1,namerpn_bbox)(rpn_bbox)# 此时获得的rpn_class_logits、rpn_class、rpn_bbox的维度是# rpn_class_logits : Batch_size, num_anchors, 2# rpn_class : Batch_size, num_anchors, 2# rpn_bbox : Batch_size, num_anchors, 4proposal_count config.POST_NMS_ROIS_TRAINING# Batch_size, proposal_count, 4rpn_rois ProposalLayer(proposal_countproposal_count,nms_thresholdconfig.RPN_NMS_THRESHOLD,nameROI,configconfig)([rpn_class, rpn_bbox, anchors])active_class_ids Lambda(lambda x: parse_image_meta_graph(x)[active_class_ids])(input_image_meta)if not config.USE_RPN_ROIS:# 使用外部输入的建议框input_rois Input(shape[config.POST_NMS_ROIS_TRAINING, 4],nameinput_roi, dtypenp.int32)# Normalize coordinatestarget_rois Lambda(lambda x: norm_boxes_graph(x, K.shape(input_image)[1:3]))(input_rois)else:# 利用预测到的建议框进行下一步的操作target_rois rpn_rois找到建议框的ground_truthInputs:proposals: [batch, N, (y1, x1, y2, x2)]建议框gt_class_ids: [batch, MAX_GT_INSTANCES]每个真实框对应的类gt_boxes: [batch, MAX_GT_INSTANCES, (y1, x1, y2, x2)]真实框的位置gt_masks: [batch, height, width, MAX_GT_INSTANCES]真实框的语义分割情况Returns: rois: [batch, TRAIN_ROIS_PER_IMAGE, (y1, x1, y2, x2)]内部真实存在目标的建议框target_class_ids: [batch, TRAIN_ROIS_PER_IMAGE]每个建议框对应的类target_deltas: [batch, TRAIN_ROIS_PER_IMAGE, (dy, dx, log(dh), log(dw)]每个建议框应该有的调整参数target_mask: [batch, TRAIN_ROIS_PER_IMAGE, height, width]每个建议框语义分割情况rois, target_class_ids, target_bbox, target_mask \DetectionTargetLayer(config, nameproposal_targets)([target_rois, input_gt_class_ids, gt_boxes, input_gt_masks])# 找到合适的建议框的classifier预测结果mrcnn_class_logits, mrcnn_class, mrcnn_bbox \fpn_classifier_graph(rois, mrcnn_feature_maps, input_image_meta,config.POOL_SIZE, config.NUM_CLASSES,train_bnconfig.TRAIN_BN,fc_layers_sizeconfig.FPN_CLASSIF_FC_LAYERS_SIZE)# 找到合适的建议框的mask预测结果mrcnn_mask build_fpn_mask_graph(rois, mrcnn_feature_maps,input_image_meta,config.MASK_POOL_SIZE,config.NUM_CLASSES,train_bnconfig.TRAIN_BN)output_rois Lambda(lambda x: x * 1, nameoutput_rois)(rois)# Lossesrpn_class_loss Lambda(lambda x: rpn_class_loss_graph(*x), namerpn_class_loss)([input_rpn_match, rpn_class_logits])rpn_bbox_loss Lambda(lambda x: rpn_bbox_loss_graph(config, *x), namerpn_bbox_loss)([input_rpn_bbox, input_rpn_match, rpn_bbox])class_loss Lambda(lambda x: mrcnn_class_loss_graph(*x), namemrcnn_class_loss)([target_class_ids, mrcnn_class_logits, active_class_ids])bbox_loss Lambda(lambda x: mrcnn_bbox_loss_graph(*x), namemrcnn_bbox_loss)([target_bbox, target_class_ids, mrcnn_bbox])mask_loss Lambda(lambda x: mrcnn_mask_loss_graph(*x), namemrcnn_mask_loss)([target_mask, target_class_ids, mrcnn_mask])# Modelinputs [input_image, input_image_meta,input_rpn_match, input_rpn_bbox, input_gt_class_ids, input_gt_boxes, input_gt_masks]if not config.USE_RPN_ROIS:inputs.append(input_rois)outputs [rpn_class_logits, rpn_class, rpn_bbox,mrcnn_class_logits, mrcnn_class, mrcnn_bbox, mrcnn_mask,rpn_rois, output_rois,rpn_class_loss, rpn_bbox_loss, class_loss, bbox_loss, mask_loss]model Model(inputs, outputs, namemask_rcnn)return model