数据集构建需求
约 3165 字大约 11 分钟
2025-04-22
1. 基本结构
文件结构
nuscenes
maps
- *.png
samples
CAM_BACK
- n008-2018-08-01-15-16-36-0400__CAM_BACK__1533151603537558.jpg
lidarseg
v1.0-mini
- 0ab9ec2730894df2b48df70d0d2e84a9_lidarseg.bin
panoptic
v1.0-mini
- *_panoptic.npz
V1.0-mini
- attribute.json
- calibrated_sensor.json
- category.json
- Instance.json
- lidarseg.json
- log.json
- map.json
- sample_annotation.json
- sample_data.json
- sample.json
- scene.json
- sensor.json
- visibility.json
1.1 nuScenes devkit
获取 scene 中的 sample
first_sample_token = my_scene['first_sample_token']
my_sample = nusc.get('sample', first_sample_token)
my_sample
{'token': 'ca9a282c9e77460f8360f564131a8af5',
'timestamp': 1532402927647951,
'prev': '',
'next': '39586f9d59004284a7114a68825e8eec',
'scene_token': 'cc8c0bf57f984915a77078b10eb33198',
'data': {'RADAR_FRONT': '37091c75b9704e0daa829ba56dfa0906',
'RADAR_FRONT_LEFT': '11946c1461d14016a322916157da3c7d',
'RADAR_FRONT_RIGHT': '491209956ee3435a9ec173dad3aaf58b',
'RADAR_BACK_LEFT': '312aa38d0e3e4f01b3124c523e6f9776',
'RADAR_BACK_RIGHT': '07b30d5eb6104e79be58eadf94382bc1',
'LIDAR_TOP': '9d9bf11fb0e144c8b446d54a8a00184f',
'CAM_FRONT': 'e3d495d4ac534d54b321f50006683844',
'CAM_FRONT_RIGHT': 'aac7867ebf4f446395d29fbd60b63b3b',
'CAM_BACK_RIGHT': '79dbb4460a6b40f49f9c150cb118247e',
'CAM_BACK': '03bea5763f0f4722933508d5999c5fd8',
'CAM_BACK_LEFT': '43893a033f9c46d4a51b5e08a67a1eb7',
'CAM_FRONT_LEFT': 'fe5422747a7d4268a4b07fc396707b23'},
'anns': ['ef63a697930c4b20a6b9791f423351da',
'6b89da9bf1f84fd6a5fbe1c3b236f809',
'924ee6ac1fed440a9d9e3720aac635a0',
'91e3608f55174a319246f361690906ba',
'cd051723ed9c40f692b9266359f547af',
'36d52dfedd764b27863375543c965376',
'70af124fceeb433ea73a79537e4bea9e',
'63b89fe17f3e41ecbe28337e0e35db8e',
'e4a3582721c34f528e3367f0bda9485d',
'fcb2332977ed4203aa4b7e04a538e309',
'a0cac1c12246451684116067ae2611f6',
'02248ff567e3497c957c369dc9a1bd5c',
'9db977e264964c2887db1e37113cddaa',
'ca9c5dd6cf374aa980fdd81022f016fd',
'179b8b54ee74425893387ebc09ee133d',
'5b990ac640bf498ca7fd55eaf85d3e12',
'16140fbf143d4e26a4a7613cbd3aa0e8',
'54939f11a73d4398b14aeef500bf0c23',
'83d881a6b3d94ef3a3bc3b585cc514f8',
'74986f1604f047b6925d409915265bf7',
'e86330c5538c4858b8d3ffe874556cc5',
'a7bd5bb89e27455bbb3dba89a576b6a1',
'fbd9d8c939b24f0eb6496243a41e8c41',
'198023a1fb5343a5b6fad033ab8b7057',
'ffeafb90ecd5429cba23d0be9a5b54ee',
'cc636a58e27e446cbdd030c14f3718fd',
'076a7e3ec6244d3b84e7df5ebcbac637',
'0603fbaef1234c6c86424b163d2e3141',
'd76bd5dcc62f4c57b9cece1c7bcfabc5',
'5acb6c71bcd64aa188804411b28c4c8f',
'49b74a5f193c4759b203123b58ca176d',
'77519174b48f4853a895f58bb8f98661',
'c5e9455e98bb42c0af7d1990db1df0c9',
'fcc5b4b5c4724179ab24962a39ca6d65',
'791d1ca7e228433fa50b01778c32449a',
'316d20eb238c43ef9ee195642dd6e3fe',
'cda0a9085607438c9b1ea87f4360dd64',
'e865152aaa194f22b97ad0078c012b21',
'7962506dbc24423aa540a5e4c7083dad',
'29cca6a580924b72a90b9dd6e7710d3e',
'a6f7d4bb60374f868144c5ba4431bf4c',
'f1ae3f713ba946069fa084a6b8626fbf',
'd7af8ede316546f68d4ab4f3dbf03f88',
'91cb8f15ed4444e99470d43515e50c1d',
'bc638d33e89848f58c0b3ccf3900c8bb',
'26fb370c13f844de9d1830f6176ebab6',
'7e66fdf908d84237943c833e6c1b317a',
'67c5dbb3ddcc4aff8ec5140930723c37',
'eaf2532c820740ae905bb7ed78fb1037',
'3e2d17fa9aa5484d9cabc1dfca532193',
'de6bd5ffbed24aa59c8891f8d9c32c44',
'9d51d699f635478fbbcd82a70396dd62',
'b7cbc6d0e80e4dfda7164871ece6cb71',
'563a3f547bd64a2f9969278c5ef447fd',
'df8917888b81424f8c0670939e61d885',
'bb3ef5ced8854640910132b11b597348',
'a522ce1d7f6545d7955779f25d01783b',
'1fafb2468af5481ca9967407af219c32',
'05de82bdb8484623906bb9d97ae87542',
'bfedb0d85e164b7697d1e72dd971fb72',
'ca0f85b4f0d44beb9b7ff87b1ab37ff5',
'bca4bbfdef3d4de980842f28be80b3ca',
'a834fb0389a8453c810c3330e3503e16',
'6c804cb7d78943b195045082c5c2d7fa',
'adf1594def9e4722b952fea33b307937',
'49f76277d07541c5a584aa14c9d28754',
'15a3b4d60b514db5a3468e2aef72a90c',
'18cc2837f2b9457c80af0761a0b83ccc',
'2bfcc693ae9946daba1d9f2724478fd4']}
bbox
anns = nusc.get('sample_annotation', ann_token)
# 保存 bbox 信息
box = nusc.get_box(ann_token)
# 提取位置和大小以及旋转弧度
x, y, z = box.center
width, length, height = box.wlh
yaw = box.orientation.radians
# 构成一个 7 维的向量
bbox_vector = [x, y, z, width, length, height, yaw]
# # 保存 bbox token
ins_token = anns['instance_token']
# 保存 类别信息
anns = nusc.get('sample_annotation', ann_token)
categories = anns['category_name']
外参
lidar_token = frame['data']['LIDAR_TOP']
lidar_data = nusc.get('sample_data', lidar_token)
calibrated_sensor = nusc.get('calibrated_sensor',
lidar_data['calibrated_sensor_token'])
ego pose
lidar_ego_pose0 = nusc.get('ego_pose', lidar_data['ego_pose_token'])
点云带语义信息
# 保存 pc_with_semantic 信息
pc_file_name = lidar_data['filename'] # load LiDAR names
pc0 = np.fromfile(os.path.join(data_root, pc_file_name),
dtype=np.float32,
count=-1).reshape(-1, 5)[..., :4]
lidarseg_labels_filename = os.path.join(nusc.dataroot,
nusc.get('lidarseg', lidar_token)['filename'])
points_label = np.fromfile(lidarseg_labels_filename, dtype=np.uint8).reshape([-1, 1])
points_label = np.vectorize(learning_map.__getitem__)(points_label)
pc_with_semantic = np.concatenate([pc0[:, :3], points_label], axis=1)
1.2 nuScenes lidarseg
2020年8月,我们发布了nuScenes-lidarseg。在nuScenes-lidarseg中,对于属于nuScenes数据集中关键帧的激光雷达点云中的每个点,我们用32个可能的语义标签之一注释它。因此,nuScenes-lidarseg在34,000个点云和850个场景(700个场景用于火车,150个场景)中包含11亿个注释点。我们还提供了一个测试集(150个场景中的6008个点云),用于挑战,不附带激光雷达语义分割标签。
lidarseg.json
nuScenes-lidarseg注释和sample_data之间的映射,对应于与关键帧相关的lidar点云。
slidarseg {
"token": <str> -- 唯一的标识符.
"filename": <str> -- 包含nuScenes-lidarseg标签的.bin文件的名称。这些是使用numpy以二进制格式存储的uint8的numpy数组
"sample_data_token": <str> -- 外键。Sample_data对应于带注释的激光雷达pointcloud,带有is_key_frame=True。
}
提示
data/nuscenes/lidarseg/v1.0-mini/0ab9ec2730894df2b48df70d0d2e84a9_lidarseg.bin
shape 为 (N,)
lidarseg_labels_filename = os.path.join(nusc.dataroot,
nusc.get('lidarseg',
lidar_token)['filename'])
points_label = np.fromfile(lidarseg_labels_filename,
dtype=np.uint8).reshape([-1, 1])
提示
nuscenes lidarseg 类别:23 + 9
对于 nuScenes-lidarseg,我们为激光雷达点云中的每个点标注了语义标签。除了 nuScenes 中的 23 个前景类别(事物)外,我们还增加了 9 个背景类别(背景)。有关每个类别的详细定义和示例图像,请参阅 nuScenes 和 nuScenes-lidarseg 的标注人员指导说明。
我们提供了以下类别的标注(不包括测试集):
类别 | nuScenes 立方体 | 立方体比例 | Lidarseg 点 | 点比例 |
---|---|---|---|---|
动物 | 787 | 0.07% | 5,385 | 0.01% |
人类.行人.成人 | 208,240 | 17.86% | 2,156,470 | 2.73% |
人类.行人.儿童 | 2,066 | 0.18% | 9,655 | 0.01% |
人类.行人.建筑工人 | 9,161 | 0.79% | 139,443 | 0.18% |
人类.行人.个人移动工具 | 395 | 0.03% | 8,723 | 0.01% |
人类.行人.警察 | 727 | 0.06% | 9,159 | 0.01% |
人类.行人.婴儿车 | 1,072 | 0.09% | 8,809 | 0.01% |
人类.行人.轮椅 | 503 | 0.04% | 12,168 | 0.02% |
可移动物体.障碍物 | 152,087 | 13.04% | 9,305,106 | 11.79% |
可移动物体.碎片 | 3,016 | 0.26% | 66,861 | 0.08% |
可移动物体.可推拉物体 | 24,605 | 2.11% | 718,641 | 0.91% |
可移动物体.交通锥 | 97,959 | 8.40% | 736,239 | 0.93% |
静止物体.自行车架 | 2,713 | 0.23% | 163,126 | 0.21% |
车辆.自行车 | 11,859 | 1.02% | 141,351 | 0.18% |
车辆.公交车.铰接式 | 1,820 | 0.16% | 357,463 | 0.45% |
车辆.公交车.刚性 | 14,501 | 1.24% | 4,247,297 | 5.38% |
车辆.汽车 | 493,322 | 42.30% | 38,104,219 | 48.27% |
车辆.工程车 | 14,671 | 1.26% | 1,514,414 | 1.92% |
车辆.应急车.救护车 | 49 | 0.00% | 2,218 | 0.00% |
车辆.应急车.警车 | 638 | 0.05% | 59,590 | 0.08% |
车辆.摩托车 | 12,617 | 1.08% | 427,391 | 0.54% |
车辆.拖车 | 24,860 | 2.13% | 4,907,511 | 6.22% |
车辆.卡车 | 88,519 | 7.59% | 15,841,384 | 20.07% |
总计 | 1,166,187 | 100.00% | 78,942,623 | 100.00% |
类别 | nuScenes 立方体 | 立方体比例 | Lidarseg 点 | 点比例 |
---|---|---|---|---|
平面.可行驶表面 | - | - | 316,958,899 | 28.64% |
平面.其他 | - | - | 8,559,216 | 0.77% |
平面.人行道 | - | - | 70,197,461 | 6.34% |
平面.地形 | - | - | 70,289,730 | 6.35% |
静止物体.人造物体 | - | - | 178,178,063 | 16.10% |
静止物体.其他 | - | - | 817,150 | 0.07% |
静止物体.植被 | - | - | 122,581,273 | 11.08% |
车辆.自身 | - | - | 337,070,621 | 30.46% |
噪声 | - | - | 2,061,156 | 0.19% |
总计 | - | - | 1,106,713,569 | 100.00% |
2. 标注需求(暂)
基本需求
注意
- 传感器外参: sensor -> ego
- 相机内参:camera_intrinsic
- 自车位姿:egopose
sensor
特定的传感器类型。
sensor {
"token": <str> -- 唯一的标识符.
"channel": <str> -- 传感器通道名称
"modality": <str> {camera, lidar, radar} -- 传感器形态。支持括号内的类别
}
[
{
"token": "725903f5b62f56118f4094b46a4470d8",
"channel": "CAM_FRONT",
"modality": "camera"
},
]
calibrated_sensor
定义在特定车辆上校准的特定传感器(激光雷达/雷达/摄像机)。所有外部参数都是关于自我车体框架给出的。所有相机图像都没有失真和校正。
calibrated_sensor {
"token": <str> -- 唯一的标识符
"sensor_token": <str> -- 指向传感器类型的外键。
"translation": <float> [3] -- 以米为单位的坐标系原点: x, y, z.
"rotation": <float> [4] -- 坐标系方向为四元数: w, x, y, z.
"camera_intrinsic": <float> [3, 3] -- 内置相机校准。对于非相机的传感器为空。
}
[
{
"token": "sensortoken01",
"sensor_token": "725903f5b62f56118f4094b46a4470d8",
"translation": [
0.7790881,
-0.03795504,
-0.65792318
],
"rotation": [
0.5088127886074937,
-0.5157522971924345,
0.4943634685982179,
-0.4803268418543061
],
"camera_intrinsic": [
[
3099.84911345,
0.0,
733.26224026
],
[
0.0,
3117.33700928,
432.55966456
],
[
0.0,
0.0,
1.0
]
]
},
]
ego_pose
自车具在特定时间戳的姿势。给出了全局坐标系。ego_pose是我们论文中描述的基于激光雷达地图的定位算法的输出。定位在x-y平面上,是二维的。
ego_pose {
"token": <str> -- 唯一的标识符.
"translation": <float> [3] -- 坐标系原点以米为单位:x, y, z。注意z总是0
"rotation": <float> [4] -- 坐标系方向为四元数:w x y z。
"timestamp": <int> -- 唯一的表示符.
}
[
{
"token": "egopose1",
"timestamp": "0",
"rotation": [
1.0,
0.0,
0.0,
0.0
],
"translation": [
0.0,
0.0,
0.0
]
},
]
scene
场景是从日志中提取的20秒长的连续帧序列。同一个日志可以产生多个场景。注意,对象标识(实例token)不会跨场景保存。
scene {
"token": <str> -- 唯一的标识符.
"name": <str> -- 短字符串标识符。
"description": <str> -- 更长的场景描述。
"log_token": <str> -- 外键。提取数据的位置记录日志的点。
"nbr_samples": <int> -- 这个场景中的样本数量
"first_sample_token": <str> -- 外键。指向场景中的第一个样本。
"last_sample_token": <str> -- 外键。指向场景中的最后一个样本。
}
[
{
"token": "testscenetoken",
"log_token": "logtest2",
"nbr_samples": 2,
"first_sample_token": "sampletest1",
"last_sample_token": "sampletest2",
"name": "testscenename",
"description": "testscenedescription"
}
]
sample
一个sample是一个带注释的2hz关键帧。数据收集在(大约)相同的时间戳作为单个LIDAR扫描的一部分。
sample {
"token": <str> -- 唯一的标识符.
"timestamp": <int> -- Unix时间戳
"scene_token": <str> -- 指向scene的外键
"next": <str> -- 外键。在时间上跟随这个样本。空如果场景结束。
"prev": <str> -- 外键。在时间上早于此的样本。如果场景开始为空。
}
[
{
"token": "sampletest1",
"timestamp": 0,
"prev": "",
"next": "sampletest2",
"scene_token": "testscenetoken"
},
{
"token": "sampletest2",
"timestamp": 1,
"prev": "",
"next": "",
"scene_token": "testscenetoken"
}
]
sample_data
传感器数据,如图像、点云或雷达返回。对于带有is_key_frame=True的sample_data,时间戳应该非常接近它所指向的样本。对于非关键帧,sample_data指向时间最接近的样本。
sample_data {
"token": <str> -- 唯一的标识符.
"sample_token": <str> -- 外键。与此sample_data相关联。
"ego_pose_token": <str> -- 外键.
"calibrated_sensor_token": <str> -- 外键
"filename": <str> -- 磁盘上data-blob的相对路径.
"fileformat": <str> -- 数据文件格式.
"width": <int> -- 如果样本数据是一张图像,则这是图像宽度(以像素为单位)
"height": <int> -- 如果样本数据是一张图像,则这是图像高度(以像素为单位)。
"timestamp": <int> -- Unix时间戳。
"is_key_frame": <bool> -- 如果sample_data是key_frame的一部分,则为True,否则为False。
"next": <str> -- 外键。来自同一传感器的样本数据,在时间上跟随。空如果场景结束。
"prev": <str> -- 外键。从同一传感器采样数据,在此时间之前。如果场景开始为空。
}
[
{
"token": "1",
"sample_token": "sampletest1",
"ego_pose_token": "egopose1",
"calibrated_sensor_token": "sensortoken1",
"timestamp": 0,
"fileformat": "jpg",
"is_key_frame": true,
"height": 900,
"width": 1600,
"filename": "samples/CAM_FRONT/000350.jpg",
"prew": "",
"next": ""
},
]
2.1 点云目标检测
2.1.1 标注格式
sample_annotation
定义样本中物体位置的边界框。
提示
所有位置数据都是在全局坐标系下给出的。
sample_annotation {
"token": <str> -- 唯一的标识符
"sample_token": <str> -- 外键。注意:这指向一个样本而不是一个sample_data,因为注释是在样本级别上完成的,考虑了所有相关的sample_data。
"instance_token": <str> -- 外键。这个注释是哪个对象实例。随着时间的推移,一个实例可以有多个注释。
"attribute_tokens": <str> [n] -- 外键。此注释的属性列表。属性会随着时间而改变,所以它们属于这里,而不是实例表中。
"visibility_token": <str> -- 外键。能见度也可能随着时间的推移而改变。如果没有标注可见性,则token为空字符串。
"translation": <float> [3] -- 边界框的位置,以米为单位,如center_x, center_y, center_z
"size": <float> [3] -- 边框大小以米为单位,如宽、长、高。
"rotation": <float> [4] -- 边框方向为四元数:w, x, y, z
"num_lidar_pts": <int> -- 这个检测框里的激光雷达点的数量。在此样本识别的激光雷达扫描过程中计算点。
"num_radar_pts": <int> -- 这个盒子里雷达点的数目。在此样本识别的雷达扫描过程中计算点。这个数字是所有雷达传感器的总和,没有任何无效的点滤波
"next": <str> -- 外键。来自后面同一对象实例的示例注释。如果这是该对象的最后一个注释,则为空。
"prev": <str> -- 外键。来自时间上位于此之前的同一对象实例的示例注释。如果这是该对象的第一个注释,则为空。
}
提取自车位置和传感器外参
calib_data = nusc.get('calibrated_sensor', lidar_data['calibrated_sensor_token']) ego_data = nusc.get('ego_pose', lidar_data['ego_pose_token'])
从标注中取
bbox
的位置和方向ann = nusc.get('sample_annotation', 'bc3180b07f8e4a728f504ded654df56f') center = np.array(ann['translation']) orientation = np.array(ann['rotation'])
求逆,将
bbox
从世界系转到自车坐标系# global to ego quaternion = Quaternion(ego_data['rotation']).inverse center -= np.array(ego_data['translation']) center = np.dot(quaternion.rotation_matrix, center) orientation = quaternion * orientation
求逆,将
bbox
从自车坐标系转到 lidar 系# ego to sensor quaternion = Quaternion(calib_data['rotation']).inverse center -= np.array(calib_data['translation']) center = np.dot(quaternion.rotation_matrix, center) orientation = quaternion * orientation
2.1.2 目标类别
nuscenes 类别
nuScenes数据集附带23个类的注释(细节)。其中一些只有少数几个样本。因此,我们合并了相似的类,并删除了罕见的类。
10个class。
nuScenes detection class | nuScenes general class |
---|---|
void / ignore | animal |
void / ignore | human.pedestrian.personal_mobility |
void / ignore | human.pedestrian.stroller |
void / ignore | human.pedestrian.wheelchair |
void / ignore | movable_object.debris |
void / ignore | movable_object.pushable_pullable |
void / ignore | static_object.bicycle_rack |
void / ignore | vehicle.emergency.ambulance |
void / ignore | vehicle.emergency.police |
barrier | movable_object.barrier |
bicycle | vehicle.bicycle |
bus | vehicle.bus.bendy |
bus | vehicle.bus.rigid |
car | vehicle.car |
construction_vehicle | vehicle.construction |
motorcycle | vehicle.motorcycle |
pedestrian | human.pedestrian.adult |
pedestrian | human.pedestrian.child |
pedestrian | human.pedestrian.construction_worker |
pedestrian | human.pedestrian.police_officer |
traffic_cone | movable_object.trafficcone |
trailer | vehicle.trailer |
truck | vehicle.truck |
2.2 点云语义分割
2.2.1 标注格式
注意
点云分割的mask,(N,)
对于一份点云数据中的N
个点,mask 存储每个类别的index
2.2.2 语义类别
nuscenes lidarseg 类别
nuscenes 雷达分割挑战将原来的32个类合并为16个类
lidar segmentation index | lidar segmentation class | nuScenes-lidarseg general class |
---|---|---|
0 | void / ignore | animal |
0 | void / ignore | human.pedestrian.personal_mobility |
0 | void / ignore | human.pedestrian.stroller |
0 | void / ignore | human.pedestrian.wheelchair |
0 | void / ignore | movable_object.debris |
0 | void / ignore | movable_object.pushable_pullable |
0 | void / ignore | static_object.bicycle_rack |
0 | void / ignore | vehicle.emergency.ambulance |
0 | void / ignore | vehicle.emergency.police |
0 | void / ignore | noise |
0 | void / ignore | static.other |
0 | void / ignore | vehicle.ego |
1 | barrier | movable_object.barrier |
2 | bicycle | vehicle.bicycle |
3 | bus | vehicle.bus.bendy |
3 | bus | vehicle.bus.rigid |
4 | car | vehicle.car |
5 | construction_vehicle | vehicle.construction |
6 | motorcycle | vehicle.motorcycle |
7 | pedestrian | human.pedestrian.adult |
7 | pedestrian | human.pedestrian.child |
7 | pedestrian | human.pedestrian.construction_worker |
7 | pedestrian | human.pedestrian.police_officer |
8 | traffic_cone | movable_object.trafficcone |
9 | trailer | vehicle.trailer |
10 | truck | vehicle.truck |
11 | driveable_surface | flat.driveable_surface |
12 | other_flat | flat.other |
13 | sidewalk | flat.sidewalk |
14 | terrain | flat.terrain |
15 | manmade | static.manmade |
16 | vegetation | static.vegetation |
3. nuScenes 数据转换到 Occupancy 数据
3.1 方法
3.2 转换脚本
将 nuscenes 数据转换为 occ 预测数据
SurroundOcc: generate_occupancy_nuscenes.py
- 遍历所有帧,分别拼接动态物体和静态场景的多帧激光雷达点云
- 然后将它们合并为一个完整的场景。
- 随后,采用泊松重建(Poisson Reconstruction)来增加点云的密度,并将生成的网格体素化,以获得密集的三维占用。
- 最后,使用最近邻(Nearest Neighbor, NN)算法为密集体素分配语义标签。
将 occ 预测 数据转换为 occ 全景分割数据