Perturb stain concentrations in histology images to simulate color variation across laboratories, scanners, protocols, and staining panels.
Use this transform to train pathology models against expected staining variation. It converts RGB values to optical density, separates the stain concentrations with the selected basis, perturbs the configured components, and reconstructs the RGB image.
methodSelects the stain basis:
preset.stain_matrix.
Default: "random_preset".presetPreset stain matrix used when method="preset":
method="preset", "standard" is used. Default: None.intensity_scale_rangeNon-negative range for the multiplicative concentration factor
sampled independently for hematoxylin, eosin, and any augmented third component. For example,
(0.7, 1.3) varies each concentration from 70% to 130%. Default: (0.7, 1.3).
intensity_shift_rangeRange within [-1.0, 1.0] for the additive concentration shift
sampled independently for hematoxylin, eosin, and any augmented third component. Default: (-0.2, 0.2).
augment_backgroundWhether to perturb background pixels along with tissue pixels. Default: False.
residual_modeControls the third optical-density component:
"project": Reconstruct from H&E only, retaining the two-stain model from earlier releases."preserve": Keep the derived residual or explicit third-stain concentration unchanged."augment": Independently perturb the derived residual or explicit third stain along with H&E.
Default: "project".pProbability of applying the transform. Default: 0.5.
stain_matrixFixed stain basis used when method="custom". A (2, 3) matrix contains
hematoxylin and eosin RGB optical-density vectors; "preserve" and "augment" derive the third vector
as normalize(cross(H, E)). A (3, 3) matrix supplies the third stain directly and requires
residual_mode="preserve" or "augment". Every row must contain finite values and be non-zero, and the
matrix must have full row rank. The transform copies the matrix as float32 without row normalization.
Default: None.
>>> import numpy as np
>>> import albumentations as A
>>>
>>> # Create a sample H&E stained histopathology image
>>> # For real use cases, load an actual H&E stained image
>>> image = np.zeros((300, 300, 3), dtype=np.uint8)
>>> # Simulate tissue regions with different staining patterns
>>> image[50:150, 50:150] = np.array([120, 140, 180], dtype=np.uint8) # Hematoxylin-rich region
>>> image[150:250, 150:250] = np.array([140, 160, 120], dtype=np.uint8) # Eosin-rich region
>>>
>>> # Example 1: Map HEDJitter(theta) to a full H&E+DAB basis
>>> theta = 0.05
>>> hed_basis = np.array(
... [
... [0.65, 0.70, 0.29], # Hematoxylin
... [0.07, 0.99, 0.11], # Eosin
... [0.27, 0.57, 0.78], # DAB
... ],
... dtype=np.float32,
... )
>>> transform = A.HEStain(
... method="custom",
... stain_matrix=hed_basis,
... residual_mode="augment",
... intensity_scale_range=(1 - theta, 1 + theta),
... intensity_shift_range=(-theta, theta),
... augment_background=True,
... p=1.0,
... )
>>> transformed_image = transform(image=image)["image"]
>>>
>>> # Example 2: Using a specific preset stain matrix
>>> transform = A.HEStain(
... method="preset",
... preset="standard",
... intensity_scale_range=(0.8, 1.2),
... intensity_shift_range=(-0.1, 0.1),
... augment_background=False,
... p=1.0,
... )
>>> transformed_image = transform(image=image)["image"]
>>>
>>> # Example 3: Using random preset selection
>>> transform = A.HEStain(
... method="random_preset",
... intensity_scale_range=(0.7, 1.3),
... intensity_shift_range=(-0.15, 0.15),
... p=1.0,
... )
>>> transformed_image = transform(image=image)["image"]
>>>
>>> # Example 4: Using Vahadane extraction (requires an H&E stained input)
>>> transform = A.HEStain(
... method="vahadane",
... intensity_scale_range=(0.7, 1.3),
... p=1.0,
... )
>>> transformed_image = transform(image=image)["image"]
>>>
>>> # Example 5: Using Macenko extraction (requires an H&E stained input)
>>> transform = A.HEStain(
... method="macenko",
... intensity_scale_range=(0.7, 1.3),
... intensity_shift_range=(-0.2, 0.2),
... p=1.0,
... )
>>> transformed_image = transform(image=image)["image"]
>>>
>>> # Example 6: Combining stain and brightness variation in one pipeline
>>> transform = A.Compose([
... A.HEStain(method="preset", preset="high_contrast", p=1.0),
... A.RandomBrightnessContrast(p=0.5),
... ])
>>> transformed_image = transform(image=image)["image"]M be the stain matrix and C the per-pixel concentrations. "project" solves
OD ~= C @ M, perturbs H&E, and reconstructs RGB = exp(-(C * scale + shift) @ M).(2, 3) matrix, "preserve" and "augment" derive R = normalize(cross(H, E)) and solve the full
H&E+R basis. A (3, 3) matrix uses its third row directly.