示例#1
0
class NuscenesDataset(Dataset):

    def __init__(self, data_root, version = 'v1.0-trainval', n: tuple=None):
        """
        Args:
            root_dir (string): root NuScenes directory
            transform (callable, optional): Optional transform to be applied on a sample.
            
            n - tuple with left and right index boundaries, in case we don't want to use all data
        """
        
        self.data_root = data_root
        self.nuscenes = NuScenes(version=version, dataroot=data_root, verbose=True)
       
        if n:
            assert len(n) == 2
            self.samples = self.nuscenes.sample[n[0]:n[1]]
        else:
            self.samples = self.nuscenes.sample
            
        # point_clouds_features will be of shape (N, M, 5), 
        # where N - number of samples, M - number of points in according sample's pointcloud
        self.point_clouds_features = []
        self.point_clouds_labels = []
        
        self.__set_point_clouds()
            
    def __set_point_clouds(self):

        for sample in self.samples:
            
            sample_data_token = sample['data']['LIDAR_TOP']

            my_sample_lidar_data = self.nuscenes.get('sample_data', sample_data_token)

            lidarseg_labels_filename = join(self.data_root,
                                            self.nuscenes.get('lidarseg', sample_data_token)['filename'])

            # loading directly from files to perceive the ring_index information
            points_raw = np.fromfile(self.data_root + my_sample_lidar_data["filename"], dtype=np.float32).reshape((-1, 5))
            point_labels = np.fromfile(lidarseg_labels_filename, dtype=np.uint8)
            
            self.point_clouds_features.append(points_raw)
            self.point_clouds_labels.append(point_labels)
            
        self.point_clouds_features = np.array(self.point_clouds_features)
        self.point_clouds_labels = np.array(self.point_clouds_labels)

    def __len__(self):
        return len(self.samples)

    def get_front_bb(self, sample: dict):
        """
        Function computes and returns
        An array of points of a bounding box in sensor coordinates.
        Which are in the front 90 degrees
        Each point is a (x, y) coordinate, each BB is 4 points

        :param sample: nuscenes sample dictionary
        :return: np.array of shape (N, 8, 3)
        """
        my_sample_lidar_data = self.nuscenes.get('sample_data', sample['data']['LIDAR_TOP'])
        sample_annotation = self.nuscenes.get_boxes(my_sample_lidar_data['token'])

        ego_record = self.nuscenes.get('ego_pose', my_sample_lidar_data['ego_pose_token'])
        cs_record = self.nuscenes.get('calibrated_sensor', my_sample_lidar_data['calibrated_sensor_token'])

        # first step: transform from absolute to ego
        ego_translation = -np.array(ego_record['translation'])

        # second step: transform from ego to sensor
        cs_translation = -np.array(cs_record['translation'])

        corners = []
        for box in sample_annotation:
            box.translate(ego_translation)
            box.rotate(Quaternion(ego_record['rotation']).inverse)

            box.translate(cs_translation)
            box.rotate(Quaternion(cs_record['rotation']).inverse)

            # at this point bounding boxes are in sensor coordinate system
            # now we want to exclude such BB that do not have their center
            # lying in the front 90 degrees

            if box.center[1] <= 0:
                continue

            box.center_azimuth = np.degrees(np.arctan(box.center[0] / box.center[1]))

            # Transform front azimuth to be in range from 0 to 180
            box.center_azimuth = 90 - box.center_azimuth
            if not (45 < box.center_azimuth < 135):
                continue

            corners.append(box.bottom_corners())
        
        if len(corners) > 0:
            return np.transpose(np.array(corners), (0, 2, 1))
        else:
            return np.zeros((1, 4, 3))
    
    def points_in_box(self, coordinates, bounding_box_corners):
        """
            bounding_box_corners: bbc of a single bb
            return a mask of whether points that are in the box
        """
        coords_x = coordinates[0]
        coords_y = coordinates[1]
        
        min_bb_x = bounding_box_corners[:, 0].min()
        max_bb_x = bounding_box_corners[:, 0].max()
        min_bb_y = bounding_box_corners[:, 1].min()
        max_bb_y = bounding_box_corners[:, 1].max()

        c1 = min_bb_x <= coords_x  # left_top/left_bottom.x <= coordinate.x
        c2 = max_bb_x >= coords_x  # right_bottom/right_top.x >= coordinate.x
        c3 = min_bb_y <= coords_y  # left/right_bottom.y <= coordinate.y
        c4 = max_bb_y >= coords_y  # right_top/left_top.y >= coordinate.y

        c = np.logical_and(np.logical_and(c1, c2),
                           np.logical_and(c3, c4))
        return c
    
    def get_bb_targets(self, idx, bounding_box_corners):
        coordinates = self.point_clouds_features[idx][:2] 
    
        for bb_c in bounding_box_corners:

            if self.points_in_box(coordinates, bb_c).any():
                return np.array(bb_c[:, :2])
            else:
                return np.zeros_like(bb_c[:, :2])
    
    def __getitem__(self, idx, compute_boxes=True):
        """
        compute_box will be set to False in child classes, so target boxes are computed after rotations
        
        """
        
        sample_idx = self.samples[idx]
        pcl_features = self.point_clouds_features[idx]
        pcl_labels = self.point_clouds_labels[idx]
        
        if compute_boxes:
            front_bbs = self.get_front_bb(sample_idx)
            target_bounding_boxes = self.get_bb_targets(idx, front_bbs)
    
            return pcl_features, pcl_labels, target_bounding_boxes
        
        else:
            return pcl_features, pcl_labels, []
