tf-faster-rcnn code details
生成anchors,首先生成一个base anchor,然后以base anchor为基础,在原图像中移动,生成原图像中的anchors。
由于原文中提到过:We also note that for ZF and VGG nets, the total stride on the last conv layer is 16 pixels on the re-scaled image, and thus is ∼10 pixels on a typical PASCAL image (∼500×375).
所以在conv5-3这一层中,第一个feature的点,对应的原图像中[0,0]的点,第二个feature的点,对应原图像中的[0, 16]的点。
所以,通过步长,可以建立feature map上的特征和原图像中anchors的映射关系。RPN层的第一步是用[3, 3, 512]的卷积核在conv5-3上进行卷积操作,conv5-3上的每一个像素点,对应的是原图中的一个近似于16*16的区域,所以这也就是为什么文章中说的把每一个中心点的像素转换为512-D的vector,feature map的个数变为了512,于是比如[0, 0, :]就是一个512-D的vector。
在这个512-D的vector基础上又有两个全卷机网络,一个是[1, 1]的卷积核,但是输出时9*2,因为9个anchor,每个anchor都有两个值,有目标还是没有目标,所以是18,所以这个输出的大小的height和width与conv5-4的大小一致。用于定义objectness。
同样的,在这个512-D向量的基础上,定义了一个[1, 1]的卷积核,输出为9*4,因为9个anchor,每个anchor都有4个值,这四个值为预测的tx,ty,tw,th。所以这个输出feature的height和width与conv5-3的大小一致。这是用于定义pred-box的。
rois, roi_scores = self._proposal_layer(rpn_cls_prob, rpn_bbox_pred, "rois") rpn_cls_prob: RPN层输出的objectness的值 rpn_bbox_pred: RPN层输出的box的取值,即:tx,ty,tw,th
该方法首先根据rpn_bbox_pred来生成原始图像中的anchor的预测坐标,由于rpn_bbox_pred是tx,ty,tw, th,这四个值的计算方法可一个看论文。对rpn_cls_prob进行排序,根据objectness分数的高低排序,然后选出需要保留的proposal的个数,论文中设置的为6000。然后从这些proposal中使用nms算法,筛选出最后的proposal。返回这些proposal和scors。注:scors和proposal都是排序后的。
rpn_labels = self._anchor_target_layer(rpn_cls_score, "anchor") rpn_cls_score: RPN层输出的box的取值,即:tx, ty, tw, th
该方法首先把越过边界的anchor都过滤掉,保留都在图像范围内的anchor。然后创建一个全部是-1的labels。接着计算每个anchor和ground-truth的overlap,overlap返回一个二维数组,行数代表的是anchor个数,列数代表的ground-truth的个数。从中选择max-overlap,如果max-overlap大于某个阈值,那么这个anchor的label就设置为包含目标,用1表示,如果max-overlap小于某个阈值,那么这个anchor的label就设置为0。然后再找到每个ground-truth和anchor覆盖最大的anchor的index,把这些anchor设置为1。避免某个ground-truth没有对应的anchor。对每个anchor都设置是否含有目标后,利用anchors和每个anchors对应的max-overlap的ground-truth来计算该anchor对应的tx*, ty*, tw*, th*。然后设置bbox_inside_weights,这个权值起到的作用是论文中公式(1)中的pi*。bbox_outside_weights该权值用来设置在所有样本中,positive和negitive的权值。由于上述所有操作都是在没有越界的anchor中进行的,所以需要还原回到所有的anchors中。于是使用方法_unmap。
该方法最后返回:rpn_labels:这是真实的每个anchor是含有目标还是没有目标.
rpn_bbox_targets:这个是真实的每个anchor与其覆盖最大的ground-truth来计算得到的tx, ty, tw, th。
rois, _ = self._proposal_target_layer(rois, roi_scores, "rpn_rois") rois: 为第6步方法中生成的rois roi_scores: 为第6步中生成的roi的scores
该方法首先计算fg_rois_per_image也就是在一个batch中认为是前景的roi的个数,剩余的被认为是背景。然后计算每个rois和ground-truth的overlap,该overlap返回的数组形式为[roi_size, gt_size]。从这些roi中选择随机选择一些正样本和负样本,max-overlap大于某个阈值的roi被认为是正样本,找到正样本后,建立label,label是每个正样本的类别标签,由于20类,所以就是某个数字。按照比率设定背景样本,背景样本的标签为0。该方法中调用了一个_sample_rois的方法,该方法返回值为:labels,每个roi的类别标签,rois,是对原来所有rois进行正负样本过滤,选择出来的正样本和负样本。roi_scores, 对应选择出来的正负样本的objectness scores。bbox_targets,该返回值为数组:target_nums = [num_rois, num_class*4]的数组,取其中的一行作为例子,比如target_nums[0],该vector的长度为80, 首先设置全部为0, 如果target_nums[0]的类别为3,那么设置target_nums[0, 3*4:(3*4+4)]的取值为tx,ty,tw,th。这个方法返回的rois会接着送到后面的fast-rcnn网络。该方法中计算出来的labels,boxs都作为真实值,rois from feature conv5_3
“`
vgg16_depre.py
def _add_losses(self, sigma_rpn=3.0):
with tf.variable_scope(‘vgg16-loss_’ + self._tag) as scope:
# RPN, class loss
# 计算RPN的objectness的loss,首先获取rpn网络输出的logits,然后从anchor和ground-truth中计算得到的label中选择不为-1的anchors。然后对这些anchor来计算cross-entropy
rpn_cls_score = tf.reshape(self._predictions[‘rpn_cls_score_reshape’], [-1,2])
rpn_label = tf.reshape(self._anchor_targets[‘rpn_labels’], [-1])
rpn_select = tf.where(tf.not_equal(rpn_label,-1))
rpn_cls_score = tf.reshape(tf.gather(rpn_cls_score, rpn_select),[-1,2])
rpn_label = tf.reshape(tf.gather(rpn_label, rpn_select),[-1])
rpn_cross_entropy = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=rpn_cls_score, labels=rpn_label))
# RPN, bbox loss
# 采用smooth_l1_loss来计算bbox的loss。rpn_bbox_inside_weights用于把是object的box过滤出来,因为并不是所有的anchors都是有object的。rpn_bbox_inside_weights用于设置标记为1的box和标记为0的box的权值比率。
rpn_bbox_pred = self._predictions[‘rpn_bbox_pred’]
rpn_bbox_targets = self._anchor_targets[‘rpn_bbox_targets’]
rpn_bbox_inside_weights = self._anchor_targets[‘rpn_bbox_inside_weights’]
rpn_bbox_outside_weights = self._anchor_targets[‘rpn_bbox_outside_weights’]rpn_loss_box = self._smooth_l1_loss(rpn_bbox_pred, rpn_bbox_targets, rpn_bbox_inside_weights, rpn_bbox_outside_weights, sigma=sigma_rpn, dim=[1,2,3]) # RCNN, class loss cls_score = self._predictions["cls_score"] label = tf.reshape(self._proposal_targets["labels"],[-1]) cross_entropy = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=cls_score, labels=label)) # RCNN, bbox loss bbox_pred = self._predictions['bbox_pred'] bbox_targets = self._proposal_targets['bbox_targets'] bbox_inside_weights = self._proposal_targets['bbox_inside_weights'] bbox_outside_weights = self._proposal_targets['bbox_outside_weights'] loss_box = self._smooth_l1_loss(bbox_pred, bbox_targets, bbox_inside_weights, bbox_outside_weights) self._losses['cross_entropy'] = cross_entropy self._losses['loss_box'] = loss_box self._losses['rpn_cross_entropy'] = rpn_cross_entropy self._losses['rpn_loss_box'] = rpn_loss_box loss = cross_entropy + loss_box + rpn_cross_entropy + rpn_loss_box self._losses['total_loss'] = loss self._event_summaries.update(self._losses) return loss
“`