PointPillars是一个来自工业界的模型,整体思想基于图片的处理框架,直接将点云从俯视图的视角划分为一个个的Pillar(立方柱体),从而构成了类似图片的数据,然后在使用2D的检测框架进行特征提取和密集的框预测得到检测框,从而使得该模型在速度和精度都达到了一个很好的平衡。
PointPillars网络结构总览:
网络速度精度对比:
注:(PP代表pointpillars,M代表MV3D, A代表AVOD,C代表ContFuse,V代表VoxelNet,
F代表Frustum Pointnet,S代表SECOND ,P+代表PIXOR++)
本文将会以OpenPCDet的代码基础,详细解析PointPillars的每一行代码实现以及原因。
读者可以下载OpenPCDet后根据文章进行阅读和理解。
由于本人才疏学浅,解析中难免会出现不足之处,欢迎指正、讨论,有好的建议或意见都可以在评论区留言。谢谢大家!
PointPillars的论文地址为:
https://arxiv.org/pdf/1812.05784.pdf
解析参考代码:
https://github.com/open-mmlab/OpenPCDet
注释代码仓库(仓库注释实时更新):
https://github.com/Nathansong/OpenPCDdet-annotatedhttps://github.com/Nathansong/OpenPCDdet-annotated
3D检测算法通常有以下几种形式:
(1)将点云数据划纳入一个个体素(Voxel)中,构成规则的、密集分布的体素集,如有VoxelNet和SECOND。
(2)从前视和俯视角度对点云数据进行投影映射处理,获得一个个伪图片的数据。常见的模型有MV3D和AVOD。
(3)直接将点云数据映射到鸟瞰图后,再直接使用2D的检测框架的处理方法进行特征提取和RPN,实现3D的检测,如PIXOR、本文的主角pointpillar。
(4)使用pointnet直接从点云中对数据进行特征提取后获取proposals,然后根据获取的proposals进行微调,如Pointrcnn
这里的处理过程直接将3D的点云信息直接从以俯视图的形式进行获取,在点云中假设有N*3个点的信息,所有的这些点都在kitti lidar坐标系xyz中(单位是米,其中x向前,y向左,z向上)。所有的这些点都会分配到均等大小的x-y平面的立方柱体中,这个立方柱就被称为pillar。如下图所示
左相机前视图
点云俯视图(左) 将点云分布到的均匀的立方柱体中(右)
(注:此处偷懒,没有将点云转换到FOV视角中,直接从3D点云俯视图截图,仅为pillar解释)
kitti的点云数据是4维度的数据包含(x, y, z, r)其中xyz是改点在点云中的坐标,r代表了改点的反射强度(与物体材质和激光入射角度等有关);并且在将所有点放入每个pillar中的时候不需要像voxel那样考虑高度,可以将一个pillar理解为就是一个z轴上所有voxel组成在一起的。
在进行PP的数据增强时候,需要对pillar中的数据进行增强操作,需要将每个pillar中的点增加5个维度的数据,包含 x c , y c , z c , x p 和 y p,其中下标c代表了每个点云到改点所对应pillar中所有点平均值的偏移量,下标p代表了该点距离所在pillar中心点的x,y的偏移量。所有经过数据增强操作后每个点的维度是9维;包含了x,y,z, x c , y c , z c , x p 和 y p(注在openpcdet的代码实现中是10维,多了一个zp,也就是该点在z轴上与该点所处pillar的z轴中心的偏移量)
经过上述操作之后,就可以把原始的点云结构(N*3)变换成了(D,P,N),其中D代表了每个点云的特征维度,也就是每个点云9个特征,P代表了所有非空的立方柱体,N代表了每个pillar中最多会有多少个点。
注:
1、在实现的过程中,每个pillar的长宽是0.16米,在pcdet的实现中,我们只会截取前视图的部分,进行训练,因为kitti的标注是根据2号相机进行标注的,所有x轴的负方向(即车的后方)是没有标注数据的,我们会截取掉后面的数据;同时为了保证检测的可靠性,距离太远的点,由于点云过于稀疏,也会被截取。所以在pcdet的实现中,点云空间的选取范围xyz的最小值是=[0, -39.68,-3], xyz选取的最大值是[69.12, 39.68, 1]。
2、其中每个pillar中的最大点云数量是32,如果一个pillar中的点云数量超过32,那么就会随机采样,选取32个点;如果一个pillar中的点云数量少于32;那么会对这个pillar使用0样本填充。
在经过映射后,就获得了一个(D,P,N)的张量;接下来这里使用了一个简化版的pointnet网络对点云的数据进行特征提取(即将这些点通过MLP升维,然后跟着BN层和Relu激活层),得到一个(C,P,N)形状的张量,之后再使用maxpool操作提取每个pillar中最能代表该pillar的点。那么输出会变成(C,P,N)->(C,P);在经过上述操作编码后的点,需要重新放回到原来对应pillar的x,y位置上生成伪图象数据。
下面看这部分的代码实现:
预处理实现代码 pcdet/datasets/processor/data_processor.py
在经过上面的预处理之后,就需要使用简化版的pointnet网络对每个pillar中的数据进行特征提取了。
代码在pcdet/models/backbones_3d/vfe/pillar_vfe.py
在经过简化版的pointnet网络提取出每个pillar的特征信息后,就需要将每个的pillar数据重新放回原来的坐标分布中来组成伪图像数据了。
代码在pcdet/models/backbones_2d/map_to_bev/pointpillar_scatter.py
经过上面的映射操作,将原来的pillar提取最大的数值后放回到相应的坐标后,就可以得到类似于图像的数据了;只有在有pillar非空的坐标处有提取的点云数据,其余地方都是0数据,所以得到的一个(batch_size,64, 432, 496)的张量还是很稀疏的。
下图是对得到的张量数据使用2D中的特征提取手段进行多尺度的特征提取和拼接融合。
这没有好解析的就是常规的卷积操作然后进行拼接即可,注意一下维度变换就可以。
最终经过所有上采样层得到的3个尺度的的信息 每个尺度的 shape 都是 (batch_size, 128, 248, 216) 在第一个维度上进行拼接得到x 维度是 (batch_size, 384, 248, 216)
代码在pcdet/models/backbones_2d/base_bev_backbone.py
PiontPillars中的检测头采用了类似SSD的检测头设置,在openpcdet的实现中,直接使用了一个网络训练车、人、自行车三个类别;没有像原论文中对车、人使用两种不同的网络结构。因此在检测头的先验框设置上,一共有三个类别的先验框,每个先验框都有两个方向分别是BEV视角下的0度和90度,每个类别的先验证只有一种尺度信息;分别是车 [3.9, 1.6, 1.56]、人[0.8, 0.6, 1.73]、自行车[1.76, 0.6, 1.73](单位:米)。
在anchor匹配GT的过程中,使用的是2D IOU匹配方式,直接从生成的特征图也就是BEV视角进行匹配;不需要考虑高度信息。原因有二:1、因为在kitti数据集中所有的物体都是在三维空间的同一个平面中的,没有车在车上面的一个情况。 2、所有类别物体之间的高度差别不是很大,直接使用SmoothL1回归就可以得到很好的结果。 其次是每个anchor被设置为正负样本的iou阈值是:
车匹配iou阈值大于等于0.65为正样本,小于0.45为负样本,中间的不计算损失。
人匹配iou阈值大于等于0.5为正样本,小于0.35为负样本,中间的不计算损失。
自行车匹配iou阈值大于等于0.5为正样本,小于0.35为负样本,中间的不计算损失。
其中每个anchor都需要预测7个参数,分别是 (x, y, z, w, l, h, θ),其中x, y, z预测一个anchor的中心坐标在点云中的位置, w,l,h分别预测了一个anchor的长宽高数据,θ预测了box的旋转角度。
同时,因为在角度预测时候不可以区分两个完全相反的box,所以PiontPillars的检测头中还添加了对一个anchor的方向预测;这里使用了一个基于softmax的方向分类box的两个朝向信息。
代码在 pcdet/models/dense_heads/anchor_head_single.py
在Pointpillars的loss计算中,使用了与SECOND相同的loss计算方式,每个GT框都包含了 (x, y, z, w, l, h, θ)这7个参数。
1.定位任务的回归残差定义如下:
其中x^gt代表了标注框的x长度 ;x^a代表了先验框的长度信息,d^a表示先验框长度和宽度的对角线距离,定义为:。
因此得到的总回归损失是: 。
2.类别分类任务
对于每个先验框的物体类别分类,PointPillars使用了focal loss,来完成调节正负样本均衡,和难样本挖掘。公式定义如下:
其中,aplha参数和gamma参数都和RetinaNet中的设置一样,分别为0.25和2。
3.先验框方向分类
由于在角度回归的时候,不可以完全区分两个两个方向完全相反的预测框,所以在实现的时候,作者加入了对先验框的方向分类,使用softmax函数预测方向的类别。
因此总损失定义如下:
其中,系数Beta_loc为2,Beta_cls为1,Beta_dir为0.2。
在loss计算的代码实现中涉及的代码量比较多,因此解析分为如下三个部分分别完成
1、先验框的生成
2、GT和先验框的匹配
3、loss计算实现
1、先验框的生成
代码在pcdet/models/dense_heads/target_assigner/anchor_generator.py
2、GT和先验框的匹配(target assignment)
此处代码注释已经写得很详细,可以按照注释理解如果和计算GT和所有anchor的匹配;
assign_targets完成对一帧点云数据中所有的类别和anchor的正负样本分配,
assign_targets_single完成对一帧中每个类别的GT和anchor的正负样本分配。
所以一个Batch样本中anchor与GT的匹配这里是逐帧逐类别进行的。与图像目标检测中稍有不同。
代码在pcdet/models/dense_heads/target_assigner/axis_aligned_target_assigner.py
3、box编码实现
此处根据论文中的公式对匹配被正样本的anchor_box和与之对应的GT-box的7个回归参数进行编码。
编码公式:
其中x^gt代表了标注框的x长度 ;x^a代表了先验框的长度信息,d^a表示先验框长度和宽度的对角线距离,定义为:。
代码在:pcdet/utils/box_coder_utils.py
4、loss计算实现
在PointPillars损失计算分别有三个,每个anhcor和GT的类别分类损失、box的7个回归损失、还有一个方向角预测的分类损失构成。
1、分类损失计算:
代码在pcdet/models/dense_heads/anchor_head_template.py
与之对应的focal_loss分类计算的详细实现代码在:pcdet/utils/loss_utils.py
2、box的回归SmoothL1损失计算和方向分类损失计算:
代码在:pcdet/models/dense_heads/anchor_head_template.py
方向分类的 target assignment,此处是指定该角度下应该是由分配到dir_bin中。
这里使用点云的坐标系减去了45度,可能的原因是:
减去dir_offset(45度)的原因可以参考这个issue: https://github.com/open-mmlab/OpenPCDet/issues/80https://github.com/open-mmlab/OpenPCDet/issues/80 说的呢就是因为大部分目标都集中在0度和180度,270度和90度, 这样就会导致网络在这些角度的物体的预测上面不停的摇摆。所以为了解决这个问题, 将方向分类的角度判断减去45度再进行判断,如下图所示。 这里减掉45度之后,在预测推理的时候,同样预测的角度解码之后 也要减去45度再进行之后测nms等操作。
下图来自FCOS3D论文
FCOS3D:https://arxiv.org/pdf/2104.10956.pdf
box cls target assignment代码在:pcdet/models/dense_heads/anchor_head_template.py
方向回归的smoothL1计算
代码在pcdet/utils/loss_utils.py
方向分类损失计算:
代码在pcdet/utils/loss_utils.py
一:数据增强
在PointPillars中使用了和SECOND网络中相似的数据增强手段:
1、包括建立类别GT和索引,在点云中随机放置15个、0个、8个的车辆、行人、自行车样本到任意点云帧中。
2、每一帧中的所有GT_box都会被随机旋转([ -pi/20 , pi/20);同时在x、y、z轴平移上随机平移,x、y、z取值来自期望为0,方差为0.25的正态分布。
3、全部点云沿x轴翻转、全局点云旋转和随机缩放操作;使用全局x、y、z轴平移来模拟定位噪声,x、y、z取值来自期望为0,方差为0.2的正态分布。
一、测试结果
PointPillars论文KITTI数据集测试结果
PointPillars在OpenPCDet中KITTI数据集测试结果(结果仅显示在kitti验证集moderate精度)
二、消融实验
1、空间分辨率
在实现中,每个pillar的长宽都设定在0.16m;如果增大这个数据的话,可以加快的推理速度,因为更大的pillars会使得整个点云中的非空pillar更少,同时计算得到的伪图象长宽也会更小,加快了pointnet 编码器和网络中CNN提取特征的速度;但是,更小的pillars可以使网络学习到更加细腻的特征,拥有更好的定位精度。测试结果如下:
即更大的pillar带来了更快的速度,更小的pillar拥有更高的精度。
2、每个box独立进行数据增强
虽然在VxoelNet和SECOND中都推荐大量的对每个GT_Box进行数据增强;但是在PointPillars中通过实验发现,这样的操作会使得对行人的检测性能大幅度的降低,反而较少的独立数据增强效果更好。可能的原因是在每一帧点云中放入从样本库中真实采样中的GT数据减轻了对大幅度进行独立GT增强的需要。
3、点云表达特征增强
在对每个点云x、y、z、r数据进行增强的时候,PointPillars采用了和VoxelNet一样的操作,都为每个点云空间的特征加入了当前点云到当前pilar底部中心的距离,xp和yp。这一操作使得最终的整体检测性能提高了0.5map,同时也使得论文中结果更具复现性。
4、编码器
一个可以学习的编码器对于固定的编码器来说是实现网络端到端训练的重要架构。此处对PointPillars中使用不同编码器得到的结果进行了实验,结果如下:
PointPillars在OpenPCDet中的推理代码实现
PointPillars点云检测在OpenPCDet推理代码详解_NNNNNathan的博客-CSDN博客在之前的文章中已经详细解析了PointPillars的论文和训练代码的实现和详解,可以参考之前的博客:PointPillars论文解析和OpenPCDet代码解析_NNNNNathan的博客-CSDN博客。本篇博客将会详细解析PointPillars模型在OpenPCDet中推理代码,并测试推理的效果。读者可以下载OpenPCDet后根据文章进行阅读和理解。由于本人才疏学浅,解析中难免会出现不足之处,欢迎指正、讨论,有好的建议或意见都可以在评论区留言。谢谢大家!PointPi...https://blog.csdn.net/_/article/details/?spm=1001.2014.3001.5502
参考文章或文献:
1、https://github.com/open-mmlab/OpenPCDet/
2、https://github.com/jjw-DL/OpenPCDet-Noted/
3、使用KITTI数据集实现坐标转换 - 知乎
4、【3D目标检测】PointPillars论文和代码解析 - 知乎
5、【3D目标检测】SECOND算法解析 - 知乎
6、 https://arxiv.org/abs/1812.05784
7、Sensors | Free Full-Text | SECOND: Sparsely Embedded Convolutional Detection
8、https://arxiv.org/abs/1711.06396
9、https://arxiv.org/abs/1612.00593
10、【3D计算机视觉】从PointNet到PointNet++理论及pytorch代码_小执着的博客-CSDN博客_pointnet1
11、【3D计算机视觉】PointNet++的pytorch实现代码阅读_小执着的博客-CSDN博客_pointnet++ pytorch
12、The KITTI Vision Benchmark Suite
13、KITTI数据集--参数_cuichuanchen3307的博客-CSDN博客_kitti
14、https://arxiv.org/pdf/2104.10956.pdf
到此这篇pointnet++代码详解(pointpillars代码)的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/rfx/70800.html