|
6 | 6 | from tests.helpers import assert_image_mostly_same |
7 | 7 |
|
8 | 8 |
|
| 9 | +class TestThreeCoordinateKeyPointAnnotators: |
| 10 | + @pytest.mark.parametrize( |
| 11 | + "annotator_name", ["EdgeAnnotator", "VertexLabelAnnotator"] |
| 12 | + ) |
| 13 | + @pytest.mark.parametrize("dtype", ["float32", "float64"]) |
| 14 | + @pytest.mark.parametrize("depth", [0.5, np.nan, np.inf, -np.inf]) |
| 15 | + @pytest.mark.parametrize("visible", [None, True, False]) |
| 16 | + def test_depth_does_not_change_xy_checks( |
| 17 | + self, annotator_name: str, dtype: str, depth: float, visible: bool | None |
| 18 | + ) -> None: |
| 19 | + """Depth preserves x/y validity, missing points and explicit visibility.""" |
| 20 | + scene = np.zeros((64, 64, 3), dtype=np.uint8) |
| 21 | + xy = np.array([[[0, 0], [25, 30], [50, 45], [np.nan, 30]]], dtype=dtype) |
| 22 | + xyz = np.concatenate([xy, np.full((1, 4, 1), depth, dtype=dtype)], axis=2) |
| 23 | + visibility = None if visible is None else np.full((1, 4), visible) |
| 24 | + points = sv.KeyPoints(xy=xyz.copy(), visible=visibility) |
| 25 | + reference = sv.KeyPoints(xy=xy.copy(), visible=visibility) |
| 26 | + annotator = ( |
| 27 | + sv.EdgeAnnotator(edges=[(1, 2), (2, 3), (3, 4)]) |
| 28 | + if annotator_name == "EdgeAnnotator" |
| 29 | + else sv.VertexLabelAnnotator() |
| 30 | + ) |
| 31 | + expected = annotator.annotate(scene.copy(), reference) |
| 32 | + |
| 33 | + actual = annotator.annotate(scene.copy(), points) |
| 34 | + |
| 35 | + np.testing.assert_array_equal(actual, expected) |
| 36 | + assert bool(np.any(actual)) is (visible is not False) |
| 37 | + np.testing.assert_array_equal(points.xy, xyz) |
| 38 | + if visible is None: |
| 39 | + assert points.visible is None |
| 40 | + else: |
| 41 | + np.testing.assert_array_equal(points.visible, np.full((1, 4), visible)) |
| 42 | + |
| 43 | + @pytest.mark.parametrize( |
| 44 | + "annotator_name", |
| 45 | + [ |
| 46 | + "VertexAnnotator", |
| 47 | + "VertexEllipseAreaAnnotator", |
| 48 | + "VertexEllipseOutlineAnnotator", |
| 49 | + "VertexEllipseHaloAnnotator", |
| 50 | + ], |
| 51 | + ) |
| 52 | + @pytest.mark.parametrize("dtype", ["float32", "float64"]) |
| 53 | + @pytest.mark.parametrize("hidden", [False, True]) |
| 54 | + def test_matches_two_coordinate_rendering( |
| 55 | + self, annotator_name: str, dtype: str, hidden: bool |
| 56 | + ) -> None: |
| 57 | + """An extra coordinate leaves pixel rendering and visibility unchanged.""" |
| 58 | + scene = np.zeros((64, 64, 3), dtype=np.uint8) |
| 59 | + xy = np.array([[[25.0, 30.0], [0.0, 0.0], [np.nan, np.nan]]], dtype=dtype) |
| 60 | + xyz = np.concatenate([xy, np.full((1, 3, 1), 0.2, dtype=dtype)], axis=2) |
| 61 | + visible = np.array([[not hidden, True, True]]) |
| 62 | + covariance = np.tile(np.eye(2, dtype=np.float32) * 9, (1, 3, 1, 1)) |
| 63 | + points = sv.KeyPoints( |
| 64 | + xy=xyz.copy(), |
| 65 | + visible=visible.copy(), |
| 66 | + data={"covariance": covariance.copy()}, |
| 67 | + ) |
| 68 | + reference = sv.KeyPoints( |
| 69 | + xy=xy, visible=visible, data={"covariance": covariance} |
| 70 | + ) |
| 71 | + annotator = getattr(sv, annotator_name)() |
| 72 | + expected = annotator.annotate(scene.copy(), reference) |
| 73 | + |
| 74 | + actual = annotator.annotate(scene.copy(), points) |
| 75 | + |
| 76 | + np.testing.assert_array_equal(actual, expected) |
| 77 | + assert bool(np.any(actual)) is not hidden |
| 78 | + np.testing.assert_array_equal(points.xy, xyz) |
| 79 | + np.testing.assert_array_equal(points.visible, visible) |
| 80 | + np.testing.assert_array_equal(points.data["covariance"], covariance) |
| 81 | + |
| 82 | + |
9 | 83 | class TestVertexAnnotator: |
10 | 84 | """Verify that VertexAnnotator correctly draws keypoints on an image. |
11 | 85 |
|
@@ -245,20 +319,14 @@ def test_non_finite_endpoint_does_not_hide_finite_edges(self, scene): |
245 | 319 | result = annotator.annotate(scene=scene.copy(), key_points=key_points) |
246 | 320 | assert not np.array_equal(result, scene) |
247 | 321 |
|
248 | | - def test_non_finite_z_skips_edge_despite_finite_xy(self, scene): |
249 | | - """A finite (x, y) pair with a non-finite z component is skipped. |
250 | | -
|
251 | | - Pins the current full-row finiteness rule, which matches |
252 | | - `KeyPoints.as_detections`: a non-finite z vetoes drawing even though z is never |
253 | | - rasterized by EdgeAnnotator. A future change to only check (x, y) should be a |
254 | | - deliberate decision, not an accidental one. |
255 | | - """ |
| 322 | + def test_non_finite_z_does_not_hide_finite_xy(self, scene): |
| 323 | + """Edges use finite x/y coordinates even when depth is non-finite.""" |
256 | 324 | key_points = sv.KeyPoints( |
257 | 325 | xy=np.array([[[10.0, 10.0, np.nan], [90.0, 90.0, 1.0]]], dtype=np.float32), |
258 | 326 | ) |
259 | 327 | annotator = sv.EdgeAnnotator(edges=[(1, 2)]) |
260 | 328 | result = annotator.annotate(scene=scene.copy(), key_points=key_points) |
261 | | - assert np.array_equal(result, scene) |
| 329 | + assert not np.array_equal(result, scene) |
262 | 330 |
|
263 | 331 | def test_all_non_finite_multi_detection_draws_nothing(self, scene): |
264 | 332 | """A fully non-finite, multi-detection, multi-point array draws nothing and does |
@@ -634,20 +702,14 @@ def test_non_finite_vertex_skipped_when_visible_is_set(self, scene): |
634 | 702 | result = annotator.annotate(scene=scene.copy(), key_points=key_points) |
635 | 703 | assert not np.array_equal(result, scene) |
636 | 704 |
|
637 | | - def test_non_finite_z_skips_label_despite_finite_xy(self, scene): |
638 | | - """A finite (x, y) pair with a non-finite z component is skipped. |
639 | | -
|
640 | | - Pins the current full-row finiteness rule, which matches |
641 | | - `KeyPoints.as_detections`: a non-finite z vetoes drawing even though z is never |
642 | | - rasterized by VertexLabelAnnotator. A future change to only check (x, y) should |
643 | | - be a deliberate decision, not an accidental one. |
644 | | - """ |
| 705 | + def test_non_finite_z_does_not_hide_finite_xy(self, scene): |
| 706 | + """Labels use finite x/y coordinates even when depth is non-finite.""" |
645 | 707 | key_points = sv.KeyPoints( |
646 | 708 | xy=np.array([[[50.0, 50.0, np.nan]]], dtype=np.float32), |
647 | 709 | ) |
648 | 710 | annotator = sv.VertexLabelAnnotator() |
649 | 711 | result = annotator.annotate(scene=scene.copy(), key_points=key_points) |
650 | | - assert np.array_equal(result, scene) |
| 712 | + assert not np.array_equal(result, scene) |
651 | 713 |
|
652 | 714 | def test_all_non_finite_multi_detection_draws_nothing(self, scene): |
653 | 715 | """A fully non-finite, multi-detection, multi-point array draws nothing and does |
|
0 commit comments