pointrend代码(pointpillars代码)

pointrend代码(pointpillars代码)p p Pointpillars 论文 https arxiv org abs 1812 05784 代码 GitHub open mmlab OpenPCDet OpenPCDet Toolbox for LiDAR based 3D Object Detection 算法复现

大家好,我是讯享网,很高兴认识大家。



 <p></p> 

讯享网

Pointpillars论文:https://arxiv.org/abs/1812.05784

代码:GitHub - open-mmlab/OpenPCDet: OpenPCDet Toolbox for LiDAR-based 3D Object Detection.

算法复现:基于kitti数据集的3D目标检测算法的训练流程_mini kitti 数据集-CSDN博客

文献理解:PointPillars文献理解_pillar feature net-CSDN博客

复现结果:Pointpillar算法复现结果分析_kitti ap40 results-CSDN博客

参考博客:(三)PointPillars论文的MMDetection3D代码解读——数据处理篇_pointpillars代码-CSDN博客


目录

一、简介

二、配置文件

(一)kitti-3d-3class.py

(二)pointpillars_hv_secfpn_kitti.py

(三)cyclic-40e.py

(四)default_runtime.py

三、kitti数据集处理

(一)kitti_dataset.py

(二)基类Det3DDataset的parse_data_info()函数

(三)KittiDataset的parse_ann_info函数

(四)基类Det3DDataset的parse_ann_info()函数


PointPillars 是一个来自工业界的模型,整体的思想是基于图片的处理框架,直接将点云从俯视图的视角划分为一个个的立方柱体(Pillars),从而构成了伪图片数据,然后再使用2D检测框架进行特征提取和预测得到检测框,从而使得该模型在速度和精度都达到了一个很好的平衡。 PointPillars 的网络结构如图1.1所示。

代码存放于 mmdetection3d/config/_base_/datasets/kitti-3d-3class.py

代码全文如下:

讯享网

以下是分部分解读:

 
     

这里是kitti数据集的配置,其中数据类型是kitti的数据集(kittiDataset)、数据根目录在data/kitti、类别名称为“行人、骑行者、汽车”、第四行为点云范围、输入形式为激光雷达【未使用相机视觉】、metainfo是元信息,确定了类别名称。

讯享网

这里是kitti_dbinfos_train.pkl标注文件的读取,data_root定义数据存放的目录、info_path定义信息的路径。

 
     

在train_dataloader中定义了数据加载部分的信息,其中Batch_size(批处理大小)设置为6(即一次性处理6位数据),num_workers定义了工作区间的大小为4。

data_root定义数据集的路径、ann_file定义标注文件的路径、data_prefix定义点云文件的路径、pipeline这里用于定义train_pipeline负责读取点云文件和标注文件以及一些数据增强的操作。

train_pipeline定义如下:

讯享网

第一个dict字典用于读取点云文件,第二个dict用于读取标注文件,第三到第九个dict用于点云数据的增强,最后一个dict将点云数据进行打包。

在val_dataloader和test_dataloader中的pipeline都以test_pipeline赋值,下面看test_pipeline定义:

 
     

一共三个字典定义,第一个dict用于读取点云文件,第二个dict用于点云数据的增强,第三个dict将点云数据进行打包。

代码路径:mmdetection3d/config/_base_/models/pointpillars_hv_secfpn_kitti.py,全部代码:

讯享网

该代码对应了Pointpillars网络架构的六个步骤:


讯享网

 
     

这一部分对应步骤1,Det3DDataPreprocessor。3D数据的预处理,主要将点云数据进行体素化。

原始点云是三维的,具有(x,y,z)三个坐标轴的坐标,这一步将三维空间中的点云沿着z轴的方向离散到x-y平面上均匀分布的网格中,形成了一组pillars(也就是柱状体),并拥有相对应的索引向量(图中对应的pillar index),引入索引向量之后可以解决点云无序性的问题。需要注意的是:这里pillar是在z轴方向上具有无限范围空间的体素(体积像素 voxel),故而不需要超参数控制z反向的融合/合并。

此时原始点云中的点由三维(x,y,z)扩展成九维(x,y,z,r,xc,yc,zc,xp,yp),其中xyz仍然是空间中的xyz轴坐标,r表示该点的反射强度(reflection),下标c表示该点到整个pillar中所有点的算术平均值的距离,即,x0表示每个点原始的坐标值;下标p表示该点距离pillar中心xy的偏移量。

