|
|
|
import contextlib |
|
import copy |
|
import io |
|
import json |
|
import numpy as np |
|
import os |
|
import tempfile |
|
import unittest |
|
import torch |
|
from pycocotools.coco import COCO |
|
from pycocotools.cocoeval import COCOeval |
|
|
|
from detectron2.data import DatasetCatalog |
|
from detectron2.evaluation import COCOEvaluator |
|
from detectron2.evaluation.fast_eval_api import COCOeval_opt |
|
from detectron2.structures import Boxes, Instances |
|
|
|
|
|
class TestCOCOeval(unittest.TestCase): |
|
def test_fast_eval(self): |
|
|
|
|
|
detections = [{"image_id": 139, "category_id": 1, "bbox": [417.3332824707031, 159.27003479003906, 47.66064453125, 143.00193786621094], "score": 0.9949821829795837, "segmentation": {"size": [426, 640], "counts": "Tc`52W=3N0N4aNN^E7]:4XE1g:8kDMT;U100000001O1gE[Nk8h1dFiNY9Z1aFkN]9g2J3NdN`FlN`9S1cFRN07]9g1bFoM6;X9c1cFoM=8R9g1bFQN>3U9Y30O01OO1O001N2O1N1O4L4L5UNoE3V:CVF6Q:@YF9l9@ZF<k9[O`F=];HYnX2"}}, {"image_id": 139, "category_id": 1, "bbox": [383.5909118652344, 172.0777587890625, 17.959075927734375, 36.94813537597656], "score": 0.7685421705245972, "segmentation": {"size": [426, 640], "counts": "lZP5m0Z<300O100O100000001O00]OlC0T<OnCOT<OnCNX<JnC2bQT3"}}, {"image_id": 139, "category_id": 1, "bbox": [457.8359069824219, 158.88027954101562, 9.89764404296875, 8.771820068359375], "score": 0.07092753797769547, "segmentation": {"size": [426, 640], "counts": "bSo54T=2N2O1001O006ImiW2"}}] |
|
gt_annotations = {"categories": [{"supercategory": "person", "id": 1, "name": "person"}, {"supercategory": "furniture", "id": 65, "name": "bed"}], "images": [{"license": 4, "file_name": "000000000285.jpg", "coco_url": "http://images.cocodataset.org/val2017/000000000285.jpg", "height": 640, "width": 586, "date_captured": "2013-11-18 13:09:47", "flickr_url": "http://farm8.staticflickr.com/7434/9138147604_c6225224b8_z.jpg", "id": 285}, {"license": 2, "file_name": "000000000139.jpg", "coco_url": "http://images.cocodataset.org/val2017/000000000139.jpg", "height": 426, "width": 640, "date_captured": "2013-11-21 01:34:01", "flickr_url": "http://farm9.staticflickr.com/8035/8024364858_9c41dc1666_z.jpg", "id": 139}], "annotations": [{"segmentation": [[428.19, 219.47, 430.94, 209.57, 430.39, 210.12, 421.32, 216.17, 412.8, 217.27, 413.9, 214.24, 422.42, 211.22, 429.29, 201.6, 430.67, 181.8, 430.12, 175.2, 427.09, 168.06, 426.27, 164.21, 430.94, 159.26, 440.29, 157.61, 446.06, 163.93, 448.53, 168.06, 448.53, 173.01, 449.08, 174.93, 454.03, 185.1, 455.41, 188.4, 458.43, 195.0, 460.08, 210.94, 462.28, 226.61, 460.91, 233.76, 454.31, 234.04, 460.08, 256.85, 462.56, 268.13, 465.58, 290.67, 465.85, 293.14, 463.38, 295.62, 452.66, 295.34, 448.26, 294.52, 443.59, 282.7, 446.06, 235.14, 446.34, 230.19, 438.09, 232.39, 438.09, 221.67, 434.24, 221.12, 427.09, 219.74]], "area": 2913.1103999999987, "iscrowd": 0, "image_id": 139, "bbox": [412.8, 157.61, 53.05, 138.01], "category_id": 1, "id": 230831}, {"segmentation": [[384.98, 206.58, 384.43, 199.98, 385.25, 193.66, 385.25, 190.08, 387.18, 185.13, 387.18, 182.93, 386.08, 181.01, 385.25, 178.81, 385.25, 175.79, 388.0, 172.76, 394.88, 172.21, 398.72, 173.31, 399.27, 176.06, 399.55, 183.48, 397.9, 185.68, 395.15, 188.98, 396.8, 193.38, 398.45, 194.48, 399.0, 205.75, 395.43, 207.95, 388.83, 206.03]], "area": 435.1449499999997, "iscrowd": 0, "image_id": 139, "bbox": [384.43, 172.21, 15.12, 35.74], "category_id": 1, "id": 233201}]} |
|
|
|
|
|
|
|
experiments = {"full": (detections, gt_annotations, {})} |
|
|
|
|
|
experiments["empty_dt"] = ([], gt_annotations, {}) |
|
gt = copy.deepcopy(gt_annotations) |
|
gt["annotations"] = [] |
|
experiments["empty_gt"] = (detections, gt, {}) |
|
|
|
|
|
experiments["no_categories"] = (detections, gt_annotations, {"useCats": 0}) |
|
experiments["no_ious"] = (detections, gt_annotations, {"iouThrs": []}) |
|
experiments["no_rec_thrs"] = (detections, gt_annotations, {"recThrs": []}) |
|
experiments["no_max_dets"] = (detections, gt_annotations, {"maxDets": []}) |
|
experiments["one_max_det"] = (detections, gt_annotations, {"maxDets": [1]}) |
|
experiments["no_area"] = (detections, gt_annotations, {"areaRng": [], "areaRngLbl": []}) |
|
|
|
|
|
annotation_fields = [ |
|
"id", |
|
"image_id", |
|
"category_id", |
|
"score", |
|
"area", |
|
"iscrowd", |
|
"ignore", |
|
"bbox", |
|
"segmentation", |
|
] |
|
for a in annotation_fields: |
|
gt = copy.deepcopy(gt_annotations) |
|
for g in gt["annotations"]: |
|
if a in g: |
|
del g[a] |
|
dt = copy.deepcopy(detections) |
|
for d in dt: |
|
if a in d: |
|
del d[a] |
|
experiments["omit_gt_" + a] = (detections, gt, {}) |
|
experiments["omit_dt_" + a] = (dt, gt_annotations, {}) |
|
|
|
|
|
for name, (dt, gt, params) in experiments.items(): |
|
|
|
try: |
|
with tempfile.TemporaryDirectory() as tmpdir: |
|
json_file_name = os.path.join(tmpdir, "gt_" + name + ".json") |
|
with open(json_file_name, "w") as f: |
|
json.dump(gt, f) |
|
with contextlib.redirect_stdout(io.StringIO()): |
|
coco_api = COCO(json_file_name) |
|
except Exception: |
|
pass |
|
|
|
for iou_type in ["bbox", "segm", "keypoints"]: |
|
|
|
api_exception = None |
|
try: |
|
with contextlib.redirect_stdout(io.StringIO()): |
|
coco_dt = coco_api.loadRes(dt) |
|
coco_eval = COCOeval(coco_api, coco_dt, iou_type) |
|
for p, v in params.items(): |
|
setattr(coco_eval.params, p, v) |
|
coco_eval.evaluate() |
|
coco_eval.accumulate() |
|
coco_eval.summarize() |
|
except Exception as ex: |
|
api_exception = ex |
|
|
|
|
|
opt_exception = None |
|
try: |
|
with contextlib.redirect_stdout(io.StringIO()): |
|
coco_dt = coco_api.loadRes(dt) |
|
coco_eval_opt = COCOeval_opt(coco_api, coco_dt, iou_type) |
|
for p, v in params.items(): |
|
setattr(coco_eval_opt.params, p, v) |
|
coco_eval_opt.evaluate() |
|
coco_eval_opt.accumulate() |
|
coco_eval_opt.summarize() |
|
except Exception as ex: |
|
opt_exception = ex |
|
|
|
if api_exception is not None and opt_exception is not None: |
|
|
|
|
|
api_error = "" if api_exception is None else type(api_exception).__name__ |
|
opt_error = "" if opt_exception is None else type(opt_exception).__name__ |
|
msg = "%s: comparing COCO APIs, '%s' != '%s'" % (name, api_error, opt_error) |
|
self.assertTrue(api_error == opt_error, msg=msg) |
|
else: |
|
|
|
for k in ["precision", "recall"]: |
|
diff = np.abs(coco_eval.eval[k] - coco_eval_opt.eval[k]) |
|
abs_diff = np.max(diff) if diff.size > 0 else 0.0 |
|
msg = "%s: comparing COCO APIs, %s differs by %f" % (name, k, abs_diff) |
|
self.assertTrue(abs_diff < 1e-4, msg=msg) |
|
|
|
def test_unknown_category(self): |
|
dataset = "coco_2017_val_100" |
|
evaluator = COCOEvaluator(dataset) |
|
evaluator.reset() |
|
inputs = DatasetCatalog.get(dataset)[:2] |
|
pred = Instances((100, 100)) |
|
pred.pred_boxes = Boxes(torch.rand(2, 4)) |
|
pred.scores = torch.rand(2) |
|
pred.pred_classes = torch.tensor([10, 80]) |
|
output = {"instances": pred} |
|
evaluator.process(inputs, [output, output]) |
|
with self.assertRaises(AssertionError): |
|
evaluator.evaluate() |
|
|