Spaces:
Sleeping
Sleeping
| import torch | |
| import torch.distributed as dist | |
| import abc | |
| import json | |
| import logging | |
| import time | |
| import numpy as np | |
| from .distributed import synchronize, is_main_process, all_gather_container | |
| from pycocotools.cocoeval import COCOeval | |
| # FIXME experimenting with speedups for OpenImages eval, it's slow | |
| #import pyximport; py_importer, pyx_importer = pyximport.install(pyimport=True) | |
| import effdet.evaluation.detection_evaluator as tfm_eval | |
| #pyximport.uninstall(py_importer, pyx_importer) | |
| _logger = logging.getLogger(__name__) | |
| __all__ = ['CocoEvaluator', 'PascalEvaluator', 'OpenImagesEvaluator', 'create_evaluator'] | |
| class Evaluator: | |
| def __init__(self, distributed=False, pred_yxyx=False): | |
| self.distributed = distributed | |
| self.distributed_device = None | |
| self.pred_yxyx = pred_yxyx | |
| self.img_indices = [] | |
| self.predictions = [] | |
| def add_predictions(self, detections, target): | |
| if self.distributed: | |
| if self.distributed_device is None: | |
| # cache for use later to broadcast end metric | |
| self.distributed_device = detections.device | |
| synchronize() | |
| detections = all_gather_container(detections) | |
| img_indices = all_gather_container(target['img_idx']) | |
| if not is_main_process(): | |
| return | |
| else: | |
| img_indices = target['img_idx'] | |
| detections = detections.cpu().numpy() | |
| img_indices = img_indices.cpu().numpy() | |
| for img_idx, img_dets in zip(img_indices, detections): | |
| self.img_indices.append(img_idx) | |
| self.predictions.append(img_dets) | |
| def _coco_predictions(self): | |
| # generate coco-style predictions | |
| coco_predictions = [] | |
| coco_ids = [] | |
| for img_idx, img_dets in zip(self.img_indices, self.predictions): | |
| img_id = self._dataset.img_ids[img_idx] | |
| coco_ids.append(img_id) | |
| if self.pred_yxyx: | |
| # to xyxy | |
| img_dets[:, 0:4] = img_dets[:, [1, 0, 3, 2]] | |
| # to xywh | |
| img_dets[:, 2] -= img_dets[:, 0] | |
| img_dets[:, 3] -= img_dets[:, 1] | |
| for det in img_dets: | |
| score = float(det[4]) | |
| if score < .001: # stop when below this threshold, scores in descending order | |
| break | |
| coco_det = dict( | |
| image_id=int(img_id), | |
| bbox=det[0:4].tolist(), | |
| score=score, | |
| category_id=int(det[5])) | |
| coco_predictions.append(coco_det) | |
| return coco_predictions, coco_ids | |
| def evaluate(self): | |
| pass | |
| def save(self, result_file): | |
| # save results in coco style, override to save in a alternate form | |
| if not self.distributed or dist.get_rank() == 0: | |
| assert len(self.predictions) | |
| coco_predictions, coco_ids = self._coco_predictions() | |
| json.dump(coco_predictions, open(result_file, 'w'), indent=4) | |
| class CocoEvaluator(Evaluator): | |
| def __init__(self, dataset, neptune=None, distributed=False, pred_yxyx=False): | |
| super().__init__(distributed=distributed, pred_yxyx=pred_yxyx) | |
| self._dataset = dataset.parser | |
| self.coco_api = dataset.parser.coco | |
| self.neptune = neptune | |
| def reset(self): | |
| self.img_indices = [] | |
| self.predictions = [] | |
| def evaluate(self): | |
| if not self.distributed or dist.get_rank() == 0: | |
| assert len(self.predictions) | |
| coco_predictions, coco_ids = self._coco_predictions() | |
| json.dump(coco_predictions, open('./temp.json', 'w'), indent=4) | |
| results = self.coco_api.loadRes('./temp.json') | |
| coco_eval = COCOeval(self.coco_api, results, 'bbox') | |
| coco_eval.params.imgIds = coco_ids # score only ids we've used | |
| coco_eval.evaluate() | |
| coco_eval.accumulate() | |
| coco_eval.summarize() | |
| metric = coco_eval.stats[0] # mAP 0.5-0.95 | |
| if self.neptune: | |
| self.neptune.log_metric('valid/mAP/0.5-0.95IOU', metric) | |
| self.neptune.log_metric('valid/mAP/0.5IOU', coco_eval.stats[1]) | |
| if self.distributed: | |
| dist.broadcast(torch.tensor(metric, device=self.distributed_device), 0) | |
| else: | |
| metric = torch.tensor(0, device=self.distributed_device) | |
| dist.broadcast(metric, 0) | |
| metric = metric.item() | |
| self.reset() | |
| return metric | |
| class TfmEvaluator(Evaluator): | |
| """ Tensorflow Models Evaluator Wrapper """ | |
| def __init__( | |
| self, dataset, neptune=None, distributed=False, pred_yxyx=False, | |
| evaluator_cls=tfm_eval.ObjectDetectionEvaluator): | |
| super().__init__(distributed=distributed, pred_yxyx=pred_yxyx) | |
| self._evaluator = evaluator_cls(categories=dataset.parser.cat_dicts) | |
| self._eval_metric_name = self._evaluator._metric_names[0] | |
| self._dataset = dataset.parser | |
| self.neptune = neptune | |
| def reset(self): | |
| self._evaluator.clear() | |
| self.img_indices = [] | |
| self.predictions = [] | |
| def evaluate(self): | |
| if not self.distributed or dist.get_rank() == 0: | |
| for img_idx, img_dets in zip(self.img_indices, self.predictions): | |
| gt = self._dataset.get_ann_info(img_idx) | |
| self._evaluator.add_single_ground_truth_image_info(img_idx, gt) | |
| bbox = img_dets[:, 0:4] if self.pred_yxyx else img_dets[:, [1, 0, 3, 2]] | |
| det = dict(bbox=bbox, score=img_dets[:, 4], cls=img_dets[:, 5]) | |
| self._evaluator.add_single_detected_image_info(img_idx, det) | |
| metrics = self._evaluator.evaluate() | |
| _logger.info('Metrics:') | |
| for k, v in metrics.items(): | |
| _logger.info(f'{k}: {v}') | |
| if self.neptune: | |
| key = 'valid/mAP/' + str(k).split('/')[-1] | |
| self.neptune.log_metric(key, v) | |
| map_metric = metrics[self._eval_metric_name] | |
| if self.distributed: | |
| dist.broadcast(torch.tensor(map_metric, device=self.distributed_device), 0) | |
| else: | |
| map_metric = torch.tensor(0, device=self.distributed_device) | |
| wait = dist.broadcast(map_metric, 0, async_op=True) | |
| while not wait.is_completed(): | |
| # wait without spinning the cpu @ 100%, no need for low latency here | |
| time.sleep(0.5) | |
| map_metric = map_metric.item() | |
| self.reset() | |
| return map_metric | |
| class PascalEvaluator(TfmEvaluator): | |
| def __init__(self, dataset, neptune=None, distributed=False, pred_yxyx=False): | |
| super().__init__( | |
| dataset, neptune, distributed=distributed, pred_yxyx=pred_yxyx, evaluator_cls=tfm_eval.PascalDetectionEvaluator) | |
| class OpenImagesEvaluator(TfmEvaluator): | |
| def __init__(self, dataset, distributed=False, pred_yxyx=False): | |
| super().__init__( | |
| dataset, distributed=distributed, pred_yxyx=pred_yxyx, evaluator_cls=tfm_eval.OpenImagesDetectionEvaluator) | |
| def create_evaluator(name, dataset, neptune=None, distributed=False, pred_yxyx=False): | |
| # FIXME support OpenImages Challenge2019 metric w/ image level label consideration | |
| if 'coco' in name: | |
| return CocoEvaluator(dataset, neptune, distributed=distributed, pred_yxyx=pred_yxyx) | |
| elif 'openimages' in name: | |
| return OpenImagesEvaluator(dataset, distributed=distributed, pred_yxyx=pred_yxyx) | |
| else: | |
| return CocoEvaluator(dataset, neptune, distributed=distributed, pred_yxyx=pred_yxyx) | |
| #return PascalEvaluator(dataset, neptune, distributed=distributed, pred_yxyx=pred_yxyx) | |