| import math |
| import numpy as np |
| from nuscenes.eval.common.utils import quaternion_yaw, Quaternion |
| from nuscenes.utils.data_classes import Box as NuScenesBox |
| import pyquaternion |
|
|
|
|
| def output_to_nusc_box(detection): |
| """Convert the output to the box class in the nuScenes. |
| Args: |
| detection (dict): Detection results. |
| - boxes_3d (:obj:`BaseInstance3DBoxes`): Detection bbox. |
| - scores_3d (torch.Tensor): Detection scores. |
| - labels_3d (torch.Tensor): Predicted box labels. |
| Returns: |
| list[:obj:`NuScenesBox`]: List of standard NuScenesBoxes. |
| """ |
| box3d = detection["boxes_3d"] |
| scores = detection["scores_3d"].numpy() |
| labels = detection["labels_3d"].numpy() |
| if "track_ids" in detection: |
| ids = detection["track_ids"].numpy() |
| else: |
| ids = np.ones_like(labels) |
|
|
| box_gravity_center = box3d.gravity_center.numpy() |
| box_dims = box3d.dims.numpy() |
| box_yaw = box3d.yaw.numpy() |
|
|
| |
| |
| |
| |
| box_dims[:, [0, 1, 2]] = box_dims[:, [1, 0, 2]] |
|
|
| box_list = [] |
| for i in range(len(box3d)): |
| quat = pyquaternion.Quaternion(axis=[0, 0, 1], radians=box_yaw[i]) |
| velocity = (*box3d.tensor[i, 7:9], 0.0) |
| |
| |
| |
| |
| box = NuScenesBox( |
| box_gravity_center[i], |
| box_dims[i], |
| quat, |
| label=labels[i], |
| score=scores[i], |
| velocity=velocity, |
| ) |
| box.token = ids[i] |
| box_list.append(box) |
| return box_list |
|
|
|
|
| def output_to_nusc_box_det(detection): |
| """Convert the output to the box class in the nuScenes. |
| |
| Args: |
| detection (dict): Detection results. |
| |
| - boxes_3d (:obj:`BaseInstance3DBoxes`): Detection bbox. |
| - scores_3d (torch.Tensor): Detection scores. |
| - labels_3d (torch.Tensor): Predicted box labels. |
| |
| Returns: |
| list[:obj:`NuScenesBox`]: List of standard NuScenesBoxes. |
| """ |
| if "boxes_3d_det" in detection: |
| box3d = detection["boxes_3d_det"] |
| scores = detection["scores_3d_det"].numpy() |
| labels = detection["labels_3d_det"].numpy() |
| else: |
| box3d = detection["boxes_3d"] |
| scores = detection["scores_3d"].numpy() |
| labels = detection["labels_3d"].numpy() |
|
|
| box_gravity_center = box3d.gravity_center.numpy() |
| box_dims = box3d.dims.numpy() |
| box_yaw = box3d.yaw.numpy() |
|
|
| |
| |
| |
| |
| |
| |
| |
| box_dims[:, [0, 1, 2]] = box_dims[:, [1, 0, 2]] |
|
|
| box_list = [] |
| for i in range(len(box3d)): |
| quat = Quaternion(axis=[0, 0, 1], radians=box_yaw[i]) |
| velocity = (*box3d.tensor[i, 7:9], 0.0) |
| box = NuScenesBox( |
| box_gravity_center[i], |
| box_dims[i], |
| quat, |
| label=labels[i], |
| score=scores[i], |
| velocity=velocity, |
| ) |
| box_list.append(box) |
|
|
| return box_list |
|
|
|
|
| def lidar_nusc_box_to_global( |
| info, boxes, classes, eval_configs, eval_version="detection_cvpr_2019" |
| ): |
| """Convert the box from ego to global coordinate. |
| Args: |
| info (dict): Info for a specific sample data, including the |
| calibration information. |
| boxes (list[:obj:`NuScenesBox`]): List of predicted NuScenesBoxes. |
| classes (list[str]): Mapped classes in the evaluation. |
| eval_configs (object): Evaluation configuration object. |
| eval_version (str, optional): Evaluation version. |
| Default: 'detection_cvpr_2019' |
| Returns: |
| list: List of standard NuScenesBoxes in the global |
| coordinate. |
| """ |
| box_list = [] |
| keep_idx = [] |
| for i, box in enumerate(boxes): |
| |
| box.rotate(Quaternion(info["lidar2ego_rotation"])) |
| box.translate(np.array(info["lidar2ego_translation"])) |
| |
| cls_range_map = eval_configs.class_range |
| radius = np.linalg.norm(box.center[:2], 2) |
|
|
| if classes[box.label] not in cls_range_map: |
| continue |
| det_range = cls_range_map[classes[box.label]] |
| if radius > det_range: |
| continue |
| |
| box.rotate(Quaternion(info["ego2global_rotation"])) |
| box.translate(np.array(info["ego2global_translation"])) |
| box_list.append(box) |
| keep_idx.append(i) |
| return box_list, keep_idx |
|
|
|
|
| def obtain_map_info( |
| nusc, |
| nusc_maps, |
| sample, |
| patch_size=(102.4, 102.4), |
| canvas_size=(256, 256), |
| layer_names=["lane_divider", "road_divider"], |
| thickness=10, |
| ): |
| """ |
| Export 2d annotation from the info file and raw data. |
| """ |
| l2e_r = sample["lidar2ego_rotation"] |
| l2e_t = sample["lidar2ego_translation"] |
| e2g_r = sample["ego2global_rotation"] |
| e2g_t = sample["ego2global_translation"] |
| l2e_r_mat = Quaternion(l2e_r).rotation_matrix |
| e2g_r_mat = Quaternion(e2g_r).rotation_matrix |
|
|
| scene = nusc.get("scene", sample["scene_token"]) |
| log = nusc.get("log", scene["log_token"]) |
| nusc_map = nusc_maps[log["location"]] |
| if layer_names is None: |
| layer_names = nusc_map.non_geometric_layers |
|
|
| l2g_r_mat = (l2e_r_mat.T @ e2g_r_mat.T).T |
| l2g_t = l2e_t @ e2g_r_mat.T + e2g_t |
| patch_box = (l2g_t[0], l2g_t[1], patch_size[0], patch_size[1]) |
| patch_angle = math.degrees(Quaternion(matrix=l2g_r_mat).yaw_pitch_roll[0]) |
|
|
| |
| map_mask = nusc_map.get_map_mask( |
| patch_box, patch_angle, layer_names, canvas_size=canvas_size |
| ) |
|
|
| |
| map_mask = map_mask[-2] | map_mask[-1] |
| map_mask = map_mask[np.newaxis, :] |
| map_mask = map_mask.transpose((2, 1, 0)).squeeze(2) |
|
|
| |
| erode = nusc_map.get_map_mask( |
| patch_box, patch_angle, ["drivable_area"], canvas_size=canvas_size |
| ) |
| erode = erode.transpose((2, 1, 0)).squeeze(2) |
|
|
| map_mask = np.concatenate([erode[None], map_mask[None]], axis=0) |
| return map_mask |
|
|