nuScenes自动驾驶数据集:格式转换,模型的数据加载(二)

您所在的位置:网站首页 怎么下载re_mini_scene nuScenes自动驾驶数据集:格式转换,模型的数据加载(二)

nuScenes自动驾驶数据集:格式转换,模型的数据加载(二)

2024-03-27 02:51| 来源: 网络整理| 查看: 265

文章目录 一、nuScenes数据集格式精解二、nuScenes数据格式转换(To COCO)数据格式转换框架2.1 核心:convert_nuScenes.py解析其他格式转换文件2.1.1 数据格式定义json数据文件加载与索引:NuScenes类(nuscenes.py) 2.1.2 遍历数据过程中完成格式转换 2.2 核心:雷达与图片数据转换细节描述2.2.1 雷达数据转换2.2.2 图片数据转换 三、模型对nuScenes的加载 笔者要通过阅读已有的nuScenes数据集,采集一套自己的简易数据集,阅读了大约三四天,与大家分享自己在工作过程中的一些体会。

针对nuScenes数据集,我发布了一系列连载文章,欢迎大家阅读: nuScenes自动驾驶数据集:数据格式精解,格式转换,模型的数据加载 (一) nuScenes自动驾驶数据集:格式转换,模型的数据加载(二) CenterFusion(多传感器融合目标检测网络)与自动驾驶数据集nuScenes:模型的数据加载(三) CenterFusion源码深度解读: CenterNet网络结构:DLA34 (四) - 多传感器融合目标检测系列 一、nuScenes数据集格式精解

[参考我这一篇文章,详细分析了数据集的格式]

二、nuScenes数据格式转换(To COCO) 数据格式转换框架

三个核心文件: convert_nuScenes.py(格式转换), nuscenes.py(提供数据访问接口) , pointcloud.py (底层雷达数据格式化),数据流向为从右向左

在这里插入图片描述

数据集结构图

如上图所示,为nuScenes的数据集结构(转换后),nuScenes的数据是原始数据,只有原始的图片、点云文件、nuScenes格式的标注文件,v1.0-mini下的所有json文件是原始数据,annotation_3sweeps下的mini_train.json文件和mini_val.json文件是转换后的所需数据。注意,编者介绍的转换方法是基于centerfusion网络模型提供的转换文件。

我们如果要使用,要根据模型与任务的不同去转换成不同的数据格式,例如利用nuScenes的工具将图片转换成COCO格式,编者所要做的任务是基于CenterFusion网络,需要COCO格式的数据,我下载的Centerfusion模型提供了格式转换的方法,通过运行他们官方提供的convert_nuScenes.py就可以,我们可以参考他们的转换方法自己写一个对应自己模型的转换函数。

2.1 核心:convert_nuScenes.py解析

这是最终转换的目标格式,存储到json文件中,ret为retention的意思,意味暂存数据,在转换完成后存储到json文件中。

ret = { 'images' 'annotations' 'categories' 'videos' 'attributes' 'pointclouds': } 表1.目标格式中部分项目名称的解释 项目名称内容名称解释imagesid, filename, calibrated_param, radar_pc图片相关参数annotationsid,category, location当前场景中的其他关于目标与车辆本身的信息videosnum_videos, scene_name每段video中有多个sample(关键帧),每个sample对应着一个scene(城市场景) 下图为data\nuscenes\annotations_3sweeps\mini_val.json(转换后的文件格式):下图中images对应的就是上述ret中第一项,images中包含image_info,image_info中包含图片位置,宽高等信息。 在这里插入图片描述

在这里插入图片描述

main()函数格式转换流程图

如上图所示,convert_nuScenes.py是转换的核心文件,其转换流程发生在main()函数中,它的主要功能是将原nuScenes中的数据按照COCO格式的要求转换其格式,其转化的主要内容为image_info与ann(皆包含在ret字典当中),这两个一个包含了coco所需要的图像标注信息,一个包含了其他的关键标注信息。

其他格式转换文件

在这里插入图片描述

 下面对主函数的关键点刨析:

2.1.1 数据格式定义

main() 函数,是文件中的核心函数,其主要功能是将数据集结构图中v1.0-mini的原始数据集转换成annotation_3sweeps下的mini_train.json文件和mini_val.json文件