示例#2
0
class CustomNuscDatasetMMS(Dataset):
    NumPointFeatures = 4

    def __init__(
            self,
            root_path=f'/media/starlet/LdTho/data/sets/nuscenes/v1.0-{VERSION}',
            info_path=None,
            class_names=["traffic_cone"],
            prep_func=None,
            num_point_features=None):
        self.NumPointFeatures = 4
        self.class_names = class_names
        self.nusc = NuScenes(dataroot=root_path, version=f'v1.0-{VERSION}')
        self._prep_func = prep_func
        self.filtered_sample_tokens = []
        for sample in self.nusc.sample:
            sample_token = sample['token']
            sample_lidar_token = sample['data']['LIDAR_TOP']
            boxes = self.nusc.get_boxes(sample_lidar_token)
            box_names = [
                NameMapping[b.name] for b in boxes
                if b.name in NameMapping.keys()
            ]
            for box in boxes:
                if box.name not in NameMapping.keys():
                    continue
                # if NameMapping[box.name] in self.class_names:
                if (NameMapping[box.name] in [
                        "traffic_cone"
                ]) & (box_names.count('traffic_cone') > MIN_CONES_PER_SAMPLE):
                    self.filtered_sample_tokens.append(sample_token)
                    break
        self.filtered_sample_tokens = self.filtered_sample_tokens[:round(
            len(self.filtered_sample_tokens) * TRAINVAL_SPLIT_PERCENTAGE)]

        self.split = np.arange(len(self.filtered_sample_tokens))

    def __len__(self):
        return self.split.shape[0]

    def __getitem__(self, index):
        input_dict = self.get_sensor_data(index)
        example = self._prep_func(input_dict=input_dict)
        example["metadata"] = input_dict["metadata"]
        if "anchors_mask" in example:
            example["anchors_mask"] = example["anchors_mask"].astype(np.uint8)
        return example

    def get_sensor_data(self, query, token=None):
        res = {
            'lidar': {
                'type': 'lidar',
                'points': None,
            },
            'metadata': {
                'token': self.filtered_sample_tokens[query]
            }
        }
        if token:
            query = self.filtered_sample_tokens.index(token)
        points = self.getPoints(query)
        boxes_dict = self.getBoxes(query)

        res['lidar']['points'] = points

        gt_boxes = []
        gt_names = []

        for box in boxes_dict:
            xyz = box.center
            wlh = box.wlh
            theta = quaternion_yaw(box.orientation)
            gt_boxes.append([
                xyz[0], xyz[1], xyz[2], wlh[0], wlh[1], wlh[2],
                -theta - np.pi / 2
            ])
            gt_names.append(box.name)
        gt_boxes = np.concatenate(gt_boxes).reshape(-1, 7)
        gt_names = np.array(gt_names)
        res['lidar']['annotations'] = {
            'boxes': gt_boxes,
            'names': gt_names,
        }
        return res

        ###

    def getPoints(self, index):
        sample = self.nusc.get('sample', self.filtered_sample_tokens[index])
        sample_lidar_token = sample['data']['LIDAR_TOP']

        lidar_data = self.nusc.get('sample_data', sample_lidar_token)
        ego_pose = self.nusc.get('ego_pose', lidar_data['ego_pose_token'])
        calibrated_sensor = self.nusc.get(
            'calibrated_sensor', lidar_data['calibrated_sensor_token'])

        global_from_car = transform_matrix(ego_pose['translation'],
                                           Quaternion(ego_pose['rotation']),
                                           inverse=False)
        car_from_sensor = transform_matrix(calibrated_sensor['translation'],
                                           Quaternion(
                                               calibrated_sensor['rotation']),
                                           inverse=False)
        try:
            lidar_pointcloud, times = LidarPointCloud.from_file_multisweep(
                self.nusc, sample, 'LIDAR_TOP', 'LIDAR_TOP')
            lidar_pointcloud.transform(car_from_sensor)
        except Exception as e:
            print(f"Failed to load Lidar Pointcloud for {sample}:{e}")
        points = lidar_pointcloud.points
        points[3, :] /= 255
        points[3, :] -= 0.5

        # points_cat = np.concatenate([points, times], axis=0).transpose()
        points_cat = points.transpose()
        points_cat = points_cat[~np.isnan(points_cat).any(axis=1)]

        return points_cat

    def getBoxes(self, index):

        sample = self.nusc.get('sample', self.filtered_sample_tokens[index])
        sample_lidar_token = sample['data']['LIDAR_TOP']
        lidar_data = self.nusc.get('sample_data', sample_lidar_token)
        ego_pose = self.nusc.get('ego_pose', lidar_data['ego_pose_token'])

        boxes_dict = self.nusc.get_boxes(sample_lidar_token)

        keep_box_idx = []
        for i, box in enumerate(boxes_dict):
            if box.name not in NameMapping.keys():
                continue
            if NameMapping[box.name] in self.class_names:
                box.name = NameMapping[box.name]
                keep_box_idx.append(i)

        boxes_dict = [
            box for i, box in enumerate(boxes_dict) if i in keep_box_idx
        ]
        self.move_boxes_to_car_space(boxes_dict, ego_pose)
        # print(boxes_dict)
        return boxes_dict

    def move_boxes_to_car_space(self, boxes, ego_pose):
        """
        Move boxes from world space to car space.
        Note: mutates input boxes.
        """
        translation = -np.array(ego_pose['translation'])
        rotation = Quaternion(ego_pose['rotation']).inverse

        for box in boxes:
            # Bring box to car space
            box.translate(translation)
            box.rotate(rotation)
