{"record":{"id":"32a1eaf3b4ff2711","repo":"roboflow/supervision","slug":"meanaverageprecision-with-metrictarget-oriented-b","errorCode":null,"errorMessage":"MeanAveragePrecision with `MetricTarget.ORIENTED_BOUNDING_BOXES` requires `{ORIENTED_BOX_COORDINATES}` in `data` on both predictions and targets.","messagePattern":"MeanAveragePrecision with `MetricTarget\\.ORIENTED_BOUNDING_BOXES` requires `(.+?)` in `data` on both predictions and targets\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/metrics/mean_average_precision.py","lineNumber":1478,"sourceCode":"\n        return self\n\n    def _detections_content(self, detections: Detections) -> npt.NDArray[Any] | None:\n        \"\"\"Return per-detection masks or oriented boxes for the metric target,\n        or `None` for the box target and for empty detections.\"\"\"\n        if self._metric_target == MetricTarget.BOXES or len(detections) == 0:\n            return None\n        if self._metric_target == MetricTarget.MASKS:\n            if detections.mask is None:\n                raise ValueError(\n                    \"MeanAveragePrecision with `MetricTarget.MASKS` requires\"\n                    \" masks on both predictions and targets.\"\n                )\n            return np.asarray(detections.mask).astype(bool)\n        if self._metric_target == MetricTarget.ORIENTED_BOUNDING_BOXES:\n            obb = detections.data.get(ORIENTED_BOX_COORDINATES)\n            if obb is None:\n                raise ValueError(\n                    \"MeanAveragePrecision with\"\n                    \" `MetricTarget.ORIENTED_BOUNDING_BOXES` requires\"\n                    f\" `{ORIENTED_BOX_COORDINATES}` in `data` on both\"\n                    \" predictions and targets.\"\n                )\n            return np.asarray(obb, dtype=np.float32).reshape(-1, 4, 2)\n        raise ValueError(f\"Invalid metric target: {self._metric_target}\")\n\n    def _content_area(\n        self, xywh: list[float], content: npt.NDArray[Any] | None, idx: int\n    ) -> float:\n        \"\"\"Compute the default annotation area for the metric target: bbox area\n        for boxes, pixel count for masks, polygon area for oriented boxes.\"\"\"\n        if content is None:\n            return float(xywh[2] * xywh[3])\n        if self._metric_target == MetricTarget.MASKS:\n            return float(np.count_nonzero(content[idx]))\n        x, y = content[idx, :, 0], content[idx, :, 1]","sourceCodeStart":1460,"sourceCodeEnd":1496,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/metrics/mean_average_precision.py#L1460-L1496","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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)","Use an OBB-aware connector (e.g. from_ultralytics on an OBB model) that populates the data field","If you only have axis-aligned boxes, fall back to MetricTarget.BOXES","Validate before update: check the key exists in .data for every non-empty Detections"],"exampleFix":"# before\nmap_ = sv.MeanAveragePrecision(metric_target=sv.MetricTarget.ORIENTED_BOUNDING_BOXES)\npreds = sv.Detections(xyxy=boxes, class_id=ids, confidence=confs)  # no obb data\nmap_.update(preds, targets)\n\n# after\nfrom supervision.config import ORIENTED_BOX_COORDINATES\npreds = sv.Detections(xyxy=boxes, class_id=ids, confidence=confs,\n                      data={ORIENTED_BOX_COORDINATES: pred_corners})  # (N,4,2)\ntargets = sv.Detections(xyxy=gt_boxes, class_id=gt_ids,\n                        data={ORIENTED_BOX_COORDINATES: gt_corners})\nmap_.update(preds, targets)","handlingStrategy":"validation","validationCode":"from supervision.config import ORIENTED_BOX_COORDINATES\n\ndef obb_ok(dets) -> bool:\n    \"\"\"OBB-target precondition: empty or carries the obb data key.\"\"\"\n    return len(dets) == 0 or ORIENTED_BOX_COORDINATES in dets.data\n\nassert obb_ok(preds) and obb_ok(targets)","typeGuard":"import numpy as np\nfrom supervision.config import ORIENTED_BOX_COORDINATES\nfrom supervision.detection.core import Detections\n\ndef has_obb_data(dets: Detections) -> bool:\n    \"\"\"True when Detections carries an (N, 4, 2) corner array under the obb key.\"\"\"\n    obb = dets.data.get(ORIENTED_BOX_COORDINATES)\n    return obb is not None and np.asarray(obb).ndim in (2, 3)","tryCatchPattern":null,"preventionTips":["Import ORIENTED_BOX_COORDINATES from supervision.config; never hand-type the key","Store corner arrays shaped (N, 4, 2) on both predictions and targets","Use an OBB connector for rotated-detector outputs"],"tags":["metrics","mean-average-precision","oriented-boxes","obb","validation"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}