def main(): if not os.path.exists(OUT_PATH): os.mkdir(OUT_PATH) for split in SPLITS: # data_path = DATA_PATH + '{}/'.format(SPLITS[split]) data_path = DATA_PATH #这里的NuScenes提供对json文件的查询和索引功能,主要用到nusc.get()方法 nusc = NuScenes( version=SPLITS[split], dataroot=data_path, verbose=True) out_path = OUT_PATH + '{}.json'.format(split) categories_info = [{'name': CATS[i], 'id': i + 1} for i in range(len(CATS))] #这里是关键 ret = {'images': [], 'annotations': [], 'categories': categories_info, 'videos': [], 'attributes': ATTRIBUTE_TO_ID, 'pointclouds': []} ... #此处省略,详细请看下面补充 if __name__ == "__main__": main()

由此,通过将所有数据传入NuScenes,生成的nusc实例,可以利用nusc对原标注json的文件进行操作:包括索引数据等,ret 字典就是转换后数据集的格式,其中images包括image_info(包含了COCO需要的图片信息:图片名称雷达点云坐标等),annotations(包含了三维标注框的大小,物体的种类等信息)…

json数据文件加载与索引:NuScenes类(nuscenes.py)

在这里插入图片描述

顾名思义,这个文件是nuscenes官方提供给我们加载nuscenes数据集的,如上图所示,这个类用来数据的读取并提供接口索引数据,是整个nuscenes-devkit的核心文件!

class NuScenes: def __init__(self, version: str = 'v1.0-mini', dataroot: str = '/data/sets/nuscenes', verbose: bool = True, map_resolution: float = 0.1): """ :param version: 数据集的名称 :param dataroot: 数据集路径 :param verbose: 加载的时候是否打印加载状态 :param map_resolution: 地图的分辨率 """ self.version = version self.dataroot = dataroot #这些都是将要读取的json文件 self.table_names = ['category', 'attribute', 'visibility', 'instance', 'sensor', 'calibrated_sensor', 'ego_pose', 'log', 'scene', 'sample', 'sample_data', 'sample_annotation', 'map'] # 在这里加载所有json文件,__load_table__使用了json.read()函数,这里仅进行了读取,还没有将每种属性名称与其真实数据对应 self.category = self.__load_table__('category') self.attribute = self.__load_table__('attribute') self.visibility = self.__load_table__('visibility') self.instance = self.__load_table__('instance') self.sensor = self.__load_table__('sensor') self.calibrated_sensor = self.__load_table__('calibrated_sensor') self.ego_pose = self.__load_table__('ego_pose') self.log = self.__load_table__('log') self.scene = self.__load_table__('scene') self.sample = self.__load_table__('sample') self.sample_data = self.__load_table__('sample_data') self.sample_annotation = self.__load_table__('sample_annotation') self.map = self.__load_table__('map') # -----------------------重要重要!------------------------ self.__make_reverse_index__(verbose) #make_reverse_index()将所有加载的json数据的token与其对应的真实数据对应,也是对数据加工的最后一步,这里请读者自行阅读 # Initialize NuScenesExplorer class,这个类是展示作用 self.explorer = NuScenesExplorer(self) 2.1.2 遍历数据过程中完成格式转换

main()中遍历所有的sample(关键帧),然后遍历每个sample里面的所有相机及传感器,以每个sample为一个单位,向ret字典中append一次sample的数据,最后写入COCO的标注文件mini-train.json中。

