BBoxSafeRandomCrop

Targets:
image
mask
bboxes
keypoints
volume
mask3d
Image Types:uint8, float32

Crop an area from image while ensuring all bounding boxes are preserved in the crop.

Similar to AtLeastOneBboxRandomCrop, but with a key difference:

  • BBoxSafeRandomCrop ensures ALL bounding boxes are preserved in the crop when erosion_rate=0.0
  • AtLeastOneBboxRandomCrop ensures AT LEAST ONE bounding box is present in the crop

This makes BBoxSafeRandomCrop more suitable for scenarios where:

  • You need to preserve all objects in the scene
  • Losing any bounding box would be problematic (e.g., rare object classes)
  • You're training a model that needs to detect multiple objects simultaneously

The algorithm:

  1. If bounding boxes exist:
    • Computes the union of all bounding boxes
    • Applies erosion based on erosion_rate to this union
    • Clips the eroded union to valid image coordinates [0,1]
    • Randomly samples crop coordinates within the clipped union area
  2. If no bounding boxes exist:
    • Computes crop height based on erosion_rate
    • Sets crop width to maintain original aspect ratio
    • Randomly places the crop within the image
Arguments
erosion_rate
float
0

Controls how much the valid crop region can deviate from the bbox union. Must be in range [0.0, 1.0].

  • 0.0: crop must contain the exact bbox union (safest option that guarantees all boxes are preserved)
  • 1.0: crop can deviate maximally from the bbox union (increases likelihood of cutting off some boxes) Defaults to 0.0.
p
float
1

Probability of applying the transform. Defaults to 1.0.

Examples
>>> import numpy as np
>>> import albumentations as A
>>>
>>> # Prepare sample data
>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)
>>> mask = np.random.randint(0, 2, (100, 100), dtype=np.uint8)
>>> bboxes = np.array([[10, 10, 50, 50], [40, 40, 80, 80]], dtype=np.float32)
>>> bbox_labels = [1, 2]
>>> keypoints = np.array([[20, 30], [60, 70]], dtype=np.float32)
>>> keypoint_labels = [0, 1]
>>>
>>> # Define transform with erosion_rate parameter
>>> transform = A.Compose([
...     A.BBoxSafeRandomCrop(erosion_rate=0.2),
... ], 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
>>> result = transform(
...     image=image,
...     mask=mask,
...     bboxes=bboxes,
...     bbox_labels=bbox_labels,
...     keypoints=keypoints,
...     keypoint_labels=keypoint_labels
... )
>>>
>>> # Get the transformed data
>>> transformed_image = result['image']  # Cropped image containing all bboxes
>>> transformed_mask = result['mask']    # Cropped mask
>>> transformed_bboxes = result['bboxes']  # All bounding boxes preserved with adjusted coordinates
>>> transformed_bbox_labels = result['bbox_labels']  # Original labels preserved
>>> transformed_keypoints = result['keypoints']  # Keypoints with adjusted coordinates
>>> transformed_keypoint_labels = result['keypoint_labels']  # Original keypoint labels preserved
>>>
>>> # Example with a different erosion_rate
>>> transform_more_flexible = A.Compose([
...     A.BBoxSafeRandomCrop(erosion_rate=0.5),  # More flexibility in crop placement
... ], bbox_params=A.BboxParams(coord_format='pascal_voc', label_fields=['bbox_labels']))
>>>
>>> # Apply transform with only image and bboxes
>>> result_bboxes_only = transform_more_flexible(
...     image=image,
...     bboxes=bboxes,
...     bbox_labels=bbox_labels
... )
>>> transformed_image = result_bboxes_only['image']
>>> transformed_bboxes = result_bboxes_only['bboxes']  # All bboxes still preserved
Notes
  • IMPORTANT: Using erosion_rate > 0.0 may result in some bounding boxes being cut off, particularly narrow boxes at the boundary of the union area. For guaranteed preservation of all bounding boxes, use erosion_rate=0.0.
  • Aspect ratio is preserved only when no bounding boxes are present
  • May be more restrictive in crop placement compared to AtLeastOneBboxRandomCrop
  • The crop size is determined by the bounding boxes when present