Transforms

Data transformations and augmentations for 2D and 3D BioImages

source

FunctionTransform


def FunctionTransform(
    func, has_channels:bool=True, func_kwargs:VAR_KEYWORD
):

Generic MonaiTransform to apply any NumPy or SciPy function to an image. Works with CHW/CDHW arrays or BioImageBase.


source

MonaiTransform


def MonaiTransform(
    monai_transform, # MONAI transform class (e.g., monai.transforms.GaussianSmooth)
    has_channels:bool=True, # Whether numpy input already has a channel dimension
    kwargs:VAR_KEYWORD
):

Generic MonaiTransform for MONAI transforms (non-dictionary versions).

This MonaiTransform allows MONAI transforms to be used within the BioImage transform pipeline while supporting both BioImageBase objects and NumPy arrays as inputs.

The MONAI transform is internally instantiated and applied to the input image after converting it to the appropriate tensor format.


source

RandMonaiTransform


def RandMonaiTransform(
    monai_transform, # Deterministic MONAI transform class
    prob:float=0.1, # Probability of applying transform
    param_sampler:NoneType=None, # Function that returns sampled kwargs
    has_channels:bool=True, kwargs:VAR_KEYWORD
):

Wrap a deterministic MONAI transform inside a fastai RandTransform.

Random parameters are sampled in before_call, then used to instantiate a deterministic MONAI transform.


source

ApplyTo


def ApplyTo(
    tfm, idx:int=0
):

Apply a transform to a specific element of a tuple.

Args: tfm: the transform or callable to apply idx: index of the tuple element to modify (default 0)

Size & Sampling


source

Resample


def Resample(
    sampling, # Sampling factor for isotropic resampling
    has_channels:bool=True, # Indicates whether the input image has a channel dimension (only for numpy arrays)
    kwargs:VAR_KEYWORD
):

A subclass of Spacing that handles image resampling based on specified sampling factors or voxel dimensions.

The Resample class inherits from Spacing and provides a flexible way to adjust the spacing (voxel size) of images by specifying either a sampling factor or explicitly providing new voxel dimensions.

from bioMONAI.core import cells3d
from bioMONAI.visualize import visualize_slices
torchTensor(cells3d()[:,0])
Tensor2BioImage()(torchTensor(cells3d()[:,0]))
img = BioImageStack(torchTensor(cells3d()[:,0]))
visualize_slices(img, showlines=False)

img2 = Resample(4)(img)
print(type(img2))
visualize_slices(img2, showlines=False)

img2 = Resample(4)(cells3d()[:,0])
print(type(img2))
visualize_slices(img2, showlines=False)

source

Resize


def Resize(
    size:NoneType=None, # Target dimensions for resizing (height, width). If its length is smaller than the spatial dimensions, values will be repeated. If an int is provided, it will be broadcast to all spatial dimensions.
    kwargs:VAR_KEYWORD
):

A subclass of Reshape that handles image resizing based on specified target dimensions.

The Resize class inherits from Reshape and provides a flexible way to adjust the size of images by specifying either a target size or scaling factors.

print(img.size())
visualize_slices(img, showlines=False)

img2 = Resize(50)(img)
print(img2.size())
visualize_slices(img2, showlines=False)

source

CropND


def CropND(
    slices:list, # List of slice objects or tuples (start, end) for each dimension
    dims:list=None, # List of dimensions to apply the slices. If None, slices are applied to all dimensions.
):

Crops an ND tensor along a specified dimension.

This transform crops an ND tensor along specified dimensions, which could be a channel or spatial dimension.

# Define the slices for cropping
slices = [(30, 50)]

# Create an instance of CropND
crop_transform = CropND(slices=slices)

img3 = crop_transform(img)
print(img3.size())
visualize_slices(img3, showlines=False)
# Define the slices for cropping
slices = [(30, 50), (0, 40)]
dims = [0, 2]
# Create an instance of CropND
crop_transform = CropND(slices=slices, dims=dims)

img3 = crop_transform(img)
print(img3.size())
visualize_slices(img3, showlines=False)