示例#3
0
def parse_sequence(
        data: NuScenes, add_nonkey_frames: bool,
        scene_info: Tuple[str, str]) -> Tuple[List[Frame], List[FrameGroup]]:
    """Parse a full NuScenes sequence and convert it into scalabel frames."""
    sample_token, scene_name = scene_info
    frames, groups = [], []
    frame_index = 0
    while sample_token:
        sample = data.get("sample", sample_token)
        lidar_token = sample["data"]["LIDAR_TOP"]
        lidar_data = data.get("sample_data", lidar_token)
        calibration_lidar = data.get("calibrated_sensor",
                                     lidar_data["calibrated_sensor_token"])
        timestamp = lidar_data["timestamp"]
        lidar_filepath = lidar_data["filename"]
        ego_pose = data.get("ego_pose", lidar_data["ego_pose_token"])
        frame_names = []
        next_nonkey_frames = []
        for cam in cams:
            cam_token = sample["data"][cam]
            boxes = data.get_boxes(lidar_token)
            frame, next_token = parse_frame(data, scene_name, frame_index,
                                            cam_token, boxes)
            frame_names.append(frame.name)
            frames.append(frame)
            if add_nonkey_frames and next_token is not None:
                next_nonkey_frames.append(next_token)

        group = FrameGroup(
            name=sample_token,
            videoName=scene_name,
            frameIndex=frame_index,
            url=lidar_filepath,
            extrinsics=get_extrinsics(ego_pose, calibration_lidar),
            timestamp=timestamp,
            frames=frame_names,
            labels=parse_labels(data, data.get_boxes(lidar_token), ego_pose,
                                calibration_lidar),
        )
        groups.append(group)
        frame_index += 1

        nonkey_count = 0
        while len(next_nonkey_frames) > 0:
            new_next_nonkey_frames = []
            frame_names = []
            nonkey_count += 1
            for cam_token in next_nonkey_frames:
                assert cam_token is not None, "camera for non-key missing!"
                frame, next_token = parse_frame(data, scene_name, frame_index,
                                                cam_token)
                frame_names.append(frame.name)
                frames.append(frame)
                if add_nonkey_frames and next_token is not None:
                    new_next_nonkey_frames.append(next_token)

            group = FrameGroup(
                name=sample_token + f"_{nonkey_count}",
                videoName=scene_name,
                frameIndex=frame_index,
                frames=frame_names,
            )
            groups.append(group)
            next_nonkey_frames = new_next_nonkey_frames
            frame_index += 1

        sample_token = sample["next"]

    return frames, groups
