예제 #1
0
class DeepScoresV2Dataset(CocoDataset):
    def __init__(self,
                 ann_file,
                 pipeline,
                 classes=None,
                 data_root=None,
                 img_prefix='',
                 seg_prefix=None,
                 proposal_file=None,
                 test_mode=False,
                 filter_empty_gt=True,
                 use_oriented_bboxes=True):
        self.filter_empty_gt = filter_empty_gt
        super(DeepScoresV2Dataset,
              self).__init__(ann_file, pipeline, data_root, img_prefix,
                             seg_prefix, proposal_file, test_mode)
        #self.CLASSES = self.get_classes(classes)
        self.use_oriented_bboxes = use_oriented_bboxes

    @classmethod
    def get_classes(cls, classes=None):
        """Get class names of current dataset.
        Args:
            classes (Sequence[str] | str | None): If classes is None, use
                default CLASSES defined by builtin dataset. If classes is a
                string, take it as a file name. The file contains the name of
                classes where each line contains one class name. If classes is
                a tuple or list, override the CLASSES defined by the dataset.
        Returns:
            tuple[str] or list[str]: Names of categories of the dataset.
        """
        if classes is None:
            return cls.CLASSES

        if isinstance(classes, str):
            # take it as a file path
            class_names = mmcv.list_from_file(classes)
        elif isinstance(classes, (tuple, list)):
            class_names = classes
        else:
            raise ValueError(f'Unsupported type {type(classes)} of classes.')

        return class_names

    def load_annotations(self, ann_file):
        self.obb = OBBAnns(ann_file)
        self.obb.load_annotations()
        self.obb.set_annotation_set_filter(['deepscores'])
        # self.obb.set_class_blacklist(["staff"])
        self.cat_ids = list(self.obb.get_cats().keys())
        self.cat2label = {cat_id: i for i, cat_id in enumerate(self.cat_ids)}
        self.label2cat = {v: k for k, v in self.cat2label.items()}
        self.CLASSES = tuple(
            [v["name"] for (k, v) in self.obb.get_cats().items()])
        self.img_ids = [id['id'] for id in self.obb.img_info]

        return self.obb.img_info

    def get_ann_info(self, idx):
        return self._parse_ann_info(*self.obb.get_img_ann_pair(idxs=[idx]))

    def _filter_imgs(self, min_size=32):
        valid_inds = []
        for i, img_info in enumerate(self.obb.img_info):
            if self.filter_empty_gt and len(img_info['ann_ids']) == 0:
                continue
            if min(img_info['width'], img_info['height']) >= min_size:
                valid_inds.append(i)
        return valid_inds

    def _parse_ann_info(self, img_info, ann_info):
        img_info, ann_info = img_info[0], ann_info[0]
        gt_bboxes = []
        gt_labels = []
        gt_bboxes_ignore = np.zeros((0, 8 if self.use_oriented_bboxes else 4),
                                    dtype=np.float32)

        for i, ann in ann_info.iterrows():
            # we have no ignore feature
            if ann['area'] <= 0:
                continue

            bbox = ann['o_bbox' if self.use_oriented_bboxes else 'a_bbox']
            gt_bboxes.append(bbox)
            gt_labels.append(self.cat2label[ann['cat_id'][0]])

        gt_bboxes = np.array(gt_bboxes, dtype=np.float32)
        gt_labels = np.array(gt_labels, dtype=np.int64)

        ann = dict(bboxes=gt_bboxes,
                   labels=gt_labels,
                   bboxes_ignore=gt_bboxes_ignore,
                   masks=None,
                   seg_map=None)
        return ann

    def prepare_json_dict(self, results):
        json_results = {"annotation_set": "deepscores", "proposals": []}
        for idx in range(len(self)):
            img_id = self.img_ids[idx]
            result = results[idx]
            for label in range(len(result)):
                bboxes = result[label]
                for i in range(bboxes.shape[0]):
                    data = dict()
                    data['img_id'] = img_id

                    if len(bboxes[i]) == 8:
                        data['bbox'] = [str(nr) for nr in bboxes[i]]
                        data['score'] = 1
                    else:
                        data['bbox'] = [str(nr) for nr in bboxes[i][0:-1]]
                        data['score'] = str(bboxes[i][-1])
                    data['cat_id'] = self.label2cat[label]
                    json_results["proposals"].append(data)
        return json_results

    def write_results_json(self, results, filename=None):
        if filename is None:
            filename = "deepscores_results.json"
        json_results = self.prepare_json_dict(results)

        with open(filename, "w") as fo:
            json.dump(json_results, fo)

        return filename

    def evaluate(self,
                 results,
                 metric='bbox',
                 logger=None,
                 jsonfile_prefix=None,
                 classwise=True,
                 proposal_nums=(100, 300, 1000),
                 iou_thrs=np.arange(0.5, 0.96, 0.05),
                 average_thrs=False,
                 work_dir=None):
        """Evaluation in COCO protocol.

        Args:
            results (list): Testing results of the dataset.
            metric (str | list[str]): Metrics to be evaluated.
            logger (logging.Logger | str | None): Logger used for printing
                related information during evaluation. Default: None.
            jsonfile_prefix (str | None): The prefix of json files. It includes
                the file path and the prefix of filename, e.g., "a/b/prefix".
                If not specified, a temp file will be created. Default: None.
            classwise (bool): Whether to evaluating the AP for each class.
            proposal_nums (Sequence[int]): Proposal number used for evaluating
                recalls, such as recall@100, recall@1000.
                Default: (100, 300, 1000).
            iou_thrs (Sequence[float]): IoU threshold used for evaluating
                recalls. If set to a list, the average recall of all IoUs will
                also be computed. Default: 0.5.

        Returns:
            dict[str: float]
        """

        metrics = metric if isinstance(metric, list) else [metric]
        allowed_metrics = ['bbox']
        for metric in metrics:
            if metric not in allowed_metrics:
                raise KeyError(f'metric {metric} is not supported')

        filename = self.write_results_json(results)

        self.obb.load_proposals(filename)
        metric_results = self.obb.calculate_metrics(iou_thrs=iou_thrs,
                                                    classwise=classwise,
                                                    average_thrs=average_thrs)

        categories = self.obb.get_cats()
        metric_results = {
            categories[key]['name']: value
            for (key, value) in metric_results.items()
        }

        # add occurences
        occurences_by_class = self.obb.get_class_occurences()
        for (key, value) in metric_results.items():
            value.update(no_occurences=occurences_by_class[key])

        if work_dir is not None:
            import pickle
            import os
            out_file = os.path.join(work_dir, "dsv2_metrics.pkl")
            pickle.dump(metric_results, open(out_file, 'wb'))
        print(metric_results)
        return metric_results
