nuScenes自动驾驶数据集:格式转换,模型的数据加载(二)
文章目錄
- 一、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.目標格式中部分項目名稱的解釋| images | id, filename, calibrated_param, radar_pc | 圖片相關參數 |
| annotations | id,category, location | 當前場景中的其他關于目標與車輛本身的信息 |
| videos | num_videos, scene_name | 每段video中有多個sample(關鍵幀),每個sample對應著一個scene(城市場景) |
- 下圖為data\nuscenes\annotations_3sweeps\mini_val.json(轉換后的文件格式):
- 下圖中images對應的就是上述ret中第一項,images中包含image_info,image_info中包含圖片位置,寬高等信息。
如上圖所示,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 = versionself.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]):continueif sample['prev'] == '':#如果sample的是scenes的首個關鍵幀print('scene_name', scene_name)num_videos += 1ret['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 雷達數據轉換
點云(傳感器坐標系)-> 汽車坐標系 -> 世界坐標系
如上圖所示,雷達點云通過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_intrinsiccalib = calib[:3]frame_ids[sensor_name] += 1三、模型對nuScenes的加載
- 在我的下一篇文章里
總結
以上是生活随笔為你收集整理的nuScenes自动驾驶数据集:格式转换,模型的数据加载(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: IDC是什么意思
- 下一篇: ICH10R服务器主板是什么芯片,主板上