读Mask R-CNN源码备忘录(预测部分)

源码:https://github.com/matterport/Mask_RCNN

接着前一篇训练部分

预测部分

模型输入:

input_image (batch_size, height, width, channels) #默认(2, 1024, 1024, 3)

input_image_meta (batch_size, 1 + 3 + 3 + 4 + 1 + config.NUM_CLASSES) #默认(2, 93)

input_anchors (batch_size, num_anchors, 4) #默认(2, 216888, 4)

第一步:resnet_graph网络

C2, C3, C4, C5 为resnet_graph的四个stage输出,输出尺寸依次为:

C2: (batch_size, config.IMAGE_SHAPE[0] / config.BACKBONE_STRIDES[0], config.IMAGE_SHAPE[1] / config.BACKBONE_STRIDES[0], 256) #默认(2, 256, 256, 256)

C3: (batch_size, config.IMAGE_SHAPE[0] / config.BACKBONE_STRIDES[1], config.IMAGE_SHAPE[1] / config.BACKBONE_STRIDES[1], 512) #默认(2, 128, 128, 512)

C4: (batch_size, config.IMAGE_SHAPE[0] / config.BACKBONE_STRIDES[2], config.IMAGE_SHAPE[1] / config.BACKBONE_STRIDES[2], 1024) #默认(2, 64, 64, 1024)

C5: (batch_size, config.IMAGE_SHAPE[0] / config.BACKBONE_STRIDES[3], config.IMAGE_SHAPE[1] / config.BACKBONE_STRIDES[3], 2048) #默认(2, 32, 32, 2048)

P5: 对C5做(1, 1)卷积filters=256,效果就是在维度不变的情况下,将特征图数量由2048降为256 #默认(2, 32, 32, 256)

P4: 对P5做(2, 2)上采样,并且将C4做(1, 1)卷积filters=256,然后将两者相加,效果就是P5尺寸加倍与C4卷积后尺寸相等,将两者相加作为P4 #默认(2, 64, 64, 256)

P3: 对P4做(2, 2)上采样,并且将C3做(1, 1)卷积filters=256,然后将两者相加,效果就是P4尺寸加倍与C3卷积后尺寸相等,将两者相加作为P3 #默认(2, 128, 128, 256)

P2: 同P3,P4 #默认(2, 256, 256, 256)

对P2, P3, P4, P5做(3, 3)卷积filters=256,padding="SAME",所以尺寸不变,只是对特征抽象级别提升了一下。

这里多出来一个P6,P6是对P5做了(1, 1)池化stride=2,所以尺寸减半,效果是对原图像做了隔像素采样。#默认(2, 16, 16, 256)

[P2, P3, P4, P5, P6]用于RPN网络

[P2, P3, P4, P5]用于classifier heads

第二步:生成ANCHORS

anchorsinput_anchors #默认(2, 216888, 4)

第三步:创建RPN模型,对每一层特征图做预测

输入:

rpn_feature_maps #默认[(2, 256, 256, 256), (2, 128, 128, 256), (2, 64, 64, 256), (2, 32, 32, 256), (2, 16, 16, 256)]

输出:

rpn_class_logits #默认(2, 261888, 2)

rpn_class #默认(2, 261888, 2)

rpn_bbox #默认(2, 261888, 4)

思路:通过对每个特征图做卷积操作,分别得到class和bbox偏移量的回归网络分支

shared:对特征图做(3, 3)卷积,filters=512, strides=anchor_stride,padding='same' #默认(以P2特征图为例)(2, 256, 256, 512)

x:对shared做(1, 1)卷积,filters=2 * anchors_per_location,padding='valid' #默认(以P2特征图为例)(2, 256, 256, 6)

rpn_class_logits:对上步x做reshape(batch_size, -1, 2)  #默认(以P2特征图为例)(2, 196608, 2)

rpn_probs:对上步rpn_class_logits做softmax #默认(以P2特征图为例)(2, 196608, 2)

