roboflow/supervision · error · ValueError

MeanAveragePrecision with `MetricTarget.ORIENTED_BOUNDING_BO

Error message

MeanAveragePrecision with `MetricTarget.ORIENTED_BOUNDING_BOXES` requires `{ORIENTED_BOX_COORDINATES}` in `data` on both predictions and targets.

What it means

For MeanAveragePrecision with metric_target=MetricTarget.ORIENTED_BOUNDING_BOXES, oriented-box coordinates are not a first-class Detections field — they live in the data dict under the ORIENTED_BOX_COORDINATES key ('obb_boxes'-style constant) as an (N, 4, 2) corner array. This ValueError fires in _detections_content when a non-empty Detections lacks that data key, and names the exact constant required.

Source

Thrown at src/supervision/metrics/mean_average_precision.py:1478

        return self

    def _detections_content(self, detections: Detections) -> npt.NDArray[Any] | None:
        """Return per-detection masks or oriented boxes for the metric target,
        or `None` for the box target and for empty detections."""
        if self._metric_target == MetricTarget.BOXES or len(detections) == 0:
            return None
        if self._metric_target == MetricTarget.MASKS:
            if detections.mask is None:
                raise ValueError(
                    "MeanAveragePrecision with `MetricTarget.MASKS` requires"
                    " masks on both predictions and targets."
                )
            return np.asarray(detections.mask).astype(bool)
        if self._metric_target == MetricTarget.ORIENTED_BOUNDING_BOXES:
            obb = detections.data.get(ORIENTED_BOX_COORDINATES)
            if obb is None:
                raise ValueError(
                    "MeanAveragePrecision with"
                    " `MetricTarget.ORIENTED_BOUNDING_BOXES` requires"
                    f" `{ORIENTED_BOX_COORDINATES}` in `data` on both"
                    " predictions and targets."
                )
            return np.asarray(obb, dtype=np.float32).reshape(-1, 4, 2)
        raise ValueError(f"Invalid metric target: {self._metric_target}")

    def _content_area(
        self, xywh: list[float], content: npt.NDArray[Any] | None, idx: int
    ) -> float:
        """Compute the default annotation area for the metric target: bbox area
        for boxes, pixel count for masks, polygon area for oriented boxes."""
        if content is None:
            return float(xywh[2] * xywh[3])
        if self._metric_target == MetricTarget.MASKS:
            return float(np.count_nonzero(content[idx]))
        x, y = content[idx, :, 0], content[idx, :, 1]

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Attach oriented boxes on both sides: detections.data[ORIENTED_BOX_COORDINATES] = np.array corners of shape (N, 4, 2), importing the constant from supervision.config (print it from the error message if unsure of the exact name)
  2. Use an OBB-aware connector (e.g. from_ultralytics on an OBB model) that populates the data field
  3. If you only have axis-aligned boxes, fall back to MetricTarget.BOXES
  4. Validate before update: check the key exists in .data for every non-empty Detections

Example fix

# before
map_ = sv.MeanAveragePrecision(metric_target=sv.MetricTarget.ORIENTED_BOUNDING_BOXES)
preds = sv.Detections(xyxy=boxes, class_id=ids, confidence=confs)  # no obb data
map_.update(preds, targets)

# after
from supervision.config import ORIENTED_BOX_COORDINATES
preds = sv.Detections(xyxy=boxes, class_id=ids, confidence=confs,
                      data={ORIENTED_BOX_COORDINATES: pred_corners})  # (N,4,2)
targets = sv.Detections(xyxy=gt_boxes, class_id=gt_ids,
                        data={ORIENTED_BOX_COORDINATES: gt_corners})
map_.update(preds, targets)
Defensive patterns

Strategy: validation

Validate before calling

from supervision.config import ORIENTED_BOX_COORDINATES

def obb_ok(dets) -> bool:
    """OBB-target precondition: empty or carries the obb data key."""
    return len(dets) == 0 or ORIENTED_BOX_COORDINATES in dets.data

assert obb_ok(preds) and obb_ok(targets)

Type guard

import numpy as np
from supervision.config import ORIENTED_BOX_COORDINATES
from supervision.detection.core import Detections

def has_obb_data(dets: Detections) -> bool:
    """True when Detections carries an (N, 4, 2) corner array under the obb key."""
    obb = dets.data.get(ORIENTED_BOX_COORDINATES)
    return obb is not None and np.asarray(obb).ndim in (2, 3)

Prevention

When it happens

Trigger: Setting metric_target=ORIENTED_BOUNDING_BOXES but building Detections with only xyxy (rotated-box data never attached); using a connector that stores OBB under a custom/differently named data key; predictions carry data but targets (or vice versa) were built without it; renaming or hand-rolling the constant string instead of importing it from supervision.config.

Common situations: Evaluating OBB models (rotated YOLO, aerial/sar/ship datasets, DOTA-style) where predictions and annotations must both be converted to corner arrays; migrating between supervision versions where the data-key constant changed; partial pipelines that populate OBB on inference output but not on parsed GT labels.

Related errors


AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15). Data as JSON: /api/errors/32a1eaf3b4ff2711. Report an issue: GitHub.