一.简介
源码链接:
https://github.com/open-mmlab/OpenPCDethttps://github.com/open-mmlab/OpenPCDet
OpenPCDet 是一套基于PyTorch实现的点云3D目标检测代码库。(也是个框架)
设计思想:点云数据集(KITTI、NuScene、Lyft、Waymo、PandaSet等)在数据格式与3D坐标系上往往定义各不相同,各式各样的点云感知算法(point-based、 voxel-based、one-stage/two-stage等)也形态各异
因此基于数据-模型分离的顶层代码框架设计思想,设计一个统一的架构,使得相关研究者可以在一个统一的框架内进行各种组合实验。
统一的坐标定义:
PCDet 中我们采用了固定的统一点云坐标系,以及更规范的3D检测框定义,贯穿整个数据增强、处理、模型计算以及检测后处理过程。3D检测框的7维信息定义如下:
3D bounding box: (cx, cy, cz, dx, dy, dz, heading)
(cx, cy, cz)
为物体3D框的几何中心位置(dx, dy, dz)
分别为物体3D框在heading角度为0时沿着x-y-z三个方向的长度heading
为物体在俯视图下的朝向角 (沿着x轴方向为0度角,逆时针x到y角度增加)。
模块化模型拓扑设计
在PCDet中搭建3D目标检测框架只需要写config文件将所需模块定义清楚,然后PCDet将自动根据模块间的拓扑顺序组合为3D目标检测框架,来进行训练和测试。
模块化设计如下图所示:
PCDet可以支持目前已有的绝大多数面向LiDAR点云的3D目标检测算法,包括voxel-based,point-based,point-voxel hybrid以及one-stage/two-stage等等3D目标检测算法。 如下图所示:
二.代码结构解析
文件结构如下图:
数据处理流程
-
步骤1:
__getitem__(
/OpenPCDet/pcdet/datasets/kitti/kitti_dataset.py def __getitem__(self, index) {})
作用:从磁盘上加载数据并统一坐标系。
如果只更换数据集,则需要重写__getitem__
-
步骤2:
data_augmentor
作用: 数据增强的方法,例如随机裁剪、随机旋转等… -
步骤3:
point_feature_encoder
作用:选择一些特征的编码,输入的特征是points:(N,3+C_in)
经过选择和编码后,输出的特征是points:(N,3+C_out)
-
步骤4:
data_processor
作用:处理输入的数据,比如mask_point_boxes_outside_range、sample_points等 -
步骤5:
collate_batch
作用:将数据整理成batch
模型拓扑的依赖关系
建立model的过程在【pcdet/models/detectors/detector3d_template.py】中
其中build_networks
是根据拓扑信息(module_topology
)建立网络, 并保存在module list
中。
module_topology
的定义如下:
模型的前向传播和最优
以point_rcnn
为例,定义了一个PointRCNN的类,继承的是Detector3DTemplate的类。
-
前向传播
- 首先遍历module_list, 通过
topology
顺序的调用各个模型。如果是训练过程(training
), 则调用get_traing_loss
计算损失。如果是推理过程,则调用post_processing
进行后处理
- 首先遍历module_list, 通过
-
最优化
计算的损失包括两个部分- DETECTOR.get_training_loss()
- HEAD.get_loss()
模型
models包括了网络的结构,backbone,head,detectors等,关于网络结构的实现代码都在这里,需要修改网络结构的代码在这里修改,注意在对应文件夹内的init.py进行初始化.
detector
对于所有detector的顶级模板如下
- 建立网络结构(Build_networks)
- 前向传播(Forword)
- 计算损失(Loss calculation)
- 后处理 (Post_processing:NMS+score threshold)
3D backbone network
3d主干网络的作用:提取基于体素的或者基于点云的特征。
3d主干网络主要有如下几种:
3d encoder with sparse convolution(with VFE)
功能:通过稀疏卷积进行编码
实例:VoxelBackBone8x、VolxelResBackBone8x
3d UNet with sparse convolution(with VFE)
功能:通过稀疏网络进行编码和解码两个部分
实例:UNetV2
point-wise networks(PointNet++)
功能: 用PointNet++ 直接提取点的特征
实例:PointNet2MSG
2D Backbone network
2d主干网络的作用: 提取2d特征图。
2d主干网络主要有如下几种:
1.Map_to_bev_module(把3d特征映射到2d上)
HeightCompression
PointPillarScatter
2.2d convolution encoder with FPN-like unsampling
BaseBEVBackbone
Denseheads
作用:生成dense 3d boxes, 真正进行检测的过程。
AnchorHead包含如下两部分:
target assigning:对于每个anchor判断是否是正样本以及要朝着那个groud truth 回归。
head loss calculation:计算loss,包括分类和回归的损失。
分类:
基于BEV 特征的 Dense head (继承于AnchorHeadTemplate)
AnchorHeadSingle: 只输入一个特征图,基于anchor进行检测
AnchorHeadMulti:输入多个特征图,基于anchor检测
CentorHead:anchor- free检测,对于每个pixel输出一个检测框。
基于点特征的Dense head (继承于PointHeadTemplate)
PointHeadSimple: 只做分割,判断每个点是前景点还是背景点。
PointHeadBox: 不仅做分割,还做预测。对于每个点预测一个3d 的bonding box。
PointIntraPartOffsetHead: 除了分割和预测外,还可以预测Intra part offset
RoIHeads
- 作用:Refine 3D proposals with RoI-aligned features
- Extract RoI-aligned features
- proposal_layer
- ProposalTargetLayer
- Head loss calcution
- 二阶段的ROI检测(继承于ROITemplate)
- PointRCNNHead
- PartA2Head
- PVRCNNHead
配置文件
通过.yaml
文件进行多层次的配置。
例如如下图所示的pv_rcnn.yaml
是个整体的配置。其中嵌套了三个下一级的配置,包括DATA_CONFIG
、MODEL
、OPTIMIZATION
三.OpenPCDet使用
快速demo
KITTI数据集训练
为了快速训练演示,选取100个数据进行训练,将数据集按照以下目录格式存放。
OpenPCDet
├── data
│ ├── kitti
│ │ │── ImageSets
│ │ │── training
│ │ │ ├──calib & velodyne & label_2 & image_2 & (optional: planes) & (optional: depth_2)
│ │ │── testing
│ │ │ ├──calib & velodyne & image_2
├── pcdet
├── tools
ImageSets中存在train.txt val.txt test.txt文本,其内容为训练、验证和测试使用的数据。
运行下面的代码以生成infos,生成的文件可在data/kitti找到。
python -m pcdet.datasets.kitti.kitti_dataset create_kitti_infos tools/cfgs/dataset_configs/kitti_dataset.yaml
openPCDet的可训练网络配置(KITTI数据集)存放在cfgs/kitti_models目录下。以pointrcnn训练为例,由于本次没有使用planes数据,将kitti_dataset.yaml中的USE_ROAD_PLANE改成False。
之后在tools目录下运行下面代码即可进行训练。
训练结束后可以在output/kitti_models目录中找到模型文件。
测试和评估预训练的模型
KITTI 3D目标检测的评估指标
四种指标表示:
- bbox:2D检测框的准确率,
- bev: BEV视图下检测框的准确率,
- 3d: 3D检测框的准确率,
- aos: 检测目标旋转角度的准确率
三列表示: Easy Moderate Hard
0.70/0.50表示:最小 Overlap
上面图中给出car AP @0.7 0.7 0.7 和car AP @0.7 0.5 0.5。
表示的是不同难度情况下算法的平均精度(难度评价根据所标注包围框是否被遮挡、遮挡程度进行评价),AP表示的是平均精度、0.7表示的是最小IOU(交并比)
例如:
Car @ 0.70、0.70、0.70 表示在容易,中等,困难的情况下评估汽车性能,并使用0.7(简单),0.7(mod),0.7(困难)作为overlap阈值。
目标检测中几个重要的定义:
IoU(Intersection over union):交并比IoU衡量的是两个区域的重叠程度,是两个区域重叠部分面积占二者总面积的比例。在目标检测中,如果模型输出的结果与真值gt的交并比 > 某个阈值(0.5或0.7)时,即认为我们的模型输出了正确的结果。
Precision :检索出来的条目中有多大比例是我们需要的。
Recall:我们需要的条目中有多大比例被检索出来了。
AP(Average Precision):平均精准度,对Precision-Recall曲线上的Precision值求均值。
四.自定义模块
如何组合、改进旧模型+支持新的模型?
PCDet中实际上已经支持了绝大部分的模块。对于一个新的(组合的)3D检测模型来说,只要在PCDet框架中实现其所特有的模块(比如新的backbone或新的head)来替换掉原有模块,并修改响应模型配置文件,其他模块以及数据处理部分直接利用PCDet中已有部分即可。
使用自定义的模型
步骤如下:
- 继承
DetectorTemplate
来写自定义的detector - 写自定义的配置文件
- 在对应的目录下写对应的模型
- 重载
forward()
函数 - 重载
get_training_loss()
函数
目标任务:
以OpenPCDet中的pointrcnn为基础,单独剥离其中的前景点分割网络,将POINT_HEAD模块替换为自定义的CLS_HEAD。采用新建detector、新建模块、新建网络的方式实现任务。目标网络简单:pointnet+【256 256】的mlp。
步骤一:网络构建
在OpenPCDet中,有八个基本模块, 'vfe', 'backbone_3d', 'map_to_bev_module', 'pfe', 'backbone_2d', 'dense_head', 'point_head', 'roi_head'每个模块中都有若干网络可供选择。
①自定义detector:新建detector.py 若需要在8个基础模块外添加模块,把新建模块名加入module_topology簇
把新detector类加入簇,detector init
②自定义模块:在detector3d_template定义新模块
③自定义网络:
新建cls2_head_template.py,为之后的ClsHead继承使用:
新建cls_head.py定义模块内的具体网络,这里采用两层mlp,网络的预测结果要存在batch_dict,方便模块化。
把新网络加入模块的簇
④新建detector的yaml配置文件
注意YAML中的NAME和簇里的名字需要保持一致,也就是和类名保持一致