BEVerse のデータセット前処理コードの分析

BEVerse のデータセット前処理コードの分析

主に、BEVerse プロジェクトの Nuscenes データセットの前処理部分を分析します。

1. データセット生成スクリプト

詳細なリファレンス:BEVerse/docs/data_preparation.md

  • 1.1 データ構造
BEVerse
├── mmdet3d
├── tools
├── configs
├── projects
├── data
│   ├── nuscenes
│   │   ├── maps
│   │   ├── samples
│   │   ├── sweeps
│   │   ├── v1.0-test
|   |   ├── v1.0-trainval
  • 1.2 data_info 生成スクリプト
python tools/create_data.py nuscenes --root-path ./data/nuscenes --out-dir ./data/nuscenes --extra-tag nuscenes

2. 生成されたスクリプトを詳しく見る

ファイルの場所:BEVerse/tools/create_data.py

コア処理: main 関数では、「kitti」、「nuscenes」などのデータセットのさまざまなデータ型に応じて、さまざまなデータ前処理が実行され、キーと値のペアの情報ファイルにパッケージ化され、保管されています。この記事では主に nuscenes_data_prep() 関数の分析に焦点を当てます。

メインコード:

def nuscenes_data_prep(root_path,
                       info_prefix,
                       version,
                       dataset_name,
                       out_dir,
                       max_sweeps=10):
    """Prepare data related to nuScenes dataset.

    Related data consists of '.pkl' files recording basic infos,
    2D annotations and groundtruth database.

    Args:
        root_path (str): Path of dataset root.
        info_prefix (str): The prefix of info filenames.
        version (str): Dataset version.
        dataset_name (str): The dataset class name.
        out_dir (str): Output directory of the groundtruth database info.
        max_sweeps (int): Number of input consecutive frames. Default: 10
    """

    import os
    info_save_path = 'data/nuscenes_infos'
    os.makedirs(info_save_path, exist_ok=True)

    nuscenes_converter.create_nuscenes_infos(
        root_path, info_prefix, version=version, max_sweeps=max_sweeps, info_save_path=info_save_path)

    if version == 'v1.0-test':
        info_test_path = osp.join(
            info_save_path, f'{info_prefix}_infos_test.pkl')
        nuscenes_converter.export_2d_annotation(
            root_path, info_test_path, version=version)
        return

    info_train_path = osp.join(
        info_save_path, f'{info_prefix}_infos_train.pkl')
    info_val_path = osp.join(info_save_path, f'{info_prefix}_infos_val.pkl')

    nuscenes_converter.export_2d_annotation(
        root_path, info_train_path, version=version)
    nuscenes_converter.export_2d_annotation(
        root_path, info_val_path, version=version)

Nuscenes データの前処理には主に、ストレージ パスの確立、データ情報の確立 (create_nuscenes_infos)、2D 注釈のエクスポート (export_2d_annotation) などが含まれます。

3. コア機能

3.1 データ情報の作成

1. 関数プロトタイプ
ファイルの場所:BEVerse/tools/data_converter/nuscenes_converter.py

def create_nuscenes_infos(root_path,
                          info_prefix,
                          version='v1.0-trainval',
                          max_sweeps=10,
                          info_save_path=None):
    """Create info file of nuscene dataset.

    Given the raw data, generate its related info file in pkl format.

    Args:
        root_path (str): Path of the data root.
        info_prefix (str): Prefix of the info file to be generated.
        version (str): Version of the data.
            Default: 'v1.0-trainval'
        max_sweeps (int): Max number of sweeps.
            Default: 10
    """
    ## 1. 利用Nuscenes工具导入scene场景
    from nuscenes.nuscenes import NuScenes
    nusc = NuScenes(version=version, dataroot=root_path, verbose=True)
    from nuscenes.utils import splits
    available_vers = ['v1.0-trainval', 'v1.0-test', 'v1.0-mini']
    assert version in available_vers
    if version == 'v1.0-trainval':
        train_scenes = splits.train
        val_scenes = splits.val
    elif version == 'v1.0-test':
        train_scenes = splits.test
        val_scenes = []
    elif version == 'v1.0-mini':
        train_scenes = splits.mini_train
        val_scenes = splits.mini_val
    else:
        raise ValueError('unknown')
    
    # 2. filter existing scenes.
    available_scenes = get_available_scenes(nusc)
    available_scene_names = [s['name'] for s in available_scenes]
    train_scenes = list(
        filter(lambda x: x in available_scene_names, train_scenes))
    val_scenes = list(filter(lambda x: x in available_scene_names, val_scenes))
    train_scenes = set([
        available_scenes[available_scene_names.index(s)]['token']
        for s in train_scenes
    ])
    val_scenes = set([
        available_scenes[available_scene_names.index(s)]['token']
        for s in val_scenes
    ])

    test = 'test' in version
    if test:
        print('test scene: {}'.format(len(train_scenes)))
    else:
        print('train scene: {}, val scene: {}'.format(
            len(train_scenes), len(val_scenes)))
    
    ## 3. 填充训练集验证集信息
    train_nusc_infos, val_nusc_infos = _fill_trainval_infos(
        nusc, train_scenes, val_scenes, test, max_sweeps=max_sweeps)

    info_save_path = info_save_path if info_save_path else root_path

    metadata = dict(version=version)
    if test:
        print('test sample: {}'.format(len(train_nusc_infos)))
        data = dict(infos=train_nusc_infos, metadata=metadata)
        info_path = osp.join(info_save_path,
                             '{}_infos_test.pkl'.format(info_prefix))
        mmcv.dump(data, info_path)
    else:
        print('train sample: {}, val sample: {}'.format(
            len(train_nusc_infos), len(val_nusc_infos)))
        # train infos
        data = dict(infos=train_nusc_infos, metadata=metadata)
        info_path = osp.join(info_save_path,
                             '{}_infos_train.pkl'.format(info_prefix))
        mmcv.dump(data, info_path)
        # val infos
        data['infos'] = val_nusc_infos
        info_val_path = osp.join(info_save_path,
                                 '{}_infos_val.pkl'.format(info_prefix))
        mmcv.dump(data, info_val_path)
        # trainval infos
        trainval_nusc_infos = train_nusc_infos + val_nusc_infos
        data['infos'] = trainval_nusc_infos
        info_trainval_path = osp.join(info_save_path,
                                      '{}_infos_trainval.pkl'.format(info_prefix))
        mmcv.dump(data, info_trainval_path)

