diff --git a/src/supervision/config.py b/src/supervision/config.py index 252f5e92..e1692674 100644 --- a/src/supervision/config.py +++ b/src/supervision/config.py @@ -1,5 +1,12 @@ CLASS_NAME_DATA_FIELD: str = "class_name" -# Used by move_detections (coordinate transform) and InferenceSlicer (OBB thread-safety -# detection). Any code that sets this key in Detections.data also affects slicer -# scheduling: InferenceSlicer switches to sequential mode when this key is present. +#: Key for oriented bounding-box corner coordinates in ``Detections.data``. +#: +#: Value layout: ``np.ndarray`` of shape ``(N, 4, 2)``, dtype ``float32``, pixel +#: coordinates ordered as ``[[x1, y1], [x2, y2], [x3, y3], [x4, y4]]`` per +#: detection where the four points are the corners of the oriented box. +#: Used by :func:`~supervision.dataset.formats.yolo.detections_to_yolo_annotations` +#: (``is_obb=True``) and +#: :func:`~supervision.dataset.formats.yolo.yolo_annotations_to_detections` +#: (``is_obb=True``). +#: Also triggers sequential mode in ``InferenceSlicer`` when present. ORIENTED_BOX_COORDINATES: str = "xyxyxyxy" diff --git a/src/supervision/dataset/formats/yolo.py b/src/supervision/dataset/formats/yolo.py index e28b8362..dc5a3dc5 100644 --- a/src/supervision/dataset/formats/yolo.py +++ b/src/supervision/dataset/formats/yolo.py @@ -379,6 +379,13 @@ def detections_to_yolo_annotations( if is_obb: corners = np.asarray(data[ORIENTED_BOX_COORDINATES], dtype=np.float32) + if corners.shape != (4, 2): + raise ValueError( + f"OBB data for each detection must have shape (4, 2), " + f"got {corners.shape}. Ensure " + f"`detections.data['{ORIENTED_BOX_COORDINATES}']` has " + "shape (N, 4, 2) before exporting." + ) next_object = object_to_yolo( xyxy=xyxy, class_id=class_id_int, diff --git a/tests/dataset/formats/test_yolo.py b/tests/dataset/formats/test_yolo.py index 12d5303f..9c56a4c6 100644 --- a/tests/dataset/formats/test_yolo.py +++ b/tests/dataset/formats/test_yolo.py @@ -617,6 +617,28 @@ def test_detections_to_yolo_annotations_obb_multiple_detections() -> None: ) +def test_detections_to_yolo_annotations_obb_data_ignored_when_is_obb_false() -> None: + """OBB data in detections.data must be silently ignored when is_obb=False.""" + corners = np.array( + [[[50.0, 10.0], [90.0, 50.0], [50.0, 90.0], [10.0, 50.0]]], + dtype=np.float32, + ) + detections = Detections( + xyxy=np.array([[10.0, 10.0, 90.0, 90.0]], dtype=np.float32), + class_id=np.array([0], dtype=int), + data={ORIENTED_BOX_COORDINATES: corners}, + ) + + lines = detections_to_yolo_annotations( + detections=detections, image_shape=(100, 100, 3), is_obb=False + ) + + assert len(lines) == 1 + assert len(lines[0].split()) == 5, ( + "Without is_obb=True, OBB data must be ignored and bbox format (5 tokens) used" + ) + + @pytest.mark.parametrize( ("is_obb_save", "expected_tokens"), [