FasterRCNN目标检测

发布时间:2023年12月22日

R-CNN

在这里插入图片描述

四个步骤:

  1. 对输入图片提取候选区(region proposal),每张大约2000个。论文中采用selective search的方法。
  2. 对每个候选区采用CNN网络提取特征。此处需要将proposal的尺寸缩放成统一的227x227,以匹配CNN网络。最终提取到的特征展平处理,长度为4096。
  3. 类别判断。将所提特征送入每一类的SVM分类器,判断是否属于该类。
  4. 候选框位置回归。

Fast RCNN

在这里插入图片描述

Fast RCNN相比于RCNN改进了3点:

  1. 共享卷积:将整幅图输出卷积神经网络,得到特征图,而不是像RCNN那样一个一个的候选区域输入卷积神经网络。
  2. ROI Pooling:将最后一个卷积层的SSP Layer改为RoI Pooling Layer,利用特征池化的方法,进行特征尺度变换,这样可以有任意大小的图片输入。
  3. 多任务损失函数:将边框回归直接加入到CNN网络中训练,同时包含了候选区域分类损失和位置回归损失。

Faster RCNN

在这里插入图片描述

  1. Conv layers。作为一种CNN网络目标检测方法,Faster RCNN首先使用一组基础的conv+relu+pooling层提取image的feature maps。该feature maps被共享用于后续RPN层和全连接层。
  2. Region Proposal Networks。RPN网络用于生成region proposals。该层通过softmax判断anchors属于positive或者negative,再利用bounding box regression修正anchors获得精确的proposals。
  3. Roi Pooling。该层收集输入的feature maps和proposals,综合这些信息后提取proposal feature maps,送入后续全连接层判定目标类别。
  4. Classification。利用proposal feature maps计算proposal的类别,同时再次bounding box regression获得检测框最终的精确位置。
    在这里插入图片描述

在这里插入图片描述

作为 torchvision 中目标检测基类,GeneralizedRCNN 继承了 torch.nn.Module,后续 FasterRCNN 、MaskRCNN 都继承 GeneralizedRCNN。

4个步骤:

  1. 将 transform 后的图像输入到 backbone 模块提取特征图

    #GeneralizedRCNN.forward(…)
    features = self.backbone(images.tensors)
    backbone 一般为 VGG、ResNet、MobileNet 等网络。

  2. 然后经过 rpn 模块生成 proposals 和 proposal_losses

    #GeneralizedRCNN.forward(…)
    proposals, proposal_losses = self.rpn(images, features, targets)

  3. 接着进入 roi_heads 模块(即 roi_pooling + 分类)

    #GeneralizedRCNN.forward(…)
    detections, detector_losses = self.roi_heads(features, proposals, images.image_sizes, targets)

  4. 最后经 postprocess 模块(进行 NMS,同时将 box 通过 original_images_size映射回原图)
    #GeneralizedRCNN.forward(…)
    detections = self.transform.postprocess(detections, images.image_sizes, original_image_sizes)

在这里插入图片描述

对于普通的 FasterRCNN 只需要将 feature_map 输入到 rpn 网络生成 proposals 即可。但是由于加入 FPN,需要将多个 feature_map 逐个输入到 rpn 网络。

在这里插入图片描述

class AnchorGenerator(nn.Module):
    def generate_anchors(self, scales, aspect_ratios, dtype=torch.float32, device="cpu"):
    def set_cell_anchors(self, dtype, device):

首先,每个位置有 5 种 anchor_size 和 3 种 aspect_ratios,所以每个位置生成 15 个 base_anchors:

[ -23., -11., 23., 11.] # w = h = 32, ratio = 2
[ -16., -16., 16., 16.] # w = h = 32, ratio = 1
[ -11., -23., 11., 23.] # w = h = 32, ratio = 0.5
[ -45., -23., 45., 23.] # w = h = 64, ratio = 2
[ -32., -32., 32., 32.] # w = h = 64, ratio = 1
[ -23., -45., 23., 45.] # w = h = 64, ratio = 0.5
[ -91., -45., 91., 45.] # w = h = 128, ratio = 2
[ -64., -64., 64., 64.] # w = h = 128, ratio = 1
[ -45., -91., 45., 91.] # w = h = 128, ratio = 0.5
[-181., -91., 181., 91.] # w = h = 256, ratio = 2
[-128., -128., 128., 128.] # w = h = 256, ratio = 1
[ -91., -181., 91., 181.] # w = h = 256, ratio = 0.5
[-362., -181., 362., 181.] # w = h = 512, ratio = 2
[-256., -256., 256., 256.] # w = h = 512, ratio = 1
[-181., -362., 181., 362.] # w = h = 512, ratio = 0.5

由于有 FPN 网络,所以输入 rpn 的是多个特征。为了方便介绍,以下都是以某一个特征进行描述,其他特征类似。

假设有h * w的特征,首先会计算这个特征相对于输入图像的下采样倍数 stride:
比如原图800*800大小的图片,输出特征图50*50,stride则为800/50=16
在这里插入图片描述
然后生成一个h*w大小的网格,每个格子长度为 stride,如下图:
在这里插入图片描述