Noise


source

RandCameraNoise


def RandCameraNoise(
    p:float=1.0, # Probability of applying Transform
    damp:float=0.01, # Dampening factor to prevent saturation when adding noise
    qe:float=0.7, # Quantum efficiency of the camera (0 to 1).
    gain:int=2, # Camera gain factor. If an array, it should be broadcastable with input_image shape.
    offset:int=100, # Camera offset in ADU. If an array, it should be broadcastable with input_image shape.
    exp_time:float=0.1, # Exposure time in seconds.
    dark_current:float=0.6, # Dark current per pixel in electrons/second.
    readout:float=1.5, # Readout noise standard deviation in electrons.
    bitdepth:int=16, # Bit depth of the camera output.
    seed:int=42, # Seed for random number generator for reproducibility.
    simulation:bool=False, # If True, assumes input_image is already in units of photons and does not convert from electrons.
    camera:str='cmos', # Specifies the type of camera ('cmos' or any other). Used to add CMOS fixed pattern noise if 'cmos' is specified.
    gain_variance:float=0.1, # Variance for the gain noise in CMOS cameras. Only applicable if camera type is 'cmos'.
    offset_variance:int=5, # Variance for the offset noise in CMOS cameras. Only applicable if camera type is 'cmos'.
):

Simulates camera noise by adding Poisson shot noise, dark current noise, and optionally CMOS fixed pattern noise.

Returns: numpy.ndarray: The noisy image as a NumPy array with dimensions of input_image.

from bioMONAI.visualize import plot_image
img3 = img[30]
# Original clean image
plot_image(img3)
# Noisy image simulating a CMOS camera
plot_image(RandCameraNoise(camera = 'cmos').encodes(img3))
# Noisy image simulating a CCD camera
plot_image(RandCameraNoise(camera = 'ccd', readout=2).encodes(img3))

source

Blur


def Blur(
    sigma:float=1.0, ksize:int=5, prob:float=0.5
):

Apply Gaussian blur to the image.

# Generate a random NumPy array 
random_array = np.random.rand(64, 64)
# Apply Blur transform to the image
blur_transform = Blur(ksize=15, prob=1) 
blurred_nparray = blur_transform(random_array)

# plot the original image
plot_image(random_array)
# Plot the blurred image
plot_image(blurred_nparray)
# Apply Blur transform to the image
blur_transform = Blur(sigma=5, prob=1.0) 
blurred_img = blur_transform(img)

# plot the original image
plot_image(img)
# Plot the blurred image
plot_image(blurred_img)

source

SavitzkyGolaySmooth


def SavitzkyGolaySmooth(
    window_length, order, axis:int=1, mode:str='zeros', kwargs:VAR_KEYWORD
):

Smooth data using a Savitzky-Golay filter.


source

MedianSmooth


def MedianSmooth(
    radius:int=1, kwargs:VAR_KEYWORD
):

Apply median filtering to the input image.


source

GaussianSmooth


def GaussianSmooth(
    sigma:float=1.0, approx:str='erf', kwargs:VAR_KEYWORD
):

Apply Gaussian smoothing to the input image.


source

GaussianSharpen


def GaussianSharpen(
    sigma1:float=3.0, sigma2:float=1.0, alpha:float=30.0, approx:str='erf', kwargs:VAR_KEYWORD
):

Sharpen images using Gaussian filtering.


source

RandGaussianSmooth


def RandGaussianSmooth(
    sigma_x:tuple=(0.5, 1.5), sigma_y:NoneType=None, sigma_z:NoneType=None, prob:float=0.1, approx:str='erf',
    has_channels:bool=True
):

Apply Gaussian smoothing with randomly sampled sigma.


source

RandGaussianSharpen


def RandGaussianSharpen(
    sigma1:tuple=(0.5, 1.0), sigma2:tuple=(0.5, 1.0), alpha:tuple=(10.0, 30.0), prob:float=0.1, approx:str='erf',
    has_channels:bool=True
):

Randomly sharpen image using Gaussian kernels.


source

RandGaussianNoise


