ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

mmdetection3D数据集准备全攻略:从KITTI到自定义数据避坑指南

mmdetection3D数据集准备全攻略:从KITTI到自定义数据避坑指南 1. 项目概述为什么数据集准备是3D检测的“胜负手”搞3D目标检测尤其是用mmdetection3D这个框架很多朋友一上来就急着跑模型、调参结果往往在第一步——数据集准备上就栽了跟头。我见过太多人代码跑通了模型训起来了但精度死活上不去排查半天才发现是数据格式没对齐、标注信息有丢失或者预处理步骤压根没做对。这就像盖楼地基没打牢上面再怎么精雕细琢都是白搭。mmdetection3D作为一个强大的开源工具箱它支持KITTI、nuScenes、Waymo、SUN RGB-D等一系列主流3D数据集。但“支持”不代表“开箱即用”。官方代码仓库里提供的脚本和工具往往预设了特定的数据目录结构和处理流程。如果你拿到的原始数据是“毛坯房”那么“数据集准备”这个环节就是按照框架的“装修图纸”把数据整理、转换、预处理成它能直接“入住”的标准化格式。这个过程直接决定了后续所有实验的可靠性和效率。今天我就结合自己趟过的坑把mmdetection3D数据集准备的完整流程、核心细节和避坑指南给你掰开揉碎了讲清楚。无论你是想跑通KITTI的demo还是处理自定义的激光雷达点云数据这篇文章都能给你一套清晰、可复现的操作手册。2. 核心思路拆解从原始数据到训练流水线在动手之前我们必须理解mmdetection3D期望的数据是什么样子。它的核心设计思想是标准化和模块化。框架不希望用户直接操作原始的、五花八门的数据文件而是通过一套预定义的转换流程tools/dataset_converters/下的脚本将不同来源的数据集统一转换成一种中间表示格式。这个流程可以概括为以下几个关键阶段原始数据获取与理解下载官方数据集并搞清楚其目录结构、文件格式如.bin点云、.json标注、.pkl元数据和坐标系定义。这是所有工作的基础。目录结构创建严格按照mmdetection3D要求的格式在本地创建对应的文件夹。这一步看似简单但路径错一个字母后续所有脚本都会报错。数据格式转换使用框架提供的转换脚本将原始数据转换为.pkl或.bin等mmdetection3D内部使用的格式。这个过程通常包含了关键信息的提取、重新组织有时还会进行一些初步的数据清洗。生成信息文件转换完成后脚本会生成一个.pkl文件如kitti_infos_train.pkl这个文件不包含实际的数据点云、图像而是记录了所有样本的元信息索引比如每个样本对应的点云文件路径、标注信息、标定参数等。训练和测试时数据加载器就是通过读取这个信息文件来定位和加载具体数据的。(可选) 数据预处理与增强在模型训练前通常会在数据加载管道pipeline中定义一系列操作如点云范围过滤、点云采样、数据增强旋转、平移、缩放等。这部分虽然不严格属于“准备”阶段但它的配置与数据集特性强相关。理解了这个流程我们就知道所谓的“准备”核心是正确完成格式转换并生成标准的信息文件。接下来我们以最经典的KITTI数据集为例深入每个环节的实操细节。3. 实战演练以KITTI数据集为例的完整准备流程KITTI是3D检测领域的基准数据集mmdetection3D对其的支持也最为完善。通过它我们可以掌握数据集准备的全貌。3.1 环境与数据准备首先确保你已经按照官方指南安装好了mmdetection3D及其所有依赖。然后去KITTI官网下载你需要的数据。对于3D目标检测通常需要下载以下文件包数据包:data_object_image_2.zip(左图彩色图像)数据包:data_object_velodyne.zip(Velodyne 64线激光雷达点云)标注包:data_object_label_2.zip(标注文件)开发工具包:data_object_calib.zip(相机标定参数)下载后假设你将所有zip文件放在/path/to/kitti/目录下。3.2 创建标准目录结构在开始转换前我们需要先创建mmdetection3D要求的目录结构。官方推荐的结构如下mmdetection3d ├── data │ ├── kitti │ │ ├── ImageSets │ │ ├── training │ │ │ ├── calib │ │ │ ├── image_2 │ │ │ ├── label_2 │ │ │ └── velodyne │ │ └── testing │ │ ├── calib │ │ ├── image_2 │ │ └── velodyne你需要手动创建data/kitti/这个目录data目录通常与mmdetection3d代码仓库同级。然后将下载的zip文件解压并把文件移动到对应的文件夹里。这里有一个关键操作也是新手常错的地方解压后的文件夹名可能类似training/image_2你需要做的是将image_2文件夹下的所有图片文件移动到data/kitti/training/image_2/下而不是移动整个training文件夹。calib,label_2,velodyne同理。ImageSets文件夹需要单独处理。你需要从mmdetection3D源码的data/kitti/目录下找到ImageSets/文件夹将其复制到你的data/kitti/下。这个文件夹里包含了train.txt,val.txt,test.txt等文件定义了训练集、验证集和测试集的样本划分。注意绝对不要想当然地自己划分数据集。必须使用框架提供的ImageSets文件因为后续的信息文件生成和评估都依赖于这个固定的划分。自己乱划分会导致无法与官方基准结果进行公平比较。3.3 执行数据转换脚本目录准备好后就可以运行转换脚本了。转换脚本位于tools/dataset_converters/kitti_data_converter.py。打开终端进入mmdetection3d的根目录执行以下命令python tools/dataset_converters/kitti_data_converter.py --data-root ./data/kitti --save-dir ./data/kitti --workers 4参数解析--data-root: 指定你刚刚创建好的kitti数据根目录的路径。--save-dir: 指定转换后生成的.pkl信息文件保存的目录通常与>python tools/dataset_converters/kitti_converter.py --data-root ./data/kitti --save-path ./data/kitti --workers 4这个脚本会读取原始点云文件.bin。应用一个预定义的“范围过滤器”只保留感兴趣区域通常是以传感器为中心的一个立方体区域内的点云这能显著减少数据量并聚焦于相关场景。将过滤后的点云保存为新的.bin文件默认放在data/kitti/training/velodyne_reduced和testing/velodyne_reduced目录下。为什么这是可选的因为不是所有模型都需要这个步骤。一些较新的模型或配置文件可能直接使用原始点云。但为了确保兼容性特别是当你打算运行官方提供的标准配置文件时我强烈建议你执行这一步。否则在训练时可能会遇到“找不到文件”的错误。4. 核心细节与原理深度解析完成了上面的步骤数据集就算准备好了。但如果你想真正搞懂或者处理自定义数据就必须理解下面这些细节。4.1 信息文件(.pkl)里到底有什么我们可以用Python简单加载一个.pkl文件看看import pickle with open(./data/kitti/kitti_infos_train.pkl, rb) as f: infos pickle.load(f) print(fTotal samples: {len(infos)}) print(Keys of the first sample:, infos[0].keys()) # 通常输出dict_keys([image, point_cloud, calib, annos])image: 一个字典包含图像路径image_idx和图像本身的形状image_shape。point_cloud: 一个字典包含点云文件的路径velodyne_path和点云的数量num_features对于KITTI通常是4代表x, y, z, intensity。calib: 一个字典包含所有相机的内参矩阵P0-P3和从雷达坐标系到各相机坐标系的变换矩阵R0_rect,Tr_velo_to_cam。这是进行坐标系转换的生命线。annos: 最重要的部分包含所有标注信息。name: 物体类别如 ‘Car’, ‘Pedestrian’, ‘Cyclist’。truncated: 截断程度0-1。occluded: 遮挡等级0123。alpha: 观察角。bbox: 2D边界框在图像坐标系中[x1, y1, x2, y2]。dimensions: 3D尺寸 [高宽长] 注意顺序在KITTI中是h, w, l但不同框架可能不同。location: 3D位置 [x, y, z] 在相机坐标系下。rotation_y: 绕Y轴的旋转角在相机坐标系下。score: 通常用于测试结果训练时为None。关键点annos里的location,dimensions,rotation_y最初都是在相机坐标系下的。在kitti_data_converter.py脚本中会利用calib里的Tr_velo_to_cam矩阵将这些3D框转换到激光雷达坐标系下并保存在信息文件中。模型训练时读取的已经是雷达坐标系下的标注了。4.2 坐标系转换一切的根源3D感知中坐标系混乱是万恶之源。KITTI涉及至少四个坐标系相机坐标系 (Camera)原点在相机光心Z轴向前Y轴向下X轴向右。激光雷达坐标系 (LiDAR/Velodyne)原点在激光雷达中心X轴向前Y轴向左Z轴向上。图像像素坐标系 (Image)原点在图像左上角u轴向右v轴向下。世界坐标系 (World)全局坐标系。转换脚本的核心任务之一就是把标注从相机坐标系转换到激光雷达坐标系。它依赖的公式本质上是P_lidar Tr_velo_to_cam^(-1) * (R0_rect^(-1) * P_camera)其中P_camera是相机坐标系下的点如物体中心Tr_velo_to_cam是从雷达到相机的变换矩阵包含旋转和平移R0_rect是校正矩阵。求逆之后就得到了从相机到雷达的变换。实操心得当你处理自定义数据集时如果精度不对第一个要怀疑的就是坐标系定义和转换是否正确。务必弄清楚你的传感器标定文件提供的变换矩阵是从谁到谁的变换。4.3 数据增强与管道配置数据集准备妥当后在训练配置文件中你会看到一个很长的train_pipeline。这不是数据集准备的一部分但与之紧密相关。它定义了数据加载后的一系列在线处理操作。例如一个典型的点云检测pipeline可能包括train_pipeline [ dict(typeLoadPointsFromFile, ...), # 从.bin文件加载点云 dict(typeLoadAnnotations3D, ...), # 加载3D标注 dict(typeObjectSample, ...), # 数据增强从GT数据库中复制物体到当前场景 dict(typeRandomFlip3D, ...), # 随机水平翻转 dict(typeGlobalRotScaleTrans, ...), # 随机旋转、缩放、平移 dict(typePointsRangeFilter, ...), # 过滤点云范围 dict(typeObjectRangeFilter, ...), # 过滤标注范围 dict(typeDefaultFormatBundle3D, ...), # 转换为Tensor格式 dict(typeCollect3D, ...), # 指定最终需要哪些键值对进入网络 ]注意事项PointsRangeFilter和ObjectRangeFilter中设置的point_cloud_range必须与你生成velodyne_reduced时或你自定义的预处理使用的范围严格一致。通常KITTI是[0, -40, -3, 70.4, 40, 1]代表[x_min, y_min, z_min, x_max, y_max, z_max]。这个范围定义了网络的感知范围范围外的点和物体都会被丢弃。5. 处理自定义数据集思路与挑战官方数据集跑通了但更多时候我们需要处理自己的数据。这没有标准脚本但思路是相通的。5.1 核心步骤拆解模仿结构在你的data/目录下为你的数据集例如mydata创建与KITTI类似的目录结构。至少要有组织好的点云文件夹、标注文件夹和标定文件。编写转换脚本参考kitti_data_converter.py编写你自己的mydata_converter.py。脚本的核心任务是遍历你的数据列表。读取你的原始标注格式可能是JSON、XML或自定义二进制。解析出每个目标的类别、3D框位置、尺寸、朝向。这里最重要的是明确你的3D框是在哪个坐标系下定义的。如果你的标注是在相机坐标系下而模型需要在雷达坐标系下训练那么你必须进行坐标系转换。你需要提供准确的标定参数外参矩阵。将处理后的信息按照mmdetection3D信息文件的格式参考第4.1节组织成一个字典列表最后用pickle保存。修改配置文件你需要创建一个新的数据集配置文件例如mydata.py放在configs/_base_/datasets/下。这个文件主要定义data_root你的数据根路径。ann_file你上一步生成的.pkl信息文件路径。classes你的数据集中有哪些类别例如[person, car, bicycle]。这里的顺序必须和你的标注文件中的类别ID对应起来因为它决定了模型输出通道的含义。point_cloud_range你的点云有效范围。可能还需要修改pipeline中的一些参数比如点云加载的维度如果你的点云除了xyz还有其他特征。5.2 常见陷阱与解决方案问题1类别ID不匹配。你的标注文件里car的ID是0但在配置文件classes列表里car被放在了第1位索引为1。这会导致模型学习完全错误的分类。解决确保classes列表的顺序与标注ID严格对应。可以在转换脚本里建立一个映射字典。问题23D框尺寸定义不一致。KITTI的dimensions是[h, w, l]高度、宽度、长度但你的数据可能是[l, w, h]。或者KITTI的rotation_y绕的是相机Y轴而你的朝向角定义不同。解决在转换脚本中必须将你的框参数统一转换到mmdetection3D预期的格式。仔细阅读框架源码中关于框格式的定义通常在core/bbox/structures目录下。问题3点云文件格式不兼容。mmdetection3D默认的LoadPointsFromFile支持读取.bin,.npy,.npz等格式但期望点云数据是一个N x C的数组其中C至少包含3个坐标维度。解决如果你的点云是.pcd或.las格式你需要先用其他库如open3d,laspy将其读取并保存为.npy或.bin文件。在配置文件中通过coord_typeLIDAR,load_dim和use_dim参数来指定如何加载。问题4训练时Loss为NaN或非常大。排查首先检查数据是否包含非法值如inf, nan。其次检查3D框的标注是否在点云范围内是否有尺寸为0或负数的框。最后检查point_cloud_range设置是否正确如果范围设置得比实际点云小很多可能导致有效点云被全部过滤掉。6. 其他数据集nuScenes/Waymo准备要点除了KITTImmdetection3D也支持nuScenes和Waymo等大型数据集。它们的准备流程逻辑相似但细节更复杂。nuScenes数据量巨大标注信息丰富包含速度、属性等。官方提供了详细的准备脚本tools/dataset_converters/nuscenes_converter.py。你需要先下载官方的nuScenes devkit并使用其Python API来解析原始数据。nuScenes的转换会生成多个.pkl文件和一个nuscenes_infos_train.pkl等索引文件。特别注意nuScenes需要使用其官方的map expansion包来生成向量化的高清地图信息如果你要做感知预测融合这一步必不可少。Waymo数据以TFRecord格式存储。准备过程首先需要用Waymo官方的waymo_open_dataset库来解析TFRecord提取点云和标注然后再转换成mmdetection3D的格式。这个过程耗时较长且对内存要求高。一个重要的优化点是Waymo数据集通常需要被分割splitting成多个小文件来处理避免单个文件过大。通用建议对于这些大型数据集第一次运行转换脚本前务必先仔细阅读脚本开头的注释和官方文档。最好先用--num-workers 1和少量样本例如通过修改脚本只处理前10个文件跑一遍流程确保每一步都正确无误再开始全量转换否则一个错误可能意味着数小时的等待白费。7. 质量检查与验证数据集转换完成后不要急着开始训练。进行质量检查是至关重要的一步可以帮你提前发现80%的问题。可视化检查mmdetection3D提供了强大的可视化工具tools/misc/browse_dataset.py。你可以运行它来查看数据加载和增强后的效果。python tools/misc/browse_dataset.py configs/pointpillars/hv_pointpillars_secfpn_6x8_160e_kitti-3d-3class.py --show-interval 1这会弹出一个窗口循环显示数据样本。请仔细观察点云和3D框是否对齐数据增强翻转、旋转是否应用正确类别颜色映射是否正确数据统计编写一个小脚本统计信息文件中标注的数量、每个类别的实例数、3D框的平均尺寸和位置分布。严重的类别不平衡或异常的尺寸值如长度50米的“汽车”都预示着标注可能有问题。加载测试在你的训练配置文件中将workers_per_gpu设为0并设置一个很小的samples_per_gpu如1然后尝试运行一个epoch的训练或验证。如果数据流能顺利通过没有报错且Loss开始正常下降说明数据管道基本是通的。数据集准备是3D检测研究中最“脏活累活”但也是最基础的一环。它没有模型调参那样立竿见影的成就感但它的正确性决定了整个项目天花板的高度。花时间把这一步做扎实理解每一个字节的来龙去脉后续的所有工作才会事半功倍。当你成功地将自己的数据灌入mmdetection3D并跑出第一个Loss曲线时你会觉得这一切都是值得的。
返回列表