x: 对shared做(1, 1)卷积,filters=4 * anchor_per_location, padding='valid' #默认(以P2特征图为例)(2, 256, 256, 8)

rpn_bbox:对上步x做reshape(batch_size, -1, 4) #默认(以P2特征图为例)(2, 196608, 4)

至此rpn网络生成:输入每层特征图input_feature_map,输出每层特征图预测到的rpn_class_logits, rpn_probs, rpn_bbox

对每层输出做KL.Concatenate操作后最终得到的输出

第四步:创建ProposalLayer层,对上一步结果通过NMS获取rpn_rois

计算proposal_count #默认1000

ProposalLayer层:

输入:

rpn_class #默认(2, 261888, 2)

rpn_bbox #默认(2, 261888, 4)

anchors #默认(2, 261888, 4)

输出:

rpn_rois #默认(2, 1000, 4)

思路:

scores: rpn_class代表此实例是前景(fg)或者背景(bg)的概率预测,[batch, num_anchors, (bg prob, fg prob)],所以第三维的第二项可以看成这个实例检测到物体的分数 scores = rpn_class[:, :, 1] #默认(2, 261888, 1)

deltas: rpn_bbox代表原anchor与真实实例位置的偏移量,deltas = rpn_bbox * np.reshape(self.config.RPN_BBOX_STD_DEV, [1, 1, 4]) #默认(2, 261888, 4)

anchors即上边通过feature_map得到的所有anchor #默认(2, 261888, 4)

首先通过scores倒排序,取前pre_nms_limit个(去除分数低的实例),得到scores,deltas,pre_nms_anchors。#默认pre_nms_limit为6000

scores #默认(2, 6000, 1)

deltas #默认(2, 6000, 4)

pre_nms_anchors #默认(2, 6000, 4)

boxes: 然后用deltas调整pre_nms_anchors位置,并且剪切超出边界的anchor,得到boxes #默认(2, 6000, 4)

proposals:对boxes做NMS操作,并且根据scores取其中前proposal_count个,不足用0padding #默认(2, 1000, 4)

target_rois: rpn_rois: 等于proposals #默认(2, 1000, 4)

第五步:创建fpn_classifier_graph (FPN)

fpn_classifier_graph:

输入rois, feature_maps, image_meta, pool_size, num_classes, train_bn=True, fc_layers_size=1024

rois: rpn_rois  #默认(2, 1000, 4)

feature_maps: mrcnn_feature_maps #默认[P2, P3, P4, P5] [(2, 256, 256, 256), (2, 128, 128, 256), (2, 64, 64, 256), (2, 32, 32, 256)]

image_meta: input_image_meta #默认(2, 93)

pool_size #默认7

num_classes #默认81

输出mrcnn_class_logits, mrcnn_class, mrcnn_bbox

mrcnn_class_logits #默认(2, 1000, 81)

mrcnn_class #默认(2, 1000, 81)

mrcnn_bbox #默认(2, 1000, 81, 4)

思路:

首先通过PyramidROIAlign 层对每个feature_map用roi提取区域特征:

***PyramidROIAlign 开始***

输入:

boxes: rois #默认(2, 1000, 4)

image_meta #默认(2, 93)

feature_maps #默认[P2, P3, P4, P5] [(2, 256, 256, 256), (2, 128, 128, 256), (2, 64, 64, 256), (2, 32, 32, 256)]

输出:

pooled regions,即roi坐标对应的feature_map上的一小块区域的集合 #默认(2, 1000, 7, 7, 3)

思路:

首先根据每个roi对应的面积计算roi_level #默认(2, 1000)

循环依次处理2~5层特征图

处理方法:以P2层为例,先在roi_level中过滤出level=2的索引ix, 然后将这些roi从boxes中取出,得到level_boxes,获取level_boxes中每个roi对应的batch索引box_indices,用tf.image.crop_and_resize获得level_boxes在feature_map上的区域特征并存入pooled,以此类推获取每层中提取的区域特征。

ix #默认(level=2的roi个数, 2)

level_boxes #默认(level=2的roi个数, 4)

