diff --git a/src/supervision/annotators/core.py b/src/supervision/annotators/core.py index 7ec078602..6c59d1e39 100644 --- a/src/supervision/annotators/core.py +++ b/src/supervision/annotators/core.py @@ -573,8 +573,15 @@ def annotate( if detections.mask is None: return scene + masks = detections.mask + compact_mask = masks if isinstance(masks, CompactMask) else None for detection_idx in range(len(detections)): - mask = cast(npt.NDArray[np.bool_], detections.mask[detection_idx]) + if compact_mask is None: + mask = cast(npt.NDArray[np.bool_], masks[detection_idx]) + offset = None + else: + mask = compact_mask.crop(detection_idx) + offset = compact_mask.offsets[detection_idx] color = resolve_color( color=self.color, detections=detections, @@ -584,6 +591,8 @@ def annotate( else custom_color_lookup, ) for polygon in mask_to_polygons(mask=mask): + if offset is not None: + polygon = polygon + offset scene = draw_polygon( scene=scene, polygon=cast(npt.NDArray[np.int_], polygon), diff --git a/tests/annotators/test_core.py b/tests/annotators/test_core.py index 005f81666..f47308420 100644 --- a/tests/annotators/test_core.py +++ b/tests/annotators/test_core.py @@ -289,6 +289,43 @@ def test_annotate_with_single_mask(self, test_image, test_mask): result = annotator.annotate(scene=test_image.copy(), detections=detections) assert_image_mostly_same(test_image, result, similarity_threshold=0.85) + def test_compact_mask_uses_crops_and_matches_dense_mask(self, monkeypatch): + """CompactMask polygons use crops without full-frame mask indexing.""" + height, width = 80, 90 + scene = np.zeros((height, width, 3), dtype=np.uint8) + masks = np.zeros((3, height, width), dtype=bool) + masks[0, 10:20, 15:30] = True + masks[0, 32:45, 40:55] = True + masks[1] = False + masks[2, 50:70, 5:25] = True + xyxy = np.array( + [[15, 10, 54, 44], [60, 5, 70, 15], [5, 50, 24, 69]], + dtype=np.float32, + ) + dense = Detections(xyxy=xyxy, mask=masks, class_id=np.array([0, 1, 2])) + compact = Detections( + xyxy=xyxy, + mask=CompactMask.from_dense(masks, xyxy, (height, width)), + class_id=np.array([0, 1, 2]), + ) + annotator = PolygonAnnotator( + color=Color.WHITE, thickness=1, color_lookup=ColorLookup.INDEX + ) + expected = annotator.annotate(scene=scene.copy(), detections=dense) + + original_getitem = CompactMask.__getitem__ + + def fail_int_index(self, index): + if isinstance(index, (int, np.integer)): + raise AssertionError("PolygonAnnotator must use CompactMask.crop") + return original_getitem(self, index) + + monkeypatch.setattr(CompactMask, "__getitem__", fail_int_index) + result = annotator.annotate(scene=scene.copy(), detections=compact) + + assert not np.array_equal(result, scene), "annotator painted nothing" + np.testing.assert_array_equal(result, expected) + class TestColorAnnotator: """Tests for ColorAnnotator class"""