当前位置:   article > 正文

rcnn代码实现_Faster-RCNN论文细节原理解读+代码实现gluoncv(MXNet)

rcnn 代码实现

ab51e94bd20eb8b6262de36754bf05b5.png

Faster-RCNN开创了基于锚框(anchors)的目标检测框架,并且提出了RPN(Region proposal network),来生成RoI,用来取代之前的selective search方法。Faster-RCNN无论是训练/测试速度,还是物体检测的精度都超过了Fast-RCNN,并且实现了end-to-end训练。

从RCNN到Fast-RCNN再到Faster-RCNN,后者无疑达到了这一系列算法的巅峰,并且后来的YOLO、SSD、Mask-RCNN、RFCN等物体检测框架都是借鉴了Faster-RCNN

Faster-RCNN作为一种two-stage的物体检测框架,流程无疑比SSD这种one-stage物体检测框架要复杂,在阅读论文,以及代码复现的过程中也理解了很多细节,在这里记录一下自己的学习过程和自己的一点体会。

背景介绍

Fast-RCNN通过共享卷积层,极大地提升了整体的运算速度。Selective Search 反倒成为了限制计算效率的瓶颈。Faster-RCNN中使用卷积神经网络取代了Selective Search,这个网络就是Region Proposal Networks(RPN),Faster-RCNN将所有的步骤都包含到一个完整的框架中,真正实现了端对端(end-to-end)的训练。

论文主要贡献

  • 提出RPN,实现了端对端的训练
  • 提出了基于anchors的物体检测方法

1、网络框架

Faster-RCNN总体流程框图如下(点击原图查看大图),通过这个框图我们比较一下Faster-RCNN和SSD的不同: SSD中每一阶段生成的特征图,每个cell都会生成锚框,并且进行类别+边界框回归。 Faster-RCNN只对basenet提取出的特征图上生成锚框,并且对该锚框进行二分类(背景 or 有物体)+边界框回归,然后会进行NMS移除相似的结果,这样RPN最后会输出一系列region proposal,将这些region proposal区域从feature map中提取出来即为RoI,之后将会通过RoI pooling,进行真正的类别预测(判断属于哪一类)+边界框回归

可以看出Faster-RCNN之所以被称为two-stage,是由于需要有RPN生成region proposal这一步骤。相比来看SSD可以看做是稠密采样,它对所有生成的锚框进行了预测,而没有进行筛选。

RPN中还有一些细节操作,比如说采样比例的设置,如何进行预测,这个在后面的部分会详细说明。

fa4852b41b589e5a040ba593bfa70725.png

2、RPN(Region Proposal Network)

处理流程

RPN在Faster-RCNN中作用为生成RoI,RPN的处理流程具体如下,一些细节将在之后介绍: 1. 输入为base_net提取出来的feature map,首先在feature map上生成锚框(anchor),其中每个cell有多个锚框。 2. 通过一个conv_3x3,stride=1,padding=1的卷积层,进一步提取特征,输出特征图的大小不变,这里称为rpn_feature。 3. 在rpn_feature上用两个1x1卷积层进行预测输出,分别为每个锚框的二分类分数、每个锚框的坐标偏移量。 4. 利用上面预测的分数以及偏移量,对锚框(anchor)进行非极大值抑制(NMS)操作,最终输出RoI候选区域

dfa88dc198e28d155e7b74f7b13f77a7.png

详细步骤及代码

在feature_map上生成锚框

这一步中,会在feature_map每个cell上生成一系列不同大小和宽高比例的锚框。生成锚框的方式如下: 1. 选定一个锚框的基准大小,记为base,比如为16 2. 选定一组宽高比例(aspect ratios),比如为【0.5、1、2】 3. 选定一组大小比例(scales),比如为【16、32、64】 4. 那么每个cell将会生成ratios*scales个锚框,而每个锚框的形状大小的计算公式如下:

widthanchor=sizebasetimesscaletimessqrt1/ratio
heightanchor=sizebasetimesscaletimessqrtratio
举个例子,我们按照论文中取3种大小比例以及3种长宽比例,那么每个cell生成的锚框个数为$k=9$,而假设我们的特征图大小为$Wtimes H=2400$,那么我们一共生成了$WHk$个锚框。可以看到,生成的锚框数量非常多,有大量的重复区域。RPN输出时不应该使用所有锚框,所以采用NMS 来去除大量重复的锚框,而只选择一些得分较高的锚框作为RoI输出。其实,RPN在训练时也进行了采样,这个后面具体介绍。RPN生成的锚框如下图所示:

c214f8857c0a03596f579e8530528ec3.png

MXNet中,生成锚框的类源码如下所示:

  1. class RPNAnchorGenerator(gluon.Block):
  2. """
  3. @输入参数
  4. stride:int
  5. 特征图的每个像素感受野大小,通常为原图和特征图尺寸比例
  6. base_size:int
  7. 默认大小
  8. ratios:int
  9. 宽高比
  10. scales:int
  11. 大小比例
  12. 每个锚框为 width = base_size*size/sqrt(ratio)
  13. height = base_size*size*sqrt(ratio)
  14. alloc_size:(int,int)
  15. 默认的特征图大小(H,W),以后每次生成直接索引切片
  16. """
  17. def __init__(self, stride, base_size, ratios, scales, alloc_size, **kwargs):
  18. super(RPNAnchorGenerator, self).__init__(**kwargs)
  19. if not base_size:
  20. raise ValueError("Invalid base_size: {}".format(base_size))
  21. # 防止非法输入
  22. if not isinstance(ratios, (tuple, list)):
  23. ratios = [ratios]
  24. if not isinstance(scales, (tuple, list)):
  25. scales = [scales]
  26. # 每个像素的锚框数
  27. self._num_depth = len(ratios) * len(scales)
  28. # 预生成锚框
  29. anchors = self._generate_anchors(stride, base_size, ratios, scales, alloc_size)
  30. self.anchors = self.params.get_constant('anchor_', anchors)
  31. @property
  32. def num_depth(self):
  33. return self._num_depth
  34. def _generate_anchors(self, stride, base_size, ratios, scales, alloc_size):
  35. # 计算中心点坐标
  36. px, py = (base_size - 1) * 0.5, (base_size - 1) * 0.5
  37. base_sizes = []
  38. for r in ratios:
  39. for s in scales:
  40. size = base_size * base_size / r
  41. ws = np.round(np.sqrt(size))
  42. w = (ws * s - 1) * 0.5
  43. h = (np.round(ws * r) * s - 1) * 0.5
  44. base_sizes.append([px - w, py - h, px + w, py + h])
  45. # 每个像素的锚框
  46. base_sizes = np.array(base_sizes)
  47. # 下面进行偏移量的生成
  48. width, height = alloc_size
  49. offset_x = np.arange(0, width * stride, stride)
  50. offset_y = np.arange(0, height * stride, stride)
  51. offset_x, offset_y = np.meshgrid(offset_x, offset_x)
  52. # 生成(H*W,4)
  53. offset = np.stack((offset_x.ravel(), offset_y.ravel(),
  54. offset_x.ravel(), offset_y.ravel()), axis=1)
  55. # 下面广播到每一个anchor中 (1,N,4) + (M,1,4)
  56. anchors = base_sizes.reshape((1, -1, 4)) + offset.reshape((-1, 1, 4))
  57. anchors = anchors.reshape((1, 1, width, height, -1)).astype(np.float32)
  58. return anchors
  59. # 对原始生成的锚框进行切片操作
  60. def forward(self, x):
  61. # 切片索引
  62. anchors = self.anchors.value
  63. a = nd.slice_like(anchors, x * 0, axes=(2, 3))
  64. return a.reshape((1, -1, 4))

用conv3x3卷积进一步提取特征图

这一步中就是RPN进一步抽取特征,生成的RPN-feature map提供给之后的类别预测和回归预测。该步骤中使用的是kernel_size=3x3,strides=1,padding=1,Activation='relu'的卷积层,不改变特征图的尺寸,这也是为了之后的1x1卷积层预测时,空间位置能够一一对应,而用通道数来表示预测的类别分数和偏移量。这一步的代码很简单,就是单独的构建了一个3x3 Conv2D的卷积层。

  1. # 第一个提取特征的3x3卷积
  2. self.conv1 = nn.Sequential()
  3. self.conv1.add(nn.Conv2D(channels, kernel_size=3, strides=1, padding&#
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/250155
推荐阅读
相关标签
  

闽ICP备14008679号