fix(annotators): clip CropAnnotator boxes to the scene before cropping (#2391)
This commit is contained in:
parent
f196e15f26
commit
0d4c3a4fcf
|
|
@ -3019,17 +3019,21 @@ class CropAnnotator(BaseAnnotator):
|
|||
"""
|
||||
if not isinstance(scene, np.ndarray):
|
||||
return scene
|
||||
crops = [
|
||||
crop_image(image=scene, xyxy=xyxy) for xyxy in detections.xyxy.astype(int)
|
||||
]
|
||||
resized_crops = [
|
||||
scale_image(image=crop, scale_factor=self.scale_factor) for crop in crops
|
||||
]
|
||||
image_height, image_width = scene.shape[:2]
|
||||
clipped_xyxy: npt.NDArray[np.int32] = clip_boxes(
|
||||
xyxy=detections.xyxy,
|
||||
resolution_wh=(image_width, image_height),
|
||||
).astype(int)
|
||||
anchors: npt.NDArray[np.int32] = detections.get_anchors_coordinates(
|
||||
anchor=self.position
|
||||
).astype(int)
|
||||
|
||||
for idx, (resized_crop, anchor) in enumerate(zip(resized_crops, anchors)):
|
||||
for idx, (xyxy, anchor) in enumerate(zip(clipped_xyxy, anchors)):
|
||||
x_min, y_min, x_max, y_max = xyxy
|
||||
if x_max - x_min <= 0 or y_max - y_min <= 0:
|
||||
continue
|
||||
crop = crop_image(image=scene, xyxy=xyxy)
|
||||
resized_crop = scale_image(image=crop, scale_factor=self.scale_factor)
|
||||
crop_wh = resized_crop.shape[1], resized_crop.shape[0]
|
||||
(x1, y1), (x2, y2) = self.calculate_crop_coordinates(
|
||||
anchor=anchor, crop_wh=crop_wh, position=self.position
|
||||
|
|
|
|||
|
|
@ -1323,6 +1323,58 @@ class TestCropAnnotator:
|
|||
result = annotator.annotate(scene=gradient_image.copy(), detections=detections)
|
||||
assert not np.array_equal(gradient_image, result)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"xyxy",
|
||||
[
|
||||
pytest.param([-5, 20, 40, 60], id="negative-x-min"),
|
||||
pytest.param([20, -5, 60, 40], id="negative-y-min"),
|
||||
pytest.param([-10, -10, 30, 30], id="negative-x-and-y-min"),
|
||||
pytest.param([60, 20, 140, 60], id="past-right-edge"),
|
||||
pytest.param([-20, -20, 140, 140], id="larger-than-scene"),
|
||||
],
|
||||
)
|
||||
def test_annotate_with_box_crossing_scene_border(
|
||||
self, gradient_image, xyxy: list[int]
|
||||
) -> None:
|
||||
"""Boxes extending past the scene border are clipped instead of raising"""
|
||||
detections = _create_detections(xyxy=[xyxy], class_id=[0])
|
||||
annotator = CropAnnotator()
|
||||
|
||||
result = annotator.annotate(scene=gradient_image.copy(), detections=detections)
|
||||
|
||||
assert result.shape == gradient_image.shape
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"xyxy",
|
||||
[
|
||||
pytest.param([150, 150, 200, 200], id="fully-outside"),
|
||||
pytest.param([-50, -50, -10, -10], id="fully-negative"),
|
||||
pytest.param([30, 20, 30, 60], id="zero-width"),
|
||||
pytest.param([30, 30, 30, 30], id="zero-area"),
|
||||
],
|
||||
)
|
||||
def test_annotate_skips_boxes_empty_after_clipping(
|
||||
self, gradient_image, xyxy: list[int]
|
||||
) -> None:
|
||||
"""Boxes with no visible area are skipped instead of raising cv2.error"""
|
||||
detections = _create_detections(xyxy=[xyxy], class_id=[0])
|
||||
annotator = CropAnnotator()
|
||||
|
||||
result = annotator.annotate(scene=gradient_image.copy(), detections=detections)
|
||||
|
||||
assert np.array_equal(gradient_image, result)
|
||||
|
||||
def test_annotate_mixed_valid_and_degenerate_boxes(self, gradient_image) -> None:
|
||||
"""A degenerate box does not prevent valid boxes from being drawn"""
|
||||
detections = _create_detections(
|
||||
xyxy=[[150, 150, 200, 200], [10, 10, 90, 90]], class_id=[0, 1]
|
||||
)
|
||||
annotator = CropAnnotator()
|
||||
|
||||
result = annotator.annotate(scene=gradient_image.copy(), detections=detections)
|
||||
|
||||
assert not np.array_equal(gradient_image, result)
|
||||
|
||||
|
||||
class TestBackgroundOverlayAnnotator:
|
||||
"""Tests for BackgroundOverlayAnnotator class"""
|
||||
|
|
|
|||
Loading…
Reference in New Issue