def RandGaussianNoise(
    mean:float=0.0, std:float=0.1, prob:float=0.1, dtype:type=float32, has_channels:bool=True
):

Add Gaussian noise to image.

Intensity Normalization


source

ScaleImage


def ScaleImage(
    x, # The input image to scale.
    min:float=0.0, # The minimum intensity value.
    max:float=1.0, # The maximum intensity value.
    axis:NoneType=None, # The axis or axes along which to compute the minimum and maximum values.
    eps:float=1e-20, # A small value to prevent division by zero.
    dtype:type=float32, # The data type to use for the output image.
):

Image normalization.


source

ScaleImagePercentiles


def ScaleImagePercentiles(
    x, # The input image to scale.
    pmin:int=3, # The minimum percentile value.
    pmax:float=99.8, # The maximum percentile value.
    axis:NoneType=None, # The axis or axes along which to compute the minimum and maximum values.
    clip:bool=True, # If True, clips the output values to the specified range.
    b_min:float=0.0, # The minimum intensity value.
    b_max:float=1.0, # The maximum intensity value.
    eps:float=1e-20, # A small value to prevent division by zero.
    dtype:type=float32, # The data type to use for the output image.
):

Percentile-based image normalization.


source

ScaleImageVariance


def ScaleImageVariance(
    target_variance:float=1.0, # The desired variance for the scaled intensities.
    ndim:int=2, # Number of spatial dimensions in the image.
):

Scales the intensity variance of an ND image to a target value.

# Example usage with a random tensor of shape (1, 3, 256, 256)
rand_tensor = BioImageBase(torch.rand(1, 3, 256, 256))

transform = ScaleImageVariance(ndim=4)

# Apply the transform to the tensor
scaled_tensor = transform(rand_tensor)

print('Original Tensor Variance:', rand_tensor.var().item())
print('Scaled Tensor Variance:', scaled_tensor.var().item())

source

ScaleIntensityRangePercentiles


def ScaleIntensityRangePercentiles(
    lower, upper, b_min, b_max, clip:bool=False, relative:bool=False, channel_wise:bool=False, dtype:type=float32,
    kwargs:VAR_KEYWORD
):

Scale intensity values based on image percentiles.

The transform maps the intensity range defined by two percentiles to a target range.


source

ScaleIntensityRange


def ScaleIntensityRange(
    a_min, a_max, b_min:NoneType=None, b_max:NoneType=None, clip:bool=False, dtype:type=float32, kwargs:VAR_KEYWORD
):

Linearly rescale intensities from one range to another.


source

ScaleIntensityFixedMean


def ScaleIntensityFixedMean(
    factor:int=0, preserve_range:bool=False, fixed_mean:bool=True, channel_wise:bool=False, dtype:type=float32,
    kwargs:VAR_KEYWORD
):

Scale intensity while preserving the original mean.

After scaling intensities, the transform shifts the output so that the mean intensity remains equal to the input mean.


source

ScaleIntensity


def ScaleIntensity(
    minv:float=0.0, maxv:float=1.0, factor:NoneType=None, channel_wise:bool=False, dtype:type=float32,
    kwargs:VAR_KEYWORD
):

Scale image intensity to a specified range or by a multiplicative factor.


source

HistogramNormalize


def HistogramNormalize(
    num_bins:int=256, min:int=0, max:int=255, mask:NoneType=None, dtype:type=float32, kwargs:VAR_KEYWORD
):

Normalize image intensity using histogram equalization.


source

NormalizeIntensity


def NormalizeIntensity(
    subtrahend:NoneType=None, divisor:NoneType=None, nonzero:bool=False, channel_wise:bool=False, dtype:type=float32,
    kwargs:VAR_KEYWORD
):

Normalize image intensity using mean and standard deviation.

The transform performs: (img - subtrahend) / divisor

Label Mapping


source

RelabelInstances


def RelabelInstances(
    background:int=0, # Label considered background
    dtype:type=int32, # Output dtype
):

Remap instance mask labels to consecutive integers.

Example: [0, 1, 3, 7, 18] → [0, 1, 2, 3, 4]