box_indices #默认(level=2的roi个数)

理解上边的处理思路可以参考这几句代码:

>>> import tensorflow as tf
>>> import numpy as np
>>> roi_level = np.array([[1, 0, 3], [2, 0, 0]])
>>> boxes = np.array([[[1,2,3,4],[5,6,7,8],[9,10,11,12]], [[13,14,15,16],[17,18,19,20],[21,22,23,24]]])
>>> ix = tf.where(tf.equal(roi_level, 0))
>>> ix
<tf.Tensor: shape=(3, 2), dtype=int64, numpy=
array([[0, 1],
       [1, 1],
       [1, 2]])>
>>> level_boxes = tf.gather_nd(boxes, ix)
>>> level_boxes
<tf.Tensor: shape=(3, 4), dtype=int64, numpy=
array([[ 5,  6,  7,  8],
       [17, 18, 19, 20],
       [21, 22, 23, 24]])>
>>> box_indices = tf.cast(ix[:, 0], tf.int32)
>>> box_indices
<tf.Tensor: shape=(3,), dtype=int32, numpy=array([0, 1, 1], dtype=int32)>

pooled #默认(2 * 1000, 7, 7, 3)

box_range #默认(2 * 1000, 1)

box_to_level #默认(2 * 1000, (batch_index, roi_index, box_range_value))

ix:对box_to_level根据batch_index * 10000 + roi_index倒序排序以保持原boxes倒序排序 #默认(2 * 1000, 1)

pooled 根据ix重排序 #默认(2 * 1000, 7, 7, 3)

对pooled reshape(2, 1000, 7, 7, 3)作为输出,至此PyramidROIAlign完成,输出x #默认(2, 1000, 7, 7, 3)

***PyramidROIAlign 结束***

x: 用TimeDistributed对batch中每个sample(特征图)的每个roi做(7, 7)卷积,filters=1024,padding=valid,然后做batchnorm操作 #默认(2, 1000, 1, 1, 1024)

x: 用TimeDistributed对batch中每个sample(特征图)的每个roi做(1, 1)卷积,filters=1024,然后做batchnorm操作 #默认(2, 1000, 1, 1, 1024)

shared: 对x做squeeze操作  #默认(2, 1000, 1024)

mrcnn_class_logits: 对shared每个roi做dense(81)操作 #默认(2, 1000, 81)

mrcnn_probs: 对mrcnn_class_logits中每个roi做softmax操作 #默认(2, 1000, 81)

x: 对shared中每个roi做dense(4 * 81)操作 #默认(2, 1000, 4 * 81)

mrcnn_bbox: 对x做reshape #默认(2, 1000, 81, 4)

第六步:DetectionLayer

输入:

rpn_rois #默认(2, 1000, 4)

mrcnn_class #默认(2, 1000, 81)

mrcnn_bbox #默认(2, 1000, 81, 4)

input_image_meta #默认(2, 93)

输出:

detections: (batch, num_detections, (y1, x1, y2, x2, class_id, score)) #默认(2, 100, 6)

***refine_detections_graph 开始***

refine_detections_graph

*注意:此函数是针对batch内某个sample的操作,所以不包括batch维

输入:

rois: rpn_rois #默认(1000, 4)

probs: mrcnn_class #默认(1000, 81)

deltas: mrcnn_bbox #默认(1000, 81, 4)

window #默认(4)

输出:

detections #默认(100, 6)

思路:

首先通过上一步回归预测到的probs得到每一个roi对应的预测class_ids,以及对应的预测偏移量deltas_specific,将偏移量应用到roi上矫正,通过probs对每个roi的预测得分class_scores,过滤掉score<config.DETECTION_MIN_CONFIDENCE(0.7)的roi,分别对每一类(class_id)检测目标应用nms,并且合并结果得到nms_keep,从nms_keep中保留class_scores分数较高项

