|
|
|
""" |
|
COCO evaluator that works in distributed mode. |
|
|
|
Mostly copy-paste from https://github.com/pytorch/vision/blob/edfd5a7/references/detection/coco_eval.py |
|
The difference is that there is less copy-pasting from pycocotools |
|
in the end of the file, as python3 can suppress prints with contextlib |
|
""" |
|
import os |
|
import contextlib |
|
import copy |
|
import numpy as np |
|
import torch |
|
|
|
from pycocotools.cocoeval import COCOeval |
|
from pycocotools.coco import COCO |
|
import pycocotools.mask as mask_util |
|
|
|
from util.misc import all_gather |
|
|
|
|
|
class CocoEvaluator(object): |
|
def __init__(self, coco_gt, iou_types, useCats=True): |
|
assert isinstance(iou_types, (list, tuple)) |
|
coco_gt = copy.deepcopy(coco_gt) |
|
self.coco_gt = coco_gt |
|
|
|
self.iou_types = iou_types |
|
self.coco_eval = {} |
|
for iou_type in iou_types: |
|
self.coco_eval[iou_type] = COCOeval(coco_gt, iouType=iou_type) |
|
self.coco_eval[iou_type].useCats = useCats |
|
|
|
self.img_ids = [] |
|
self.eval_imgs = {k: [] for k in iou_types} |
|
self.useCats = useCats |
|
|
|
def update(self, predictions): |
|
img_ids = list(np.unique(list(predictions.keys()))) |
|
self.img_ids.extend(img_ids) |
|
|
|
for iou_type in self.iou_types: |
|
results = self.prepare(predictions, iou_type) |
|
|
|
|
|
with open(os.devnull, 'w') as devnull: |
|
with contextlib.redirect_stdout(devnull): |
|
coco_dt = COCO.loadRes(self.coco_gt, results) if results else COCO() |
|
coco_eval = self.coco_eval[iou_type] |
|
|
|
coco_eval.cocoDt = coco_dt |
|
coco_eval.params.imgIds = list(img_ids) |
|
coco_eval.params.useCats = self.useCats |
|
img_ids, eval_imgs = evaluate(coco_eval) |
|
|
|
self.eval_imgs[iou_type].append(eval_imgs) |
|
|
|
def synchronize_between_processes(self): |
|
for iou_type in self.iou_types: |
|
self.eval_imgs[iou_type] = np.concatenate(self.eval_imgs[iou_type], 2) |
|
create_common_coco_eval(self.coco_eval[iou_type], self.img_ids, self.eval_imgs[iou_type]) |
|
|
|
def accumulate(self): |
|
for coco_eval in self.coco_eval.values(): |
|
coco_eval.accumulate() |
|
|
|
def summarize(self): |
|
for iou_type, coco_eval in self.coco_eval.items(): |
|
print("IoU metric: {}".format(iou_type)) |
|
coco_eval.summarize() |
|
|
|
def prepare(self, predictions, iou_type): |
|
if iou_type == "bbox": |
|
return self.prepare_for_coco_detection(predictions) |
|
elif iou_type == "segm": |
|
return self.prepare_for_coco_segmentation(predictions) |
|
elif iou_type == "keypoints": |
|
return self.prepare_for_coco_keypoint(predictions) |
|
else: |
|
raise ValueError("Unknown iou type {}".format(iou_type)) |
|
|
|
def prepare_for_coco_detection(self, predictions): |
|
coco_results = [] |
|
for original_id, prediction in predictions.items(): |
|
if len(prediction) == 0: |
|
continue |
|
|
|
boxes = prediction["boxes"] |
|
boxes = convert_to_xywh(boxes).tolist() |
|
if not isinstance(prediction["scores"], list): |
|
scores = prediction["scores"].tolist() |
|
else: |
|
scores = prediction["scores"] |
|
if not isinstance(prediction["labels"], list): |
|
labels = prediction["labels"].tolist() |
|
else: |
|
labels = prediction["labels"] |
|
|
|
|
|
try: |
|
coco_results.extend( |
|
[ |
|
{ |
|
"image_id": original_id, |
|
"category_id": labels[k], |
|
"bbox": box, |
|
"score": scores[k], |
|
} |
|
for k, box in enumerate(boxes) |
|
] |
|
) |
|
except: |
|
import ipdb; ipdb.set_trace() |
|
return coco_results |
|
|
|
def prepare_for_coco_segmentation(self, predictions): |
|
coco_results = [] |
|
for original_id, prediction in predictions.items(): |
|
if len(prediction) == 0: |
|
continue |
|
|
|
scores = prediction["scores"] |
|
labels = prediction["labels"] |
|
masks = prediction["masks"] |
|
|
|
masks = masks > 0.5 |
|
|
|
scores = prediction["scores"].tolist() |
|
labels = prediction["labels"].tolist() |
|
|
|
rles = [ |
|
mask_util.encode(np.array(mask[0, :, :, np.newaxis], dtype=np.uint8, order="F"))[0] |
|
for mask in masks |
|
] |
|
for rle in rles: |
|
rle["counts"] = rle["counts"].decode("utf-8") |
|
|
|
coco_results.extend( |
|
[ |
|
{ |
|
"image_id": original_id, |
|
"category_id": labels[k], |
|
"segmentation": rle, |
|
"score": scores[k], |
|
} |
|
for k, rle in enumerate(rles) |
|
] |
|
) |
|
return coco_results |
|
|
|
def prepare_for_coco_keypoint(self, predictions): |
|
coco_results = [] |
|
for original_id, prediction in predictions.items(): |
|
if len(prediction) == 0: |
|
continue |
|
|
|
boxes = prediction["boxes"] |
|
boxes = convert_to_xywh(boxes).tolist() |
|
scores = prediction["scores"].tolist() |
|
labels = prediction["labels"].tolist() |
|
keypoints = prediction["keypoints"] |
|
keypoints = keypoints.flatten(start_dim=1).tolist() |
|
|
|
coco_results.extend( |
|
[ |
|
{ |
|
"image_id": original_id, |
|
"category_id": labels[k], |
|
'keypoints': keypoint, |
|
"score": scores[k], |
|
} |
|
for k, keypoint in enumerate(keypoints) |
|
] |
|
) |
|
return coco_results |
|
|
|
|
|
def convert_to_xywh(boxes): |
|
xmin, ymin, xmax, ymax = boxes.unbind(1) |
|
return torch.stack((xmin, ymin, xmax - xmin, ymax - ymin), dim=1) |
|
|
|
|
|
def merge(img_ids, eval_imgs): |
|
all_img_ids = all_gather(img_ids) |
|
all_eval_imgs = all_gather(eval_imgs) |
|
|
|
merged_img_ids = [] |
|
for p in all_img_ids: |
|
merged_img_ids.extend(p) |
|
|
|
merged_eval_imgs = [] |
|
for p in all_eval_imgs: |
|
merged_eval_imgs.append(p) |
|
|
|
merged_img_ids = np.array(merged_img_ids) |
|
merged_eval_imgs = np.concatenate(merged_eval_imgs, 2) |
|
|
|
|
|
merged_img_ids, idx = np.unique(merged_img_ids, return_index=True) |
|
merged_eval_imgs = merged_eval_imgs[..., idx] |
|
|
|
return merged_img_ids, merged_eval_imgs |
|
|
|
|
|
def create_common_coco_eval(coco_eval, img_ids, eval_imgs): |
|
img_ids, eval_imgs = merge(img_ids, eval_imgs) |
|
img_ids = list(img_ids) |
|
eval_imgs = list(eval_imgs.flatten()) |
|
|
|
coco_eval.evalImgs = eval_imgs |
|
coco_eval.params.imgIds = img_ids |
|
coco_eval._paramsEval = copy.deepcopy(coco_eval.params) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def evaluate(self): |
|
''' |
|
Run per image evaluation on given images and store results (a list of dict) in self.evalImgs |
|
:return: None |
|
''' |
|
p = self.params |
|
|
|
if p.useSegm is not None: |
|
p.iouType = 'segm' if p.useSegm == 1 else 'bbox' |
|
print('useSegm (deprecated) is not None. Running {} evaluation'.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} |
|
|
|
evaluateImg = self.evaluateImg |
|
maxDet = p.maxDets[-1] |
|
evalImgs = [ |
|
evaluateImg(imgId, catId, areaRng, maxDet) |
|
for catId in catIds |
|
for areaRng in p.areaRng |
|
for imgId in p.imgIds |
|
] |
|
|
|
evalImgs = np.asarray(evalImgs).reshape(len(catIds), len(p.areaRng), len(p.imgIds)) |
|
self._paramsEval = copy.deepcopy(self.params) |
|
|
|
return p.imgIds, evalImgs |
|
|
|
|
|
|
|
|
|
|