小工具知识网

当前位置: 首页 > 行业知识 > mmdet3D中文注释

行业知识

mmdet3D中文注释

2026-11-13 17:41:00 来源:互联网转载
# 版权声明: 本代码版权所有 (c) OpenMMLab。import osfrom collections import OrderedDictfrom os import path as ospfrom typing import List, Tuple, Unionimport mmcvimport numpy as npfrom nuscenes.nuscenes import NuScenesfrom nuscenes.utils.geometry_utils import view_pointsfrom pyquaternion import Quaternionfrom shapely.geometry import MultiPoint, boxfrom mmdet3d.core.bbox import points_cam2imgfrom mmdet3d.datasets import NuScenesDataset# 定义nuScenes数据集的类别和属性nus_categories = ('car', 'truck', 'trailer', 'bus', 'construction_vehicle',                  'bicycle', 'motorcycle', 'pedestrian', 'traffic_cone',                  'barrier')nus_attributes = ('cycle.with_rider', 'cycle.without_rider',                  'pedestrian.moving', 'pedestrian.standing',                  'pedestrian.sitting_lying_down', 'vehicle.moving',                  'vehicle.parked', 'vehicle.stopped', 'None')def create_nuscenes_infos(root_path,                          info_prefix,                          version='v1.0-trainval',                          max_sweeps=10):    """    创建nuScenes数据集的信息文件。    给定原始数据,生成相关的信息文件,并以pkl格式保存。    参数:        root_path (str): 数据根目录路径。        info_prefix (str): 要生成的信息文件的前缀。        version (str, 可选): 数据版本,默认为'v1.0-trainval'。        max_sweeps (int, 可选): 最大sweeps数量,默认为10。    """    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('未知版本')    # 过滤现有场景    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('测试场景数量: {}'.format(len(train_scenes)))    else:        print('训练场景数量: {},验证场景数量: {}'.format(            len(train_scenes), len(val_scenes)))    train_nusc_infos, val_nusc_infos = _fill_trainval_infos(        nusc, train_scenes, val_scenes, test, max_sweeps=max_sweeps)    metadata = dict(version=version)    if test:        print('测试样本数量: {}'.format(len(train_nusc_infos)))        data = dict(infos=train_nusc_infos, metadata=metadata)        info_path = osp.join(root_path,                             '{}_infos_test.pkl'.format(info_prefix))        mmcv.dump(data, info_path)    else:        print('训练样本数量: {},验证样本数量: {}'.format(            len(train_nusc_infos), len(val_nusc_infos)))        data = dict(infos=train_nusc_infos, metadata=metadata)        info_path = osp.join(root_path,                             '{}_infos_train.pkl'.format(info_prefix))        mmcv.dump(data, info_path)        data['infos'] = val_nusc_infos        info_val_path = osp.join(root_path,                                 '{}_infos_val.pkl'.format(info_prefix))        mmcv.dump(data, info_val_path)def get_available_scenes(nusc):    """    从输入的nuScenes类中获取可用场景。    给定原始数据,获取可用场景的信息以便生成进一步的信息。    参数:        nusc (class): nuScenes数据集的类。    返回:        available_scenes (list[dict]): 可用场景的基本信息列表。    """    available_scenes = []    print('总场景数量: {}'.format(len(nusc.scene)))    for scene in nusc.scene:        scene_token = scene['token']        scene_rec = nusc.get('scene', scene_token)        sample_rec = nusc.get('sample', scene_rec['first_sample_token'])        sd_rec = nusc.get('sample_data', sample_rec['data']['LIDAR_TOP'])        has_more_frames = True        scene_not_exist = False        while has_more_frames:            lidar_path, boxes, _ = nusc.get_sample_data(sd_rec['token'])            lidar_path = str(lidar_path)            if os.getcwd() in lidar_path:                # 从lyftdataset获取的路径是绝对路径                lidar_path = lidar_path.split(f'{os.getcwd()}/')[-1]                # 相对路径            if not mmcv.is_filepath(lidar_path):                scene_not_exist = True                break            else:                break        if scene_not_exist:            continue        available_scenes.append(scene)    print('存在的场景数量: {}'.format(len(available_scenes)))    return available_scenesdef _fill_trainval_infos(nusc,                         train_scenes,                         val_scenes,                         test=False,                         max_sweeps=10):    """    从原始数据生成训练/验证信息。    参数:        nusc (:obj:`NuScenes`): nuScenes数据集类。        train_scenes (list[str]): 训练场景的基本信息。        val_scenes (list[str]): 验证场景的基本信息。        test (bool, 可选): 是否使用测试模式。在测试模式中,不能访问注释。默认为False。        max_sweeps (int, 可选): 最大sweeps数量。默认为10。    返回:        tuple[list[dict]]: 将保存到信息文件的训练集和验证集信息。    """    train_nusc_infos = []    val_nusc_infos = []    for sample in mmcv.track_iter_progress(nusc.sample):        lidar_token = sample['data']['LIDAR_TOP']        sd_rec = nusc.get('sample_data', sample['data']['LIDAR_TOP'])        cs_record = nusc.get('calibrated_sensor',                             sd_rec['calibrated_sensor_token'])        pose_record = nusc.get('ego_pose', sd_rec['ego_pose_token'])        lidar_path, boxes, _ = nusc.get_sample_data(lidar_token)        mmcv.check_file_exist(lidar_path)        info = {            'lidar_path': lidar_path,            'token': sample['token'],            'sweeps': [],            'cams': dict(),            'lidar2ego_translation': cs_record['translation'],            'lidar2ego_rotation': cs_record['rotation'],            'ego2global_translation': pose_record['translation'],            'ego2global_rotation': pose_record['rotation'],            'timestamp': sample['timestamp'],        }        l2e_r = info['lidar2ego_rotation']        l2e_t = info['lidar2ego_translation']        e2g_r = info['ego2global_rotation']        e2g_t = info['ego2global_translation']        l2e_r_mat = Quaternion(l2e_r).rotation_matrix        e2g_r_mat = Quaternion(e2g_r).rotation_matrix        # 获取每帧的6个图像的信息        camera_types = [            'CAM_FRONT',            'CAM_FRONT_RIGHT',            'CAM_FRONT_LEFT',            'CAM_BACK',            'CAM_BACK_LEFT',            'CAM_BACK_RIGHT',        ]        for cam in camera_types:            cam_token = sample['data'][cam]            cam_path, _, cam_intrinsic = nusc.get_sample_data(cam_token)            cam_info = obtain_sensor2top(nusc, cam_token, l2e_t, l2e_r_mat,                                         e2g_t, e2g_r_mat, cam)            cam_info.update(cam_intrinsic=cam_intrinsic)            info['cams'].update({cam: cam_info})        # 获取单个关键帧的sweeps        sd_rec = nusc.get('sample_data', sample['data']['LIDAR_TOP'])        sweeps = []        while len(sweeps)  0                 for anno in annotations],                dtype=bool).reshape(-1)            # 将速度从全局转换为激光雷达            for i in range(len(boxes)):                velo = np.array([*velocity[i], 0.0])                velo = velo @ np.linalg.inv(e2g_r_mat).T @ np.linalg.inv(                    l2e_r_mat).T                velocity[i] = velo[:2]            names = [b.name for b in boxes]            for i in range(len(names)):                if names[i] in NuScenesDataset.NameMapping:                    names[i] = NuScenesDataset.NameMapping[names[i]]            names = np.array(names)            # 我们需要将box尺寸转换为            # 我们激光雷达坐标系的格式            # 即x_size, y_size, z_size(对应于l, w, h)            gt_boxes = np.concatenate([locs, dims[:, [1, 0, 2]], rots], axis=1)            assert len(gt_boxes) == len(                annotations), f'{len(gt_boxes)}, {len(annotations)}'            info['gt_boxes'] = gt_boxes            info['gt_names'] = names            info['gt_velocity'] = velocity.reshape(-1, 2)            info['num_lidar_pts'] = np.array(                [a['num_lidar_pts'] for a in annotations])            info['num_radar_pts'] = np.array(                [a['num_radar_pts'] for a in annotations])            info['valid_flag'] = valid_flag        if sample['scene_token'] in train_scenes:            train_nusc_infos.append(info)        else:            val_nusc_infos.append(info)    return train_nusc_infos, val_nusc_infosdef obtain_sensor2top(nusc,                      sensor_token,                      l2e_t,                      l2e_r_mat,                      e2g_t,                      e2g_r_mat,                      sensor_type='lidar'):    """    获取从一般传感器到顶部激光雷达的RT矩阵信息。    参数:        nusc (class): nuScenes数据集类。        sensor_token (str): 与特定传感器类型对应的样本数据token。        l2e_t (np.ndarray): 从激光雷达到ego的平移向量,形状为(1, 3)。        l2e_r_mat (np.ndarray): 从激光雷达到ego的旋转矩阵,形状为(3, 3)。        e2g_t (np.ndarray): 从ego到全局的平移向量,形状为(1, 3)。        e2g_r_mat (np.ndarray): 从ego到全局的旋转矩阵,形状为(3, 3)。        sensor_type (str, 可选): 要校准的传感器类型。默认为'lidar'。    返回:        sweep (dict): 经过转换后的sweep信息。    """    sd_rec = nusc.get('sample_data', sensor_token)    cs_record = nusc.get('calibrated_sensor',                         sd_rec['calibrated_sensor_token'])    pose_record = nusc.get('ego_pose', sd_rec['ego_pose_token'])    data_path = str(nusc.get_sample_data_path(sd_rec['token']))    if os.getcwd() in data_path:  # 从lyftdataset获取的路径是绝对路径        data_path = data_path.split(f'{os.getcwd()}/')[-1]  # 相对路径    sweep = {        'data_path': data_path,        'type': sensor_type,        'sample_data_token': sd_rec['token'],        'sensor2ego_translation': cs_record['translation'],        'sensor2ego_rotation': cs_record['rotation'],        'ego2global_translation': pose_record['translation'],        'ego2global_rotation': pose_record['rotation'],        'timestamp': sd_rec['timestamp']    }    l2e_r_s = sweep['sensor2ego_rotation']    l2e_t_s = sweep['sensor2ego_translation']    e2g_r_s = sweep['ego2global_rotation']    e2g_t_s = sweep['ego2global_translation']    # 获取从传感器到顶部激光雷达的RT    # sweep->ego->global->ego'->lidar    l2e_r_s_mat = Quaternion(l2e_r_s).rotation_matrix    e2g_r_s_mat = Quaternion(e2g_r_s).rotation_matrix    R = (l2e_r_s_mat.T @ e2g_r_s_mat.T) @ (        np.linalg.inv(e2g_r_mat).T @ np.linalg.inv(l2e_r_mat).T)    T = (l2e_t_s @ e2g_r_s_mat.T + e2g_t_s) @ (        np.linalg.inv(e2g_r_mat).T @ np.linalg.inv(l2e_r_mat).T)    T -= e2g_t @ (np.linalg.inv(e2g_r_mat).T @ np.linalg.inv(l2e_r_mat).T                  ) + l2e_t @ np.linalg.inv(l2e_r_mat).T    sweep['sensor2lidar_rotation'] = R.T  # points @ R.T + T    sweep['sensor2lidar_translation'] = T    return sweepdef export_2d_annotation(root_path, info_path, version, mono3d=True):    """    从信息文件和原始数据导出2D注释。    参数:        root_path (str): 原始数据的根路径。        info_path (str): 信息文件的路径。        version (str): 数据集版本。        mono3d (bool, 可选): 是否导出mono3d注释。默认为True。    """    # 获取相机的bbox注释    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                # 添加一个空键用于coco格式                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')def get_2d_boxes(nusc,                 sample_data_token: str,                 visibilities: List[str],                 mono3d=True):    """    获取给定sample_data_token的2D注释记录。    参数:        sample_data_token (str): 属于相机关键帧的样本数据token。        visibilities (list[str]): 可见性过滤器。        mono3d (bool): 是否获取带有mono3d注释的box。    返回:        list[dict]: 属于输入sample_data_token的2D注释记录列表。    """    # 获取样本数据和与该样本数据对应的样本。    sd_rec = nusc.get('sample_data', sample_data_token)    assert sd_rec[        'sensor_modality'] == 'camera', '错误: get_2d_boxes仅适用于相机样本数据!'    if not sd_rec['is_key_frame']:        raise ValueError(            '2D重新投影仅适用于关键帧。')    s_rec = nusc.get('sample', sd_rec['sample_token'])    # 获取校准传感器和自我姿态记录,以获取转换矩阵。    cs_rec = nusc.get('calibrated_sensor', sd_rec['calibrated_sensor_token'])    pose_rec = nusc.get('ego_pose', sd_rec['ego_pose_token'])    camera_intrinsic = np.array(cs_rec['camera_intrinsic'])    # 获取所有具有指定可见性的注释。    ann_recs = [        nusc.get('sample_annotation', token) for token in s_rec['anns']    ]    ann_recs = [        ann_rec for ann_rec in ann_recs        if (ann_rec['visibility_token'] in visibilities)    ]    repro_recs = []    for ann_rec in ann_recs:        # 增加sample_annotation的token信息。        ann_rec['sample_annotation_token'] = ann_rec['token']        ann_rec['sample_data_token'] = sample_data_token        # 获取全局坐标中的box。        box = nusc.get_box(ann_rec['token'])        # 将它们移动到自我姿态框架。        box.translate(-np.array(pose_rec['translation']))        box.rotate(Quaternion(pose_rec['rotation']).inverse)        # 将它们移动到校准传感器框架。        box.translate(-np.array(cs_rec['translation']))        box.rotate(Quaternion(cs_rec['rotation']).inverse)        # 过滤掉不在校准传感器前面的角。        corners_3d = box.corners()        in_front = np.argwhere(corners_3d[2, :] > 0).flatten()        corners_3d = corners_3d[:, in_front]        # 将3D box投影到2D。        corner_coords = view_points(corners_3d, camera_intrinsic,                                    True).T[:, :2].tolist()        # 仅保留落在图像内的角。        final_coords = post_process_coords(corner_coords)        # 如果重新投影的角的凸包不与图像画布相交,则跳过。        if final_coords is None:            continue        else:            min_x, min_y, max_x, max_y = final_coords        # 生成要包含在.json文件中的字典记录。        repro_rec = generate_record(ann_rec, min_x, min_y, max_x, max_y,                                    sample_data_token, sd_rec['filename'])        # 如果mono3d=True,则在相机坐标中添加3D注释        if mono3d and (repro_rec is not None):            loc = box.center.tolist()            dim = box.wlh            dim[[0, 1, 2]] = dim[[1, 2, 0]]  # 将wlh转换为我们的lhw            dim = dim.tolist()            rot = box.orientation.yaw_pitch_roll[0]            rot = [-rot]  # 将旋转转换为我们的相机坐标            global_velo2d = nusc.box_velocity(box.token)[:2]            global_velo3d = np.array([*global_velo2d, 0.0])            e2g_r_mat = Quaternion(pose_rec['rotation']).rotation_matrix            c2e_r_mat = Quaternion(cs_rec['rotation']).rotation_matrix            cam_velo3d = global_velo3d @ np.linalg.inv(                e2g_r_mat).T @ np.linalg.inv(c2e_r_mat).T            velo = cam_velo3d[0::2].tolist()            repro_rec['bbox_cam3d'] = loc + dim + rot            repro_rec['velo_cam3d'] = velo            center3d = np.array(loc).reshape([1, 3])            center2d = points_cam2img(                center3d, camera_intrinsic, with_depth=True)            repro_rec['center2d'] = center2d.squeeze().tolist()            # 标准化center2D + 深度            # 如果深度小于0的样本将被移除            if repro_rec['center2d'][2]  Union[Tuple[float, float, float, float], None]:    """    获取重新投影的bbox角的凸包和图像画布的交集,如果没有交集则返回None。    参数:        corner_coords (list[int]): 重新投影的bbox角的坐标。        imsize (tuple[int]): 图像画布的尺寸。    返回:        tuple[float]: 2D box角的凸包和图像画布的交集。    """    polygon_from_2d_box = MultiPoint(corner_coords).convex_hull    img_canvas = box(0, 0, imsize[0], imsize[1])    if polygon_from_2d_box.intersects(img_canvas):        img_intersection = polygon_from_2d_box.intersection(img_canvas)        intersection_coords = np.array(            [coord for coord in img_intersection.exterior.coords])        min_x = min(intersection_coords[:, 0])        min_y = min(intersection_coords[:, 1])        max_x = max(intersection_coords[:, 0])        max_y = max(intersection_coords[:, 1])        return min_x, min_y, max_x, max_y    else:        return Nonedef generate_record(ann_rec: dict, x1: float, y1: float, x2: float, y2: float,                    sample_data_token: str, filename: str) -> OrderedDict:    """    给定各种信息和2D边界框坐标,生成一个2D注释记录。    参数:        ann_rec (dict): 原始3d注释记录。        x1 (float): x坐标的最小值。        y1 (float): y坐标的最小值。        x2 (float): x坐标的最大值。        y2 (float): y坐标的最大值。        sample_data_token (str): 样本数据token。        filename (str): 注释所在的对应图像文件。    返回:        dict: 一个2D注释记录。            - file_name (str): 文件名            - image_id (str): 样本数据token            - area (float): 2d box的面积            - category_name (str): 类别名称            - category_id (int): 类别id            - bbox (list[float]): 2d box的左x, 顶y, dx, dy            - iscrowd (int): 区域是否是拥挤的    """    repro_rec = OrderedDict()    repro_rec['sample_data_token'] = sample_data_token    coco_rec = dict()    relevant_keys = [        'attribute_tokens',        'category_name',        'instance_token',        'next',        'num_lidar_pts',        'num_radar_pts',        'prev',        'sample_annotation_token',        'sample_data_token',        'visibility_token',    ]    for key, value in ann_rec.items():        if key in relevant_keys:            repro_rec[key] = value    repro_rec['bbox_corners'] = [x1, y1, x2, y2]    repro_rec['filename'] = filename    coco_rec['file_name'] = filename    coco_rec['image_id'] = sample_data_token    coco_rec['area'] = (y2 - y1) * (x2 - x1)    if repro_rec['category_name'] not in NuScenesDataset.NameMapping:        return None    cat_name = NuScenesDataset.NameMapping[repro_rec['category_name']]    coco_rec['category_name'] = cat_name    coco_rec['category_id'] = nus_categories.index(cat_name)    coco_rec['bbox'] = [x1, y1, x2 - x1, y2 - y1]    coco_rec['iscrowd'] = 0    return coco_rec

上一篇:linux怎么用postfix服务器?

下一篇:细探PHP源码奥秘,深度解析PHP源代码世界