2. プロセス分析
1) ミニ データセットを例に挙げると、まず Nuscenes の組み込みツールを使用して train_scenes (8 つのトレーニング シーン) と val_scenes (2 つの検証シーン) を生成します。
2) 次に、既存のシーンをフィルタリングし、既存のトレーニング シーンと検証シーンからトークンを抽出し、セットを再構成して、それぞれ と に保存しtrain_scenesますval_scenes3) 元のデータからトレーニング セットと検証セットの情報 (_fill_trainval_infos) を生成し、さまざまなデータと属性を抽出して、キーと値のペア
の形式で保存します4) 最後に、train_nusc_infos/val_nusc_infos とメタデータ (バージョンのキーと値のペア) をまとめてパックし、 に配置し、mmcv.dump を通じて pkl ファイルとして保存します。train_nusc_infosval_nusc_infos
data

3.2 2D 注釈のエクスポート

1. 関数プロトタイプ

def export_2d_annotation(root_path, info_path, version, mono3d=True):
    """Export 2d annotation from the info file and raw data.

    Args:
        root_path (str): Root path of the raw data.
        info_path (str): Path of the info file.
        version (str): Dataset version.
        mono3d (bool): Whether to export mono3d annotation. Default: True.
    """
    # get bbox annotations for camera
    camera_types = [
        'CAM_FRONT',
        'CAM_FRONT_RIGHT',
        'CAM_FRONT_LEFT',
        'CAM_BACK',
        'CAM_BACK_LEFT',
        'CAM_BACK_RIGHT',
    ]
    nusc_infos = mmcv.load(info_path)['infos']
    nusc = NuScenes(version=version, dataroot=root_path, verbose=True)
    # info_2d_list = []
    cat2Ids = [
        dict(id=nus_categories.index(cat_name), name=cat_name)
        for cat_name in nus_categories
    ]
    coco_ann_id = 0
    coco_2d_dict = dict(annotations=[], images=[], categories=cat2Ids)
    for info in mmcv.track_iter_progress(nusc_infos):
        for cam in camera_types:
            cam_info = info['cams'][cam]
            coco_infos = get_2d_boxes(
                nusc,
                cam_info['sample_data_token'],
                visibilities=['', '1', '2', '3', '4'],
                mono3d=mono3d)
            (height, width, _) = mmcv.imread(cam_info['data_path']).shape
            coco_2d_dict['images'].append(
                dict(
                    file_name=cam_info['data_path'].split('data/nuscenes/')
                    [-1],
                    id=cam_info['sample_data_token'],
                    token=info['token'],
                    cam2ego_rotation=cam_info['sensor2ego_rotation'],
                    cam2ego_translation=cam_info['sensor2ego_translation'],
                    ego2global_rotation=info['ego2global_rotation'],
                    ego2global_translation=info['ego2global_translation'],
                    cam_intrinsic=cam_info['cam_intrinsic'],
                    width=width,
                    height=height))
            for coco_info in coco_infos:
                if coco_info is None:
                    continue
                # add an empty key for coco format
                coco_info['segmentation'] = []
                coco_info['id'] = coco_ann_id
                coco_2d_dict['annotations'].append(coco_info)
                coco_ann_id += 1
    if mono3d:
        json_prefix = f'{info_path[:-4]}_mono3d'
    else:
        json_prefix = f'{info_path[:-4]}'
    mmcv.dump(coco_2d_dict, f'{json_prefix}.coco.json')

