AlbumentationsX vs Legacy Albumentations
On this page
- Decide by the annotation contract
- Add project-specific targets to a custom transform
- Native 3D geometry is wider than slice-wise augmentation
- Make semantic remapping part of the pipeline
- Oriented boxes fail early when a transform cannot support them
- Bind per-object annotations when filtering can remove rows
- Other public additions over legacy 2.0.8
- Performance
- Migration checklist
- Sources
The legacy albumentations package already applies one sampled transform consistently to its built-in image and annotation targets. This page starts from that shared behavior and lists the public APIs added after legacy 2.0.8: custom apply_to_<target> handlers, wider native 3D support, semantic label mappings, oriented bounding boxes, instance binding, CPU Tensor input, and inspection tools.
Use the current library when your pipeline needs one of those additions. For licensing, see the License Guide.
Decide by the annotation contract
| Need | Current library | Legacy 2.0.8 |
|---|---|---|
| Apply the same sampled parameters to a project-specific target | CustomTransformsApplyMixin registers user-defined apply_to_<target> methods for Compose and ReplayCompose | A custom transform can override targets; apply_to_<target> methods are not registered automatically |
| Apply continuous 3D geometry | Affine3D, Resize3D, reflections, right-angle rotations, and 3D grid shuffling | 3D crop, pad, dropout, and cubic symmetry; no Affine3D or Resize3D |
| Keep left/right or other orientation-dependent semantics correct | KeypointParams.label_mapping and semantic_mask_label_mappings apply a configured mapping after a realized orientation change | No pipeline-level label-mapping API |
| Train an oriented-object detector | bbox_type="obb", with pipeline-time validation that each geometric transform supports OBB | Axis-aligned bounding boxes; no bbox_type="obb" contract |
| Keep object masks, boxes, and pose groups attached during filtering and mixing | instance_binding treats each object as one surviving or dropped row | Separate target arrays; no instance_binding row contract |
| Inspect a sampled policy | save_applied_params=True plus Compose.from_applied_transforms(...) for a deterministic constructor-level probe | ReplayCompose and applied-parameter logging, without Compose.from_applied_transforms(...) |
| Pass CPU tensors into the pipeline | A bounded CPU Tensor input contract | NumPy pipeline input; tensor conversion normally follows augmentation |
Add project-specific targets to a custom transform
The legacy package already dispatches built-in targets such as images, masks, bounding boxes, and keypoints through a custom transform. The added extension point is automatic dispatch for a new named target. Put A.CustomTransformsApplyMixin before the transform base class and define apply_to_<target> methods such as apply_to_label or apply_to_metadata.
Each registered target receives the same parameters sampled for the image. It also follows the transform's p value and works inside Compose and ReplayCompose. Built-in target handlers still take priority. Use user_data instead when a value should pass through unchanged by default and needs only the generic apply_to_user_data hook.
Native 3D geometry is wider than slice-wise augmentation
The legacy package already included 3D crop, pad, dropout, and cubic-symmetry transforms. The current library adds:
- Affine3D for one sampled 3D scale, rotation, and translation matrix shared by
volume,mask3d, andxyzkeypoints. - Resize3D for resampling depth, height, and width together, with separate volume and mask interpolation.
- Flip3D, RandomRotate90_3D, and GridShuffle3D for exact discrete volume geometry.
- Anisotropy3D to simulate lower resolution on selected acquisition axes. It changes
volumeonly; masks and keypoints intentionally stay untouched.
Current release notes also record true volumetric modes for AdditiveNoise and GaussianBlur. Check the Supported Targets by Transform table for the target contract of a particular transform.
For NumPy, a single-channel volume uses (D, H, W) and a multichannel volume uses (D, H, W, C); mask3d has the matching spatial shape. The CPU Tensor route uses (C, D, H, W) for volume. A 3D transform does not change physical spacing, orientation, or an imaging affine: update that metadata in the data layer if a downstream consumer relies on it.
import albumentations as A
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)
mask3d[4:12, 20:44, 30:66] = 1
keypoints = np.array([[48.0, 32.0, 8.0]], dtype=np.float32)
transform = A.Compose(
[
A.Affine3D(
rotate_range={"x": (0.0, 0.0), "y": (0.0, 0.0), "z": (-10.0, 10.0)},
scale_range={"x": (1.0, 1.0), "y": (1.0, 1.0), "z": (1.0, 1.0)},
p=1.0,
),
A.Resize3D(size=(24, 80, 120), p=1.0),
],
keypoint_params=A.KeypointParams(coord_format="xyz"),
seed=137,
strict=True,
)
result = transform(volume=volume, mask3d=mask3d, keypoints=keypoints)
assert result["volume"].shape == (24, 80, 120, 1)
assert result["mask3d"].shape == (24, 80, 120)
assert result["keypoints"].shape == (1, 3)
See Volumetric (3D) Augmentation for data layout and policy design.
Make semantic remapping part of the pipeline
Geometry alone cannot tell a library that left_eye becomes right_eye, or that a class ID means the left rather than the right side of an object. The mapping must be defined by the user. It is applied only when the corresponding orientation-changing event actually occurs.
2D keypoints and semantic masks
label_mapping is keyed by transform name, label-field name, then source and target label values. semantic_mask_label_mappings is keyed by the realized event name and maps source class IDs to target class IDs. Mappings are simultaneous, so {2: 3, 3: 2} is a safe swap.
import albumentations as A
import numpy as np
image = np.zeros((3, 6, 3), dtype=np.uint8)
mask = np.array([[2, 3, 0, 2, 3, 0]] * 3, dtype=np.uint8)
keypoints = np.array([[1.0, 1.0], [4.0, 1.0]], dtype=np.float32)
keypoint_labels = ["left_eye", "right_eye"]
transform = A.Compose(
[A.HorizontalFlip(p=1.0)],
keypoint_params=A.KeypointParams(
coord_format="xy",
label_fields=["keypoint_labels"],
label_mapping={
"HorizontalFlip": {
"keypoint_labels": {
"left_eye": "right_eye",
"right_eye": "left_eye",
},
},
},
),
semantic_mask_label_mappings={"HorizontalFlip": {2: 3, 3: 2}},
seed=137,
)
result = transform(
image=image,
mask=mask,
keypoints=keypoints,
keypoint_labels=keypoint_labels,
)
assert result["keypoint_labels"] == ["right_eye", "left_eye"]
np.testing.assert_array_equal(result["mask"], np.array([[0, 2, 3, 0, 2, 3]] * 3))
For paired labels in a 2D reflection, the current library swaps complete keypoint records, not only the label values. This preserves the row-index convention used by landmark heads. A one-way mapping updates the label when its counterpart is absent. Do not rely on the library to infer either mapping from label spelling.
D4 and SquareSymmetry use the underlying event name — HorizontalFlip, VerticalFlip, or Transpose — rather than their class name. Rotations and the identity operation do not trigger a semantic mapping.
3D mask3d and keypoints
Transform3D keeps the transformed keypoint rows in place and renames the configured label-field values. Flip3D emits a Flip3D mapping event only for reflections across an odd number of axes. Its semantic mapping applies to mask3d and its aliases, not to a 2D mask or masks target.
import albumentations as A
import numpy as np
volume = np.zeros((2, 3, 4, 1), dtype=np.uint8)
mask3d = np.array(
[
[[1, 2, 0, 1], [2, 0, 1, 2], [0, 1, 2, 0]],
[[2, 1, 0, 2], [1, 0, 2, 1], [0, 2, 1, 0]],
],
dtype=np.uint8,
)
transform = A.Compose(
[A.Flip3D(flip_axes=(0,), p=1.0)],
semantic_mask_label_mappings={"Flip3D": {1: 2, 2: 1}},
seed=137,
)
result = transform(volume=volume, mask3d=mask3d)
assert set(np.unique(result["mask3d"])) == {0, 1, 2}
Use a fixed flip_axes configuration when this is a deterministic test-time transform. In random mode, an even number of reflected axes preserves orientation, so no Flip3D semantic remapping occurs.
Oriented boxes fail early when a transform cannot support them
Set bbox_type="obb" in BboxParams to represent a rotated box with five coordinate values, including its angle. The current library validates the whole Compose tree at construction and raises if a geometric transform has no OBB contract.
OBB support is deliberately transform-specific. Build the pipeline first, let validation identify an unsupported operation, then replace that operation or use its axis-aligned alternative. The oriented bounding boxes guide shows the accepted coordinate formats and a working pipeline.
Bind per-object annotations when filtering can remove rows
Use instance_binding when an object has a bbox plus a mask and/or keypoints that must survive together. Pass instances as a list of per-object dictionaries and configure at least two of "mask", "masks", "bboxes", and "keypoints".
When bbox filtering drops an object, the current library drops its bound fields too. With a bound mask and bbox, an object is also removed when its transformed mask has no non-zero pixels. This covers the object-row relationship through crops, Mosaic, and CopyAndPaste.
Do not use instance binding for one semantic segmentation mask whose values are class IDs. It is for one row per object. The instance segmentation guide documents the instances input layout and the distinction between mask and stacked masks.
Other public additions over legacy 2.0.8
| Area | Additions to consider |
|---|---|
| Detection and mixing | LetterBox, CopyAndPaste, PixelSpread, and BBoxSubsetSafeRandomCrop |
| Sensor and image simulation | RicianNoise, Dithering, FilmGrain, Halftone, and AtmosphericFog |
| Colour and appearance | ChannelSwap, Colorize, Enhance, ExposureMatching, and PlanckianJitter |
| Policy inspection | Compose.from_applied_transforms(...), trace records, and portable policy serialization. Use ReplayCompose when you need exact runtime replay: the applied-config reconstruction fixes constructor-level choices, not crop coordinates or dropout masks. |
| Project-specific targets | user_data passes arbitrary Python values through unchanged by default; a custom transform can override apply_to_user_data. For named project-specific targets, CustomTransformsApplyMixin registers each apply_to_<target> method and sends that target the same sampled parameters as the built-in targets. |
| CPU Tensor input | Compose accepts CPU tensors for the documented image, volume, mask, bbox, and keypoint target ranks. The input must be strided, not require gradients, and use supported dtypes. The direct Tensor route is kept only when every transform supports it; otherwise the pipeline bridges through NumPy once. CUDA tensors and autograd tensors are outside this contract. |
The Explore entry for each transform and the Supported Targets by Transform reference define its actual target coverage. Do not generalize the coverage of one transform to its whole family.
Performance
Feature support and throughput answer different questions. The public benchmark pages report measured RGB, multichannel, video, and DataLoader routes separately; use the row that matches your transform sequence and hardware rather than extrapolating from a different route.
Migration checklist
pip install albumentationsx
import albumentations as A
Then validate the actual pipeline rather than only its import:
- Keep the legacy package and expected inputs pinned for the comparison.
- Run representative
image,mask,bboxes,keypoints,volume, andmask3dsamples through the current pipeline. - Add semantic or instance contracts only where the annotations need them.
- Check output shapes, dtypes, coordinate formats, label values, and any physical 3D metadata your application owns.
- Use replay or applied parameters to investigate any changed sample before changing the augmentation policy.