Openpcdet 系列 Pointpillar代码逐行解析之Voxel Feature Encoding (VFE)模块

OpenPCdet的 VFE模块

在这里插入图片描述

整个VFE模块包含这些不同的Voxel Feature
Encoding的模块,后续会逐一讲解,Pointpillar使用的是PillarVFE模块。

__all__ = {
    'VFETemplate': VFETemplate,
    'MeanVFE': MeanVFE,
    'PillarVFE': PillarVFE,
    'ImageVFE': ImageVFE,
    'DynMeanVFE': DynamicMeanVFE,
    'DynPillarVFE': DynamicPillarVFE,
    'DynamicPillarVFESimple2D': DynamicPillarVFESimple2D,
    'DynamicVoxelVFE': DynamicVoxelVFE,
}

PillarVFE代码讲解

pillar操作

PillarVFE模块是OpenPCDet(Open Point Cloud
Detection)中的一个特征编码模块,用于将点云数据转换为结构化的特征表示。它是PointPillars算法中的关键组件。
PointPillars是一种基于单张俯视图的点云目标检测算法,它将点云数据投影到二维的俯视图平面上,并使用PillarVFE模块对每个投影区域(称为pillar)进行特征编码。
PillarVFE模块的主要功能包括:

  1. 构建pillar:将点云数据投影到二维的俯视图平面,并将投影区域划分为多个小的正方形区域,称为pillar。每个pillar代表一个局部区域。
  2. 特征编码:对每个pillar中的点云数据进行特征编码。PillarVFE模块使用一个卷积神经网络(通常是多层的3D卷积操作)来提取pillar内点云的特征表示。这些特征表示捕捉了点云数据的局部结构信息。
  3. 特征聚合:PillarVFE模块还执行特征聚合操作,将每个pillar的特征表示合并为一个全局的特征表示。这样可以利用全局特征进行目标检测和定位。
    通过PillarVFE模块的特征编码和特征聚合操作,PointPillars算法能够有效地处理大规模点云数据并提取有用的特征表示,从而实现高效的点云目标检测。

代码实现

整个pillarVFE 操作的代码在

OpenPCDet/pcdet/models/backbones_3d/vfe/pillar_vfe.py

这段代码实现了PillarVFE模块的前向传播过程。我将逐行解释其功能和操作:

class PillarVFE(VFETemplate):
    def __init__(self, model_cfg, num_point_features, voxel_size, point_cloud_range, **kwargs):
        super().__init__(model_cfg=model_cfg)

这是PillarVFE类的定义,它继承自VFETemplate类。在初始化函数中,它接收模型配置(model_cfg)、点云特征的数量(num_point_features)、体素的大小(voxel_size)和点云范围(point_cloud_range)等参数。

        self.use_norm = self.model_cfg.USE_NORM
        self.with_distance = self.model_cfg.WITH_DISTANCE
        self.use_absolute_xyz = self.model_cfg.USE_ABSLOTE_XYZ

这些变量存储了模型配置中的一些标志位,用来控制特征编码过程中的不同选项。

        num_point_features += 6 if self.use_absolute_xyz else 3
        if self.with_distance:
            num_point_features += 1

根据标志位的设置,调整输入点云特征的数量。如果设置了"use_absolute_xyz"标志位,将会增加6个坐标特征(x、y、z的原始坐标和相对坐标),否则增加3个坐标特征。

如果设置了"with_distance"标志位,还会增加一个距离特征。

        self.num_filters = self.model_cfg.NUM_FILTERS
        assert len(self.num_filters) > 0
        num_filters = [num_point_features] + list(self.num_filters)

从模型配置中获取特征编码的卷积层的通道数配置。将输入点云特征的数量(num_point_features)作为第一个通道数,然后依次添加后续的通道数。

        pfn_layers = []
        for i in range(len(num_filters) - 1):
            in_filters = num_filters[i]
            out_filters = num_filters[i + 1]
            pfn_layers.append(
                PFNLayer(in_filters, out_filters, self.use_norm, last_layer=(i >= len(num_filters) - 2))
            )
        self.pfn_layers = nn.ModuleList(pfn_layers)