source

InstanceToMaskAndDistance


def InstanceToMaskAndDistance(
    has_channels:bool=True, # If False, adds channel dim
    normalize:bool=True, # Normalize distance map per instance
):

Converts a single-channel instance segmentation map (CHW or CDHW) into a 2-channel image:

Channel 0: Binary mask (instances > 0) Channel 1: Distance transform (inside objects)

md = InstanceToMaskAndDistance()(np.random.rand(1, 33, 90, 90))
test_eq(md.shape, (2, 33, 90, 90))

source

ComputeHoVerMaps


def ComputeHoVerMaps(
    dtype:str='float32', kwargs:VAR_KEYWORD
):

Compute HoVer maps (horizontal and vertical distance maps) from instance masks.

These maps are commonly used in HoVer-Net based nucleus segmentation.

Thresholding / Masking


source

ForegroundMask


def ForegroundMask(
    threshold:str='otsu', hsv_threshold:NoneType=None, invert:bool=False, kwargs:VAR_KEYWORD
):

Generate a binary foreground mask based on image intensity.


source

MaskIntensity


def MaskIntensity(
    mask_data:NoneType=None, select_fn:NoneType=None, kwargs:VAR_KEYWORD
):

Mask intensity values using a binary mask.

Pixels outside the mask are set to zero.


source

ThresholdIntensity


def ThresholdIntensity(
    threshold, above:bool=True, cval:float=0.0, kwargs:VAR_KEYWORD
):

Filter image intensities using a threshold.

Values above or below the threshold are replaced with a constant value.

Color space


source

RGB2HED


def RGB2HED(
    enc:NoneType=None, dec:NoneType=None, split_idx:NoneType=None, order:NoneType=None
):

Convert RGB images to HED color space.


source

HED2RGB


def HED2RGB(
    enc:NoneType=None, dec:NoneType=None, split_idx:NoneType=None, order:NoneType=None
):

Convert HED images back to RGB color space.

Data Augmentation


source

RandCrop2D


def RandCrop2D(
    size:int | tuple, # Size to crop to, duplicated if one value is specified
    lazy:bool=False, # a flag to indicate whether this transform should execute lazily or not. Defaults to False
    kwargs:VAR_KEYWORD
):

Randomly crops a 2D image to a specified size.

This transform randomly crops a 2D image to a specified size during training and performs a center crop during validation.


source

RandCropND


def RandCropND(
    size:int | tuple, # Size to crop to, duplicated if one value is specified
    lazy:bool=False, # a flag to indicate whether this transform should execute lazily or not. Defaults to False
    kwargs:VAR_KEYWORD
):

Randomly crops an ND image to a specified size.

This transform randomly crops an ND image to a specified size during training and performs a center crop during validation. It supports both 2D and 3D images and videos, assuming the first dimension is the batch dimension.

# Define a random tensor
orig_size = (65, 65)
rand_tensor = BioImageBase(torch.rand(8, *orig_size))

for i in range(100):
    test_eq((8,64,64),RandCropND((64,64))(rand_tensor).shape)

source

RandFlip


def RandFlip(
    prob:float=0.1, # Probability of flipping
    spatial_axis:NoneType=None, # Axes to flip. Default is None (random selection)
    ndim:int=2, # Number of spatial dimensions
    lazy:bool=False, # Flag for lazy execution (for BioImageBase)
    kwargs:VAR_KEYWORD
):

Randomly flips an ND image over a specified axis. Works with both NumPy arrays and BioImageBase objects.

image = np.random.rand(1, 3, 3)  
flip_transform = RandFlip(prob=1., ndim=2, spatial_axis=1)  
flipped_image = flip_transform(image)
print('original:\n', image)
print('flipped:\n', flipped_image)  
test_eq(image[0,0,0], flipped_image[0,2,0]) # Check if the image is flipped correctly
# Define a random tensor
orig_size = (1,4,4)
rand_tensor = BioImageBase(torch.rand(*orig_size))
print('orig tensor: ', rand_tensor, '\n')
for i in range(3):
    print(RandFlip(prob=.75, spatial_axis=None)(rand_tensor))

