tf-faster-rcnn github:https://github.com/endernewton/tf-faster-rcnngit
backbone,例如vgg,conv层不改变feature大小,pooling层输出(w/2, h/2),有4个pooling因此featuremap变为原图1/16大小。github
检测RPN模块:算法
例如任意图片reshape到800*600,输入网络过vgg,conv5_3->rpn_conv/3*3->rpn_relu 获得feature map (1,512,50,38),接下来两个1*1的卷积分别用于每一个点9个anchor前背景分类(1,18,50,38),anchor偏移量的预测(1,36,50,38),50 x 38 x 9 = 17100 从原图中扣出来的anchor数。(rpn_bbox_pred(偏移)+rpn_cls_prob_reshape(前背景))->proposal_layer 修正后的proposal,滤掉超出原图的proposal后NMS以及几率排序等操做后得到最终的boundingbox。输出大小为:(N,4),这里的N与NMS以及几率排序阈值有关,获得的就是boundingbox的四个坐标。数组
1. 生成anchors,首先生成一个base anchor,而后以base anchor为基础,在原图像中移动,生成原图像中的anchors。网络
2. 在conv5-3这一层中,第一个feature的点,对应的原图像中[0,0]的点,第二个feature的点,对应原图像中的[16,0]的点,经过乘以步长,能够创建feature map上的特征和原图像中anchors的映射关系。ide
3. RPN层的第一步是用[3, 3, 512]的卷积核在conv5-3上进行卷积操做,conv5-3上的每个像素点,对应的是原图中的一个近似于16*16的区域,因此这也就是为何文章中说的把每个中心点的像素转换为256-D的vector,但TensorFlow实现这里用了更厚的feature map,个数变为了512,因而好比[0, 0, :]就是一个512-D的vector。post
4. 在这个512-D的vector基础上又有两个全卷积网络,一个是[1, 1]的卷积核,可是输出是9*2,由于9个anchor,每一个anchor都有两个值,有目标仍是没有目标,因此是18,因此这个输出的大小的height和width与conv5-4的大小一致。用于定义objectness。spa
5. 一样的,在这个512-D向量的基础上,定义了一个[1, 1]的卷积核,输出为9*4,由于9个anchor,每一个anchor都有4个值,这四个值为预测的tx,ty,tw,th。因此这个输出feature的height和width与conv5-3的大小一致。这是用于定义pred-box的。code
6. blog
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和scores。注:scores和proposal都是排序后的。
7.
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是含有目标仍是没有目标. 用于loss计算。
rpn_bbox_targets:这个是真实的每一个anchor与其覆盖最大的ground-truth来计算获得的tx, ty, tw, th。
8.
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
9. loss
# 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’]
1 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]) 2 3 # RCNN, class loss 4 cls_score = self._predictions["cls_score"] 5 label = tf.reshape(self._proposal_targets["labels"],[-1]) 6 cross_entropy = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=cls_score, labels=label)) 7 8 # RCNN, bbox loss 9 bbox_pred = self._predictions['bbox_pred'] 10 bbox_targets = self._proposal_targets['bbox_targets'] 11 bbox_inside_weights = self._proposal_targets['bbox_inside_weights'] 12 bbox_outside_weights = self._proposal_targets['bbox_outside_weights'] 13 14 loss_box = self._smooth_l1_loss(bbox_pred, bbox_targets, bbox_inside_weights, bbox_outside_weights) 15 16 self._losses['cross_entropy'] = cross_entropy 17 self._losses['loss_box'] = loss_box 18 self._losses['rpn_cross_entropy'] = rpn_cross_entropy 19 self._losses['rpn_loss_box'] = rpn_loss_box 20 21 loss = cross_entropy + loss_box + rpn_cross_entropy + rpn_loss_box 22 self._losses['total_loss'] = loss 23 24 self._event_summaries.update(self._losses) 25 26 return loss
10 RoI pooling
对每个RoI,将RoI的坐标从原图映射到feature map就是简单除以原图到feature的放缩尺度16,从而获得feature map上的box坐标,因为box大小不一,因此要逆向考虑,在Pooling的过程当中须要计算Pooling后的结果对应到feature map上所占的范围,在这个范围内作max pooling。
计算RoI在feature map上的宽高与pooled宽高的比值求得bin的大小[即pooling后featuremap上一个点与RoI上一个patch的映射关系,更具体的就是把feature map分割为 pooled_w*pooled_h 这么多份],因为roi的大小不一致,因此每次都须要计算一次bin的大小。最后在pooled上面循环遍历channel,h,w这3个维度,将映射后的区域平动到RoI对应的位置[hstart, wstart, hend, wend],统计该bin区域的最大值。
11. 输入处理
通常resize到固定尺寸,若是要求任意尺寸的输入那么限制条件就是fc层,如何处理呢?方法一:采用spp-net,固定size的特征金字塔;方法二:直接把fc换为global average pooling