rpn的作用是从图像中提取proposals(这里是1000个),也就是预测框。在faster-rcnn之前,提取的方法有selective search等。但RPN是可以端到端训练的。我们看下torchvision的RPN:
rpn = RegionProposalNetwork(
rpn_anchor_generator, rpn_head,
rpn_fg_iou_thresh, rpn_bg_iou_thresh,
rpn_batch_size_per_image, rpn_positive_fraction,
rpn_pre_nms_top_n, rpn_post_nms_top_n, rpn_nms_thresh)
从类的初始化也可以看出,RPN主要有rpn_anchor_generator, rpn_head两部分,其余是可调节的参数。
(一)rpn_anchor_generator
从上几篇教程我们知道,我们输入的图片size为3x800x800,也就是长宽为800。特征大小为torch.Size([1, 256, 50, 50]),所以缩放比例(stride)为800/50=16。现在在800x800的图像上,我们以16x16为单位进行分割,我们标记出这些16x16的中心点,那么就是下面这样,这些点我们叫做anchors
为了说明,替换成空白图像
对于每个中心点(anchors)我们创建3个anchor box,也就是矩形框。
中间的正方形anchor box大小是128,其他两个长宽比分别为1:2和2:1.
所有anchor生成的anchor box就变成下面这样,密密麻麻,总共50x50x3=7500个box
由于我们使用FPN,提取的是分层的特征
torch.Size([1, 256, 200, 200])
torch.Size([1, 256, 100, 100])
torch.Size([1, 256, 50, 50])
torch.Size([1, 256, 25, 25])
torch.Size([1, 256, 13, 13])
上面介绍到torch.Size([1, 256, 50, 50])这个提取了50x50x3=7500个anchor box,同理其它三层anchor box数量为200x200x3,100x100x3,25x25x3,13x13x3合并起来就有159882个anchor box。
(二) rpn_head
rpn head将图像特征作为输入,生成框(anchors)的分类objectness(这里只分为两类:1.包含物体 2. 图像背景,也就是不包含物体)和anchor box回归系数pred_bbox_deltas。
objectness: 数值越大说明越可能包含物体而不是背景
pred_bbox_deltas:前面我们生成的是固定的基本框anchor box。虽然我们在上面的图片上可以看到基本上框已经密密麻麻地覆盖了整个图片,但实际的物体位置肯定不是固定的,需要调整。怎么在上面生成的框的基础上调整?那就是使用预测出来的pred_bbox_deltas。
图片来自:http://www.telesens.co/2018/03/11/object-detection-and-classification-using-r-cnns/
图片中红色框是我们的基础anchor box,蓝色是预测框。有了回归系数(pred_bbox_deltas),应用上面的公式,我们可以很方便地在基础anchor box(上一节生成的159882个框)和预测框之间转换。
proposals = self.box_coder.decode(pred_bbox_deltas.detach(), anchors)
torchvision/models/detection/_utils.py中的BoxCoder类实现了上面的转换方法,分别是encode、decode方法
(三)filter_proposals
现在我们已经有每个anchor box 的预测分数,现在在每一层我们选取前1000个分数高的,每一层anchor的数量我们上面算过,由于最后一层只有507,所以我们总共选取了4507个anchor box。
这样我们便从159882个anchor box,筛减到4507个。现在我们做进一步的过滤。
- 先将超出图像的边框裁剪到不超出图像
- 边框太小,小于某个阀值的去掉
- 使用Non-Maximum Suppression (NMS)算法去掉重叠度比较高的
- 最后我们设定了一个阀值,最多保留n个(这里我们取1000)
Non-Maximum Suppression算法在下一章单独介绍
这样我们RPN就得到了1000个proposals。