Super雨

其实没有那么多观众,大胆地自由地生活!

三维目标检测OpenpcDet库代码阅读-构建数据集infos

三维目标检测OpenpcDet库代码阅读-构建数据集infos

一、写在前面

引言

OpenPCDet 主要聚焦在基于点云的 3D 目标检测这一种设定上,代码结构相对简单轻量一些,对于入门基于点云的三维目标检测是一个很好的选择。接触到一个新的框架需要对其有一个相对详细的认识,之后才能在此基础上作出修改以适应自己的需求。在使用openpcdet之前,会有一个构建数据集info的操作,这是后续训练模型的基础。但是源代码使用多线程,想要单步查看代码执行结果不太方便,本文对构建info的过程作了介绍。

读完本篇,你将获得

kitti数据集的处理为例,了解openpcdet数据集构建info的过程,对构建结果有清晰的认识。

二、步入正题

1、准备工作

在官方的getting started有如下指示:通过运行以下命令生成数据信息:

1
python -m pcdet.datasets.kitti.kitti_dataset create_kitti_infos tools/cfgs/dataset_configs/kitti_dataset.yaml

上述指令会调用pcdet.datasets.kitti.kitti_dataset模块生成infos,-m参数会使pcdet.datasets.kitti.kitti_dataset作为模块执行,不方便单步查看执行过程,作出如下改变:

新建临时文件temp.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pcdet.datasets.kitti.kitti_dataset import create_kitti_infos
import sys
#debug,手动传入参数
sys.argv = ["","create_kitti_infos","tools/cfgs/dataset_configs/kitti_dataset.yaml"]
if sys.argv.__len__() > 1 and sys.argv[1] == 'create_kitti_infos':
import yaml
from pathlib import Path
from easydict import EasyDict
dataset_cfg = EasyDict(yaml.safe_load(open(sys.argv[2])))
ROOT_DIR = (Path("/home/app/OpenPCDet/pcdet/datasets/kitti/kitti_dataset.py").resolve().parent / '../../../').resolve()
create_kitti_infos(
dataset_cfg=dataset_cfg,
class_names=['Car', 'Pedestrian', 'Cyclist'],
data_path=ROOT_DIR / 'data' / 'kitti',
save_path=ROOT_DIR / 'data' / 'kitti'
)

临时改变/home/app/OpenPCDet/pcdet/datasets/kitti/kitti_dataset.py的多线程部分,使之单线程运行,方便查看中间结果:

1
2
3
# with futures.ThreadPoolExecutor(num_workers) as executor:
# infos = executor.map(process_single_scene, sample_id_list)
infos = [ k for k in map(process_single_scene, sample_id_list)]

接下来可以调试temp.py,查看执行过程。

ps:为什么要这些多余的操作?

由于kitti_dataset.py是pcdet包的子模块,在子模块内往往使用了相对导入,如下所示:

1
2
3
4
from . import kitti_utils
from ...ops.roiaware_pool3d import roiaware_pool3d_utils
from ...utils import box_utils, calibration_kitti, common_utils, object3d_kitti
from ..dataset import DatasetTemplate

直接调试kitti_dataset.py会报错,所以要单独新建temp.py。然而使用-m参数运行kitti_dataset.py却不会报错,具体原因和其背后的潜在知识读者自行尝试并搜索。

### 2、构建infos

构建infos相关的文件是OpenPCDet/pcdet/datasets/kitti/kitti_dataset.py,主要包含下图所示的两块主要部分。

image-20220811184706352

create_kitti_infos是留给外部的接口,所需的具体操作实现在KittiDataset里完成。

可以看到,KittiDataset的定义及其内部的操作都是很常规的:以get开头的函数一般是根据id,去kitti数据集文件里读取相关的条目,

image-20220811185109780

create_kitti_infos主要使用dataset.get_infos构建infos:

image-20220811185348197

分别构建了train 和test 两个info集合,主要使用的函数是:dataset.get_infos,他的内部主要使用process_single_scene按照id获取point_cloud、calib等信息,以构建train info 为例:process_single_scene处理id为3的结果是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
{
'point_cloud': {
'num_features': 4,
'lidar_idx': '000003'
},
'image': {
'image_idx': '000003',
'image_shape': array([375, 1242], dtype = int32)
},
'calib': {
'P2': array([
[7.21537720e+02, 0.00000000e+00, 6.09559326e+02, 4.48572807e+01],
[0.00000000e+00, 7.21537720e+02, 1.72854004e+02, 2.16379106e-01],
[0.00000000e+00, 0.00000000e+00, 1.00000000e+00, 2.74588400e-03],
[0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.00000000e+00]
]),
'R0_rect': array([
[0.9999239, 0.00983776, -0.00744505, 0.],
[-0.0098698, 0.9999421, -0.00427846, 0.],
[0.00740253, 0.00435161, 0.9999631, 0.],
[0., 0., 0., 1.]
],
dtype = float32),
'Tr_velo_to_cam': array([
[7.53374491e-03, -9.99971390e-01, -6.16602018e-04, -4.06976603e-03],
[1.48024904e-02, 7.28073297e-04, -9.99890208e-01, -7.63161778e-02],
[9.99862075e-01, 7.52379000e-03, 1.48075502e-02, -2.71780610e-01],
[0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
1.00000000e+00
]
])
},
'annos': {
'name': array(['Car', 'DontCare', 'DontCare'], dtype = '<U8'),
'truncated': array([0., -1., -1.]),
'occluded': array([0., -1., -1.]),
'alpha': array([1.55, -10., -10.]),
'bbox': array([
[614.24, 181.78, 727.31, 284.77],
[5., 229.89, 214.12, 367.61],
[522.25, 202.35, 547.77, 219.71]
], dtype = float32),
'dimensions': array([
[4.15, 1.57, 1.73],
[-1., -1., -1.],
[-1., -1., -1.]
]),
'location': array([
[1., 1.75, 13.22],
[-1000., -1000., -1000.],
[-1000., -1000., -1000.]
], dtype = float32),
'rotation_y': array([1.62, -10., -10.]),
'score': array([-1., -1., -1.]),
'difficulty': array([0, 0, -1], dtype = int32),
'index': array([0, -1, -1], dtype = int32),
'gt_boxes_lidar': array([
[13.51070309, -0.98177999, -0.90948993, 4.15, 1.73,
1.57, -3.19079633
]
]),
'num_points_in_gt': array([674, -1, -1], dtype = int32)
}
}

可以看到,所谓的构建infos只不过是:针对单个id,读取出来所有的附属信息,将其包裹起来,与id绑定。这一步操作对构建pytorch训练所需的batch很有用,在训练之前统一批量构建好信息相比于在训练过程单独的读取数据要方便不少。

另外一个重要的操作是create_groundtruth_database,他会抽取真值框的信息,并且为每一帧的每一个目标都生成一个单独的文件,例如:'000000_Pedestrian_0.bin',代表第000000帧的Pedestrain类别的第0个目标的真值框信息文件,该文件包含的信息举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
'name': 'Pedestrian',
'path': 'gt_database/000000_Pedestrian_0.bin',
'image_idx': '000000',
'gt_idx': 0,
'box3d_lidar': array([8.73138046, -1.85591757, -0.65469939, 1.2, 0.48,
1.89, -1.58079633
]),
'num_points_in_gt': 383,
'difficulty': 0,
'bbox': array([712.4, 143., 810.73, 307.92], dtype = float32),
'score': -1.0
}

整个构建infos的过程基本就包含这两个重要的步骤,后续内容会介绍OpenpcDet的训练过程。