source

RandRot90


def RandRot90(
    prob:float=0.1, # Probability of rotating
    k:NoneType=None, # Number of times to rotate by 90 degrees. If k is None, it will be set randomly in `before_call`.
    max_k:int=3, # Max number of times to rotate by 90 degrees
    spatial_axes:tuple=(0, 1), # Spatial axes that define the plane around which to rotate. Default: (0, 1), this are the first two axis in spatial dimensions.
    ndim:int=2, # Number of spatial dimensions
    lazy:bool=False, # Flag to indicate whether this transform should execute lazily or not. Defaults to False
    kwargs:VAR_KEYWORD
):

Randomly rotate an ND image by 90 degrees in the plane specified by axes.

# Define a random tensor
orig_size = (1,4,4)
rand_tensor = BioImageBase(torch.rand(*orig_size))

print('orig tensor: ', rand_tensor, '\n')

for i in range(3):
    print(RandRot90(prob=1, k=i+1)(rand_tensor))

source

RandRotate


def RandRotate(
    prob:float=0.1, # Probability of rotating
    range_x:NoneType=None, range_y:NoneType=None, range_z:NoneType=None, ndim:int=2, has_channels:bool=True,
    keep_size:bool=True, mode:GridSampleMode=bilinear, padding_mode:GridSamplePadMode=border,
    align_corners:bool=False, dtype:dtype=torch.float32,
    lazy:bool=False, # Flag to indicate whether this transform should execute lazily or not. Defaults to False
    kwargs:VAR_KEYWORD
):

Randomly rotate the input arrays.

print('orig tensor: ', rand_tensor, '\n')

for i in range(3):
    print(RandRotate(prob=1, range_x=1)(rand_tensor))

source

RandZoom


def RandZoom(
    prob:float=0.1, # Probability of the transform
    min_zoom:float=0.9, max_zoom:float=1.1, has_channels:bool=True, keep_size:bool=True, mode:InterpolateMode=area,
    padding_mode:NumpyPadMode=edge, align_corners:NoneType=None, dtype:dtype=torch.float32,
    lazy:bool=False, # Flag to indicate whether this transform should execute lazily or not. Defaults to False
    kwargs:VAR_KEYWORD
):

Randomly zoom the input arrays with given probability within given zoom range.

print('orig tensor: ', rand_tensor, '\n')

for i in range(3):
    print(RandZoom(prob=1)(rand_tensor))

Intensity Shifting


source

StdShiftIntensity


def StdShiftIntensity(
    factor, nonzero:bool=False, channel_wise:bool=False, dtype:type=float32, kwargs:VAR_KEYWORD
):

Shift intensity values using the image standard deviation.

The transform modifies intensity values using: v = v + factor * std(v)


source

ShiftIntensity


def ShiftIntensity(
    offset, safe:bool=False, kwargs:VAR_KEYWORD
):

Shift the intensity values of the entire image by a constant offset.

This transform uniformly adds an offset to all pixel/voxel values.


source

RandStdShiftIntensity


def RandStdShiftIntensity(
    factors, prob:float=0.1, nonzero:bool=False, channel_wise:bool=False, has_channels:bool=True
):

Randomly shift intensity by a factor of the image standard deviation.


source

RandShiftIntensity


def RandShiftIntensity(
    offsets, prob:float=0.1, safe:bool=False, has_channels:bool=True
):

Randomly shift image intensity.

Equivalent to MONAI RandShiftIntensity but uses the deterministic ShiftIntensity transform internally.

Contrast Adjustment


source

AdjustContrast


def AdjustContrast(
    gamma, invert_image:bool=False, retain_stats:bool=False, kwargs:VAR_KEYWORD
):

Adjust image contrast using gamma transformation.

Each pixel intensity is transformed using a gamma function.


source

RandAdjustContrast


def RandAdjustContrast(
    gamma, prob:float=0.1, invert_image:bool=False, retain_stats:bool=False, has_channels:bool=True
):

Randomly adjust image contrast using gamma correction.