예제 #2
0
def main(image_set, single_scale=False):
    # (1)
    if image_set == 'train':
        #dir_txt = os.path.join(dir_dataset, 'labelTxt', image_set)
        out_dir_json = os.path.join(dir_dataset, 'annotations', image_set)
        os.makedirs(out_dir_json, exist_ok=True)
        #txt2json(dir_txt, out_dir_json)

        o = OBBAnns(dir_dataset + 'deepscores_train.json')
        o.load_annotations()
        #print(o)
        cats = o.get_cats()
        img_idxs = [i for i in range(len(o.img_info))]
        imgs, anns = o.get_img_ann_pair(idxs=img_idxs,
                                        ann_set_filter="deepscores")
        filenames = []

        for img in anns:
            objs = []
            img_np = np.array(img)
            for object_instance in img_np:
                obj = dict()
                coord = np.array(object_instance[1],
                                 dtype=np.float32).reshape([4, 2])
                bbox = cv.boxPoints(cv.minAreaRect(coord)).astype(
                    np.int).tolist()
                obj['name'] = cats[object_instance[2][0]]['name']
                obj['bbox'] = bbox
                objs.append(obj)
            if objs:
                filename = o.get_imgs(ids=[int(img_np[0][4])])[0]['filename']
                filenames.append(os.path.splitext(filename)[0])
                json_filename = os.path.splitext(filename)[0] + '.json'
                json.dump(objs,
                          open(os.path.join(out_dir_json, json_filename),
                               'wt'),
                          indent=2)

        # Split images and json annotations in train and val files
        out_dir_train = os.path.join(dir_dataset, 'images2', 'train')
        out_dir_val = os.path.join(dir_dataset, 'images2', 'val')
        out_dir_val_json = os.path.join(dir_dataset, 'annotations', 'val')
        out_dir_test = os.path.join(dir_dataset, 'images2', 'test')
        os.makedirs(out_dir_train, exist_ok=True)
        os.makedirs(out_dir_val, exist_ok=True)
        os.makedirs(out_dir_test, exist_ok=True)
        os.makedirs(out_dir_val_json, exist_ok=True)

        filenames_train, filenames_val = train_test_split(filenames,
                                                          test_size=272,
                                                          random_state=8)

        for filename in os.listdir(os.path.join(dir_dataset, 'images')):
            if os.path.splitext(filename)[0] in filenames_train:
                shutil.move(os.path.join(dir_dataset, 'images', filename),
                            os.path.join(out_dir_train, filename))
            elif os.path.splitext(filename)[0] in filenames_val:
                shutil.move(os.path.join(dir_dataset, 'images', filename),
                            os.path.join(out_dir_val, filename))
                shutil.move(
                    os.path.join(out_dir_json,
                                 os.path.splitext(filename)[0] + '.json'),
                    os.path.join(out_dir_val_json,
                                 os.path.splitext(filename)[0] + '.json'))
            else:
                shutil.move(os.path.join(dir_dataset, 'images', filename),
                            os.path.join(out_dir_test, filename))

        os.rmdir(os.path.join(dir_dataset, 'images'))
        os.rename(os.path.join(dir_dataset, 'images2'),
                  os.path.join(dir_dataset, 'images'))

    # (2)
    pairs = []
    for filename in os.listdir(os.path.join(dir_dataset, 'images', image_set)):
        anno = os.path.join(dir_dataset, 'annotations', image_set,
                            filename.replace('png', 'json'))
        img = os.path.join(dir_dataset, 'images', image_set, filename)
        if not os.path.exists(anno):
            anno = None
        pairs.append([img, anno])

    overlap = 0.25
    sizes = [768] if single_scale else [512, 768, 1024, 1536]
    save_empty = image_set == 'test'
    image_set = f"{image_set}-{sizes[0]}" if single_scale else image_set

    out_dir_images = os.path.join(dir_dataset, 'images', f'{image_set}-crop')
    out_dir_annos = os.path.join(dir_dataset, 'annotations',
                                 f'{image_set}-crop')

    cropper = Cropper(sizes, overlap)
    cropper.crop_batch(pairs, out_dir_images, out_dir_annos, save_empty)

    # (3)
    pairs = []
    for filename in os.listdir(out_dir_images):
        img = os.path.join('images', f'{image_set}-crop', filename)
        anno = None if image_set == 'test' else os.path.join(
            'annotations', f'{image_set}-crop', filename.replace(
                'jpg', 'json'))
        pairs.append([img, anno])
    out_dir = os.path.join(dir_dataset, 'image-sets')
    os.makedirs(out_dir, exist_ok=True)
    json.dump(pairs,
              open(os.path.join(out_dir, f'{image_set}.json'), 'wt'),
              indent=2)
