Skip to content

deeplabcut.core.metrics.bbox

Bounding box metrics.

Metrics are currently computed using pycocotools, which can be installed with pypi (see https://github.com/ppwwyyxx/cocoapi/tree/master).

Functions:

Name Description
compute_bbox_metrics

Computes bbox mAP and mAR metrics for bounding boxes.

compute_bbox_metrics

compute_bbox_metrics(ground_truth: dict[str, dict], detections: dict[str, dict]) -> dict[str, float]

Computes bbox mAP and mAR metrics for bounding boxes.

Parameters:

Name Type Description Default

ground_truth

dict[str, dict]

A dictionary mapping image UIDs (such as image paths or filenames) to a ground truth labels dict. The labels dict should contain the keys "width" (image width), "height" (image height) and "bboxes" (a numpy array of shape (num_gt_bboxes, 4) containing the ground truth bounding boxes in format xywh).

required

detections

dict[str, dict]

A dictionary mapping image UIDs (such as image paths or filenames) to a predicted bounding box dict. The detections dict should contain the keys "bboxes" (a numpy array of shape (num_detected_bboxes, 4) containing the predicted bounding boxes in format xywh) and "scores" (a numpy array of length num_detected_bboxes containing the confidence score for each predicted bounding box).

required

Returns:

Type Description
dict[str, float]

The bounding box mAP/mAR metrics in a dictionary.

Raises:

Type Description
ModuleNotFoundError

if pycocotools is not installed

ValueError

if there are mismatches in the keys of ground_truth and detections

Source code in deeplabcut/core/metrics/bbox.py
@patch("pycocotools.coco.print", Mock())
@patch("pycocotools.cocoeval.print", Mock())
def compute_bbox_metrics(
    ground_truth: dict[str, dict],
    detections: dict[str, dict],
) -> dict[str, float]:
    """Computes bbox mAP and mAR metrics for bounding boxes.

    Args:
        ground_truth: A dictionary mapping image UIDs (such as image paths or filenames)
            to a ground truth labels dict. The labels dict should contain the keys
            "width" (image width), "height" (image height) and "bboxes" (a numpy array
            of shape (num_gt_bboxes, 4) containing the ground truth bounding boxes in
            format xywh).
        detections: A dictionary mapping image UIDs (such as image paths or filenames)
            to a predicted bounding box dict. The detections dict should contain the
            keys "bboxes" (a numpy array of shape (num_detected_bboxes, 4) containing
            the predicted bounding boxes in format xywh) and "scores" (a numpy array of
            length num_detected_bboxes containing the confidence score for each
            predicted bounding box).

    Returns:
        The bounding box mAP/mAR metrics in a dictionary.

    Raises:
        ModuleNotFoundError: if ``pycocotools`` is not installed
        ValueError: if there are mismatches in the keys of ground_truth and detections
    """
    if not with_pycocotools:
        raise ModuleNotFoundError("pycocotools not installed! can't compute bbox mAP")

    if len(detections) != len(ground_truth):
        raise ValueError()

    coco = COCO()
    coco.dataset["annotations"] = []
    coco.dataset["categories"] = [{"id": 1, "name": "animals", "supercategory": "obj"}]
    coco.dataset["images"] = []
    coco.dataset["info"] = {
        "description": "Generated by DeepLabCut",
        "year": datetime.now().year,
        "date_created": datetime.now().strftime("%Y-%m-%d"),
    }
    predictions = []
    for idx, (img, gt) in enumerate(ground_truth.items()):
        img_id = idx + 1
        coco.dataset["images"].append(
            {
                "id": img_id,
                "file_name": img,
                "width": gt["width"],
                "height": gt["height"],
            }
        )
        for bbox in gt["bboxes"][:, :4]:
            ann_id = len(coco.dataset["annotations"]) + 1
            coco.dataset["annotations"].append(
                {
                    "id": ann_id,
                    "image_id": img_id,
                    "category_id": 1,
                    "area": max(1, (bbox[2] * bbox[3]).item()),
                    "bbox": bbox,
                    "iscrowd": 0,
                }
            )

        for bbox, score in zip(detections[img]["bboxes"], detections[img]["scores"], strict=False):
            predictions.append(np.array([img_id, *bbox, score, 1]))

    if len(predictions) == 0:
        return {
            "mAP@50:95": 0.0,
            "mAP@50": 0.0,
            "mAP@75": 0.0,
            "mAR@50:95": 0.0,
            "mAR@50": 0.0,
            "mAR@75": 0.0,
        }

    predictions = np.stack(predictions, axis=0)
    coco.createIndex()
    coco_det = coco.loadRes(predictions)
    coco_eval = COCOeval(coco, coco_det, iouType="bbox")
    coco_eval.evaluate()
    coco_eval.accumulate()
    return {
        name: val
        for name, val in [
            _get_metric(coco_eval, recall=False),
            _get_metric(coco_eval, recall=False, iou_threshold=0.5),
            _get_metric(coco_eval, recall=False, iou_threshold=0.75),
            _get_metric(coco_eval, recall=True),
            _get_metric(coco_eval, recall=True, iou_threshold=0.5),
            _get_metric(coco_eval, recall=True, iou_threshold=0.75),
        ]
    }