2. プロセス分析
1) まず、3.1 で格納した infos 情報である nusc_infos を取得します。
2) 10 個のカテゴリを ID インデックス値に配置します。
3) infos 情報を走査し、複数のカメラを個別に走査し、sample_data_token、さまざまな回転行列、ラベル情報などの各カメラの属性情報を coco_2d_dict に保存します。


4. 生成されるファイルとその内容の説明

参考記事3Dオブジェクト検出NuScenesデータセット

詳細についてはdocs/datasets/nuscenes_det.md英語版のドキュメントを参照してください データのディレクトリ構成は以下のとおりですが、ここでは.pkl点群を用いた手法で使用されるファイルを中心に説明します。.json画像ベースの 2D および 3D 検出など、Coco スタイルの画像ベースのアプローチです。

├── data
│   ├── nuscenes
│   │   ├── maps
│   │   ├── samples
│   │   ├── sweeps
│   │   ├── v1.0-test
|   |   ├── v1.0-trainval
│   │   ├── nuscenes_database
│   │   ├── nuscenes_infos_train.pkl
│   │   ├── nuscenes_infos_val.pkl
│   │   ├── nuscenes_infos_test.pkl
│   │   ├── nuscenes_dbinfos_train.pkl
│   │   ├── nuscenes_infos_train_mono3d.coco.json
│   │   ├── nuscenes_infos_val_mono3d.coco.json
│   │   ├── nuscenes_infos_test_mono3d.coco.json
  • nuscenes_database/xxxxx.bin: トレーニング データセットの各 3D バウンディング ボックスに含まれる点群データ。
  • nuscenes_infos_train.pkl: トレーニング データ セット情報。次の 2 つのキー値が含まれますmetadata 和 infosメタデータはデータセット自体の基本情報 ({'version': 'v1.0-trainval'} など) であり、infos には詳細情報が含まれます。
    • info['lidar_path']: LIDAR 点群データのファイル パス。
    • info['token']: サンプル データ トークン。
    • info['スイープ']: スキャン情報。有别于samples关键帧
      • info['スイープ'][i]['data_path']: i 番目のスキャンのデータ パス。
      • info['sweets'][i]['type']: スキャン データのタイプ (「lidar」など)。
      • info['スイープs'][i]['sample_data_token']: スイープ サンプル データ トークン。
      • info['スイープ'][i]['sensor2ego_translation']: 現在のセンサーから自我車両への変換 (1x3 リスト)。
      • info['スイープ'][i]['sensor2ego_rotation']: 現在のセンサーから自我車両までの回転 (クォータニオン形式の 1x4 リスト)。
      • info['スイープ'][i]['ego2global_translation']: 自我車両からグローバル座標への変換 (1x3 リスト)。
      • info['スイープ'][i]['ego2global_rotation']: 自我車両からグローバル座標への回転 (クォータニオン形式の 1x4 リスト)。
      • info['スイープ'][i]['タイムスタンプ']: スキャンされたデータのタイムスタンプ。
      • info['スイープ'][i]['sensor2lidar_translation']: 現在のセンサー (スキャン データの収集に使用) から LIDAR への変換 (1x3 リスト)。
      • info['スイープ'][i]['sensor2lidar_rotation']: 現在のセンサー (スキャン データの収集に使用) から LIDAR (クォータニオン形式の 1x4 リスト) への回転。
    • info['cams']: カメラのキャリブレーション情報。これには、各カメラに対応する 6 つのキー値 (「CAM_FRONT」、「CAM_FRONT_RIGHT」、「CAM_FRONT_LEFT」、「CAM_BACK」、「CAM_BACK_LEFT」、「CAM_BACK_RIGHT」) が含まれています。
    • info['lidar2ego_translation']: Lidar からエゴへの変換 (1x3 リスト)。
    • info['lidar2ego_rotation']: LIDAR から自我車両への回転 (クォータニオン形式の 1x4 リスト)。
    • info['ego2global_translation']: エゴからグローバル座標への変換 (1x3 リスト)。
    • info['ego2global_rotation']: 自我車両からグローバル座標への回転 (四元数形式の 1x4 リスト)。
    • info['timestamp']: サンプル データのタイムスタンプ。
    • info['gt_boxes']: 7 自由度の 3D バウンディング ボックス、Nx7 配列。
    • info['gt_names']: 3D バウンディング ボックスのカテゴリ、1xN 配列。
    • info['gt_velocity']: 3D バウンディング ボックスの速度 (不正確のため垂直方向の測定はありません)、Nx2 配列。
    • info['num_lidar_pts']: 各 3D バウンディング ボックスに含まれる LIDAR ポイントの数。
    • info['num_radar_pts']: 各 3D 境界ボックスに含まれるレーダー ポイントの数。
    • info['valid_flag']: 各境界ボックスが有効かどうか。一般に、至少一个LIDAR またはミリ波レーダー ポイントを含む 3D ボックスのみが有効なボックスと見なされます。

おすすめ

転載: blog.csdn.net/weixin_36354875/article/details/127127454