Stay updated

News & Insights

albumentations.augmentations.geometric.rotate


Randomly rotate the input by 90 degrees zero or more times.

Members

RandomRotate90class

RandomRotate90(
    p: float = 1,
    group_element: 'e' | 'r90' | 'r180' | 'r270' | None
)

Randomly rotate the input by 90 degrees zero or more times. Even with p=1.0, the transform has a 1/4 probability of being identity: - With probability p * 1/4: no rotation (0 degrees) - With probability p * 1/4: rotate 90 degrees - With probability p * 1/4: rotate 180 degrees - With probability p * 1/4: rotate 270 degrees For example: - With p=1.0: Each rotation angle (including 0°) has 0.25 probability - With p=0.8: Each rotation angle has 0.2 probability, and no transform has 0.2 probability - With p=0.5: Each rotation angle has 0.125 probability, and no transform has 0.5 probability Common applications: - Aerial/satellite imagery: Objects can appear in any orientation - Medical imaging: Scans/slides may not have a consistent orientation - Document analysis: Pages or symbols might be rotated - Microscopy: Cell orientation is often arbitrary - Game development: Sprites/textures that should work in multiple orientations Not recommended for: - Natural scene images where gravity matters (e.g., landscape photography) - Face detection/recognition tasks - Text recognition (unless text can appear rotated) - Tasks where object orientation is important for classification Note: If your domain has both 90-degree rotation AND flip symmetries (e.g., satellite imagery, microscopy), consider using `D4` transform instead. `D4` is more efficient and mathematically correct as it: - Samples uniformly from all 8 possible combinations of rotations and flips - Properly represents the dihedral group D4 symmetries - Avoids potential correlation between separate rotation and flip augmentations `inverse()` requires `group_element` to be set explicitly; raises `ValueError` otherwise. When `group_element` is specified, the transform is deterministic—useful for TTA (Test Time Augmentation) where you need to apply each of the 4 rotations (0°, 90°, 180°, 270°) explicitly and invert predictions. Uses the same naming as D4: C4 is the rotation subgroup of D4. Call `inverse()` on a deterministic instance to get a new transform that undoes the rotation (r90 ↔ r270, r180 ↔ r180, e ↔ e).

Parameters

NameTypeDefaultDescription
pfloat1probability of applying the transform. Default: 1.0. Note that even with p=1.0, there's still a 0.25 probability of getting a 0-degree rotation (identity transform).
group_element
One of:
  • 'e'
  • 'r90'
  • 'r180'
  • 'r270'
  • None
-If set, always apply this C4 group element: "e"=identity, "r90"=90°, "r180"=180°, "r270"=270° counterclockwise. Use for TTA. Default: None (random choice).

Examples