中间变量维度为,class_ids(1000, ),indices(1000, 2),class_scores(1000, ),deltas_specific(1000, 4),refined_rois(1000, 4),keep(可信度高的正例样本个数, ),pre_nms_class_ids(可信度高的正例样本个数, ),pre_nms_scores(可信度高的正例样本个数, ),pre_nms_rois(可信度高的正例样本个数, 4),unique_pre_nms_class_ids(可信度高的正例样本个数, ),nms_keep(100, ),detections(100, 6)

***refine_detections_graph 结束***

第七步:build_fpn_mask_graph

输入:

rois: detection_boxes #默认(2, 100, 4)

feature_maps: mrcnn_feature_maps #默认[P2, P3, P4, P5] [(2, 256, 256, 256), (2, 128, 128, 256), (2, 64, 64, 256), (2, 32, 32, 256)]

image_meta: input_image_meta #默认(2, 93)

pool_size #默认14

num_classes #默认81

train_bn #默认False

输出:

mrcnn_mask #默认(2, 100, 28, 28, 81)

思路:

x: 首先通过PyramidROIAlign层对每个feature_map用roi提取区域特征,具体过程参照第六步中相同操作 #默认(2, 100, 14, 14, 3)

x: 对x中每个roi做(3, 3)卷积,filters=256,padding=same,然后batchnorm,重复4次 #默认(2, 100, 14, 14, 256)

x: 对x中每个roi做(2, 2)反卷积,filters=256,strides=2 #默认(2, 100, 28, 28, 256)

#计算反卷积的尺寸公式
new_rows = (rows - 1) * strides[0] + kernel_size[0] - 2 * padding[0] + output_padding[0]
new_cols = (cols - 1) * strides[1] + kernel_size[1] - 2 * padding[1] + output_padding[1]

#默认
new_rows = (14 - 1) * 2 + 2 - 2 * 0 + 0 = 28
new_cols = (14 - 1) * 2 + 2 - 2 * 0 + 0 = 28

x: mrcnn_mask 对x每个roi做(1, 1)卷积,filters=81,strides=1,以此作为mrcnn_mask输出 #默认(2, 100, 28, 28, 81)

第八步:创建模型

inputs=[input_image, input_image_meta, input_anchors]

outputs=[detections, mrcnn_class, mrcnn_bbox, mrcnn_mask, rpn_rois, rpn_class, rpn_bbox]

第九步:detect

输入:

images #默认(2, origin_h, origin_w, 3)

输出:

results = 

[{

                "rois": final_rois, #默认(实际正样本个数, 4)

                "class_ids": final_class_ids, #默认(实际正样本个数, )

                "scores": final_scores, #默认(实际正样本个数, )

                "masks": final_masks, #默认(28, 28, 实际正样本个数)

            }]

思路:

***mold_inputs开始***

输入:

images #默认(2, origin_h, origin_w, 3)

输出:

molded_images #默认(2, 1024, 1024, 3)

image_metas #默认(2, 93) 

windows #默认(2, 4)

***mold_inputs结束***

 

***获取anchors开始***

anchors=get_anchors(image_shape) #默认(2, 261888, 4)

***获取anchors结束***

 

***predict开始***

输入:

molded_images #默认(2, 1024, 1024, 3)

image_metas #默认(2, 93)

anchors #默认(2, 261888, 4)

输出:

detections #默认(2, 100, 6)

mrcnn_mask #默认(2, 100, 28, 28, 81)

***predict结束***

 

***unmold_detections开始***

*注意:此操作为batch内单sample操作,所以不带batch维

输入:

detections: (N, (y1, x1, y2, x2, class_id, score)) #默认(100, 6)

mrcnn_mask: #默认(100, 28, 28, 81)

original_image_shape #默认(3, ) 值(origin_h, origin_w, origin_c)

image_shape #默认(3, ) 值(1024, 1024, 3)

window: [y1, x1, y2, x2] #默认(1)

输出:

boxes #默认(实际正样本个数, 4)

class_ids #默认(实际正样本个数, )

scores #默认(实际正样本个数, )

masks #默认(28, 28, 实际正样本个数)

***unmold_detections结束***

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值