Perturb hematoxylin and eosin concentrations in H&E histology images to simulate stain variation across laboratories, scanners, and protocols.
Use this transform to train pathology models against expected staining variation. It converts RGB values to optical density, separates hematoxylin and eosin with the selected stain basis, perturbs both concentrations, 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 and eosin. 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 and eosin. Default: (-0.2, 0.2).
augment_backgroundWhether to perturb background pixels along with tissue pixels. Default: False.
residual_modeControls the optical-density component orthogonal to the H&E plane:
"project": Reconstruct from H&E only, retaining the two-stain model from earlier releases."preserve": Derive the residual basis vector and keep its concentration unchanged."augment": Independently perturb the derived residual along with H&E.
Default: "project".pProbability of applying the transform. Default: 0.5.
stain_matrixFixed H&E stain basis used when method="custom". The matrix must have
shape (2, 3): row 0 is the hematoxylin RGB optical-density vector and row 1 is the eosin vector. Both
rows must contain finite values, be non-zero, and be linearly independent. The transform copies the
matrix as float32 and uses its values 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: Using a custom stain matrix calibrated for an acquisition pipeline
>>> stain_matrix = np.array(
... [
... [0.71, 0.65, 0.27], # Hematoxylin
... [0.18, 0.91, 0.37], # Eosin
... ],
... dtype=np.float32,
... )
>>> transform = A.HEStain(
... method="custom",
... stain_matrix=stain_matrix,
... residual_mode="augment",
... intensity_scale_range=(0.8, 1.2),
... intensity_shift_range=(-0.1, 0.1),
... 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 (2, 3) H&E matrix and C the per-pixel concentrations. "project" solves
OD ~= C @ M, perturbs H&E, and reconstructs RGB = exp(-(C * scale + shift) @ M)."preserve" and "augment" derive R = normalize(cross(H, E)), solve the full H&E+R basis, and either
retain or perturb the residual concentration.