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
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)
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