albumentations.augmentations.transforms3d.transforms
Apply a sampled 3D affine mapping to volume and mask3d by rotating, scaling, and shifting voxel coordinates for robust medical-imaging augmentation.
Members
- classAffine3D
- classCenterCrop3D
- classCoarseDropout3D
- classCubicSymmetry
- classFlip3D
- classGridShuffle3D
- classPad3D
- classPadIfNeeded3D
- classRandomCrop3D
- classRandomRotate90_3D
- classResize3D
Affine3Dclass
Affine3D(
rotate_range: dict[['x', 'y', 'z'], tuple[float, float]] = {'x': (0.0, 0.0), 'y': (0.0, 0.0), 'z': (0.0, 0.0)},
scale_range: dict[['x', 'y', 'z'], tuple[float, float]] = {'x': (1.0, 1.0), 'y': (1.0, 1.0), 'z': (1.0, 1.0)},
translate_percent_range: dict[['x', 'y', 'z'], tuple[float, float]] = {'x': (0.0, 0.0), 'y': (0.0, 0.0), 'z': (0.0, 0.0)},
interpolation: 0 | 1 = 1,
mask_interpolation: 0 | 1 = 0,
border_mode: 0 | 1 = 0,
fill: tuple[float, ...] | float = 0,
fill_mask: tuple[float, ...] | float = 0,
p: float = 0.5
)Apply a sampled 3D affine mapping to volume and mask3d by rotating, scaling, and shifting voxel coordinates for robust medical-imaging augmentation. `Affine3D` resamples depth, height, and width jointly through Albucore `warp_affine3d`; it never treats depth as a batch axis. The output grid keeps the input `(D, H, W)` shape. It samples positive per-axis scales, rotations, and relative translations independently, then applies the same forward matrix to `volume`, `mask3d`, and `xyz` keypoints.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| rotate_range | dict[['x', 'y', 'z'], tuple[float, float]] | {'x': (0.0, 0.0), 'y': (0.0, 0.0), 'z': (0.0, 0.0)} | Inclusive degree ranges around the `x`, `y`, and `z` axes. Axis names use the `(x, y, z)` voxel coordinate order. Default: all `(0.0, 0.0)`. |
| scale_range | dict[['x', 'y', 'z'], tuple[float, float]] | {'x': (1.0, 1.0), 'y': (1.0, 1.0), 'z': (1.0, 1.0)} | Positive multiplicative scale ranges for `x`, `y`, and `z`. `1.0` leaves an axis unchanged. Default: all `(1.0, 1.0)`. |
| translate_percent_range | dict[['x', 'y', 'z'], tuple[float, float]] | {'x': (0.0, 0.0), 'y': (0.0, 0.0), 'z': (0.0, 0.0)} | Relative translation ranges for `x`, `y`, and `z`. A value of `1.0` moves by the corresponding input-axis length. Default: all `(0.0, 0.0)`. |
| interpolation | One of:
| 1 | Volume interpolation: `cv2.INTER_NEAREST` or `cv2.INTER_LINEAR`. Default: `cv2.INTER_LINEAR`. |
| mask_interpolation | One of:
| 0 | `mask3d` interpolation: `cv2.INTER_NEAREST` or `cv2.INTER_LINEAR`. Default: `cv2.INTER_NEAREST`. |
| border_mode | One of:
| 0 | Border policy: `cv2.BORDER_CONSTANT` or `cv2.BORDER_REPLICATE`. Default: `cv2.BORDER_CONSTANT`. |
| fill | One of:
| 0 | Constant fill for volume channels when `border_mode` is constant. Default: `0`. |
| fill_mask | One of:
| 0 | Constant fill for `mask3d` when `border_mode` is constant. Default: `0`. |
| p | float | 0.5 | Probability of applying the transform. Default: `0.5`. |
Returns
- dict[str, Any]: Augmented targets when the transform is executed through `Compose`.
Examples
>>> import albumentations as A
>>> import cv2
>>> import numpy as np
>>> volume = np.random.default_rng(137).random((16, 64, 96, 1), dtype=np.float32)
>>> mask3d = np.zeros((16, 64, 96), dtype=np.uint8)
>>> keypoints = np.array([[48.0, 32.0, 8.0]], dtype=np.float32)
>>> transform = A.Compose([
... A.Affine3D(
... rotate_range={"x": (-10.0, 10.0), "y": (-5.0, 5.0), "z": (-15.0, 15.0)},
... scale_range={"x": (0.9, 1.1), "y": (0.9, 1.1), "z": (0.95, 1.05)},
... translate_percent_range={"x": (-0.1, 0.1), "y": (-0.1, 0.1), "z": (-0.05, 0.05)},
... interpolation=cv2.INTER_LINEAR,
... mask_interpolation=cv2.INTER_NEAREST,
... p=1.0,
... ),
... ], keypoint_params=A.KeypointParams(coord_format="xyz"), strict=True)
>>> result = transform(volume=volume, mask3d=mask3d, keypoints=keypoints)
>>> result["volume"].shape, result["mask3d"].shape
((16, 64, 96, 1), (16, 64, 96))Notes
- Volume arrays are `(D, H, W, C)` and keypoints are `(x, y, z)`. The centred forward matrix applies scale, then x-, y-, and z-axis rotations, then translation. One sampled matrix is shared across all elements of a sampled transform. - Scale factors must be positive, so this transform does not sample reflections. Use `Flip3D` for reflections. - Transform parameters use voxel coordinates only; physical spacing, orientation, and affine metadata remain unchanged.
CenterCrop3Dclass
CenterCrop3D(
size: tuple[int, int, int],
pad_if_needed: bool = False,
fill: tuple[float, ...] | float = 0,
fill_mask: tuple[float, ...] | float = 0,
p: float = 1.0
)Take the center sub-volume to fixed (depth, height, width). pad_if_needed fills when smaller; fill, fill_mask. For fixed-size 3D inputs (e.g. CT, MRI). Targets: volume, mask3d, keypoints
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| size | tuple[int, int, int] | - | Desired output size of the crop in format (depth, height, width) |
| pad_if_needed | bool | False | Whether to pad if the volume is smaller than desired crop size. Default: False |
| fill | One of:
| 0 | Padding value for image if pad_if_needed is True. Default: 0 |
| fill_mask | One of:
| 0 | Padding value for mask if pad_if_needed is True. Default: 0 |
| p | float | 1.0 | probability of applying the transform. Default: 1.0 |
Examples
>>> import numpy as np
>>> import albumentations as A
>>>
>>> # Prepare sample data
>>> volume = np.random.randint(0, 256, (20, 200, 200), dtype=np.uint8) # (D, H, W)
>>> mask3d = np.random.randint(0, 2, (20, 200, 200), dtype=np.uint8) # (D, H, W)
>>> keypoints = np.array([[100, 100, 10], [150, 150, 15]], dtype=np.float32) # (x, y, z)
>>> keypoint_labels = [1, 2] # Labels for each keypoint
>>>
>>> # Create the transform - crop to 16x128x128 from center
>>> transform = A.Compose([
... A.CenterCrop3D(
... size=(16, 128, 128), # Output size (depth, height, width)
... pad_if_needed=True, # Pad if input is smaller than crop size
... fill=0, # Fill value for volume padding
... fill_mask=1, # Fill value for mask padding
... p=1.0
... )
... ], keypoint_params=A.KeypointParams(coord_format='xyz', label_fields=['keypoint_labels']))
>>>
>>> # Apply the transform
>>> transformed = transform(
... volume=volume,
... mask3d=mask3d,
... keypoints=keypoints,
... keypoint_labels=keypoint_labels
... )
>>>
>>> # Get the transformed data
>>> cropped_volume = transformed["volume"] # Shape: (16, 128, 128)
>>> cropped_mask3d = transformed["mask3d"] # Shape: (16, 128, 128)
>>> cropped_keypoints = transformed["keypoints"] # Keypoints shifted relative to center crop
>>> cropped_keypoint_labels = transformed["keypoint_labels"] # Labels remain unchanged
>>>
>>> # Example with a small volume that requires padding
>>> small_volume = np.random.randint(0, 256, (10, 100, 100), dtype=np.uint8)
>>> small_transform = A.Compose([
... A.CenterCrop3D(
... size=(16, 128, 128),
... pad_if_needed=True, # Will pad since the input is smaller
... fill=0,
... p=1.0
... )
... ])
>>> small_result = small_transform(volume=small_volume)
>>> padded_and_cropped = small_result["volume"] # Shape: (16, 128, 128), padded to sizeNotes
If you want to perform cropping only in the XY plane while preserving all slices along the Z axis, consider using CenterCrop instead. CenterCrop will apply the same XY crop to each slice independently, maintaining the full depth of the volume.
CoarseDropout3Dclass
CoarseDropout3D(
num_holes_range: tuple[int, int] = (1, 1),
hole_depth_range: tuple[float, float] = (0.1, 0.2),
hole_height_range: tuple[float, float] = (0.1, 0.2),
hole_width_range: tuple[float, float] = (0.1, 0.2),
fill: tuple[float, ...] | float = 0,
fill_mask: tuple[float, ...] | float | None,
p: float = 0.5
)Randomly drop cuboid regions from a 3D volume (and optionally mask) to simulate occlusion. Hole size/count configurable.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| num_holes_range | tuple[int, int] | (1, 1) | Range (min, max) for the number of cuboid regions to drop out. Default: (1, 1) |
| hole_depth_range | tuple[float, float] | (0.1, 0.2) | Range (min, max) for the depth of dropout regions as a fraction of the volume depth (between 0 and 1). Default: (0.1, 0.2) |
| hole_height_range | tuple[float, float] | (0.1, 0.2) | Range (min, max) for the height of dropout regions as a fraction of the volume height (between 0 and 1). Default: (0.1, 0.2) |
| hole_width_range | tuple[float, float] | (0.1, 0.2) | Range (min, max) for the width of dropout regions as a fraction of the volume width (between 0 and 1). Default: (0.1, 0.2) |
| fill | One of:
| 0 | Value for the dropped voxels. Can be: - int or float: all channels are filled with this value - tuple: tuple of values for each channel Default: 0 |
| fill_mask | One of:
| - | Fill value for dropout regions in the 3D mask. If None, mask regions corresponding to volume dropouts are unchanged. Default: None |
| p | float | 0.5 | Probability of applying the transform. Default: 0.5 |
Examples
>>> import numpy as np
>>> import albumentations as A
>>> volume = np.random.randint(0, 256, (10, 100, 100), dtype=np.uint8) # (D, H, W)
>>> mask3d = np.random.randint(0, 2, (10, 100, 100), dtype=np.uint8) # (D, H, W)
>>> aug = A.CoarseDropout3D(
... num_holes_range=(3, 6),
... hole_depth_range=(0.1, 0.2),
... hole_height_range=(0.1, 0.2),
... hole_width_range=(0.1, 0.2),
... fill=0,
... p=1.0
... )
>>> transformed = aug(volume=volume, mask3d=mask3d)
>>> transformed_volume, transformed_mask3d = transformed["volume"], transformed["mask3d"]Notes
- The actual number and size of dropout regions are randomly chosen within the specified ranges. - All values in hole_depth_range, hole_height_range and hole_width_range must be between 0 and 1. - If you want to apply dropout only in the XY plane while preserving the full depth dimension, consider using CoarseDropout instead. CoarseDropout will apply the same rectangular dropout to each slice independently, effectively creating cylindrical dropout regions that extend through the entire depth of the volume.
CubicSymmetryclass
CubicSymmetry(
p: float = 1.0
)Randomly reorient a 3D volume with one of 48 exact cubic symmetries, using only axis permutations and reflections for interpolation-free augmentation. This transform is intended for augmentation. Use a transform with explicit deterministic parameters for TTA. This transform is a 3D extension of D4. While D4 handles the 8 symmetries of a square (4 rotations x 2 reflections), CubicSymmetry handles all 48 symmetries of a cube. Like D4, this transform does not create any interpolation artifacts as it only remaps voxels from one position to another without any interpolation. The 48 transformations consist of: - 24 rotations (orientation-preserving): * 4 rotations around each face diagonal (6 face diagonals x 4 rotations = 24) - 24 rotoreflections (orientation-reversing): * Reflection through a plane followed by any of the 24 rotations For a cube, these transformations preserve: - All face centers (6) - All vertex positions (8) - All edge centers (12) works with 3D volume data and masks of the shape (D, H, W) or (D, H, W, C)
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| p | float | 1.0 | Probability of applying the transform. Default: 1.0 |
Examples
>>> import numpy as np
>>> import albumentations as A
>>> volume = np.random.randint(0, 256, (10, 100, 100), dtype=np.uint8) # (D, H, W)
>>> mask3d = np.random.randint(0, 2, (10, 100, 100), dtype=np.uint8) # (D, H, W)
>>> transform = A.CubicSymmetry(p=1.0)
>>> transformed = transform(volume=volume, mask3d=mask3d)
>>> transformed_volume = transformed["volume"]
>>> transformed_mask3d = transformed["mask3d"]Notes
- This transform is particularly useful for data augmentation in 3D medical imaging, crystallography, and voxel-based 3D modeling where the object's orientation is arbitrary. - All transformations preserve the object's chirality (handedness) when using pure rotations (indices 0-23) and invert it when using rotoreflections (indices 24-47).
Flip3Dclass
Flip3D(
axes: tuple[[0, 1, 2], ...] = (0, 1, 2),
flip_axes: tuple[[0, 1, 2], ...] | None,
p: float = 1.0
)Reflect a volume independently across depth, height, and width voxel-index axes while retaining its shape and channel layout. In random mode, each allowed axis is independently reflected, including the empty subset (identity). This samples the full reflection group uniformly. Set `flip_axes` to a fixed subset, including `()`, for reversible test-time augmentation; reflections are self-inverse. This is a voxel-index reflection only: it does not update affine metadata or perform a physical-space reorientation.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| axes | tuple[[0, 1, 2], ...] | (0, 1, 2) | Non-empty spatial axes that random mode may flip, in `(depth, height, width)` order. Default: `(0, 1, 2)`. |
| flip_axes | One of:
| - | Fixed subset of `axes` to reflect for deterministic test-time augmentation. Use `()` for identity. Default: None. |
| p | float | 1.0 | Probability of applying the transform. Default: 1.0. |
Examples
>>> import numpy as np
>>> import albumentations as A
>>> volume = np.arange(2 * 3 * 5, dtype=np.uint8).reshape(2, 3, 5, 1)
>>> transform = A.Flip3D(flip_axes=(0, 2), p=1.0)
>>> result = transform(volume=volume)
>>> result["volume"].shape
(2, 3, 5, 1)Notes
- A realized reflection across an odd number of axes emits the `Flip3D` label-mapping event. Its semantic-mask mapping applies only to `mask3d`; keypoint label mappings rename label values without changing coordinate-row order. - A reflection across an even number of axes, including identity, preserves orientation and does not emit this event. Without an explicit mapping, labels stay unchanged.
GridShuffle3Dclass
GridShuffle3D(
grid_zyx: tuple[int, int, int] = (2, 2, 2),
p: float = 0.5
)Randomly shuffles the grid's cells on a 3D volume, mask3d, or keypoints, effectively rearranging patches within the volume. This transformation divides the volume into a 3D grid and then permutes these grid cells based on a random mapping. Unlike the 2D version, this does not support bounding boxes as 3D bounding boxes are not yet implemented.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| grid_zyx | tuple[int, int, int] | (2, 2, 2) | Size of the grid for splitting the volume into cells along (Z, Y, X) axes, corresponding to (depth, height, width) dimensions. Each cell is shuffled randomly. For example, (2, 3, 3) will divide the volume into 2 slices along Z, 3 along Y, and 3 along X, resulting in 18 cells to be shuffled. Default: (2, 2, 2) |
| p | float | 0.5 | Probability that the transform will be applied. Should be in the range [0, 1]. Default: 0.5 |
Examples
>>> import numpy as np
>>> import albumentations as A
>>> # Prepare sample data
>>> volume = np.random.randint(0, 256, (10, 100, 100), dtype=np.uint8) # (D, H, W)
>>> mask3d = np.random.randint(0, 2, (10, 100, 100), dtype=np.uint8) # (D, H, W)
>>> keypoints = np.array([[20, 30, 5], [60, 70, 8]], dtype=np.float32) # (x, y, z)
>>> keypoint_labels = [1, 2] # Labels for each keypoint
>>>
>>> # Define transform with grid_zyx as a tuple (Z, Y, X)
>>> transform = A.Compose([
... A.GridShuffle3D(grid_zyx=(2, 3, 3), p=1.0),
... ], keypoint_params=A.KeypointParams(coord_format='xyz', label_fields=['keypoint_labels']))
>>>
>>> # Apply the transform
>>> transformed = transform(
... volume=volume,
... mask3d=mask3d,
... keypoints=keypoints,
... keypoint_labels=keypoint_labels
... )
>>>
>>> # Get the transformed data
>>> transformed_volume = transformed['volume'] # Grid-shuffled volume
>>> transformed_mask3d = transformed['mask3d'] # Grid-shuffled mask
>>> transformed_keypoints = transformed['keypoints'] # Grid-shuffled keypoints
>>> transformed_keypoint_labels = transformed['keypoint_labels'] # Labels remain unchangedNotes
- This transform maintains consistency across all targets. If applied to a volume and its corresponding mask3d or keypoints, the same shuffling will be applied to all. - The number of cells in the grid should be at least 2 (i.e., grid_zyx should be at least (1, 1, 2), (1, 2, 1), (2, 1, 1) or larger) for the transform to have any effect. - Keypoints are moved along with their corresponding grid cell. - The grid_zyx parameter corresponds to volume dimensions: Z (depth), Y (height), X (width).
Pad3Dclass
Pad3D(
padding: int | tuple[int, int, int] | tuple[int, int, int, int, int, int],
fill: tuple[float, ...] | float = 0,
fill_mask: tuple[float, ...] | float = 0,
p: float = 1.0
)Add voxels around a 3D volume. Padding: int or per-side (depth, height, width); fill, fill_mask. For fixed-size batches or avoiding crop boundaries. Targets: volume, mask3d, keypoints
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| padding | One of:
| - | Padding values. Can be: * int - pad all sides by this value * tuple[int, int, int] - symmetric padding (depth, height, width) where each value is applied to both sides of the corresponding dimension * tuple[int, int, int, int, int, int] - explicit padding per side in order: (depth_front, depth_back, height_top, height_bottom, width_left, width_right) |
| fill | One of:
| 0 | Padding value for image |
| fill_mask | One of:
| 0 | Padding value for mask |
| p | float | 1.0 | probability of applying the transform. Default: 1.0. |
Examples
>>> import numpy as np
>>> import albumentations as A
>>>
>>> # Prepare sample data
>>> volume = np.random.randint(0, 256, (10, 100, 100), dtype=np.uint8) # (D, H, W)
>>> mask3d = np.random.randint(0, 2, (10, 100, 100), dtype=np.uint8) # (D, H, W)
>>> keypoints = np.array([[20, 30, 5], [60, 70, 8]], dtype=np.float32) # (x, y, z)
>>> keypoint_labels = [1, 2] # Labels for each keypoint
>>>
>>> # Create the transform with symmetric padding
>>> transform = A.Compose([
... A.Pad3D(
... padding=(2, 5, 10), # (depth, height, width) applied symmetrically
... fill=0,
... fill_mask=1,
... p=1.0
... )
... ], keypoint_params=A.KeypointParams(coord_format='xyz', label_fields=['keypoint_labels']))
>>>
>>> # Apply the transform
>>> transformed = transform(
... volume=volume,
... mask3d=mask3d,
... keypoints=keypoints,
... keypoint_labels=keypoint_labels
... )
>>>
>>> # Get the transformed data
>>> padded_volume = transformed["volume"] # Shape: (14, 110, 120)
>>> padded_mask3d = transformed["mask3d"] # Shape: (14, 110, 120)
>>> padded_keypoints = transformed["keypoints"] # Keypoints shifted by padding
>>> padded_keypoint_labels = transformed["keypoint_labels"] # Labels remain unchangedNotes
Input volume should be a numpy array with dimensions ordered as (z, y, x) or (depth, height, width), with optional channel dimension as the last axis.
PadIfNeeded3Dclass
PadIfNeeded3D(
min_zyx: tuple[int, int, int] | None,
pad_divisor_zyx: tuple[int, int, int] | None,
position: 'center' | 'random' = center,
fill: tuple[float, ...] | float = 0,
fill_mask: tuple[float, ...] | float = 0,
p: float = 1.0
)Pad 3D volume to min dimensions (min_zyx) and/or divisibility (pad_divisor_zyx). position, fill, fill_mask. At least one of min_zyx or pad_divisor_zyx required.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| min_zyx | One of:
| - | Minimum desired size as (depth, height, width). Ensures volume dimensions are at least these values. If not specified, pad_divisor_zyx must be provided. |
| pad_divisor_zyx | One of:
| - | If set, pads each dimension to make it divisible by corresponding value in format (depth_div, height_div, width_div). If not specified, min_zyx must be provided. |
| position | One of:
| center | Position where the volume is to be placed after padding. Default is 'center'. |
| fill | One of:
| 0 | Value to fill the border voxels for volume. Default: 0 |
| fill_mask | One of:
| 0 | Value to fill the border voxels for masks. Default: 0 |
| p | float | 1.0 | Probability of applying the transform. Default: 1.0 |
Examples
>>> import numpy as np
>>> import albumentations as A
>>>
>>> # Prepare sample data
>>> volume = np.random.randint(0, 256, (10, 100, 100), dtype=np.uint8) # (D, H, W)
>>> mask3d = np.random.randint(0, 2, (10, 100, 100), dtype=np.uint8) # (D, H, W)
>>> keypoints = np.array([[20, 30, 5], [60, 70, 8]], dtype=np.float32) # (x, y, z)
>>> keypoint_labels = [1, 2] # Labels for each keypoint
>>>
>>> # Create a transform with both min_zyx and pad_divisor_zyx
>>> transform = A.Compose([
... A.PadIfNeeded3D(
... min_zyx=(16, 128, 128), # Minimum size (depth, height, width)
... pad_divisor_zyx=(8, 16, 16), # Make dimensions divisible by these values
... position="center", # Center the volume in the padded space
... fill=0, # Fill value for volume
... fill_mask=1, # Fill value for mask
... p=1.0
... )
... ], keypoint_params=A.KeypointParams(coord_format='xyz', label_fields=['keypoint_labels']))
>>>
>>> # Apply the transform
>>> transformed = transform(
... volume=volume,
... mask3d=mask3d,
... keypoints=keypoints,
... keypoint_labels=keypoint_labels
... )
>>>
>>> # Get the transformed data
>>> padded_volume = transformed["volume"] # Shape: (16, 128, 128)
>>> padded_mask3d = transformed["mask3d"] # Shape: (16, 128, 128)
>>> padded_keypoints = transformed["keypoints"] # Keypoints shifted by padding
>>> padded_keypoint_labels = transformed["keypoint_labels"] # Labels remain unchangedNotes
Input volume should be a numpy array with dimensions ordered as (z, y, x) or (depth, height, width), with optional channel dimension as the last axis.
RandomCrop3Dclass
RandomCrop3D(
size: tuple[int, int, int],
pad_if_needed: bool = False,
fill: tuple[float, ...] | float = 0,
fill_mask: tuple[float, ...] | float = 0,
p: float = 1.0
)Extract a random 3D sub-volume of given (depth, height, width). pad_if_needed when smaller; fill, fill_mask. For spatial augmentation of volumetric data. Targets: volume, mask3d, keypoints
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| size | tuple[int, int, int] | - | Desired output size of the crop in format (depth, height, width) |
| pad_if_needed | bool | False | Whether to pad if the volume is smaller than desired crop size. Default: False |
| fill | One of:
| 0 | Padding value for image if pad_if_needed is True. Default: 0 |
| fill_mask | One of:
| 0 | Padding value for mask if pad_if_needed is True. Default: 0 |
| p | float | 1.0 | probability of applying the transform. Default: 1.0 |
Examples
>>> import numpy as np
>>> import albumentations as A
>>>
>>> # Prepare sample data
>>> volume = np.random.randint(0, 256, (20, 200, 200), dtype=np.uint8) # (D, H, W)
>>> mask3d = np.random.randint(0, 2, (20, 200, 200), dtype=np.uint8) # (D, H, W)
>>> keypoints = np.array([[100, 100, 10], [150, 150, 15]], dtype=np.float32) # (x, y, z)
>>> keypoint_labels = [1, 2] # Labels for each keypoint
>>>
>>> # Create the transform with random crop and padding if needed
>>> transform = A.Compose([
... A.RandomCrop3D(
... size=(16, 128, 128), # Output size (depth, height, width)
... pad_if_needed=True, # Pad if input is smaller than crop size
... fill=0, # Fill value for volume padding
... fill_mask=1, # Fill value for mask padding
... p=1.0
... )
... ], keypoint_params=A.KeypointParams(coord_format='xyz', label_fields=['keypoint_labels']))
>>>
>>> # Apply the transform
>>> transformed = transform(
... volume=volume,
... mask3d=mask3d,
... keypoints=keypoints,
... keypoint_labels=keypoint_labels
... )
>>>
>>> # Get the transformed data
>>> cropped_volume = transformed["volume"] # Shape: (16, 128, 128)
>>> cropped_mask3d = transformed["mask3d"] # Shape: (16, 128, 128)
>>> cropped_keypoints = transformed["keypoints"] # Keypoints shifted relative to random crop
>>> cropped_keypoint_labels = transformed["keypoint_labels"] # Labels remain unchangedNotes
If you want to perform random cropping only in the XY plane while preserving all slices along the Z axis, consider using RandomCrop instead. RandomCrop will apply the same XY crop to each slice independently, maintaining the full depth of the volume.
RandomRotate90_3Dclass
RandomRotate90_3D(
axis_pairs: tuple[tuple[[0, 1, 2], [0, 1, 2]], ...] = ((0, 1), (0, 2), (1, 2)),
axis_pair: tuple[[0, 1, 2], [0, 1, 2]] | None,
group_element: 'e' | 'r90' | 'r180' | 'r270' | None,
p: float = 1.0
)Rotate a volume by a random 90-degree multiple across one spatial axis pair, rotating mask3d and XYZ keypoints without reflections. Quarter turns swap the selected depth, height, or width lengths while preserving the channel axis. Axis indices always use `(depth, height, width)` order: `(0, 1)` rotates depth with height, `(0, 2)` rotates depth with width, and `(1, 2)` rotates height with width. A 90-degree or 270-degree turn swaps the two selected lengths, so non-cubic input can change shape. A 180-degree turn and the identity preserve the original shape. The transform does not reflect data. Set both `axis_pair` and `group_element` for deterministic test-time augmentation. `inverse()` then returns the rotation that restores the original voxel and keypoint coordinates.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| axis_pairs | tuple[tuple[[0, 1, 2], [0, 1, 2]], ...] | ((0, 1), (0, 2), (1, 2)) | Non-empty set of axis pairs sampled in random mode. Each pair must list two distinct axes in ascending `(depth, height, width)` order. Default: `((0, 1), (0, 2), (1, 2))`. |
| axis_pair | One of:
| - | Fixed spatial axis pair. Set with `group_element` for deterministic TTA. Default: None. |
| group_element | One of:
| - | If set, always apply this C4 group element: `"e"`=identity, `"r90"`=90°, `"r180"`=180°, `"r270"`=270° counterclockwise. Use for TTA. Default: None (random choice). |
| p | float | 1.0 | Probability of applying the transform. Default: 1.0. |
Examples
>>> import numpy as np
>>> import albumentations as A
>>> volume = np.arange(2 * 3 * 5, dtype=np.uint8).reshape(2, 3, 5, 1)
>>> transform = A.RandomRotate90_3D(axis_pair=(0, 2), group_element="r90", p=1.0)
>>> result = transform(volume=volume)
>>> result["volume"].shape
(5, 3, 2, 1)Resize3Dclass
Resize3D(
size: tuple[int, int, int],
interpolation: 0 | 1 = 1,
mask_interpolation: 0 | 1 = 0,
p: float = 1.0
)Resize a volume to a fixed `(depth, height, width)` shape, preserving all channels, dtype, and its public layout intact. `Resize3D` resamples all three spatial axes together; depth is never treated as a batch axis. Single-volume intensity data and categorical masks use independently configurable interpolation. The routed Albucore backend supports only linear and nearest-neighbor interpolation, ensuring the same public contract for NumPy and CPU Tensor inputs.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| size | tuple[int, int, int] | - | Target spatial shape in `(depth, height, width)` order. |
| interpolation | One of:
| 1 | Interpolation for a volume: `cv2.INTER_LINEAR` or `cv2.INTER_NEAREST`. Default: `cv2.INTER_LINEAR`. |
| mask_interpolation | One of:
| 0 | Interpolation for `mask3d`: `cv2.INTER_LINEAR` or `cv2.INTER_NEAREST`. Default: `cv2.INTER_NEAREST`. |
| p | float | 1.0 | Probability of applying the transform. Default: `1.0`. |
Examples
>>> import albumentations as A
>>> import cv2
>>> import numpy as np
>>> volume = np.random.default_rng(137).random((16, 64, 96, 1), dtype=np.float32)
>>> mask3d = np.zeros((16, 64, 96), dtype=np.uint8)
>>> transform = A.Compose([
... A.Resize3D(
... size=(32, 128, 128),
... interpolation=cv2.INTER_LINEAR,
... mask_interpolation=cv2.INTER_NEAREST,
... ),
... ])
>>> result = transform(volume=volume, mask3d=mask3d)
>>> result["volume"].shape, result["mask3d"].shape
((32, 128, 128, 1), (32, 128, 128))Notes
- NumPy volume data use channel-last `(D, H, W, C)` layout. CPU Tensor volume data use channel-first `(C, D, H, W)` layout. - `uint8` output preserves dtype; linear resampling rounds and saturates to `[0, 255]`. Float32 output remains float32. - Keypoints use `(x, y, z)` order and scale from the voxel-grid origin by `(W2 / W1, H2 / H1, D2 / D1)`, matching the 2D resize convention. - This transform changes voxel-grid coordinates only. It does not update physical voxel spacing or orientation metadata.