第一次接触KITTI数据集时,我被它丰富的多模态数据震撼到了。这个数据集包含了相机图像、激光雷达点云、GPS/IMU数据等多种传感器信息,是自动驾驶领域最常用的基准数据集之一。作为算法工程师,我们经常需要同时处理这些不同类型的数据,而可视化就是理解它们的最佳方式。
KITTI数据集主要包含以下几个关键部分:
我建议新手先从最简单的图像可视化开始,逐步深入到点云和联合可视化。记得第一次尝试时,我花了整整一天才搞明白如何正确显示一个3D边界框,但这个过程让我对传感器标定有了更深的理解。
在开始之前,我们需要准备以下工具:
bash复制pip install numpy opencv-python pillow matplotlib mayavi pyqt5
这里有几个关键点需要注意:
正确的目录结构对后续操作至关重要。我通常这样组织我的KITTI工作区:
code复制kitti_visualization/
├── dataset/
│ └── KITTI/
│ └── object/
│ ├── training/
│ │ ├── calib/
│ │ ├── image_2/
│ │ ├── label_2/
│ │ └── velodyne/
│ └── testing/
└── scripts/
├── visualization.py
└── utils.py
记得检查每个子目录下的文件数量是否匹配。有一次我花了两个小时debug,最后发现是漏了几个标定文件。
最简单的可视化就是从显示原始图像开始:
python复制import cv2
from PIL import Image
img = cv2.imread('dataset/KITTI/object/training/image_2/000000.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
Image.fromarray(img).show()
显示2D边界框时,KITTI的标注格式是(x_min, y_min, x_max, y_max)。我写了一个简单的绘制函数:
python复制def draw_2d_boxes(image, objects):
for obj in objects:
cv2.rectangle(image,
(int(obj.xmin), int(obj.ymin)),
(int(obj.xmax), int(obj.ymax)),
(0,255,0), 2)
return image
使用Mayavi显示原始点云:
python复制from mayavi import mlab
def show_lidar(points):
fig = mlab.figure(bgcolor=(0,0,0), size=(1280, 720))
mlab.points3d(points[:,0], points[:,1], points[:,2],
mode="point", colormap="spectral")
mlab.show()
第一次看到点云时,我被它的稀疏程度惊讶到了。实际道路上的物体在点云中可能只有几十个点,这让我理解了为什么自动驾驶感知这么具有挑战性。
将激光雷达点云投影到图像平面需要标定参数。这个转换过程涉及多个坐标系变换:
python复制def project_velo_to_image(velo_points, calib):
# 转换矩阵
R = calib.R0_rect # 旋转矩阵
P = calib.P2 # 投影矩阵
Tr_velo_to_cam = calib.Tr_velo_to_cam # 激光雷达到相机的变换
# 坐标变换
points_cam = np.dot(R, np.dot(Tr_velo_to_cam, velo_points.T)).T
points_img = np.dot(P, np.hstack([points_cam, np.ones((points_cam.shape[0],1))]).T).T
points_img[:,:2] /= points_img[:,2][:,np.newaxis]
return points_img[:,:2]
这是最具挑战性的部分,需要处理多个坐标系的转换。我总结了一个标准的流程:
python复制def compute_3d_box_cam2(h, w, l, x, y, z, yaw):
# 计算3D框的8个角点
corners = np.array([
[l/2, l/2, -l/2, -l/2, l/2, l/2, -l/2, -l/2],
[w/2, -w/2, -w/2, w/2, w/2, -w/2, -w/2, w/2],
[0, 0, 0, 0, h, h, h, h]])
# 应用旋转
rot_mat = np.array([
[np.cos(yaw), -np.sin(yaw), 0],
[np.sin(yaw), np.cos(yaw), 0],
[0, 0, 1]])
corners = np.dot(rot_mat, corners)
# 平移
corners += np.array([[x],[y],[z]])
return corners
鸟瞰图(BEV)是分析点云数据的强大工具。我通常这样处理:
python复制def create_bev(points, x_range=(0,70.4), y_range=(-40,40), res=0.1):
# 创建网格
xbins = np.arange(x_range[0], x_range[1]+res, res)
ybins = np.arange(y_range[0], y_range[1]+res, res)
# 计算2D直方图
hist, xedges, yedges = np.histogram2d(
points[:,0], points[:,1], bins=(xbins, ybins))
# 归一化并转换为图像
max_val = np.percentile(hist, 99.9)
hist = np.clip(hist, 0, max_val)
hist = (hist / max_val * 255).astype(np.uint8)
return cv2.applyColorMap(hist, cv2.COLORMAP_JET)
为了获得更好的分析效果,我开发了一个同步显示多个视图的工具:
这个工具让我能够快速验证传感器标定的准确性。有一次我发现投影的点云和图像边缘不匹配,最终发现是标定文件读取时的小数点解析错误。
KITTI使用了多种坐标系,新手很容易混淆:
我建议在代码中明确标注每个变量的坐标系,比如:
python复制points_velo # 激光雷达坐标系下的点
points_cam # 相机坐标系下的点
points_img # 图像坐标系下的点
当处理大量数据时,可视化可能会变得很慢。我总结了几个优化方法:
python复制# 点云下采样示例
def downsample(points, voxel_size=0.1):
voxel_grid = VoxelGrid(points, voxel_size)
return voxel_grid.get_sampled_points()
通过调整可视化参数,可以获得更清晰的展示效果。我常用的配置包括:
python复制# Mayavi自定义样式示例
mlab.points3d(x, y, z, mode="sphere",
scale_factor=0.2,
color=(0,1,0),
opacity=0.8)
为了提升工作效率,我将可视化工具集成到了我的开发流程中:
python复制# Jupyter中显示Mayavi图形
from mayavi import mlab
mlab.init_notebook()
mlab.points3d(x, y, z)