{"record":{"id":"b68e6553b31aa5ed","repo":"roboflow/supervision","slug":"obb-data-for-each-detection-must-have-shape-4-2","errorCode":null,"errorMessage":"OBB data for each detection must have shape (4, 2), got {corners.shape}. Ensure `detections.data['{ORIENTED_BOX_COORDINATES}']` has shape (N, 4, 2) before exporting.","messagePattern":"OBB data for each detection must have shape \\(4, 2\\), got (.+?)\\. Ensure `detections\\.data\\['(.+?)'\\]` has shape \\(N, 4, 2\\) before exporting\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/dataset/formats/yolo.py","lineNumber":398,"sourceCode":"            UserWarning,\n            stacklevel=2,\n        )\n\n    annotation: list[str] = []\n    for xyxy, mask, _, class_id, _, data in detections:\n        if class_id is None:\n            raise ValueError(\"Class ID is required for YOLO annotations.\")\n        if not isinstance(class_id, (int, np.integer)):\n            raise ValueError(\n                f\"Detections class_id must be an integer for YOLO export, \"\n                f\"got {type(class_id)!r}.\"\n            )\n        class_id_int = int(class_id)\n\n        if is_obb:\n            corners = np.asarray(data[ORIENTED_BOX_COORDINATES], dtype=np.float32)\n            if corners.shape != (4, 2):\n                raise ValueError(\n                    f\"OBB data for each detection must have shape (4, 2), \"\n                    f\"got {corners.shape}. Ensure \"\n                    f\"`detections.data['{ORIENTED_BOX_COORDINATES}']` has \"\n                    \"shape (N, 4, 2) before exporting.\"\n                )\n            next_object = object_to_yolo(\n                xyxy=xyxy,\n                class_id=class_id_int,\n                image_shape=image_shape,\n                polygon=corners,\n            )\n            annotation.append(next_object)\n            continue\n\n        if mask is not None:\n            polygons = approximate_mask_with_polygons(\n                mask=mask,\n                min_image_area_percentage=min_image_area_percentage,","sourceCodeStart":380,"sourceCodeEnd":416,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/dataset/formats/yolo.py#L380-L416","documentation":"Raised during OBB YOLO export when the per-detection oriented corner array in data[ORIENTED_BOX_COORDINATES] does not have shape (4, 2). Each oriented box is defined by exactly 4 corner points with x,y coordinates; a wrong shape means the stored geometry is not a valid single OBB (e.g. a flattened array, wrong corner count, or the whole (N, 4, 2) batch stored per detection).","triggerScenarios":"sv.detections_to_yolo_annotations(..., is_obb=True) where detections.data[ORIENTED_BOX_COORDINATES] was set manually to e.g. an (8,) flat array, an (N, 4, 2) array incorrectly indexed, or an (4, 3) array.","commonSituations":"Hand-building OBB data from model output that returns flat 8-value OBB params (x,y,w,h,angle) instead of corners — forgetting cv2.boxPoints; reshaping errors; passing per-detection rows of a batch array without indexing.","solutions":["Reshape each detection's corners to exactly 4 rows of (x, y): corners.reshape(4, 2).","If starting from rotated-rect parameters, convert with cv2.boxPoints(((cx,cy),(w,h),angle)) which returns (4, 2).","Store the batch as one (N, 4, 2) array in detections.data; the loader indexes rows per detection.","Print corners.shape in a small repro to confirm the fix."],"exampleFix":"# before\ndets.data[sv.ORIENTED_BOX_COORDINATES] = flat_corners  # shape (N, 8)\n# after\nimport numpy as np\nN = len(dets)\ndets.data[sv.ORIENTED_BOX_COORDINATES] = flat_corners.reshape(N, 4, 2)","handlingStrategy":"validation","validationCode":"import numpy as np\nimport supervision as sv\n\ndef obb_shapes_valid(detections) -> bool:\n    \"\"\"Check ORIENTED_BOX_COORDINATES is (N, 4, 2) with N matching detections.\"\"\"\n    c = detections.data.get(sv.ORIENTED_BOX_COORDINATES)\n    return c is not None and c.shape == (len(detections), 4, 2)","typeGuard":null,"tryCatchPattern":"try:\n    lines = sv.detections_to_yolo_annotations(dets, image_shape=shape, is_obb=True)\nexcept ValueError as e:\n    if 'must have shape (4, 2)' in str(e):\n        c = dets.data[sv.ORIENTED_BOX_COORDINATES]\n        dets.data[sv.ORIENTED_BOX_COORDINATES] = np.asarray(c).reshape(len(dets), 4, 2)\n        lines = sv.detections_to_yolo_annotations(dets, image_shape=shape, is_obb=True)\n    else:\n        raise","preventionTips":["Always store corners via cv2.boxPoints output reshaped to (N, 4, 2).","Unit-test data shapes when building OBB Detections by hand.","Keep the batch array intact in data; do not store per-detection slices."],"tags":["yolo","obb","export","shape"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}