Spaces:
Build error
Build error
| # Copyright (c) Facebook, Inc. and its affiliates. | |
| import copy | |
| import logging | |
| import numpy as np | |
| import time | |
| from pycocotools.cocoeval import COCOeval | |
| from detectron2 import _C | |
| logger = logging.getLogger(__name__) | |
| class COCOeval_opt(COCOeval): | |
| """ | |
| This is a slightly modified version of the original COCO API, where the functions evaluateImg() | |
| and accumulate() are implemented in C++ to speedup evaluation | |
| """ | |
| def evaluate(self): | |
| """ | |
| Run per image evaluation on given images and store results in self.evalImgs_cpp, a | |
| datastructure that isn't readable from Python but is used by a c++ implementation of | |
| accumulate(). Unlike the original COCO PythonAPI, we don't populate the datastructure | |
| self.evalImgs because this datastructure is a computational bottleneck. | |
| :return: None | |
| """ | |
| tic = time.time() | |
| p = self.params | |
| # add backward compatibility if useSegm is specified in params | |
| if p.useSegm is not None: | |
| p.iouType = "segm" if p.useSegm == 1 else "bbox" | |
| logger.info("Evaluate annotation type *{}*".format(p.iouType)) | |
| p.imgIds = list(np.unique(p.imgIds)) | |
| if p.useCats: | |
| p.catIds = list(np.unique(p.catIds)) | |
| p.maxDets = sorted(p.maxDets) | |
| self.params = p | |
| self._prepare() # bottleneck | |
| # loop through images, area range, max detection number | |
| catIds = p.catIds if p.useCats else [-1] | |
| if p.iouType == "segm" or p.iouType == "bbox": | |
| computeIoU = self.computeIoU | |
| elif p.iouType == "keypoints": | |
| computeIoU = self.computeOks | |
| self.ious = { | |
| (imgId, catId): computeIoU(imgId, catId) for imgId in p.imgIds for catId in catIds | |
| } # bottleneck | |
| maxDet = p.maxDets[-1] | |
| # <<<< Beginning of code differences with original COCO API | |
| def convert_instances_to_cpp(instances, is_det=False): | |
| # Convert annotations for a list of instances in an image to a format that's fast | |
| # to access in C++ | |
| instances_cpp = [] | |
| for instance in instances: | |
| instance_cpp = _C.InstanceAnnotation( | |
| int(instance["id"]), | |
| instance["score"] if is_det else instance.get("score", 0.0), | |
| instance["area"], | |
| bool(instance.get("iscrowd", 0)), | |
| bool(instance.get("ignore", 0)), | |
| ) | |
| instances_cpp.append(instance_cpp) | |
| return instances_cpp | |
| # Convert GT annotations, detections, and IOUs to a format that's fast to access in C++ | |
| ground_truth_instances = [ | |
| [convert_instances_to_cpp(self._gts[imgId, catId]) for catId in p.catIds] | |
| for imgId in p.imgIds | |
| ] | |
| detected_instances = [ | |
| [convert_instances_to_cpp(self._dts[imgId, catId], is_det=True) for catId in p.catIds] | |
| for imgId in p.imgIds | |
| ] | |
| ious = [[self.ious[imgId, catId] for catId in catIds] for imgId in p.imgIds] | |
| if not p.useCats: | |
| # For each image, flatten per-category lists into a single list | |
| ground_truth_instances = [[[o for c in i for o in c]] for i in ground_truth_instances] | |
| detected_instances = [[[o for c in i for o in c]] for i in detected_instances] | |
| # Call C++ implementation of self.evaluateImgs() | |
| self._evalImgs_cpp = _C.COCOevalEvaluateImages( | |
| p.areaRng, maxDet, p.iouThrs, ious, ground_truth_instances, detected_instances | |
| ) | |
| self._evalImgs = None | |
| self._paramsEval = copy.deepcopy(self.params) | |
| toc = time.time() | |
| logger.info("COCOeval_opt.evaluate() finished in {:0.2f} seconds.".format(toc - tic)) | |
| # >>>> End of code differences with original COCO API | |
| def accumulate(self): | |
| """ | |
| Accumulate per image evaluation results and store the result in self.eval. Does not | |
| support changing parameter settings from those used by self.evaluate() | |
| """ | |
| logger.info("Accumulating evaluation results...") | |
| tic = time.time() | |
| assert hasattr( | |
| self, "_evalImgs_cpp" | |
| ), "evaluate() must be called before accmulate() is called." | |
| self.eval = _C.COCOevalAccumulate(self._paramsEval, self._evalImgs_cpp) | |
| # recall is num_iou_thresholds X num_categories X num_area_ranges X num_max_detections | |
| self.eval["recall"] = np.array(self.eval["recall"]).reshape( | |
| self.eval["counts"][:1] + self.eval["counts"][2:] | |
| ) | |
| # precision and scores are num_iou_thresholds X num_recall_thresholds X num_categories X | |
| # num_area_ranges X num_max_detections | |
| self.eval["precision"] = np.array(self.eval["precision"]).reshape(self.eval["counts"]) | |
| self.eval["scores"] = np.array(self.eval["scores"]).reshape(self.eval["counts"]) | |
| toc = time.time() | |
| logger.info("COCOeval_opt.accumulate() finished in {:0.2f} seconds.".format(toc - tic)) | |