예제 #3
0
class DeepScoresV2Dataset(CocoDataset):
    def load_annotations(self, ann_file):
        self.obb = OBBAnns(ann_file)
        self.obb.load_annotations()
        self.obb.set_annotation_set_filter(['deepscores'])
        self.obb.set_class_blacklist(["staff"])
        self.cat_ids = list(self.obb.get_cats().keys())
        self.cat2label = {cat_id: i for i, cat_id in enumerate(self.cat_ids)}
        self.label2cat = {v: k for k, v in self.cat2label.items()}
        self.CLASSES = tuple(
            [v["name"] for (k, v) in self.obb.get_cats().items()])
        self.img_ids = [id['id'] for id in self.obb.img_info]

        return self.obb.img_info

    def get_ann_info(self, idx):
        return self._parse_ann_info(*self.obb.get_img_ann_pair(idxs=[idx]))

    def _filter_imgs(self, min_size=32):
        valid_inds = []
        for i, img_info in enumerate(self.obb.img_info):
            if self.filter_empty_gt and len(img_info['ann_ids']) == 0:
                continue
            if min(img_info['width'], img_info['height']) >= min_size:
                valid_inds.append(i)
        return valid_inds

    def _parse_ann_info(self, img_info, ann_info):
        img_info, ann_info = img_info[0], ann_info[0]
        gt_bboxes = []
        gt_labels = []
        gt_bboxes_ignore = np.zeros((0, 4), dtype=np.float32)

        for i, ann in ann_info.iterrows():
            # we have no ignore feature
            if ann['area'] <= 0:
                continue

            bbox = ann['a_bbox']
            gt_bboxes.append(bbox)
            gt_labels.append(self.cat2label[ann['cat_id'][0]])

        gt_bboxes = np.array(gt_bboxes, dtype=np.float32)
        gt_labels = np.array(gt_labels, dtype=np.int64)

        ann = dict(bboxes=gt_bboxes,
                   labels=gt_labels,
                   bboxes_ignore=gt_bboxes_ignore,
                   masks=None,
                   seg_map=None)
        return ann

    def prepare_json_dict(self, results):
        json_results = {"annotation_set": "deepscores", "proposals": []}
        for idx in range(len(self)):
            img_id = self.img_ids[idx]
            result = results[idx]
            for label in range(len(result)):
                bboxes = result[label]
                for i in range(bboxes.shape[0]):
                    data = dict()
                    data['img_id'] = img_id
                    data['bbox'] = [str(nr) for nr in bboxes[i][0:-1]]
                    data['score'] = str(bboxes[i][-1])
                    data['cat_id'] = self.label2cat[label]
                    json_results["proposals"].append(data)
        return json_results

    def write_results_json(self, results, filename=None):
        if filename is None:
            filename = "deepscores_results.json"
        json_results = self.prepare_json_dict(results)

        with open(filename, "w") as fo:
            json.dump(json_results, fo)

        return filename

    def evaluate(self,
                 results,
                 metric='bbox',
                 logger=None,
                 jsonfile_prefix=None,
                 classwise=True,
                 proposal_nums=(100, 300, 1000),
                 iou_thrs=np.arange(0.5, 0.96, 0.05),
                 average_thrs=False):
        """Evaluation in COCO protocol.

        Args:
            results (list): Testing results of the dataset.
            metric (str | list[str]): Metrics to be evaluated.
            logger (logging.Logger | str | None): Logger used for printing
                related information during evaluation. Default: None.
            jsonfile_prefix (str | None): The prefix of json files. It includes
                the file path and the prefix of filename, e.g., "a/b/prefix".
                If not specified, a temp file will be created. Default: None.
            classwise (bool): Whether to evaluating the AP for each class.
            proposal_nums (Sequence[int]): Proposal number used for evaluating
                recalls, such as recall@100, recall@1000.
                Default: (100, 300, 1000).
            iou_thrs (Sequence[float]): IoU threshold used for evaluating
                recalls. If set to a list, the average recall of all IoUs will
                also be computed. Default: 0.5.

        Returns:
            dict[str: float]
        """

        metrics = metric if isinstance(metric, list) else [metric]
        allowed_metrics = ['bbox']
        for metric in metrics:
            if metric not in allowed_metrics:
                raise KeyError(f'metric {metric} is not supported')

        filename = self.write_results_json(results)

        self.obb.load_proposals(filename)
        metric_results = self.obb.calculate_metrics(iou_thrs=iou_thrs,
                                                    classwise=classwise,
                                                    average_thrs=average_thrs)

        metric_results = {
            self.CLASSES[self.cat2label[key]]: value
            for (key, value) in metric_results.items()
        }

        # add occurences
        occurences_by_class = self.obb.get_class_occurences()
        for (key, value) in metric_results.items():
            value.update(no_occurences=occurences_by_class[key])

        if True:
            import pickle
            pickle.dump(metric_results,
                        open('evaluation_renamed_rcnn.pickle', 'wb'))
        print(metric_results)
        return metric_results