PointPillar 3D目标检测模型详解
一、参考资料
pointpillars 论文
pointpillars 论文
PointPillars - gitbook_docs
使用 NVIDIA CUDA-Pointpillars 检测点云中的对象
3D点云 (Lidar)检测入门篇 - PointPillars PyTorch实现
模型部署入门教程(三):PyTorch 转 ONNX 详解
PointPillar代码解析-OpenPCDet
pointpillars deployment 学习
模型部署——pointpillars转一个onnx
二、重要说明
pointpillars算法最突出的是提出一种柱形的编码功能,点云依然采取常用的体素组织起来。VoxelNet 直接采用体素3D卷积,SECOND采用稀疏卷积,pointpillars采用pillar方式转换成为2D卷积来加深网络,以此来提高效率与精度。至于后面接SSD还是RPN等网络,只是相对于2d卷积下的网络根据应用场景与需求来进行选取。
三、相关介绍
1. 3D目标检测
3D检测论文阅读简记
自动驾驶中基于Lidar的object检测,简单的说,就是从3D点云数据中定位到object的框和类别。具体地,输入是点云 $X∈R^{N×c} (一般c=4),输出是n个检测框bboxes,以第i个检测框bbox为例,它==包括∗∗位姿信息∗∗(一般 c=4 ),输出是 n 个检测框bboxes, 以第 i 个检测框bbox为例, 它==包括**位姿信息**(一般c=4),输出是n个检测框bboxes,以第i个检测框bbox为例,它==包括∗∗位姿信息∗∗(x_i,y_i,z_i,w_i,l_i,h_i,θ_i) $和类别信息 (labeli,scorei)(label_i,score_i)(labeli,scorei)。==
基于Lidar的object检测模型包括:Point-based,Voxel-base,Point-Voxel-based,Multi-view-based。
1.1 Point-based
经典模型:PointNet,PointNet++,[PointRCNN(CVPR19),IA-SSD(CVPR22)等]。
基于 Point-based 的模型,直接对点云进行处理,可以减少位置信息的损失,但同时也带来了巨大的计算资源消耗,使其很难做到实时。
1.2 Voxel-based
经典模型:[PointPillars(CVPR19),CenterPoint(CVPR21)等]。
基于 Voxel-base 的模型,相较于 Point-base 的模型在推理速度上有所提升,但是由于模型中使用了三维卷积的 backbone,所以也仍然很难做到实时。
相较于其他的模型,PointPillars 在推理速度方面有着明显的优势(遥遥领先),同时又能保持着不错的准确性。
1.3 Point-Voxel-based
经典模型:[PV-RCNN(CVPR20),HVPR(CVPR21)等]。
1.4 Multi-view-based
经典模型:[PIXOR(CVPR18)等]。
2. voxelization(体素化)
2.1 体素化简介
voxel-base 的模型中常常使用 voxelization(体素化)。在实际使用过程中我们都希望我们的模型又快有准,所以为了可以权衡速度和精度,VoxelNet 提出了使用 voxelization(体素化)的方法来处理点云。
点云是三维空间中的物体表示,因此一个自然的思路是将空间在长宽高三个方向划分格子,每个格子称为 voxel(体素),通过处理将其转换为 3 维数组的形式,再使用 3D 卷积和 2D 卷积的网络处理,如下图所示:
2.2 体素化存在的问题
体素化也会带来一些问题,例如不可避免的会造成一些信息的丢失,对体素参数较为敏感,以及转换成 3 维数组后提取特征时通常需要用到 3 维卷积。3 维的卷积是一个相当耗时的操作,所以当我们设置体素化的粒度过大时会导致较多的信息丢失,但如果粒度过小又会导致计算时间几何增加。
2.3 改进体素化
PointPillars 在 VoxelNet 中的 voxel 的基础上提出了一种改进版本的点云表征方法 pillar,可以将点云转换成伪图像的形式,进而通过 2D 卷积实现目标检测,相较于 VoxelNet 将点云转换成 voxel 形式然后使用相当耗时的 3 维卷积来处理特征,PointPillars 这种使用 2 维卷积的网络在推理速度上有很大的优势。
什么是 pillar?原文中的描述是“ a pillar is a voxel with unlimited spatial extent in the z direction ”,其实很简单,将空间的 x,y 轴两个方向上划分格子,然后再将每个格子在 z 轴上拉伸,使其可以覆盖整个空间 z 轴,就可以得到一个 pillar,且空间中的每个点都可以划分到某个 pillar 中。
3. 点云配准
点云批准论文
4. 相机标定
3D雷达与相机的标定方法详细教程
5. 体素特征编码层(VFE)
VFE(Voxel Feature Encode),体素特征编码层,其实是简化版的pointnet。
四、PointPillar相关介绍
【模型加速】PointPillars模型TensorRT加速实验(1)
【模型加速】PointPillars模型TensorRT加速实验(2)
【模型加速】PointPillars模型TensorRT加速实验(3)
1. 问题引入(背景)
3D卷积太昂贵了,不适合边缘设备部署。
PointPillar延续了VoxelNet和SECOND的思路,VoxelNet的方法是直接用体素做3D卷积,SECOND用了稀疏卷积,而PointPillar使用了pillar的方式,直接转成2维卷积进行提速。
2. PointPillar贡献
PointPillars 是一个既简单又实用的模型,在保持较高精度的同时又有很高的推理速度,同时部署也很友好,是一个十分常用的模型。
- 提出了一种新的点云编码器和新网络pointpillar,实现对三维目标检测网络的端到端训练;
- 将三维点云处理为二维伪图像,用传统CNN对伪图像进行特征提取,推理速度显著提升,是其他方法(含3维卷积)的2-4倍。
3. PointPillar网络结构
整个算法逻辑包含3个部分:数据预处理,神经网络,后处理。其中神经网络部分,原论文将其结构描述为3个部分:
- PFN(Pillar Feature Net):将输入的点云转换为稀疏的伪图像的特征形式。
- Backbone(2D CNN):使用 2D 的 CNN 处理伪图像特征得到高维度的特征。
- Detection Head(SSD):检测和回归 3D 边界框。
在实际部署的时候,结构拆分和论文中的稍微有些出入。主要是分成PFN(Pillar Feature Network),MFN和RPN。其中MFN是用来将PFN提取的Pillar级的点云深度特征进一步转化为伪点云图像。RPN就是Backbone,而检测头的部分功能被包含在后处理的逻辑里面。
4. PFN模块
PFN(Pillar Feature Network)。
因为不同点云帧的点云数量是变化的,非空Pillar的数量自然也是不同的,在考虑将PFN导出为ONNX模型时,需要采用dynamic shape。
从PFN的8个输入可知,num_points表示每个Pillar包含的实际点云数量,这个轴是dynamic的。
- 首先将一个样本的点云空间划分成(在 X 轴方向上点云空间的范围 / pillar size,在 Y 轴方向上点云空间的范围 / pillar size)pillar 网格,样本中的点根据会被包含在各个 pillar 中,没有点的 pillar 则视为空 pillar。
- 假设样本中包含的非空 pillar 数量为 P,同时限制每个 pillar 中的点的最大数量为 N,如果一个 pillar 中点的数量不及 N,则用 0 补全,若超过 N,则从 pillar 内的点中采样出 N 个点来。并对 pillar 中的每个点进行编码,其中每个点的表示会包括点的坐标,反射强度,pillar 的几何中心,点与 pillar 几何中心的相对位置,将每个点的表示的长度记为 D。这样我们的一个点云样本就可以用一个(P,N,D)的张量来表示。
- 得到点云的 pillar 表示的张量后,我们对其进行处理提取特征,通过使用简化版的 PointNet 中的 SA 模块来处理每个 pillar。即先对每个 pillar 中的点使用多层 MLP 来使得每个点的维度从 D 变成 C,这样张量变成了(P,N,C),然后对每个 pillar 中的点使用 Max Pooling,得到每个 pillar 的特征向量,也使得张量中的 N 的维度消失,得到了(P,C)维度的特征图。
- 最后将(P,C)的特征根据 pillar 的位置展开成伪图像特征,将 P 展开为(H,W)。这样我们就获得了类似图像的(C,H,W)形式的特征表示。
总结:shape变化,(P,N,D) ——》(P,N,C)——》(P,C)——》(C,H,W)。
4.1 PFN的输入
PFN有8个输入:
- pillar_x:包含Pillar化后的点云x坐标,shape为(1,1,P,100);
- pillar_y:包含Pillar化后的点云y坐标,shape为(1,1,P,100);
- pillar_z:包含Pillar化后的点云z坐标, shape为(1,1,P,100);
- pillar_i:包含Pillar化后的点云强度值,shape为(1,1,P,100);
- num_points: 保存每个Pillar包含的实际点云数量,shape为(1,P);
- x_sub_shaped:保存Pillar的中心x坐标,shape为(1,1,P,100);
- y_sub_shaped:保存Pillar的中心y坐标,shape为(1,1,P,100);
- mask:pillar点云掩码,shape为(1,1,P,100);
#准备输入
pillar_x = torch.ones([1, 1, 9918, 100], dtype=torch.float32, device="cpu")
pillar_y = torch.ones([1, 1, 9918, 100], dtype=torch.float32, device="cpu")
pillar_z = torch.ones([1, 1, 9918, 100], dtype=torch.float32, device="cpu")
pillar_i = torch.ones([1, 1, 9918, 100], dtype=torch.float32, device="cpu")
num_points_per_pillar = torch.ones([1, 9918], dtype=torch.float32, device="cpu")
x_sub_shaped = torch.ones([1, 1, 9918, 100], dtype=torch.float32, device="cpu")
y_sub_shaped = torch.ones([1, 1, 9918, 100], dtype=torch.float32, device="cpu")
mask = torch.ones([1, 1, 9918, 100], dtype=torch.float32, device="cpu")
4.2 PFN的输出
PFN的输出shape为(1,64,pillar_num,1),pillar_num表示非空pillar的数量,是dynamic shape。因为不同点云帧的点云数量是变化的,非空Pillar的数量自然也是不同的。
5. MFN模块
MFN(Middle Feature Extractor Network)是用来将PFN提取的Pillar级的点云深度特征进一步转化为伪点云图像。
5.1 MFN的输入
MFN有2个输入,且都是dynamic的:
- voxel_features:voxel_features是PFN的输出,经过PFN后每个非空pillar用表示为一个64维的深度特征,其shape为(1,64,P,1),p为pillar的数量,是dynamic的;
- coords:pillar在x-y网格中的坐标,shape为(P,4),p为pillar的数量,是dynamic的;
5.2 MFN输出
MFN的输出spatial_features是一个固定尺寸(1,64,496,432)的特征图,是RPN网络的唯一的输入。
6. RPN(Backbone)模块
RPN的输入
RPN有一个输入,且是固定尺寸的。
rpn_input = torch.ones([1, 64, 496, 432], dtype=float_type, device=device)
torch.onnx.export(net.rpn, rpn_input, rpn_onnx_file, verbose=verbose)
print('rpn.onnx transfer success ...')
五、PointPillar源码解析
代码阅读 :SECOND pytorch版本
3D点云 (Lidar)检测入门篇 - PointPillars PyTorch实现
GitHub - zhulf0804/PointPillars: A Simple PointPillars PyTorch Implenmentation for 3D Lidar(KITTI) Detection.
GitHub - jjw-DL/OpenPCDet-Noted: OpenPCDet代码分析与注释
1. 重要说明
参数项 | 参数含义 |
---|---|
P | 非空 pillar 数量为P,训练集中最多16000, 测试集中最多40000 |
N | 每个 pillar 中存储的最大点云数量为 N,如果一个 pillar 中点的数量不及 N,则用 0 补全,若超过 N,则从 pillar 内的点中采样出 N 个点来 |
C | 每个pillar encoder后的channel数量为C |
D | 对 pillar 中的每个点进行编码,其中每个点的表示会包括点的坐标,反射强度,pillar 的几何中心,点与 pillar 几何中心的相对位置,将每个点的表示的长度记为 D |
(P,N,D) | 一个点云样本用一个(P,N,D)的张量表示 |
M | 32 |
2. 项目结构
second.pytorch --------|---images|---second ----|---apex|---torchplus |---builder|---configs|---core|---data|---framework|---kittiviewer|---protos|---pytorch ------|---builder|---spconv |---core|---utils |---models|---utils
- apex与spconv是进行second.pytorch安装的第三方依赖库;
- builder为基础网络的构建基础代码;
- configs为网络参数配置文件夹;
- core为基础功能文件夹,包括anchor、box_coder等些实现;
- data文件夹为数据处理模块;
- framework文件暂不清楚,好像是测试模块;
- kittiviewer很显然为可视化功能模块;
- protos模块读取内部的proto才构成py文件,具体不太清楚(理解后来填坑);
- pytorch文件夹为second.pytorch的核心,涉及训练、预测、网络等代码;
- utils为基础功能文件夹;
3. 代码流程
4. 坐标系转换
因为gt label中提供的bbox信息是Camera坐标系的,因此在训练时需要使用外参等将其转换到Lidar坐标系; 有时想要把3d bbox映射到图像坐标系中的2d bbox方便可视化,此时需要内参。具体转换关系如Figure 2。坐标系转换的代码见utils/process.py
。
5. 数据增强
数据增强应该是Lidar检测中很重要的一环。发现其与2D检测中的增强差别较大,比如3D中会做database sampling(我理解的是把gt bbox进行cut-paste), 会做碰撞检测等。在本库中主要使用了采用了5种数据增强, 相关代码在dataset/data_aug.py
。
- 采样gt bbox并将其复制到当前帧的点云
- 因为当前帧点云中objects(gt_bboxes)可能比较少, 不利于训练; 因此从Car, Pedestrian, Cyclist的database数据集中随机采集一定数量的bbox及inside points, 使当前帧中每类gt_bboxes的数量分别达到15, 10, 10.
- 但因为在实际情况中, gt_bboxes是没有overlap的(若存在overlap, 就表示有碰撞了); 因此需要将采样的bboxes先与当前帧点云中的gt_bboxes进行碰撞检测, 通过碰撞检测的bboxes和对应labels加到gt_bboxes_3d, gt_labels; 同时把当前帧点云中位于这些采样bboxes内的点删除掉, 替换成采样的bboxes(包括inside points).
- bbox 随机旋转平移
- 以某个bbox为例, 随机产生num_try个平移向量t和旋转角度r, 旋转角度可以转成旋转矩阵(mat).
- 对bbox进行旋转和平移, 找到num_try中第一个通过碰撞测试的平移向量t和旋转角度r(mat).
- 对bbox内部的点进行旋转和平移.
- 对bbox进行旋转和平移.
- 随机水平翻转
- points水平翻转
- bboxes水平翻转
- 整体旋转/平移/缩放
- object旋转, 缩放和平移
- point旋转, 缩放和平移
- 对points进行shuffle: 打乱点云数据中points的顺序。
Figure3是对上述前4种数据增强的可视化结果。
6. 网络结构
对于输入点云 X∈RN×4X∈R^{N×4}X∈RN×4 , PointPillars是如何一步步地得到bbox的呢 ? 相关代码见model/pointpillars.py
。
输入项 | 含义 | shape |
---|---|---|
voxels(pillars) | [20000, 32, 4] | |
coors(coors_batch) | [20000, 4] | |
num_points(npoints_per_pillars) | [20000] |
6.1 PillarLayer
Lidar的range是[0, -39.68, -3, 69.12, 39.68, 1], 即(xmin, ymin, zmin, xmax, ymax, zmax)。
- 基于预先设定好的voxel_size=(0.16, 0.16, 4), 将点云 X 中的 N 个点划分到(432, 496, 1)个Pillars里。
- voxel_size中的0.16是根据KITTI数据集和经验得到的,是先验值;h=4是因为point_range=[0, -39.68, -3, 69.12, 39.68, 1]中的[-3, 1],差值为4。
- (432, 496, 1)是根据point_range除以voxel_size得到的。
- 选择 P (训练集中最多16000, 测试集中最多40000)个Pillars, 并且每个Pillar选择 M=32 个点, 不足32个点时补(0, 0, 0, 0)。
- 数据shape的变化: (N,4) -> (P,M,4) , 同时记录这 P 个Pillars在(432, 496)的map中的位置(coors)和每个Pillar中有效点的数量(npoints_per_pillar)。
6.2 PillarEncoder
- 对每个Pillar中的点进行去均值编码: (P,M,4) -> (P,M,3)
- 对每个Pillar中有效点进行去中心编码: (P,M,4) -> (P,M,2)
- 合并编码: 将原始的 (P,M,4) 同去均值编码和去中心编码的结果进行cat, 得到 (P,M,9) 的向量。这里有两点需要注意: (1)每个Pillar中只对有效点(npoints_per_pillar)进行操作, 即(0, 0, 0, 0)还是保持(0, 0, 0, 0); (2)这应该是一个trick, 把9维编码向量中的前2维换成去中心编码的向量, 详情见https://github.com/open-mmlab/mmdetection3d/issues/1150。
- 进行embedding(卷积核池化): (P,M,9) -> (P,M,64) -> (P,64) 。
- Pillar scatter: 根据Pillars在map中的位置(coors), 将 P 个pillars的特征scatter到(432, 496)的特征图上(没有Pillar的位置补0向量), 得到 (64,496,432) 的特征图, 这里不妨记为 (C,H,W)。
- 数据shape的变化: (P,M,4) -> (C,H,W) 。
6.3 Backbone
- 在得到了 (C,H,W) 的特征图后, Backbone及接下来的Neck, Head都是在2D上进行操作了,基本是Conv2d + BN + ReLU的组合,所以接下来主要介绍tensor的shape变化。
- block1: (C,H,W) -> (C,H/2,W/2) , 即 (64,496,432) -> (64,248,216) 。
- block2: (C,H/2,W/2) -> (2∗C,H/4,W/4) , 即 (64,248,216) -> (128,124,108) 。
- block3: (2∗C,H/4,W/4) -> (4∗C,H/8,W/8) , 即 (128,124,108) -> (256,62,54) 。
- 数据shape的变化: (C,H,W) -> [(C,H/2,W/2),(2∗C,H/4,W/4),(4∗C,H/8,W/8)] 。
6.4 Neck
- decoder1: (C,H/2,W/2) -> (2∗C,H/2,W/2) 。
- decoder2: (2∗C,H/4,W/4) -> (2∗C,H/2,W/2) 。
- decoder3: (4∗C,H/8,W/8) -> (2∗C,H/2,W/2) 。
- cat: [(2∗C,H/2,W/2),(2∗C,H/2,W/2),(2∗C,H/2,W/2)] -> (6∗C,H/2,W/2) 。此时,得到的特征图为(384,248,216) 。
- 数据shape的变化: [(C,H/2,W/2),(2∗C,H/4,W/4),(4∗C,H/8,W/8)] -> (6∗C,H/2,W/2) 。
6.5 Head(Anchor3DHead)
- PointPillars共有3个不同尺寸的anchors(详情见2.2小节), 每个尺寸的anchor有2个角度, 因此共有6个anchors。网络训练了3个类别: Pedestrian, Cyclist和Car。
- 类别分类branch: (6∗C,H/2,W/2) -> (6∗3,H/2,W/2) , 即 (384,248,216) -> (18,248,216) 。
- bbox回归branch: (6∗C,H/2,W/2) -> (6∗7,H/2,W/2) , 即 (384,248,216) -> (42,248,216) 。
- 朝向分类branch: (6∗C,H/2,W/2) -> (6∗2,H/2,W/2) , 即 (384,248,216) -> (12,248,216) 。
- 数据shape的变化: (6∗C,H/2,W/2) -> [(6∗3,H/2,W/2),(6∗7,H/2,W/2),(6∗2,H/2,W/2)] 。
输出项 | 含义 | shape |
---|---|---|
bbox_pred(bbox_preds) | bbox回归 | [1, 42, 248, 216] |
bbox_cls_pred(cls_scores) | 类别分类 | [1, 18, 248, 216] |
bbox_dir_cls_pred(dir_cls_preds) | 朝向分类 | [1, 12, 248, 216] |
7. GT值生成
3D点云 (Lidar)检测入门篇 - PointPillars PyTorch实现
Head的3个分支是基于anchor分别预测了类别, bbox框(相对于anchor的偏移量和尺寸比)和旋转角度的类别, 那么在训练时, 如何得到每一个anchor对应的GT值呢 ? 相关代码见model/anchors.py
。
8. 损失函数
3D点云 (Lidar)检测入门篇 - PointPillars PyTorch实现
现在知道了类别分类head, bbox回归head和朝向分类head的预测值和GT值, 接下来介绍损失函数。相关代码见loss/loss.py
。
9. 单帧预测和可视化
3D点云 (Lidar)检测入门篇 - PointPillars PyTorch实现
基于Head的预测值和anchors, 如何得到最后的候选框呢 ? 相关代码见model/pointpillars.py
。
10. 模型评估
评估指标同2D检测类似, 也是采用AP, 即Precison-Recall曲线下的面积。不同的是, 在3D中可以计算3D bbox, BEV bbox 和 (2D bbox, AOS)的AP。
11. configs文件
car.fhd.config
model: {second: {network_class_name: "VoxelNet"# 体素生成voxel_generator {point_cloud_range : [0, -40, -3, 70.4, 40, 1] # 点云范围# point_cloud_range : [0, -32.0, -3, 52.8, 32.0, 1]voxel_size : [0.05, 0.05, 0.1] # 体素大小max_number_of_points_per_voxel : 5 # 每个体素的最大点数}# 体素特征提取器voxel_feature_extractor: {module_class_name: "SimpleVoxel"num_filters: [16]with_distance: falsenum_input_features: 4}# 中间特征提取器middle_feature_extractor: {module_class_name: "SpMiddleFHD"# num_filters_down1: [] # protobuf don't support empty list.# num_filters_down2: []downsample_factor: 8num_input_features: 4}# RPN网络rpn: {module_class_name: "RPNV2"layer_nums: [5]layer_strides: [1]num_filters: [128]upsample_strides: [1]num_upsample_filters: [128]use_groupnorm: falsenum_groups: 32num_input_features: 128}# 损失函数loss: {classification_loss: {weighted_sigmoid_focal: {alpha: 0.25gamma: 2.0anchorwise_output: true}}localization_loss: {weighted_smooth_l1: {sigma: 3.0code_weight: [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]}}classification_weight: 1.0localization_weight: 2.0}num_point_features: 4 # model's num point feature should be independent of dataset# Outputsuse_sigmoid_score: trueencode_background_as_zeros: trueencode_rad_error_by_sin: truesin_error_factor: 1.0use_direction_classifier: true # this can help for orientation benchmarkdirection_loss_weight: 0.2 # enough.num_direction_bins: 2direction_limit_offset: 1# Losspos_class_weight: 1.0neg_class_weight: 1.0loss_norm_type: NormByNumPositives# Postprocesspost_center_limit_range: [0, -40, -2.2, 70.4, 40, 0.8]nms_class_agnostic: false # only valid in multi-class nmsbox_coder: {ground_box3d_coder: {linear_dim: falseencode_angle_vector: false}}target_assigner: {class_settings: {anchor_generator_range: {sizes: [1.6, 3.9, 1.56] # wlhanchor_ranges: [0, -40.0, -1.00, 70.4, 40.0, -1.00] # carefully set z centerrotations: [0, 1.57] # DON'T modify this unless you are very familiar with my code.}matched_threshold : 0.6unmatched_threshold : 0.45class_name: "Car"use_rotate_nms: trueuse_multi_class_nms: falsenms_pre_max_size: 1000nms_post_max_size: 100nms_score_threshold: 0.3 # 0.4 in submit, but 0.3 can get better hard performancenms_iou_threshold: 0.01region_similarity_calculator: {nearest_iou_similarity: {}}}# anchor_generators: {# anchor_generator_stride: {# sizes: [1.6, 3.9, 1.56] # wlh# strides: [0.4, 0.4, 0.0] # if generate only 1 z_center, z_stride will be ignored# offsets: [0.2, -39.8, -1.00] # origin_offset + strides / 2# rotations: [0, 1.57] # DON'T modify this unless you are very familiar with my code.# matched_threshold : 0.6# unmatched_threshold : 0.45# }# }sample_positive_fraction : -1sample_size : 512assign_per_class: true}}
}#训练输入读取器,原batch_size=8,num_workers=3
train_input_reader: {dataset: {dataset_class_name: "KittiDataset"# kitti_info_path: "/media/yy/960evo/datasets/kitti/kitti_infos_train.pkl"# kitti_root_path: "/media/yy/960evo/datasets/kitti"kitti_info_path: "/home/cv/文档/datasets/KITTI_PP/kitti_infos_train.pkl"kitti_root_path: "/home/cv/文档/datasets/KITTI_PP"}batch_size: 8preprocess: {max_number_of_voxels: 17000shuffle_points: truenum_workers: 1groundtruth_localization_noise_std: [1.0, 1.0, 0.5]# groundtruth_rotation_uniform_noise: [-0.3141592654, 0.3141592654]# groundtruth_rotation_uniform_noise: [-1.57, 1.57]groundtruth_rotation_uniform_noise: [-0.78539816, 0.78539816]global_rotation_uniform_noise: [-0.78539816, 0.78539816]global_scaling_uniform_noise: [0.95, 1.05]global_random_rotation_range_per_object: [0, 0] # pi/4 ~ 3pi/4global_translate_noise_std: [0, 0, 0]anchor_area_threshold: -1remove_points_after_sample: truegroundtruth_points_drop_percentage: 0.0groundtruth_drop_max_keep_points: 15remove_unknown_examples: falsesample_importance: 1.0random_flip_x: falserandom_flip_y: trueremove_environment: false#数据库采样器database_sampler {# database_info_path: "/media/yy/960evo/datasets/kitti/kitti_dbinfos_train.pkl"database_info_path: "/home/cv/文档/datasets/KITTI_PP/kitti_dbinfos_train.pkl"sample_groups {name_to_max_num {key: "Car"value: 15}}database_prep_steps {filter_by_min_num_points {min_num_point_pairs {key: "Car"value: 5}}}database_prep_steps {filter_by_difficulty {removed_difficulties: [-1]}}global_random_rotation_range_per_object: [0, 0]rate: 1.0}}
}train_config: {optimizer: {adam_optimizer: {learning_rate: {one_cycle: {lr_max: 2.25e-3moms: [0.95, 0.85]div_factor: 10.0pct_start: 0.4}}weight_decay: 0.01}fixed_weight_decay: trueuse_moving_average: false}# steps: 99040 # 1238 * 120# steps: 49520 # 619 * 80# steps: 30950 # 619 * 80# steps_per_eval: 3095 # 619 * 5steps: 23200 # 464 * 50steps_per_eval: 2320 # 619 * 5save_checkpoints_secs : 1800 # half hoursave_summary_steps : 10enable_mixed_precision: false loss_scale_factor: -1clear_metrics_every_epoch: true
}#测试输入读取器,原batch_size=8,num_workers=3
eval_input_reader: {dataset: {dataset_class_name: "KittiDataset"# kitti_info_path: "/media/yy/960evo/datasets/kitti/kitti_infos_val.pkl"# # kitti_info_path: "/media/yy/960evo/datasets/kitti/kitti_infos_test.pkl"# kitti_root_path: "/media/yy/960evo/datasets/kitti"kitti_info_path: "/home/cv/文档/datasets/KITTI_PP/kitti_infos_val.pkl"# kitti_info_path: "/home/cv/文档/datasets/KITTI_PP/kitti_infos_test.pkl"kitti_root_path: "/home/cv/文档/datasets/KITTI_PP"}batch_size: 8preprocess: {max_number_of_voxels: 40000shuffle_points: falsenum_workers: 3anchor_area_threshold: -1remove_environment: false}
}
12. 总结
点云检测, 相比于点云中其它任务(分类, 分割和配准等), 逻辑和代码都更加复杂, 但这并不是体现在网络结构上, 更多的是体现在数据增强, Anchors和GT生成, 单帧推理等。
点云检测, 相比于2D图像检测任务, 不同的是坐标系变换, 数据增强(碰撞检测, 点是否在立方体判断等), 斜长方体框IoU的计算等; 评估方式因为考虑到DontCare, difficulty等, 也更加复杂一些。
六、关键步骤(CPU版本)
OpenPCDet
mmdetection3d
second.pytorch
PointPillars-TF
simple-pointpillar
PointPillars
nutonomy_pointpillars
可用版本
项目代码 | 版本 | 测试人 | 是否通过 |
---|---|---|---|
PointPillars | 李梓和 | 是 | |
PointPillar(MindSpore版本) | MindSpore | ||
PointPillars (train with CUDA and inference with TensorRT) | TensorRT | ||
3d Object Detection for Pedestrian with Pointpillars, Tensorflow, Intel Realsense d435i at 120 HZ |
ROS TensorFlow2.X |
||
second.pytorch | PyTorch | 刘林军 | |
PointPillars Pytorch Model Convert To ONNX, And Using TensorRT to Load this IR(ONNX) for Fast Speeding Inference |
ONNX TensorRT |
||
PointPillars Inference with TensorRT |
TensorRT CUDA |
||
GitHub - zhulf0804/PointPillars: A Simple PointPillars PyTorch Implenmentation for 3D Lidar(KITTI) Detection. | PyTorch | 待验证 | |
1. 下载代码
git clone https://github.com/traveller59/second.pytorch.git
cd ./second.pytorch/second
2. 安装环境
推荐用Anaconda管理虚拟环境。
conda install scikit-image scipy numba pillow matplotlib
pip install fire tensorboardX protobuf opencv-python
pip install torchplus
pip install pycamia
pip install spconv
安装boost库
apt-get install libboost-all-dev
2.1 安装SparseConvNet
GitHub - facebookresearch/SparseConvNet: Submanifold sparse convolutional networks
git clone https://github.com/facebookresearch/SparseConvNet.git
cd SparseConvNet/
bash develop.sh
2.2 配置环境变量
添加second.pytorch至PYTHONPATH。
export PYTHONPATH=$PYTHONPATH:/your_second.pytorch_path/
3. 下载预训练模型
simple-pointpillar
4. 下载KITTI数据集
- KITTI数据集论文: Are we ready for autonomous driving? the kitti vision benchmark suite [CVPR 2012] 和 Vision meets robotics: The kitti dataset [IJRR 2013]
- KITTI数据集下载(下载前需要登录): point cloud(velodyne, 29GB), images(image_2, 12 GB), calibration files(calib, 16 MB)和labels(label_2, 5 MB)。数据velodyne, calib 和 label_2的读取详见
utils/io.py
。
4.1 KITTI数据集目录结构
└── KITTI_DATASET_ROOT├── training <-- 7481 train data| ├── image_2 <-- for visualization| ├── calib| ├── label_2| ├── velodyne| └── velodyne_reduced <-- empty directory└── testing <-- 7580 test data├── image_2 <-- for visualization├── calib├── velodyne└── velodyne_reduced <-- empty directory
4.2 方法一(Kaggle)
推荐:不需要科学上网,下载速度挺快的。
kitti-3d-object-detection-dataset
4.3 方法二(百度网盘)
测试数据集为KITTI数据集,KITTI官网需要翻墙,且访问速度慢。网上有很多百度网盘链接,推荐用网盘下载。
Kitti数据集百度网盘链接 00-21全
5. 数据预处理
5.1 create_data.py
指令用法
Usage: create_data.py <group|command>available groups: copy | pathlib | pickle | fire | np | imgio | sys |box_np_ops | kittiavailable commands: bound_points_jit | prog_bar | create_kitti_info_file |create_reduced_point_cloud |create_groundtruth_databaseFor detailed information on this command, run:create_data.py --help
5.2 创建 kitti infos
python create_data.py create_kitti_info_file --data_path=KITTI_DATASET_ROOT
5.3 创建 reduced point cloud
python create_data.py create_reduced_point_cloud --data_path=KITTI_DATASET_ROOT
5.4 创建 groundtruth-database infos
python create_data.py create_groundtruth_database --data_path=KITTI_DATASET_ROOT
5.5 完成数据预处理
kitti|- training|- calib (#7481 .txt)|- image_2 (#7481 .png)|- label_2 (#7481 .txt)|- velodyne (#7481 .bin)|- velodyne_reduced (#7481 .bin)|- testing|- calib (#7518 .txt)|- image_2 (#7518 .png)|- velodyne (#7518 .bin)|- velodyne_reduced (#7518 .bin)|- kitti_gt_database (# 19700 .bin)|- kitti_infos_train.pkl|- kitti_infos_val.pkl|- kitti_infos_trainval.pkl|- kitti_infos_test.pkl|- kitti_dbinfos_train.pkl
6. 修改配置
train_input_reader: {...database_sampler {database_info_path: "/path/to/kitti_dbinfos_train.pkl"...}kitti_info_path: "/path/to/kitti_infos_train.pkl"kitti_root_path: "KITTI_DATASET_ROOT"
}
...
eval_input_reader: {...kitti_info_path: "/path/to/kitti_infos_val.pkl"kitti_root_path: "KITTI_DATASET_ROOT"
}
7. 训练模型
cd ~/second.pytorch/second
python ./pytorch/train.py train --config_path=./configs/pointpillars/car/xyres_16.proto --model_dir=/path/to/model_dir
8. 评估模型
cd ~/second.pytorch/second/
python pytorch/train.py evaluate --config_path= configs/pointpillars/car/xyres_16.proto --model_dir=/path/to/model_dir
python ./pytorch/train.py evaluate --config_path=configs/pointpillars/car/xyres_16.proto --model_dir=/mnt/d/datasets/archive/car_fhd --measure_time=True --batch_size=1
python ./pytorch/train.py evaluate --config_path=configs/pointpillars/car/xyres_16.proto --model_dir=/mnt/d/datasets/archive/car_fhd --measure_time=True --batch_size=1
middle_class_name PointPillarsScatter
remain number of infos: 3769
Generate output labels...
/mnt/d/MyDocuments/cache/second.pytorch/second/../second/pytorch/models/voxelnet.py:786: UserWarning: indexing with dtype torch.uint8 is now deprecated, please use a dtype torch.bool instead. (Triggered internally at /pytorch/aten/src/ATen/native/IndexingUtils.h:25.)box_preds = box_preds[a_mask]
/mnt/d/MyDocuments/cache/second.pytorch/second/../second/pytorch/models/voxelnet.py:787: UserWarning: indexing with dtype torch.uint8 is now deprecated, please use a dtype torch.bool instead. (Triggered internally at /pytorch/aten/src/ATen/native/IndexingUtils.h:25.)cls_preds = cls_preds[a_mask]
/mnt/d/MyDocuments/cache/second.pytorch/second/../second/pytorch/models/voxelnet.py:790: UserWarning: indexing with dtype torch.uint8 is now deprecated, please use a dtype torch.bool instead. (Triggered internally at /pytorch/aten/src/ATen/native/IndexingUtils.h:25.)dir_preds = dir_preds[a_mask]
[100.0%][===================>][0.70it/s][44:38>00:01]
generate label finished(1.41/s). start eval:
avg forward time per example: 0.679
avg postprocess time per example: 0.012
Car AP@0.70, 0.70, 0.70:
bbox AP:0.00, 0.00, 0.00
Car AP@0.70, 0.50, 0.50:
bbox AP:0.00, 0.00, 0.00
资源占用情况
9. Kitti Viewer Web
详细步骤,请参考:second.pytorch
# 运行server
python ./kittiviewer/backend/main.py main --port=xxxx# 启动本地web server
cd ./kittiviewer/frontend && python -m http.server# 浏览器打开
http://127.0.0.1:8000
七、FAQ
Q:加载.h5模型错误
Traceback (most recent call last):File "point_pillars_training_run.py", line 112, in <module>pillar_net.load_weights(os.path.join(MODEL_ROOT, "model.h5"))File "/home/yoyo/miniconda3/envs/ppillar/lib/python3.8/site-packages/keras/utils/traceback_utils.py", line 70, in error_handlerraise e.with_traceback(filtered_tb) from NoneFile "/home/yoyo/miniconda3/envs/ppillar/lib/python3.8/site-packages/keras/saving/hdf5_format.py", line 835, in load_weights_from_hdf5_groupraise ValueError(
ValueError: Weight count mismatch for layer #2 (named cnn/block1/conv2d0 in the current model, cnn/block1/conv2d0 in the save file). Layer expects 1 weight(s). Received 2 saved weight(s)
解决办法:
加载模型
pillar_net.load_weights(os.path.join(MODEL_ROOT, "model.h5"))
改为
pillar_net.load_weights(os.path.join(MODEL_ROOT, "model.h5"),by_name=True , skip_mismatch=True)
Q:module 'cffi' has no attribute 'FFI'
File "/home/yoyo/miniconda3/envs/ppillar-torch/lib/python3.8/site-packages/numba/core/typing/context.py", line 158, in refreshself.load_additional_registries()File "/home/yoyo/miniconda3/envs/ppillar-torch/lib/python3.8/site-packages/numba/core/typing/context.py", line 701, in load_additional_registriesfrom . import (File "/home/yoyo/miniconda3/envs/ppillar-torch/lib/python3.8/site-packages/numba/core/typing/cffi_utils.py", line 19, in <module>ffi = cffi.FFI()
AttributeError: module 'cffi' has no attribute 'FFI'
错误原因:
pip显示已安装cffi,但是在Anaconda环境中找不到解决办法:
在Anaconda中安装cffi
conda install cffi如果安装没有权限
conda install -c local cffi
Q:module.__version__
版本检查错误
File "/home/yoyo/miniconda3/envs/ppillar-torch/lib/python3.8/site-packages/matplotlib/__init__.py", line 201, in _check_versionsif LooseVersion(module.__version__) < minver:
AttributeError: module 'kiwisolver' has no attribute '__version__'
错误原因:
在matplotlib版本检查过程中,由于kiwisolver没有 `__version__` 这个属性而报错解决办法:
注释版本检查的代码
其他类似的问题,操作一致。
Traceback (most recent call last):
...
...
...File "/home/yoyo/miniconda3/envs/ppillar-torch/lib/python3.8/site-packages/torch/utils/tensorboard/__init__.py", line 4, in <module>LooseVersion = distutils.version.LooseVersion
AttributeError: module 'distutils' has no attribute 'version'
Traceback (most recent call last):
...
...
...File "/home/yoyo/miniconda3/envs/ppillar-torch/lib/python3.8/site-packages/matplotlib/__init__.py", line 209, in _check_versionsif parse_version(module.__version__) < parse_version(minver):
AttributeError: module 'dateutil' has no attribute '__version__'
Q:imp
包被弃用
./pytorch/train.py:1: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative usesimport imp
错误原因:
DeprecationWarning:已弃用imp模块,改用importlib;有关其他用途,请参阅该模块的文档解决办法:
由于imp包暂未使用,注释掉即可
# import imp
Q:ModuleNotFoundError: No module named 'torchplus.tools'
Traceback (most recent call last):File "./pytorch/train.py", line 21, in <module>from second.pytorch.builder import (box_coder_builder, input_reader_builder, .........
"/mnt/d/MyDocuments/cache/second.pytorch/second/../second/pytorch/core/box_torch_ops.py", line 10, in <module>from torchplus.tools import torch_to_np_dtype
ModuleNotFoundError: No module named 'torchplus.tools'
错误原因:
torchplus版本太低解决办法:
卸载并安装新版本
pip uninstall torchplus
pip install torchplus
Q:ImportError: cannot import name 'BeautifulSoup' from 'bs4'
Traceback (most recent call last):File "./pytorch/train.py", line 14, in <module>import torchplus
....
...
...
File "/home/yoyo/miniconda3/envs/ppillar-torch/lib/python3.8/site-packages/notion/utils.py", line 4, in <module>from bs4 import BeautifulSoup
ImportError: cannot import name 'BeautifulSoup' from 'bs4' (/home/yoyo/miniconda3/envs/ppillar-torch/lib/python3.8/site-packages/bs4/__init__.py)
错误原因:
beautifulsoup4版本太低解决办法:
卸载并安装新版本
pip uninstall beautifulsoup4
pip install beautifulsoup4
Q:ImportError: 'pyctlib.watch.debugger' cannot be used without dependency 'line_profiler'.
Traceback (most recent call last):File "./pytorch/train.py", line 14, in <module>import torchplusFile "/home/yoyo/miniconda3/envs/ppillar-torch/lib/python3.8/site-packages/torchplus/__init__.py", line 33, in <module>from .tensor import *File "/home/yoyo/miniconda3/envs/ppillar-torch/lib/python3.8/site-packages/torchplus/tensor.py", line 33, in <module>from pyctlib.visual.debugger import profileFile "/home/yoyo/miniconda3/envs/ppillar-torch/lib/python3.8/site-packages/pyctlib/visual/debugger.py", line 18, in <module>raise ImportError("'pyctlib.watch.debugger' cannot be used without dependency 'line_profiler'. ")
ImportError: 'pyctlib.watch.debugger' cannot be used without dependency 'line_profiler'.
错误原因:
缺少line-profiler包解决办法:
安装line-profiler
pip install line-profiler
PointPillar 3D目标检测模型详解相关推荐
- 【百度飞浆】YOLO系列目标检测算法详解
YOLO系列目标检测算法详解 1 YOLO发展史 2 YOLO v3目标检测原理 3 PaddleDetection中YOLO v3模型介绍 4 YOLO v3配置演练 1 YOLO发展史 2 YOL ...
- 基于YOLOv5的目标检测系统详解(附MATLAB GUI版代码)
摘要:本文重点介绍了基于YOLOv5目标检测系统的MATLAB实现,用于智能检测物体种类并记录和保存结果,对各种物体检测结果可视化,提高目标识别的便捷性和准确性.本文详细阐述了目标检测系统的原理,并给 ...
- 【百度飞浆】RCNN系列目标检测算法详解
RCNN系列目标检测算法详解 目录 两阶段目标检测算法发展历程 R-CNN R-CNN网络结构 R-CNN网络效果 Fast R-CNN Fast R-CNN网络效果 Faster R-CNN Fas ...
- YOLOv1——YOLOX系列及FCOS目标检测算法详解
文章目录 一. 开山之作:YOLOv1 1.1. YOLOv1简介 1.2 YOLOv1 检测原理 1.3 YOLOv1网络结构 1.4 YOLOv1 损失函数 1.5 YOLOv1优缺点 二. YO ...
- YOLO系列目标检测算法详解
目录 前言 YOLO发展历程 目标检测 YOLO开山之作 ---- YOLO(v1) YOLOv2 YOLOv3 PaddleDetection中YOLOv3模型介绍 总结 前言 YOLO发展历程 F ...
- 基于mmrotate的旋转目标检测入门详解
一.旋转目标检测方法对比 1 当前前沿方法的对比 首先我们打开papers with code 网站 https://paperswithcode.com/ 我们在搜索栏输入 oriented obj ...
- single-shot detection(SSD)目标检测算法详解——(一看就懂系列!!!)
SSD是One-stage系列的优秀算法之一 one-stage的意思是挑选候选框和预测候选框两步是同时完成的,R-CNN家族都是two-stage. SSD有什么创新点? (1)基于Faster-R ...
- 目标检测之详解yolov3的anchor、置信度和类别概率
参考1 参考2 参考3 提要 yolov3 在网络最后的输出中,对于每个grid cell产生3个bounding box,每个bounding box的输出有三类参数: 一个是对象的box参数,一共 ...
- 深度学习_目标检测_Soft-MNS详解
Soft-NMS论文链接 Soft-NMS介绍 针对原始NMS过于hard的问题,论文中提出了Soft-NMS进行解决. 那原始的NMS有什么问题呢? 我们先看下面的图: 在上图中,检测算法本来应该输 ...
最新文章
- python备份cisco交换机_1.自动备份思科交换机配置
- 天津科技大学 物理实验平台使用教程及相关问题
- boost::format模块测试构造对象和基本解析
- 华为的鸿蒙系统是海思_死心了!华为鸿蒙系统首款终端确认,不是手机
- IT团队如何赢得尊重?
- class对象和class文件_Class文件格式
- leetcode141. 环形链表
- opencv copyto给图片加logo
- 字符串拼接的sql注入实战
- 笔记本电脑无法连接WiFi,如何解决
- 电驴链接服务器老是无响应,电驴emule eD2k 不能连接服务器解决办法
- OSError: exception: access violation writing 0x000000003F800000
- vue的报错 error Trailing spaces not allowed
- Richer Convolutional Features for Edge Detection(RCF论文学习笔记)
- Django之验证码(十七)
- 自建ipa下载服务器的方法(最简单,使用在线工具)
- Gluster (一)安装
- 小白兔笑话全集(转)笑死人不偿命
- 1^2+2^2+3^2+...+n^2的求和计算方法
- 拿什么拯救你,我的大规模杀伤性武器--Nokia!