当前位置:   article > 正文

Openpcdet 系列 Pointpillar代码逐行解析之检测头(DENSE_HEAD)模块_pointpillar 检测头 18

pointpillar 检测头 18

Openpcdet 的DENSE_HEAD模块

在这里插入图片描述

整个OpenPCdet的Dense head模块包含下面这些模块。Pointpillar使用的AnchorHeadSingle
模块

__all__ = {
    'AnchorHeadTemplate': AnchorHeadTemplate,
    'AnchorHeadSingle': AnchorHeadSingle,
    'PointIntraPartOffsetHead': PointIntraPartOffsetHead,
    'PointHeadSimple': PointHeadSimple,
    'PointHeadBox': PointHeadBox,
    'AnchorHeadMulti': AnchorHeadMulti,
    'CenterHead': CenterHead,
    'VoxelNeXtHead': VoxelNeXtHead,
    'TransFusionHead': TransFusionHead,
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Pointpillar Dense Head配置文件

NAME: AnchorHeadSingle: 这是头部网络的名称,可能指明了这个头部的类型或特征。

CLASS_AGNOSTIC: False: 这表示头部网络不是“类别不可知”的,也就是说,它在处理不同类别的目标时会考虑类别信息。

USE_DIRECTION_CLASSIFIER: True: 这指示网络是否使用方向分类器来预测目标的朝向。

DIR_OFFSET, DIR_LIMIT_OFFSET, NUM_DIR_BINS:
这些参数涉及到方向分类器的设置,比如方向偏移、方向限制偏移和方向的分箱数量等。

ANCHOR_GENERATOR_CONFIG: 这是一个列表,包含了针对不同类别的锚点生成器的配置信息。每个类别都有自己的配置信息,包括:

class_name: 目标类别的名称,比如 ‘Car’(车辆)、‘Pedestrian’(行人)、‘Cyclist’(骑行者)。
anchor_sizes: 锚点的尺寸,以列表形式给出,每个列表表示一个锚点的长、宽、高。 anchor_rotations:
锚点的旋转角度,通常用于处理物体可能的不同朝向。 anchor_bottom_heights: 锚点底部的高度,这是与地面的距离。
align_center: 是否对齐物体中心。 feature_map_stride: 特征图步长,与生成锚点的网络结构相关。
matched_threshold, unmatched_threshold: 匹配和不匹配的阈值,用于锚点和实际目标之间的匹配。

DENSE_HEAD:
        NAME: AnchorHeadSingle
        CLASS_AGNOSTIC: False

        USE_DIRECTION_CLASSIFIER: True
        DIR_OFFSET: 0.78539
        DIR_LIMIT_OFFSET: 0.0
        NUM_DIR_BINS: 2

        ANCHOR_GENERATOR_CONFIG: [
            {
                'class_name': 'Car',
                'anchor_sizes': [[3.9, 1.6, 1.56]],
                'anchor_rotations': [0, 1.57],
                'anchor_bottom_heights': [-1.78],
                'align_center': False,
                'feature_map_stride': 2,
                'matched_threshold': 0.6,
                'unmatched_threshold': 0.45
            },
            {
                'class_name': 'Pedestrian',
                'anchor_sizes': [[0.8, 0.6, 1.73]],
                'anchor_rotations': [0, 1.57],
                'anchor_bottom_heights': [-0.6],
                'align_center': False,
                'feature_map_stride': 2,
                'matched_threshold': 0.5,
                'unmatched_threshold': 0.35
            },
            {
                'class_name': 'Cyclist',
                'anchor_sizes': [[1.76, 0.6, 1.73]],
                'anchor_rotations': [0, 1.57],
                'anchor_bottom_heights': [-0.6],
                'align_center': False,
                'feature_map_stride': 2,
                'matched_threshold': 0.5,
                'unmatched_threshold': 0.35
            }
        ]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

Dense head功能讲解

DENSE_HEAD 是指密集点云检测模型中的头部网络结构。密集点云检测旨在处理点云中更密集的数据,通常涉及更高密度的点云数据,需要更复杂的网络结构来有效地处理这些信息。

DENSE_HEAD 通常包含用于预测物体边界框、分类和其他属性的层。这些头部网络通常由密集的卷积和特征融合层组成,以从密集的点云数据中提取丰富的特征,并用于目标检测和分类任务。这种头部网络结构设计旨在更好地处理点云中的丰富信息,以提高物体检测和定位的准确性和鲁棒性。

Dense Head 之 AnchorHeadSingle代码讲解

class AnchorHeadSingle(AnchorHeadTemplate):
  • 1

这一行定义了一个名为 AnchorHeadSingle 的类,并且这个类继承自 AnchorHeadTemplate 类。

def __init__(self, model_cfg, input_channels, num_class, class_names, grid_size, point_cloud_range,
             predict_boxes_when_training=True, **kwargs):
  • 1
  • 2

这是类的初始化函数,它接受多个参数来配置和构建头部网络。

super().__init__(
    model_cfg=model_cfg, num_class=num_class, class_names=class_names, grid_size=grid_size, point_cloud_range=point_cloud_range,
    predict_boxes_when_training=predict_boxes_when_training
)
  • 1
  • 2
  • 3
  • 4

使用 super() 调用父类的初始化方法,并传递一系列参数来配置头部网络。

self.num_anchors_per_location = sum(self.num_anchors_per_location)
  • 1

这一行计算了每个位置的锚点数量之和,并将结果保存在 self.num_anchors_per_location 中。

self.conv_cls = nn.Conv2d(
    input_channels, self.num_anchors_per_location * self.num_class,
    kernel_size=1
)
  • 1
  • 2
  • 3
  • 4

创建了一个卷积层 self.conv_cls,用于预测每个锚点位置的类别概率。

self.conv_box = nn.Conv2d(
    input_channels, self.num_anchors_per_location * self.box_coder.code_size,
    kernel_size=1
)
  • 1
  • 2
  • 3
  • 4

创建了另一个卷积层 self.conv_box,用于预测每个锚点位置的边界框坐标。

if self.model_cfg.get('USE_DIRECTION_CLASSIFIER', None) is not None:
    self.conv_dir_cls = nn.Conv2d(
        input_channels,
        self.num_anchors_per_location * self.model_cfg.NUM_DIR_BINS,
        kernel_size=1
    )
else:
    self.conv_dir_cls = None
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

根据配置选择是否创建方向分类器的卷积层。

self.init_weights()
  • 1

调用 init_weights() 方法来初始化网络层的权重。

这段代码主要是初始化了网络结构,并创建了用于预测类别、边界框以及方向(如果启用)的卷积层。它提供了头部网络所需的基本设定和功能。

def init_weights(self):
    pi = 0.01
    nn.init.constant_(self.conv_cls.bias, -np.log((1 - pi) / pi))
    nn.init.normal_(self.conv_box.weight, mean=0, std=0.001)
  • 1
  • 2
  • 3
  • 4

这是 init_weights() 方法,用于初始化网络层的权重。它使用了 PyTorch 的初始化方法来设置权重。

  • self.conv_cls.bias 的初始化使用了常数初始化方法,根据逻辑来设置偏置的初始值。

  • self.conv_box.weight 使用了正态分布初始化方法,给权重设置了均值和标准差。

def forward(self, data_dict):
    spatial_features_2d = data_dict['spatial_features_2d']

    cls_preds = self.conv_cls(spatial_features_2d)
    box_preds = self.conv_box(spatial_features_2d)

    cls_preds = cls_preds.permute(0, 2, 3, 1).contiguous()  # [N, H, W, C]
    box_preds = box_preds.permute(0, 2, 3, 1).contiguous()  # [N, H, W, C]

    self.forward_ret_dict['cls_preds'] = cls_preds
    self.forward_ret_dict['box_preds'] = box_preds
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这是 forward() 方法,用于执行前向传播。它接收一个 data_dict 字典,其中包含了特征数据。

  • 从输入数据中提取了空间特征 spatial_features_2d

  • 将特征通过 self.conv_clsself.conv_box 卷积层进行处理,生成类别预测 cls_preds 和边界框预测 box_preds

  • 调整预测结果的形状,以适应后续处理的需要,并将结果保存在 self.forward_ret_dict 字典中。

if self.conv_dir_cls is not None:
    dir_cls_preds = self.conv_dir_cls(spatial_features_2d)
    dir_cls_preds = dir_cls_preds.permute(0, 2, 3, 1).contiguous()
    self.forward_ret_dict['dir_cls_preds'] = dir_cls_preds
else:
    dir_cls_preds = None
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

如果启用了方向分类器,将空间特征通过 self.conv_dir_cls 卷积层处理,生成方向预测 dir_cls_preds

if self.training:
    targets_dict = self.assign_targets(
        gt_boxes=data_dict['gt_boxes']
    )
    self.forward_ret_dict.update(targets_dict)
  • 1
  • 2
  • 3
  • 4
  • 5

如果处于训练状态,调用 self.assign_targets 方法为每个锚点位置分配目标,并将结果更新到 self.forward_ret_dict 中。

以上是前向传播的一部分,处理了预测和目标分配(在训练时)的逻辑。接下来的代码段将处理预测框的生成和输出。

if not self.training or self.predict_boxes_when_training:
    batch_cls_preds, batch_box_preds = self.generate_predicted_boxes(
        batch_size=data_dict['batch_size'],
        cls_preds=cls_preds, box_preds=box_preds, dir_cls_preds=dir_cls_preds
    )
    data_dict['batch_cls_preds'] = batch_cls_preds
    data_dict['batch_box_preds'] = batch_box_preds
    data_dict['cls_preds_normalized'] = False
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这段代码逻辑用于生成预测的框,它将根据网络的预测情况进行不同的处理:

  • 如果不处于训练状态,或者在训练时需要预测框 (predict_boxes_when_training=True),则执行以下操作:
    • 调用 self.generate_predicted_boxes 方法生成预测的类别和边界框。
    • 将生成的类别预测 batch_cls_preds 和边界框预测 batch_box_preds 存储到 data_dict 中。
    • 设置 cls_preds_normalized 标志为 False,表示类别预测未经归一化。
return data_dict
  • 1

最后,将更新后的 data_dict 返回作为前向传播的结果。

这段代码的作用是在非训练状态下或者在训练时需要预测框时,生成预测结果,并将这些结果添加到 data_dict 中,然后将其作为输出返回。这些结果可能用于后续的评估或其他处理。

引用

Openpcdet 系列 Pointpillar代码逐行解析
OpenPCDet 环境安装
OpenPCDet KITTI数据加载过程 (Pointpillar模型)
Openpcdet 系列 Pointpillar代码逐行解析之Voxel Feature Encoding (VFE)模块
Openpcdet 系列 Pointpillar代码逐行解析之MAP_TO_BEV模块

Openpcdet 系列 Pointpillar代码逐行解析之BACKBONE_2D模块

Openpcdet 系列 Pointpillar代码逐行解析之检测头(DENSE_HEAD)模块

Openpcdet 系列 Pointpillar代码逐行解析之POST_PROCESSING模块

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
  

闽ICP备14008679号