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.
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,
... 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) stain matrix and C the per-pixel stain concentrations. The transform solves
OD ~= C @ M, samples scale and shift for each stain, then reconstructs
RGB = exp(-(C * scale + shift) @ M).