您现在的位置是:首页 >其他 >OpenPCDet系列 | 5.3 PointPillars算法——BaseBEVBackbone伪图像特征提取模块网站首页其他

OpenPCDet系列 | 5.3 PointPillars算法——BaseBEVBackbone伪图像特征提取模块

Clichong 2024-06-17 10:25:02
简介OpenPCDet系列 | 5.3 PointPillars算法——BaseBEVBackbone伪图像特征提取模块

OpenPCDet的整个结构图:
在这里插入图片描述

BaseBEVBackbone模块

在进行了bev视图的特征转换后,随后进行backbone2d模块进行进一步的特征处理,在PointPillars中选择的是BaseBEVBackbone模块,在这一部分的模块选择上只有两个,如下所示:

# 根据MODEL中的BACKBONE_2D确定选择的模块
__all__ = {
    'BaseBEVBackbone': BaseBEVBackbone,
    'BaseBEVBackboneV1': BaseBEVBackboneV1
}

这部分具体的作用是对提取出来的bev特征矩阵进行进一步的提取,所以主要是模型上的处理,比较少特征上的处理。参考的结构可以看PointPillars论文上的图,从图上面来查看结构还是比较简单的:
在这里插入图片描述

此时的batch_dict如下所示,已经构建出来了spatial_feature,也就是上图所示的伪图像特征:

在这里插入图片描述


1. BaseBEVBackbone初始化

这里更加yaml文件来进行模型的搭建,包含了上采样block的重复次数,输出维度;以及下采样的输出维度和采样步长等设置。

BACKBONE_2D:
    NAME: BaseBEVBackbone
    LAYER_NUMS: [3, 5, 5]         # 每层下采样模块block的重复次数
    LAYER_STRIDES: [2, 2, 2]
    NUM_FILTERS: [64, 128, 256]   # 每层下采样channel维度
    UPSAMPLE_STRIDES: [1, 2, 4]   # 上采样步长,用于扩大特征尺寸
    NUM_UPSAMPLE_FILTERS: [128, 128, 128]   # 每个尺度的输出channel

更具配置文件设置的模型结构如下所示,需要注意的是下采样过程中存在一个ZeroPad2d的操作:

BaseBEVBackbone(
  # 下采样部分
  (blocks): ModuleList(
    (0): Sequential(
      (0): ZeroPad2d((1, 1, 1, 1))
      (1): Conv2d(64, 64, kernel_size=(3, 3), stride=(2, 2), bias=False)
      (2): BatchNorm2d(64, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
      (3): ReLU()
      (4): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (5): BatchNorm2d(64, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
      (6): ReLU()
      (7): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (8): BatchNorm2d(64, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
      (9): ReLU()
      (10): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (11): BatchNorm2d(64, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
      (12): ReLU()
    )
    (1): Sequential(
      (0): ZeroPad2d((1, 1, 1, 1))
      (1): Conv2d(64, 128, kernel_size=(3, 3), stride=(2, 2), bias=False)
      (2): BatchNorm2d(128, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
      (3): ReLU()
      (4): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (5): BatchNorm2d(128, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
      (6): ReLU()
      (7): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (8): BatchNorm2d(128, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
      (9): ReLU()
      (10): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (11): BatchNorm2d(128, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
      (12): ReLU()
      (13): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (14): BatchNorm2d(128, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
      (15): ReLU()
      (16): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (17): BatchNorm2d(128, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
      (18): ReLU()
    )
    (2): Sequential(
      (0): ZeroPad2d((1, 1, 1, 1))
      (1): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), bias=False)
      (2): BatchNorm2d(256, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
      (3): ReLU()
      (4): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (5): BatchNorm2d(256, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
      (6): ReLU()
      (7): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (8): BatchNorm2d(256, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
      (9): ReLU()
      (10): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (11): BatchNorm2d(256, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
      (12): ReLU()
      (13): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (14): BatchNorm2d(256, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
      (15): ReLU()
      (16): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
      (17): BatchNorm2d(256, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
      (18): ReLU()
    )
  )
  # 上采样部分
  (deblocks): ModuleList(
    (0): Sequential(
      (0): ConvTranspose2d(64, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
      (1): BatchNorm2d(128, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
      (2): ReLU()
    )
    (1): Sequential(
      (0): ConvTranspose2d(128, 128, kernel_size=(2, 2), stride=(2, 2), bias=False)
      (1): BatchNorm2d(128, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
      (2): ReLU()
    )
    (2): Sequential(
      (0): ConvTranspose2d(256, 128, kernel_size=(4, 4), stride=(4, 4), bias=False)
      (1): BatchNorm2d(128, eps=0.001, momentum=0.01, affine=True, track_running_stats=True)
      (2): ReLU()
    )
  )
)

这里的ZeroPad2d操作其实就是在特征矩阵的上下左右各打了一层0的补丁来填充,这样进行3x3步长为2的卷积时特征图的尺寸就是直接的减半操作。而且,在nn.Conv中也是有padding这个参数的,所以以下两种代码是等价的:

# 第一种
(0): ZeroPad2d((1, 1, 1, 1))
(1): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), bias=False)
# 第二种
(1): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), padding=1, bias=False)

2. BaseBEVBackbone前向传播

在传入BaseBEVBackbone模块是的batch_dict数据格式如下所示,后续的操作就是对spatial_features进行特征处理。

在这里插入图片描述

在对原始的spatial_features特征进行3层block的分别得到的特征结果以及对进行上采样的结果分别如下所示:

在这里插入图片描述

随后,将上采样的ups列表中的3个不同尺度处理的特征图进行concat拼接在一起,得到了(16, 384, 246, 216)的特征矩阵,这个特征矩阵就是backbone_2d提取到的特征矩阵,这与论文中的backbone_2d结构图是完全一样的,然后不同的是第一个block重复次数是3,第二和第三个block的重复次数都是5,在yaml文件中可以自行配置。

在这里插入图片描述

将此拼接后的特征同样保存在batch_dict中,传入到pointpillars算法中的最后一个模块,就是dense_head部分,此时的data_dict更新情况如下所示:

在这里插入图片描述

这部分的模型处理代码比较简单,就不贴上来了。对着结构图就可以很容易的实现模型的搭建以及前向传播过程。


风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。