| | |
| | 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 |
| | |
| | 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() |
| |
|
| | |
| | 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 |
| | } |
| |
|
| | maxDet = p.maxDets[-1] |
| |
|
| | |
| | def convert_instances_to_cpp(instances, is_det=False): |
| | |
| | |
| | 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 |
| |
|
| | |
| | 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: |
| | |
| | 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] |
| |
|
| | |
| | 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)) |
| | |
| |
|
| | 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) |
| |
|
| | |
| | self.eval["recall"] = np.array(self.eval["recall"]).reshape( |
| | self.eval["counts"][:1] + self.eval["counts"][2:] |
| | ) |
| |
|
| | |
| | |
| | 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)) |
| |
|