# sample是某个时刻车身传感器所获取的所有数据,这里的sample是所有的数据 for sample in nusc.sample: scene_name = nusc.get('scene', sample['scene_token'])['name']#取得场景名称 if not (split in ['test']) and not (scene_name in SCENE_SPLITS[split]): continue if sample['prev'] == '':#如果sample的是scenes的首个关键帧 print('scene_name', scene_name) num_videos += 1 ret['videos'].append({'id': num_videos, 'file_name': scene_name}) frame_ids = {k: 0 for k in sample['data']} track_ids = {} #遍历每一个sample中 for sensor_name in sample['data']: if sensor_name in USED_SENSOR: image_token = sample['data'][sensor_name] image_data = nusc.get('sample_data', image_token) #get的用法就是get('data', token),获得某个json文件中某个token的原始数据:图片、点云等,也就是record #ann中包含了对于目标的其他重要信息,例如目标的三维锚框,种类id,以及深度,速度等信息。 ann = { 'id': num_anns, 'image_id': num_images, 'category_id': category_id, 'dim': [box.wlh[2], box.wlh[0], box.wlh[1]], 'location': [box.center[0], box.center[1], box.center[2]], 'depth': box.center[2], ... } #image_info是COCO所需的关键信息,包括了图片的路径、大小,同时还引入了雷达点云、相机内外参矩阵等信息。 image_info = {'id': num_images, 'file_name': image_data['filename'], 'calib': calib.tolist(), 'video_id': num_videos, 'frame_id': frame_ids[sensor_name], 'sensor_id': SENSOR_ID[sensor_name], 'sample_token': sample['token'], 'trans_matrix': trans_matrix.tolist(), 'velocity_trans_matrix': velocity_trans_matrix.tolist(), 'width': sd_record['width'], 'height': sd_record['height'], 'pose_record_trans': pose_record['translation'], 'pose_record_rot': pose_record['rotation'], 'cs_record_trans': cs_record['translation'], 'cs_record_rot': cs_record['rotation'], 'radar_pc': all_radar_pcs.points.tolist(), 'camera_intrinsic': camera_intrinsic.tolist()} #将sample中的数据放入ret中暂存 ret['images'].append(image_info) ret['annotations'].append(ann)#放入标记信息 ...... #省略 在main()函数最后,通过 json.dump(ret, open(out_path, ‘w’)),将ret所有的信息导入输出路径对应的json文件中,完成格式的转换,下一步就开始模型如何进行数据的加载。 2.2 核心:雷达与图片数据转换细节描述 2.2.1 雷达数据转换

点云(传感器坐标系)-> 汽车坐标系 -> 世界坐标系 在这里插入图片描述

点云处理过程 # get radar pointclouds all_radar_pcs = RadarPointCloud(np.zeros((18, 0))) for radar_channel in RADARS_FOR_CAMERA[sensor_name]:#RADARS_FOR_CAMERA表示的是相机覆盖范围内的所有雷达索引 radar_pcs, _ = RadarPointCloud.from_file_multisweep(nusc, sample, radar_channel, sensor_name, NUM_SWEEPS) all_radar_pcs.points = np.hstack((all_radar_pcs.points, radar_pcs.points))#累加每个雷达的点云信息 image_info = { ....#其他属性 'radar_pc': all_radar_pcs.points.tolist(), ... } ret['images'].append(image_info)

如上图所示,雷达点云通过pointclouds.py所定义的RadarPointCloud.from_file_multisweep()方法获得单个雷达下多次扫描的合成点云,最后通过累加,得到的all_radar_pc是某个相机的关键帧所覆盖到的所有雷达的本次与前面多次扫描点云信息,多次扫描是为了增加点云的冲密度。

2.2.2 图片数据转换 for sensor_name in sample['data']: if sensor_name in USED_SENSOR: image_token = sample['data'][sensor_name] image_data = nusc.get('sample_data', image_token) #get的用法就是get('data', token),获得某个json文件中某个token的原始数据,此处获得图片 num_images += 1#每次的摄像头只有一张图片 # 完成坐标转换 sd_record = nusc.get('sample_data', image_token) cs_record = nusc.get('calibrated_sensor', sd_record['calibrated_sensor_token']) pose_record = nusc.get('ego_pose', sd_record['ego_pose_token']) #世界坐标系到车辆坐标系,车辆坐标系再到相机坐标系,最后求得世界坐标系到相机坐标系的转换矩阵 global_from_car = transform_matrix(pose_record['translation'], Quaternion(pose_record['rotation']), inverse=False) car_from_sensor = transform_matrix( cs_record['translation'], Quaternion(cs_record['rotation']), inverse=False) trans_matrix = np.dot(global_from_car, car_from_sensor) #同样的转换用于速度 vel_global_from_car = transform_matrix(np.array([0,0,0]), Quaternion(pose_record['rotation']), inverse=False) vel_car_from_sensor = transform_matrix(np.array([0,0,0]), Quaternion(cs_record['rotation']), inverse=False) velocity_trans_matrix = np.dot(vel_global_from_car, vel_car_from_sensor) #获得相机的内外参数 _, boxes, camera_intrinsic = nusc.get_sample_data( image_token, box_vis_level=BoxVisibility.ANY) calib = np.eye(4, dtype=np.float32) calib[:3, :3] = camera_intrinsic calib = calib[:3] frame_ids[sensor_name] += 1 三、模型对nuScenes的加载 在我的下一篇文章里


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3