由于点云的稀疏性,绝大部分的pillar中都是空的,并且非空的pillar中通常也就只有几个点,而作者采用了非常巧妙的方法解决这个问题:通过加强每一个样本(P)中非空pillars的数量和每一个pillar(N)中点数的限制,构造了一个(D,P,N)的密集向量用于减少稀疏性。【即P为样本中非空pillars数量、N为每个pillar中点的数量、D为维度】pillar中点数大于N则随机采样,反之则使用零进行填充来限制点数的数量。

讯享网

这一块对应步骤二,pillarFeatureNet。主要用于提取体素特征、体素编码。

将1中生成的pillars采用简易版的PointNet,对于每一个点使用线性层,接下来是BatchNorm和Relu,生成规格为(C,P,N)的向量,之后再对N个通道进行最大池化操作(maxpooling)生成一个规格为(C,P)的输出向量。线性层可以表示为跨向量的1x1卷积故而可以实现高效率的计算。此时对N个点进行最大池化用于代表整个pillar的信息。

 
     

这里对应步骤3,将提取特征的点云形成伪图像。

将1中生成的索引和2中得到的规格为(C,P)的输出向量进行特征向量融合,形成伪图像。

即(P,C)+Pillar_index ——> (H,W,C),得到的是一个通道数位C的伪图像,从而可以使用最经典的图像网络进行处理。H和W分别代表图像的宽度和高度。

讯享网

这一块对应步骤4,主要使用2D bcakbone提取伪图像的特征用于步骤5。

 
     

这一块对应步骤5,使用的事SecondFPN结构进行多尺度特征融合。

主干网络有两个子网络:一个自上而下的网络以越来越小的空间分辨率生成特征,第二个网络执行以上采样以及自上而下特征的串联。最终输出的特征将用于检测头的3D边界框的预测。

自顶向下的网络可以用一系列块Block(S,L,F)表征,每个块以步长S(相对于输入伪图像测量)运行,每个块有L个3x3 2D conv层(卷积层)和F个输出通道,每个输出通道后都跟着一个BatchNorm和ReLU。层内第一个卷积步幅为用于确保该快在接受到步幅为的输入blob之后以步幅运行,块中后续卷积的步幅均为1。而最终输出特征是来自不同步长的所有特征的串联。

讯享网

最后这一块对应步骤6,主要使用Anchor3Dhead预测边界框。sizes数组记录了三种anchor,分别为预测的三种类别:行人、骑行者和汽车。loss_cls中分类预测使用的FocalLoss。边界框预测使用的SmoothL1Loss、角度二分类预测使用CrossEntropyLoss【未使用sigmoid作为激活函数,权重为0.2】。

 
     

这里定义了模型在训练中采取的策略,assigner中一共三个字典,分别定义了行人、骑行者和汽车三种预测类别的预测框和真实框进行匹配,然后基于匹配预测真值。

讯享网

最后是模型在测试中采用的策略。最后的max_num表示的是测试的时候对NMS的操作,最终只保留50个预测框。

代码存放路径:mmdetection3d/configs/_base_/schedules/cyclic-40e.py,代码如下:

 
     

开头的lr表示Learning rate,用于初始化学习率。一共有40个epoch,即运行40轮。

讯享网

这里是优化器的设置。

 
     

这里对学习率调度器进行设置,可以看到在前16个epoch中,学习率从0增加到lr*10,而在后24个epoch中,学习率从lr*10下降到lr*1e*-4。

在train_cfg中设置了max_epoch为40,即最大运行轮数为40。

代码存放路径:mmdetection3d/config/_base_/default_runtime.py,代码如下:

讯享网

在default_hooks中主要对默认hook进行设置。

存放路径: mmdetection3d/mmdet3d/datasets/kitti_dataset.py,重点关注kittidataset:

 
     

重点解读数据处理的部分:

讯享网

if self.modality['use_lidar']:

首先判断是否采用激光雷达,这里为True故进入第一个if选择结构。

if 'plane' in info:

接着判断原始数据info中是否使用了plane(轨道信息),这里为True故进入第二个if选择结构

plane = np.array(info['plane'])

