第4章:数据预处理:NuScenes数据集介绍、数据加载与可视化、标注格式解析(JSON)、生成BEV视角的GT
各位同学,欢迎来到第四章。说实话,这一章是整个BEV感知的基石。你想想看,没有高质量的数据,再牛的模型也是白搭。我在做这个课程之前,踩过不少数据处理的坑,今天我把这些经验都揉碎了讲给你听。
4.1 NuScenes数据集:自动驾驶界的“ImageNet”
NuScenes数据集,全称是nuScenes autonomous driving dataset。它由nuTonomy公司(现在被Aptiv收购)发布。我个人觉得,它是目前最接近工业级标准的开源数据集之一。
为什么这么说?因为它有以下几个硬核特点:
- 传感器配置全:6个摄像头(覆盖360°)、5个毫米波雷达、1个激光雷达、IMU/GPS。这配置,跟很多量产车已经非常接近了。
- 场景丰富:波士顿和新加坡两个城市,白天、黑夜、雨天、晴天都有。我记得有一次我在处理新加坡雨天的数据,那个点云噪点真是让人头疼。
- 标注精细:23个类别,40万+个标注框,还有8秒长的视频片段(每0.5秒一帧)。
- 数据量大:1000个场景,每个场景约20秒。总数据量超过1TB。
核心数据量一览
| 项目 | 数值 |
|---|---|
| 场景数 | 1000 |
| 关键帧数 | 40,000+ |
| 标注框数 | 1,400,000+ |
| 类别数 | 23 |
| 传感器数 | 12(6相机+5雷达+1激光) |
4.2 数据加载与可视化:先看看我们手里有什么牌
拿到数据集,第一件事不是训练,而是可视化。我习惯先看看数据长什么样,心里有个底。
NuScenes官方提供了一个Python库——nuscenes-devkit。安装很简单:
pip install nuscenes-devkit
加载数据的核心代码,我一般这么写:
from nuscenes.nuscenes import NuScenes
# 初始化,注意版本
nusc = NuScenes(version='v1.0-trainval',
dataroot='/path/to/nuscenes',
verbose=True)
# 查看第一个场景
my_scene = nusc.scene[0]
print(f"场景名称: {my_scene['name']}")
print(f"场景描述: {my_scene['description']}")
print(f"关键帧数量: {my_scene['nbr_samples']}")
嗯,这里要注意。版本号一定要写对。v1.0-trainval是完整版,还有mini版(v1.0-mini)适合快速测试。我刚开始用的时候,就搞混过版本,结果加载了半天报错。
可视化一个关键帧,我推荐用官方自带的渲染工具:
# 获取第一个关键帧
my_sample = nusc.sample[0]
# 渲染激光雷达点云
nusc.render_sample(my_sample['token'])
# 渲染某个相机的图像
camera_token = my_sample['data']['CAM_FRONT']
nusc.render_sample_data(camera_token)
我的小技巧:可视化时,建议先看CAM_FRONT和LIDAR_TOP这两个传感器。前者给你直观的视觉感受,后者让你理解3D空间分布。两个一起看,你就能建立起“图像-点云”的对应关系。
4.3 标注格式解析:JSON里的秘密
NuScenes的标注信息,全部存在JSON文件里。说白了,就是一堆嵌套的字典和列表。但别小看它,结构设计得非常精巧。
核心的数据表(table)有这些:
- scene:场景信息,包含场景名称、描述、关键帧token列表
- sample:关键帧,每个关键帧包含所有传感器的时间戳和数据token
- sample_data:传感器数据,比如某张图片、某帧点云
- sample_annotation:标注框,包含3D框的位置、尺寸、朝向、类别
- instance:物体实例,同一个物体在不同帧里是同一个instance
我们最关心的,当然是sample_annotation。一个典型的标注框长这样:
{
"token": "a1b2c3d4...",
"sample_token": "e5f6g7h8...",
"instance_token": "i9j0k1l2...",
"translation": [1.2, 0.5, 0.0], # 中心点坐标 (x, y, z)
"size": [1.8, 4.5, 1.5], # 尺寸 (宽, 长, 高)
"rotation": [0.0, 0.0, 0.7, 0.7], # 四元数 (w, x, y, z)
"category_name": "vehicle.car",
"num_lidar_pts": 125,
"num_radar_pts": 0
}
这里有几个关键点,我当年可是吃了亏的:
- translation:是全局坐标系下的坐标,单位是米。不是像素坐标,也不是图像坐标。
- size:顺序是宽、长、高。宽对应车的左右,长对应车的前后。别搞反了,否则你的框会旋转90度。
- rotation:四元数。如果你不熟悉四元数,建议先补一下。我习惯把它转成欧拉角,更直观。
避坑指南:我曾经在解析rotation时,直接用了四元数的前三个分量当欧拉角,结果画出来的框全部歪了。后来才发现,NuScenes的四元数顺序是 (w, x, y, z),而很多库默认是 (x, y, z, w)。一定要用 nusc.quaternion_to_rotation_matrix() 来转换。
4.4 生成BEV视角的GT:从3D到2D的降维打击
BEV感知的核心,就是把3D空间投影到鸟瞰图上。说白了,就是从上往下看。这样,我们就把3D检测问题,变成了2D检测问题。
生成BEV GT的步骤,我总结为三步:
- 确定BEV范围:比如 x 方向 [-50m, 50m],y 方向 [-50m, 50m]
- 确定分辨率:比如每个像素代表 0.1m,那么BEV图就是 1000x1000 像素
- 投影标注框:把3D框的8个角点投影到BEV平面,然后画多边形
代码实现,我一般这么写:
import numpy as np
import cv2
def generate_bev_gt(annotation, bev_range=50.0, resolution=0.1):
"""
生成BEV视角的GT
annotation: 单个标注框的字典
"""
# 1. 获取3D框参数
center = np.array(annotation['translation'])
size = np.array(annotation['size']) # w, l, h
rotation = annotation['rotation']
# 2. 计算8个角点(在物体坐标系下)
w, l, h = size
corners_3d = np.array([
[-l/2, -w/2, -h/2],
[ l/2, -w/2, -h/2],
[ l/2, w/2, -h/2],
[-l/2, w/2, -h/2],
[-l/2, -w/2, h/2],
[ l/2, -w/2, h/2],
[ l/2, w/2, h/2],
[-l/2, w/2, h/2],
])
# 3. 旋转+平移,转到全局坐标系
rot_matrix = nusc.quaternion_to_rotation_matrix(rotation)
corners_global = corners_3d @ rot_matrix.T + center
# 4. 只取BEV平面(x, y),忽略z
corners_bev = corners_global[:, :2]
# 5. 只保留底部的4个角点(z最小的4个点)
# 实际上,对于BEV,我们只需要底部的矩形
bottom_corners = corners_global[:4, :2]
# 6. 坐标映射到像素
# 注意:BEV坐标系:x向前,y向左
# 图像坐标系:u向右,v向下
u = (bottom_corners[:, 0] + bev_range) / resolution
v = (bev_range - bottom_corners[:, 1]) / resolution
# 7. 画多边形
bev_img = np.zeros((int(bev_range*2/resolution),
int(bev_range*2/resolution), 3), dtype=np.uint8)
pts = np.int32(np.stack([u, v], axis=1))
cv2.fillPoly(bev_img, [pts], (0, 255, 0))
return bev_img
核心要点:BEV GT生成时,最关键的是坐标系转换。NuScenes的全局坐标系是右手系,x向前,y向左,z向上。而图像坐标系是u向右,v向下。所以y方向要取反。这个细节,我当年调试了整整一个下午才想明白。
生成后的BEV GT,你可以直接用来训练。但要注意,不同类别的物体,在BEV图上应该用不同的颜色或通道来表示。我个人习惯用多通道的one-hot编码,每个类别一个通道。
4.5 本章知识体系
为了让你更直观地理解本章的知识结构,我画了一张流程图:
这张图把本章的知识点串起来了。从NuScenes数据集出发,经过数据加载、可视化、标注解析,最终生成BEV GT。每一步都有坑,但每一步也都有收获。
好了,这一章的内容就到这里。数据预处理是BEV感知的“地基”,地基打不牢,房子盖不高。希望你能亲手跑一遍代码,把NuScenes的数据结构摸透。下一章,我们会开始搭建真正的BEV感知模型。
课后练习:
- 下载NuScenes mini版,加载并可视化一个场景的所有关键帧
- 解析一个标注框的JSON,手动计算它的8个角点在BEV平面上的坐标
- 生成一张包含多个物体的BEV GT图,不同类别用不同颜色
可可精品资料库