创建PFNLayer的列表,PFNLayer是特征编码模块的基本单元。根据通道数配置,构建多个PFNLayer,并添加到列表中。

        self.voxel_x = voxel_size[0]
        self.voxel_y = voxel_size[1]
        self.voxel_z = voxel_size[2]
        self.x_offset = self.voxel_x / 2 + point_cloud_range[0]
        self.y_offset = self.voxel_y / 2 + point_cloud_range[1]
        self.z_offset = self.voxel_z / 2 + point_cloud_range[2]

存储体素的大小和点云范围,并计算体素的中心偏移量。这些偏移量将在后续的特征编码中使用。

    def get_output_feature_dim(self):
        return self.num_filters[-1]

返回特征编码后的输出特征维度,即最后一个卷积层的通道数。

    def get_paddings_indicator(self, actual_num, max_num, axis=0):
        actual_num = torch.unsqueeze(actual_num, axis + 1)
        max_num_shape = [1] * len(actual_num.shape)
        max_num_shape[axis + 1] = -1
        max_num = torch.arange(max_num, dtype=torch.int, device=actual_num.device).view(max_num_shape)
        paddings_indicator = actual_num.int() > max_num
        return paddings_indicator

定义了一个辅助函数,用于生成一个指示填充位置的张量。根据输入的实际数量(actual_num)和最大数量(max_num),生成一个形状相同的张量,其中填充位置为False,其他位置为True。

    def forward(self, batch_dict, **kwargs):
        voxel_features, voxel_num_points, coords = batch_dict['voxels'], batch_dict['voxel_num_points'], batchdict['voxel_coords']

前向传播函数。接收一个批次的输入数据(batch_dict),包括体素特征(voxel_features)、体素中点的数量(voxel_num_points)和体素坐标(coords)。

        points_mean = voxel_features[:, :, :3].sum(dim=1, keepdim=True) / voxel_num_points.type_as(voxel_features).view(-1, 1, 1)
        f_cluster = voxel_features[:, :, :3] - points_mean

计算点云的平均位置(points_mean),并将每个点的坐标减去平均位置得到聚类特征(f_cluster)。

        f_center = torch.zeros_like(voxel_features[:, :, :3])
        f_center[:, :, 0] = voxel_features[:, :, 0] - (coords[:, 3].to(voxel_features.dtype).unsqueeze(1) * self.voxel_x + self.x_offset)
        f_center[:, :, 1] = voxel_features[:, :, 1] - (coords[:, 2].to(voxel_features.dtype).unsqueeze(1) * self.voxel_y + self.y_offset)
        f_center[:, :, 2] = voxel_features[:, :, 2] - (coords[:, 1].to(voxel_features.dtype).unsqueeze(1) * self.voxel_z + self.z_offset)

计算点云中心特征(f_center),通过将每个点的x、y、z坐标减去相应的体素偏移量得到。

        if self.use_absolute_xyz:
            features = [voxel_features, f_cluster, f_center]
        else:
            features = [voxel_features[..., 3:], f_cluster, f_center]

根据"use_absolute_xyz"标志位的设置,选择不同的特征组合方式。如果设置为True,将使用原始的点云特征、聚类特征和中心特征作为输入特征。否则,将只使用点云特征的后3个维度(排除坐标特征),并与聚类特征和中心特征一起作为输入特征。

        if self.with_distance:
            points_dist = torch.norm(voxel_features[:, :, :3], 2, 2, keepdim=True)
            features.append(points_dist)
        features = torch.cat(features, dim=-1)

如果设置了"with_distance"标志位,计算点云特征的欧几里得距离,并将其作为额外的特征添加到输入特征中。

最后,将所有的特征按最后一个维度拼接起来,形成最终的输入特征。

        voxel_count = features.shape[1]
        mask = self.get_paddings_indicator(voxel_num_points, voxel_count, axis=0)
        mask = torch.unsqueeze(mask, -1).type_as(voxel_features)
        features *= mask

根据体素中点的数量生成一个掩码(mask),并将其应用于输入特征。即将不包含点的位置的特征置为0。

        for pfn in self.pfn_layers:
            features = pfn(features)
        features = features.squeeze()
        batch_dict['pillar_features'] = features
        return batch_dict

通过遍历PFNLayer列表,对输入特征进行多层卷积操作,得到编码后的特征。最后,将编码后的特征保存在batch_dict中的’pillar_features’键下,并返回更新后的batch_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模块

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jack_Man_N

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值