Albumentations to PIL/Pillow Transform Mapping
On this page
- How to Read This Page
- Channel and Target Caveats
- Direct Pillow Mappings
- No Direct Pillow Mapping
- Migration Pattern
- Related Pages
This page maps Albumentations transforms to the closest Pillow operation when Pillow has a practical direct equivalent. Use it as a migration and capability guide, not as a performance page.
How to Read This Page
- A named Pillow operation means Pillow has a practical direct image operation for the same idea.
-means Pillow does not support that transform as an augmentation primitive.- The table shows built-in Pillow operations that directly correspond to Albumentations transforms. Custom Pillow implementations are not counted as Pillow support.
- Pillow mappings are image-only. Target updates are still not supported by Pillow.
Channel and Target Caveats
Pillow operations are built around image modes such as L, RGB, and RGBA. Many color operations either convert to grayscale/RGB or assume RGB-style channels. Albumentations operates on NumPy arrays, so many image-only transforms can preserve arbitrary channel counts. RGB/color-space transforms still have channel-specific constraints.
Pillow transforms do not update masks, bounding boxes, keypoints, oriented bounding boxes (OBB), volumes, or videos. Albumentations transforms are pipeline operations and can update supported targets in the same call.
Pillow also does not provide an augmentation-policy layer comparable to Albumentations Compose, OneOf, SomeOf, or RandomOrder. Combining crop, flip, normalization, and optional branches means writing the policy as Python control flow. In Albumentations, the policy is a serializable pipeline object that works before tensors enter PyTorch, TensorFlow/Keras, JAX, or a custom GPU-training loop.
Direct Pillow Mappings
| Albumentations transform | Pillow operation |
|---|---|
| Resize | Image.resize |
| HorizontalFlip | Image.transpose(FLIP_LEFT_RIGHT) |
| VerticalFlip | Image.transpose(FLIP_TOP_BOTTOM) |
| Transpose | Image.transpose(TRANSPOSE) |
| Rotate | Image.rotate |
| Pad | ImageOps.expand |
| Affine | Image.transform(AFFINE) |
| RandomBrightnessContrast | ImageEnhance.Brightness / ImageEnhance.Contrast |
| HueSaturationValue | ImageEnhance.Color |
| AutoContrast | ImageOps.autocontrast |
| Equalize | ImageOps.equalize |
| ToGray | ImageOps.grayscale(...).convert("RGB") |
| InvertImg | ImageOps.invert |
| Posterize | ImageOps.posterize |
| Solarize | ImageOps.solarize |
| Colorize | ImageOps.colorize |
| Dithering | Image.convert("L").convert("1", dither=FLOYDSTEINBERG).convert("RGB") |
| GaussianBlur | ImageFilter.GaussianBlur |
| MedianBlur | ImageFilter.MedianFilter |
| Blur | ImageFilter.BoxBlur |
| UnsharpMask | ImageFilter.UnsharpMask |
| Enhance | ImageFilter.EDGE_ENHANCE_MORE / ImageFilter.DETAIL |
| ImageCompression | JPEG save/load roundtrip |
No Direct Pillow Mapping
Migration Pattern
Pillow can do the image edits, but the random augmentation policy becomes regular Python code:
import random
from PIL import Image
from PIL import ImageEnhance, ImageFilter
image = Image.open("image.jpg").convert("RGB")
if random.random() < 0.5:
image = image.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
if random.random() < 0.5:
angle = random.uniform(-45, 45)
image = image.rotate(angle, resample=Image.Resampling.BILINEAR, fillcolor=0)
if random.random() < 0.3:
if random.random() < 0.5:
image = image.filter(ImageFilter.GaussianBlur(radius=random.uniform(1.5, 3.5)))
else:
# Gaussian noise is not a Pillow augmentation primitive.
pass
if random.random() < 0.5:
image = ImageEnhance.Brightness(image).enhance(random.uniform(0.8, 1.2))
image = ImageEnhance.Contrast(image).enhance(random.uniform(0.8, 1.2))
The same policy in Albumentations is a pipeline object:
import numpy as np
from PIL import Image
import albumentations as A
image = np.array(Image.open("image.jpg").convert("RGB"))
pipeline = A.Compose(
[
A.HorizontalFlip(p=0.5),
A.Rotate(angle_range=(-45, 45), border_mode=0, fill=0, p=0.5),
A.OneOf(
[
A.GaussianBlur(blur_range=(3, 7), p=1.0),
A.GaussNoise(std_range=(0.05, 0.2), p=1.0),
],
p=0.3,
),
A.RandomBrightnessContrast(
brightness_range=(-0.2, 0.2),
contrast_range=(-0.2, 0.2),
p=0.5,
),
],
)
augmented = pipeline(image=image)["image"]
For non-RGB data, keep the array shape you actually train on and avoid RGB-only transforms unless the conversion is intentional.
Because this is an Albumentations pipeline object, you can serialize it and reuse the same augmentation definition outside the notebook or training script.