>>> import numpy as np
>>> import albumentations as A
>>> # Create example 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]  # Class labels for bounding boxes
>>> keypoints = np.array([[20, 30], [60, 70]], dtype=np.float32)
>>> keypoint_labels = [0, 1]  # Labels for keypoints
>>> # Define the transform
>>> transform = A.Compose([
...     A.RandomRotate90(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 to all targets
>>> transformed = transform(
...     image=image,
...     mask=mask,
...     bboxes=bboxes,
...     bbox_labels=bbox_labels,
...     keypoints=keypoints,
...     keypoint_labels=keypoint_labels
... )
>>> rotated_image = transformed["image"]
>>> rotated_mask = transformed["mask"]
>>> rotated_bboxes = transformed["bboxes"]
>>> rotated_bbox_labels = transformed["bbox_labels"]
>>> rotated_keypoints = transformed["keypoints"]
>>> rotated_keypoint_labels = transformed["keypoint_labels"]

>>> # TTA: apply each of the 4 rotations, run inference, then undo on the predicted mask
>>> from albumentations.core.type_definitions import c4_group_elements
>>> predictions = []
>>> for element in c4_group_elements:
...     aug = A.RandomRotate90(p=1.0, group_element=element)
...     aug_image = aug(image=image)["image"]
...     pred_mask = np.zeros((100, 100, 1), dtype=np.uint8)  # placeholder for model output
...     restored = aug.inverse()(image=pred_mask)["image"]
...     predictions.append(restored)

Notes

If your domain has both 90-degree rotation AND flip symmetries (e.g., satellite imagery, microscopy), consider using `D4` transform instead. `D4` is more efficient and mathematically correct as it: - Samples uniformly from all 8 possible combinations of rotations and flips - Properly represents the dihedral group D4 symmetries - Avoids potential correlation between separate rotation and flip augmentations `inverse()` requires `group_element` to be set explicitly; raises `ValueError` otherwise. When `group_element` is specified, the transform is deterministic—useful for TTA (Test Time Augmentation) where you need to apply each of the 4 rotations (0°, 90°, 180°, 270°) explicitly and invert predictions. Uses the same naming as D4: C4 is the rotation subgroup of D4. Call `inverse()` on a deterministic instance to get a new transform that undoes the rotation (r90 ↔ r270, r180 ↔ r180, e ↔ e).

Rotateclass

Rotate(
    limit: tuple[float, float] | float = (-90, 90),
    interpolation: 0 | 1 | 2 | 3 | 4 = 1,
    border_mode: 0 | 1 | 2 | 3 | 4 = 0,
    rotate_method: 'largest_box' | 'ellipse' = largest_box,
    crop_border: bool = False,
    mask_interpolation: 0 | 1 | 2 | 3 | 4 = 0,
    fill: tuple[float, ...] | float = 0,
    fill_mask: tuple[float, ...] | float = 0,
    p: float = 0.5
)

Rotate the input by an angle selected randomly from the uniform distribution.

Parameters

NameTypeDefaultDescription
limit
One of:
  • tuple[float, float]
  • float
(-90, 90)Range from which a random angle is picked. If limit is a single float, an angle is picked from (-limit, limit). Default: (-90, 90)
interpolation
One of:
  • 0
  • 1
  • 2
  • 3
  • 4
1Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.
border_mode
One of:
  • 0
  • 1
  • 2
  • 3
  • 4
0Flag that is used to specify the pixel extrapolation method. Should be one of: cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101. Default: cv2.BORDER_CONSTANT
rotate_method
One of:
  • 'largest_box'
  • 'ellipse'
largest_boxMethod to rotate bounding boxes. Should be 'largest_box' or 'ellipse'. Default: 'largest_box'
crop_borderboolFalseWhether to crop border after rotation. If True, the output image size might differ from the input. Default: False
mask_interpolation
One of:
  • 0
  • 1
  • 2
  • 3
  • 4
0flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.
fill
One of:
  • tuple[float, ...]
  • float
0Padding value if border_mode is cv2.BORDER_CONSTANT.
fill_mask
One of:
  • tuple[float, ...]
  • float
0Padding value if border_mode is cv2.BORDER_CONSTANT applied for masks.
pfloat0.5Probability of applying the transform. Default: 0.5.

Examples

>>> import numpy as np
>>> import albumentations as A
>>> # Create example 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]  # Class labels for bounding boxes
>>> keypoints = np.array([[20, 30], [60, 70]], dtype=np.float32)
>>> keypoint_labels = [0, 1]  # Labels for keypoints
>>> # Define the transform
>>> transform = A.Compose([
...     A.Rotate(limit=45, 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 to all targets
>>> transformed = transform(
...     image=image,
...     mask=mask,
...     bboxes=bboxes,
...     bbox_labels=bbox_labels,
...     keypoints=keypoints,
...     keypoint_labels=keypoint_labels
... )
>>> rotated_image = transformed["image"]
>>> rotated_mask = transformed["mask"]
>>> rotated_bboxes = transformed["bboxes"]
>>> rotated_bbox_labels = transformed["bbox_labels"]
>>> rotated_keypoints = transformed["keypoints"]
>>> rotated_keypoint_labels = transformed["keypoint_labels"]

Notes

- The rotation angle is randomly selected for each execution within the range specified by 'limit'. - When 'crop_border' is False, the output image will have the same size as the input, potentially introducing black triangles in the corners. - When 'crop_border' is True, the output image is cropped to remove black triangles, which may result in a smaller image. - Bounding boxes are rotated and may change size or shape. - Keypoints are rotated around the center of the image.

SafeRotateclass

SafeRotate(
    limit: tuple[float, float] | float = (-90, 90),
    interpolation: 0 | 1 | 2 | 3 | 4 = 1,
    border_mode: 0 | 1 | 2 | 3 | 4 = 0,
    rotate_method: 'largest_box' | 'ellipse' = largest_box,
    mask_interpolation: 0 | 1 | 2 | 3 | 4 = 0,
    fill: tuple[float, ...] | float = 0,
    fill_mask: tuple[float, ...] | float | None,
    p: float = 0.5
)

Rotate the input inside the input's frame by an angle selected randomly from the uniform distribution. This transformation ensures that the entire rotated image fits within the original frame by scaling it down if necessary. The resulting image maintains its original dimensions but may contain artifacts due to the rotation and scaling process.

Parameters

NameTypeDefaultDescription
limit
One of:
  • tuple[float, float]
  • float
(-90, 90)Range from which a random angle is picked. If limit is a single float, an angle is picked from (-limit, limit). Default: (-90, 90)
interpolation
One of:
  • 0
  • 1
  • 2
  • 3
  • 4
1Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.
border_mode
One of:
  • 0
  • 1
  • 2
  • 3
  • 4
0Flag that is used to specify the pixel extrapolation method. Should be one of: cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101. Default: cv2.BORDER_REFLECT_101
rotate_method
One of:
  • 'largest_box'
  • 'ellipse'
largest_boxMethod to rotate bounding boxes. Should be 'largest_box' or 'ellipse'. Default: 'largest_box'
mask_interpolation
One of:
  • 0
  • 1
  • 2
  • 3
  • 4
0flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.
fill
One of:
  • tuple[float, ...]
  • float
0Padding value if border_mode is cv2.BORDER_CONSTANT.
fill_mask
One of:
  • tuple[float, ...]
  • float
  • None
-Padding value if border_mode is cv2.BORDER_CONSTANT applied for masks.
pfloat0.5Probability of applying the transform. Default: 0.5.

Examples

>>> import numpy as np
>>> import albumentations as A
>>> # Create example 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]  # Class labels for bounding boxes
>>> keypoints = np.array([[20, 30], [60, 70]], dtype=np.float32)
>>> keypoint_labels = [0, 1]  # Labels for keypoints
>>> # Define the transform
>>> transform = A.Compose([
...     A.SafeRotate(limit=45, 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 to all targets
>>> transformed = transform(
...     image=image,
...     mask=mask,
...     bboxes=bboxes,
...     bbox_labels=bbox_labels,
...     keypoints=keypoints,
...     keypoint_labels=keypoint_labels
... )
>>> rotated_image = transformed["image"]
>>> rotated_mask = transformed["mask"]
>>> rotated_bboxes = transformed["bboxes"]
>>> rotated_bbox_labels = transformed["bbox_labels"]
>>> rotated_keypoints = transformed["keypoints"]
>>> rotated_keypoint_labels = transformed["keypoint_labels"]

Notes

- The rotation is performed around the center of the image. - After rotation, the image is scaled to fit within the original frame, which may cause some distortion. - The output image will always have the same dimensions as the input image. - Bounding boxes and keypoints are transformed along with the image.