之后通过np.array(info.[‘plane’])将列表info转换成Numpy数组plane;

lidar2cam = np.array(info['images']['CAM2']['lidar2cam'], dtype=np.float32)

将列表 转换成numpy数组 ;

reverse = np.linalg.inv(lidar2cam)

将Numpy数组lidar2cam进行矩阵求逆得到reverse数组;

(plane_norm_cam, plane_off_cam) = (plane[:3], -plane[:3] * plane[3])

通过Numpy数组plane得到两个新的数组plane_norm_cam和 plane_off_cam;

plane_norm_lidar =

                    (reverse[:3, :3] @ plane_norm_cam[:, None])[:, 0]

再接下来,通过numpy数组 reverse 的与numpy数组 plane_norm_cam的 矩阵相乘得到一个numpy数组 ,然后再通过索引 [:, 0]取出第0列的数据组成一个新的numpy数组plane_norm_lidar ;

plane_off_lidar = (

                    reverse[:3, :3] @ plane_off_cam[:, None][:, 0] +

                    reverse[:3, 3])

通过numpy数组 reverse 的 与numpy数组 plane_off_cam的  矩阵相乘得到一个numpy数组。 ,然后再通过索引 [:, 0]取出第0列的数据组成一个新的numpy数组,再加上numpy数组 reverse 取出第3列的前3个组成的数组,最终得到一个新的numpy数组 plane_off_lidar;

plane_lidar = np.zeros_like(plane_norm_lidar, shape=(4, ))

创建一个形状为 (4, ),类型与  相同的全 0 的numpy数组 ;

plane_lidar[:3] = plane_norm_lidar

通过 plane_norm_lidar  来设置 plane_lidar 的前3个值;

plane_lidar[3] = -plane_norm_lidar.T @ plane_off_lidar

通过负的 plane_off_lidar 转置矩阵  与 plane_norm_lidar  进行矩阵相乘得到的 来设置 plane_lidar 的第4个值;

else:

                plane_lidar = None

此处如果我们的 ,所以进入 else 选择结构,将  设置为 None;

info['plane'] = plane_lidar

通过前面得到的  来重新设置 ;

if self.load_type == 'fov_image_based' and self.load_eval_anns:

            info['instances'] = info['cam_instances'][self.default_cam_key]

这里我们的  ,if 条件不成立,所以跳过后面的语句;

info = super().parse_data_info(info)

调用基类 Det3DDataset 的  初始化 info 的其它数据。

 
     

首先还是,所以进入第一个 if 选择结构;接下来将  设置为 info添加新字段 info添加新字段 ; ,if 条件不成立,所以跳过后面的语句; ,if 条件不成立,所以跳过后面的语句; ,if not 条件成立,所以进入后面的语句;调用派生类 KittiDataset 的  初始化 info[‘ann_info’];

调用基类 Det3DDataset 的  初始化 ann_info;

首先设置一个字典 ;再通过 ,info[‘instances’]是一个字典组成的列表,每个字典包含单个实例的所有标注信息。 ,if 条件不成立,所以跳过 if 后面的语句,进入 else 后面的语句;通过 ;接下来将 ;

之后进入 for 循环,这里以  进行举例,其它的字段以此类推;

  • 通过 
  •  ,if 条件不成立,所以跳过 if 后面的语句;
  •  ,if 条件成立,所以进入 if 后面的语句;
  • 通过 ;
  •  ,if 条件不成立,所以跳过 if 后面的语句;
  •  ,if 条件成立,所以进入 if 后面的语句;
  • 通过 ;
  • 在;

遍历完整个 for 循环后,最终得到的 

接下来,在 ;

进入 for 循环,;

  •  ,if 条件成立,所以进入 if 后面的语句;
  • 通过 ;
  • 将 ,更新前后的值如下图所示;

遍历完整个 for 循环,由于这里第一张图片中只有一个人存在,因此重要 ;最后返回得到的 ann_info 给前面调用的函数中;

之后还会再次返回KittiDataset的parse_ann_info()函数和基类Det3DDataset的parse_ann_info函数中,详细细节可以参考这篇博客:(三)PointPillars论文的MMDetection3D代码解读——数据处理篇_pointpillars代码-CSDN博客


小讯
上一篇 2025-04-26 17:31
下一篇 2025-04-21 17:37

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/182663.html