示例#4
0
class CustomNuscEvalDataset(Dataset):
    NumPointFeatures = 5

    def __init__(
            self,
            root_path=f'/media/starlet/LdTho/data/sets/nuscenes/v1.0-{EVAL_VERSION}',
            info_path=None,
            class_names=["traffic_cone"],
            prep_func=None,
            num_point_features=None):
        self.NumPointFeatures = 5
        self.class_names = class_names
        self.nusc = NuScenes(dataroot=root_path,
                             version=f'v1.0-{EVAL_VERSION}')
        self._prep_func = prep_func
        self.root_path = root_path
        self.eval_version = "detection_cvpr_2019"
        # self.filtered_sample_tokens = [s['token'] for s in self.nusc.sample]

        self.filtered_sample_tokens = []
        for sample in self.nusc.sample:
            sample_token = sample['token']
            sample_lidar_token = sample['data']['LIDAR_TOP']
            boxes = self.nusc.get_boxes(sample_lidar_token)
            box_names = [
                NameMapping[b.name] for b in boxes
                if b.name in NameMapping.keys()
            ]
            for box in boxes:
                if box.name not in NameMapping.keys():
                    continue
                # if NameMapping[box.name] in self.class_names:
                if (NameMapping[box.name] in [
                        "traffic_cone"
                ]) & (box_names.count('traffic_cone') > MIN_CONES_PER_SAMPLE):
                    self.filtered_sample_tokens.append(sample_token)
                    break
        if EVAL_VERSION == "trainval":
            self.filtered_sample_tokens = self.filtered_sample_tokens[round(
                len(self.filtered_sample_tokens) * TRAINVAL_SPLIT_PERCENTAGE):]

        self.split = np.arange(len(self.filtered_sample_tokens))

    def __len__(self):
        return self.split.shape[0]

    def __getitem__(self, index):
        input_dict = self.get_sensor_data(index)
        example = self._prep_func(input_dict=input_dict)
        example["metadata"] = input_dict["metadata"]
        if "anchors_mask" in example:
            example["anchors_mask"] = example["anchors_mask"].astype(np.uint8)
        return example

    def get_sensor_data(self, query, token=None):
        res = {
            'lidar': {
                'type': 'lidar',
                'points': None,
            },
            'metadata': {
                'token': self.filtered_sample_tokens[query]
            }
        }
        if token:
            query = self.filtered_sample_tokens.index(token)
        points = self.getPoints(query)
        boxes_dict = self.getBoxes(query)

        res['lidar']['points'] = points

        gt_boxes = []
        gt_names = []

        for box in boxes_dict:
            xyz = box.center
            wlh = box.wlh
            theta = quaternion_yaw(box.orientation)
            gt_boxes.append([
                xyz[0], xyz[1], xyz[2], wlh[0], wlh[1], wlh[2],
                -theta - np.pi / 2
            ])
            gt_names.append(box.name)
        gt_boxes = np.concatenate(gt_boxes).reshape(-1, 7)
        gt_names = np.array(gt_names)
        res['lidar']['annotations'] = {
            'boxes': gt_boxes,
            'names': gt_names,
        }
        return res

        ###

    def getPoints(self, index):
        sample = self.nusc.get('sample', self.filtered_sample_tokens[index])
        sample_lidar_token = sample['data']['LIDAR_TOP']

        lidar_data = self.nusc.get('sample_data', sample_lidar_token)
        ego_pose = self.nusc.get('ego_pose', lidar_data['ego_pose_token'])
        calibrated_sensor = self.nusc.get(
            'calibrated_sensor', lidar_data['calibrated_sensor_token'])

        global_from_car = transform_matrix(ego_pose['translation'],
                                           Quaternion(ego_pose['rotation']),
                                           inverse=False)
        car_from_sensor = transform_matrix(calibrated_sensor['translation'],
                                           Quaternion(
                                               calibrated_sensor['rotation']),
                                           inverse=False)
        try:
            lidar_pointcloud, times = LidarPointCloud.from_file_multisweep(
                self.nusc, sample, 'LIDAR_TOP', 'LIDAR_TOP')
            lidar_pointcloud.transform(car_from_sensor)
        except Exception as e:
            print(f"Failed to load Lidar Pointcloud for {sample}:{e}")
        points = lidar_pointcloud.points
        points[3, :] /= 255
        points[3, :] -= 0.5

        points_cat = np.concatenate([points, times], axis=0).transpose()
        points_cat = points_cat[~np.isnan(points_cat).any(axis=1)]

        return points_cat

    def getBoxes(self, index):

        sample = self.nusc.get('sample', self.filtered_sample_tokens[index])
        sample_lidar_token = sample['data']['LIDAR_TOP']
        lidar_data = self.nusc.get('sample_data', sample_lidar_token)
        ego_pose = self.nusc.get('ego_pose', lidar_data['ego_pose_token'])

        boxes_dict = self.nusc.get_boxes(sample_lidar_token)

        keep_box_idx = []
        for i, box in enumerate(boxes_dict):
            if box.name not in NameMapping.keys():
                continue
            if NameMapping[box.name] in self.class_names:
                box.name = NameMapping[box.name]
                keep_box_idx.append(i)

        boxes_dict = [
            box for i, box in enumerate(boxes_dict) if i in keep_box_idx
        ]
        self.move_boxes_to_car_space(boxes_dict, ego_pose)
        # print(boxes_dict)
        return boxes_dict

    def move_boxes_to_car_space(self, boxes, ego_pose, eval=False):
        """
        Move boxes from world space to car space.
        Note: mutates input boxes.
        """
        translation = -np.array(ego_pose['translation'])
        rotation = Quaternion(ego_pose['rotation']).inverse
        box_list = []
        for box in boxes:
            # Bring box to car space
            box.translate(translation)
            box.rotate(rotation)
            box_list.append(box)
        return box_list

    def evaluation(self, detections, output_dir):
        """kitti evaluation is very slow, remove it.
        """
        # res_kitti = self.evaluation_kitti(detections, output_dir)
        res_nusc = self.evaluation_nusc(detections, output_dir)
        res = {
            "results": {
                "nusc": res_nusc["results"]["nusc"],
                # "kitti.official": res_kitti["results"]["official"],
                # "kitti.coco": res_kitti["results"]["coco"],
            },
            "detail": {
                "eval.nusc": res_nusc["detail"]["nusc"],
                # "eval.kitti": {
                #     "official": res_kitti["detail"]["official"],
                #     "coco": res_kitti["detail"]["coco"],
                # },
            },
        }
        return res

    def evaluation_nusc(self, detections, output_dir):
        mapped_class_names = self.class_names
        nusc_annos = {}
        token2info = {}
        for det in detections:
            annos = []
            boxes = _second_det_to_nusc_box(det)
            boxes = self.transform_box_back_to_global(boxes,
                                                      det["metadata"]["token"])

            # boxes = self.move_boxes_to_car_space(boxes, ego_pose)
            for i, box in enumerate(boxes):
                name = mapped_class_names[box.label]
                velocity = [np.nan, np.nan]
                nusc_anno = {
                    "sample_token": det["metadata"]["token"],
                    "translation": box.center.tolist(),
                    "size": box.wlh.tolist(),
                    "rotation": box.orientation.elements.tolist(),
                    "velocity": velocity,
                    "detection_name": name,
                    "detection_score": box.score,
                    "attribute_name": DefaultAttribute[name]
                }
                annos.append(nusc_anno)
            nusc_annos[det["metadata"]["token"]] = annos
        nusc_submissions = {
            "meta": {
                "use_camera": False,
                "use_lidar": False,
                "use_radar": False,
                "use_map": False,
                "use_external": False
            },
            "results": nusc_annos
        }
        res_path = Path(output_dir) / "result_nusc.json"
        with open(res_path, "w") as f:
            json.dump(nusc_submissions, f)
        eval_main_file = Path(__file__).resolve().parent / "nusc_eval.py"
        cmd = f"python {str(eval_main_file)} --root_path=\"{self.root_path}\""
        cmd += f" --version=\"v1.0-mini\" --eval_version={self.eval_version}"
        cmd += f" --res_path=\"{str(res_path)}\" --eval_set=mini_train"
        cmd += f" --output_dir=\"{output_dir}\""
        print(cmd)
        subprocess.check_output(cmd, shell=True)
        with open(Path(output_dir) / "metrics_summary.json", "r") as f:
            metrics = json.load(f)
        detail = {}
        res_path.unlink()
        result = f"Nusc {VERSION} evaluation"
        for name in mapped_class_names:
            detail[name] = {}
            for k, v in metrics["label_aps"][name].items():
                detail[name][f"dist@{k}"] = v
            tp_errs = []
            tp_names = []
            for k, v in metrics["label_tp_errors"][name].items():
                detail[name][k] = v
                tp_errs.append(f"{v:.4f}")
                tp_names.append(k)
            threshs = ', '.join(list(metrics["label_aps"][name].keys()))
            scores = list(metrics["label_aps"][name].values())
            scores = ', '.join([f"{s * 100:.2f}" for s in scores])
            result += f"{name} Nusc dist AP@{threshs} and TP errors\n"
            result += scores
            result += ', '.join(tp_names) + ": " + ", ".join(tp_errs)
            result += "\n"
        return {
            "results": {
                "nusc": result,
            },
            "detail": {
                "nusc": detail
            }
        }

    def transform_box_back_to_global(self, boxes, token):
        sample = self.nusc.get('sample', token)
        sample_lidar_token = sample['data']['LIDAR_TOP']
        lidar_data = self.nusc.get('sample_data', sample_lidar_token)
        ego_pose = self.nusc.get('ego_pose', lidar_data['ego_pose_token'])

        translation = np.array(ego_pose['translation'])
        rotation = Quaternion(ego_pose['rotation'])
        box_list = []
        for box in boxes:
            box.rotate(rotation)
            box.translate(translation)
            box_list.append(box)
        return box_list