AlbumentationsX vs Legacy Albumentations

On this page

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

NeedCurrent libraryLegacy 2.0.8
Apply the same sampled parameters to a project-specific targetCustomTransformsApplyMixin registers user-defined apply_to_<target> methods for Compose and ReplayComposeA custom transform can override targets; apply_to_<target> methods are not registered automatically
Apply continuous 3D geometryAffine3D, Resize3D, reflections, right-angle rotations, and 3D grid shuffling3D crop, pad, dropout, and cubic symmetry; no Affine3D or Resize3D
Keep left/right or other orientation-dependent semantics correctKeypointParams.label_mapping and semantic_mask_label_mappings apply a configured mapping after a realized orientation changeNo pipeline-level label-mapping API
Train an oriented-object detectorbbox_type="obb", with pipeline-time validation that each geometric transform supports OBBAxis-aligned bounding boxes; no bbox_type="obb" contract
Keep object masks, boxes, and pose groups attached during filtering and mixinginstance_binding treats each object as one surviving or dropped rowSeparate target arrays; no instance_binding row contract
Inspect a sampled policysave_applied_params=True plus Compose.from_applied_transforms(...) for a deterministic constructor-level probeReplayCompose and applied-parameter logging, without Compose.from_applied_transforms(...)
Pass CPU tensors into the pipelineA bounded CPU Tensor input contractNumPy 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, and xyz keypoints.
  • 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 volume only; 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

AreaAdditions to consider
Detection and mixingLetterBox, CopyAndPaste, PixelSpread, and BBoxSubsetSafeRandomCrop
Sensor and image simulationRicianNoise, Dithering, FilmGrain, Halftone, and AtmosphericFog
Colour and appearanceChannelSwap, Colorize, Enhance, ExposureMatching, and PlanckianJitter
Policy inspectionCompose.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 targetsuser_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 inputCompose 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:

  1. Keep the legacy package and expected inputs pinned for the comparison.
  2. Run representative image, mask, bboxes, keypoints, volume, and mask3d samples through the current pipeline.
  3. Add semantic or instance contracts only where the annotations need them.
  4. Check output shapes, dtypes, coordinate formats, label values, and any physical 3D metadata your application owns.
  5. Use replay or applied parameters to investigate any changed sample before changing the augmentation policy.

Sources