#AnchorGenerator.grid_anchors(...)
shifts_x = torch.arange(0, grid_width, dtype=torch.float32, device=device) * stride_width
shifts_y = torch.arange(0, grid_height, dtype=torch.float32, device=device) * stride_height
shift_y, shift_x = torch.meshgrid(shifts_y, shifts_x)

然后将 base_anchors 的中心从(0,0)移动到网格的点,且在网格的每个点都放置一组 base_anchors。这样就在当前 feature_map 上有了很多的 anchors。
stride 代表网络的感受野,网络不可能检测到比 feature_map 更密集的框了!所以才只会在网格中每个点设置 anchors(反过来说,如果在网格的两个点之间设置 anchors,那么就对应 feature_map 中半个点,显然不合理)。
在这里插入图片描述

#AnchorGenerator.grid_anchors(...)
anchors.append((shifts.view(-1, 1, 4) + base_anchors.view(1, -1, 4)).reshape(-1, 4))

#RPN

  1. 如果输入3*600*800的图像,经过Backbone网络,降采样率为16,输出512*37*50(600/16=37.5,800/16=50)的特征图feature map.

  2. 经过RPNhead的3*3卷积神经网络维度还是512*37*50,对于分类网络分支,1x1卷积输出18*37*50的特征,对于回归网络分支,1x1卷积输出36*37*50的数据。
    18是因为在RPN中的分类网络部分只是判断有无物体,即正负样本,不进行物体类别分类,所以分类结果只有两种(0和1),若每个特征点生成9个锚框Anchor,2*9=18.
    回归网络中每个anchor对应四个数据,tx、ty、tw、th,因此9x4=36。

  3. RPN中的AnchorGenerator 生成37*50*9=16650个锚框Anchors,有一些锚框超出了边界,因此保留边框内部部分。

  4. RPN中分类网络部分:通过计算Anchor与标签的IOU来判断是正样本还是负样本,具体实现时,需要计算每一个Anchor与每一个标签的IOU,因此会得到一个IOU矩阵。判断标准如下:

     (1) 对于任何一个Anchor,与所有标签的最大IOU小于0.3,则视为负样本。
     (2) 对于任何一个标签,与其有最大IOU的Anchor视为正样本。
     (3) 对于任何一个Anchor,与所有标签的最大IOU大于0.7,则视为正样本。
    

    由于Anchor的数量接近2万,并且大部分时背景,因此需要筛选一部分,默认选择256个Anchor。

  5. RPN的回归偏移真值:得到anchor相对于target框的偏移量后,保存在bbox_tarets中,损失函数通常使用smooth函数,smooth函数结合了1阶与2阶损失函数,原因在于,当预测偏移量与真值差距较大时,使用2阶函数时导数太大,模型容易发散而不容易收敛,因此大于1时采用导数较小的1阶段损失函数。

  6. RPN的非极大值抑制(NMS)将重叠的框去掉,最后再次根据RPN的预测得分选择前2000个,作为最终的Proposal,输出到下一阶段。这里得到2000个候选框还是太多,继续Select Sample,筛选出的正负样本总数为256,筛选标准如下:

     (1)对于任何一个proposal,其与所有标签的最大IOU如果大于等于0.5,则视为正样本。
     (2)对于任何一个proposal,其与所有标签的最大IOU如果大于等于0且小于0.5,则视为负样本。
    

    为了控制正负样本比例基本满足1:3,在此正样本数量不超过64,超过了则随机选取64个。

ROI Pooling

  1. 从RPN得到了256个ROI,以及每一个ROI对应的类别与偏移量真值,为了计算损失,还需要计算每一个ROI的预测量。由于ROI是由各种大小高宽不同的Anchor经过偏移修正、筛选得到的,但后续的RCNN全连接网络部分要求输入特征大小维度固定。Pooling的过程就是获得7*7大小区域的特征。
  2. 如输入原图像600*800,ROI为332*332,对应原图的一块子区域,经过下采样率为16的Backbone,特征图上ROI对应区域为20*20大小(332/16=20.75),然后为了获得7x7的特征,20/7=2.857,对ROI对应的20*20区域进行步长为2的最大池化。

全连接RCNN模块

  1. 在经过ROIPooling层之后,特征被池化到了固定的维度,因此接下来可以利用全连接网络进行分类与回归预测量的计算。经过池化后的256个ROI维度为256*512*7*7,作展平处理为256*25088,两个全连接网络输出256*4096,再分别进入分类网络分支和回归网络分支,得到256*21的分类输出和256*84的回归网络输出
  2. RCNn部分的损失函数计算方法与RPN相似,只不过分类类别有2变为21,回归则是至多有64个正样本参与回归计算,负样本不参与回归计算。

代码

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

网络输出
2. RCNn部分的损失函数计算方法与RPN相似,只不过分类类别有2变为21,回归则是至多有64个正样本参与回归计算,负样本不参与回归计算。

代码

[外链图片转存中…(img-ujOc2x2C-1699491243408)]

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

文章来源:https://blog.csdn.net/tao_sc/article/details/134302826
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。