Random crop of fixed size that contains at least one bbox. erosion_factor controls overlap with reference box. Use when some object loss is acceptable.
Similar to BBoxSafeRandomCrop, but with a key difference:
This makes AtLeastOneBBoxRandomCrop more flexible for scenarios where:
The algorithm:
heightFixed height of the crop
widthFixed width of the crop
erosion_factorFactor by which to erode (shrink) the reference bounding box when computing valid crop regions. Must be in range [0.0, 1.0].
pProbability of applying the transform. Defaults to 1.0.
>>> import numpy as np
>>> import albumentations as A
>>> import cv2
>>>
>>> # Prepare sample data
>>> image = np.random.randint(0, 256, (300, 300, 3), dtype=np.uint8)
>>> mask = np.random.randint(0, 2, (300, 300), dtype=np.uint8)
>>> # Create multiple bounding boxes - the transform will ensure at least one is in the crop
>>> bboxes = np.array([
... [30, 50, 100, 140], # first box
... [150, 120, 270, 250], # second box
... [200, 30, 280, 90] # third box
... ], dtype=np.float32)
>>> bbox_labels = [1, 2, 3]
>>> keypoints = np.array([
... [50, 70], # keypoint inside first box
... [190, 170], # keypoint inside second box
... [240, 60] # keypoint inside third box
... ], dtype=np.float32)
>>> keypoint_labels = [0, 1, 2]
>>>
>>> # Define transform with different erosion_factor values
>>> transform = A.Compose([
... A.AtLeastOneBBoxRandomCrop(
... height=200,
... width=200,
... erosion_factor=0.2, # Allows moderate flexibility in crop placement
... p=1.0
... ),
... ], bbox_params=A.BboxParams(coord_format='pascal_voc', label_fields=['bbox_labels']),
... keypoint_params=A.KeypointParams(coord_format='xy', label_fields=['keypoint_labels']))
>>>
>>> # Apply the transform
>>> transformed = transform(
... image=image,
... mask=mask,
... bboxes=bboxes,
... bbox_labels=bbox_labels,
... keypoints=keypoints,
... keypoint_labels=keypoint_labels
... )
>>>
>>> # Get the transformed data
>>> transformed_image = transformed['image'] # Shape: (200, 200, 3)
>>> transformed_mask = transformed['mask'] # Shape: (200, 200)
>>> transformed_bboxes = transformed['bboxes'] # At least one bbox is guaranteed
>>> transformed_bbox_labels = transformed['bbox_labels'] # Labels for the preserved bboxes
>>> transformed_keypoints = transformed['keypoints'] # Only keypoints in crop are kept
>>> transformed_keypoint_labels = transformed['keypoint_labels'] # Their labels
>>>
>>> # Verify that at least one bounding box was preserved
>>> assert len(transformed_bboxes) > 0, "Should have at least one bbox in the crop"
>>>
>>> # With erosion_factor=0.0, the crop must fully contain the selected reference bbox
>>> conservative_transform = A.Compose([
... A.AtLeastOneBBoxRandomCrop(
... height=200,
... width=200,
... erosion_factor=0.0, # No erosion - crop must fully contain a bbox
... p=1.0
... ),
... ], bbox_params=A.BboxParams(coord_format='pascal_voc', label_fields=['bbox_labels']))
>>>
>>> # With erosion_factor=1.0, the crop must only intersect with the selected reference bbox
>>> flexible_transform = A.Compose([
... A.AtLeastOneBBoxRandomCrop(
... height=200,
... width=200,
... erosion_factor=1.0, # Maximum erosion - crop only needs to intersect a bbox
... p=1.0
... ),
... ], bbox_params=A.BboxParams(coord_format='pascal_voc', label_fields=['bbox_labels']))