Source code for phenotypic.enhance._subtract_rolling_ball
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from phenotypic._core._image import Image
import numpy as np
from skimage.restoration import rolling_ball
from phenotypic.abc_ import ImageEnhancer
[docs]
class SubtractRollingBall(ImageEnhancer):
"""Remove background from ``detect_mat`` with ImageJ-style rolling-ball subtraction.
Models the background as the surface traced by rolling a parabolic ball
under the image intensity landscape, then subtracts it. Effectively
removes slow illumination gradients and agar shading while preserving
colony structures. Handles non-Gaussian intensity ramps better than
:class:`SubtractGaussian`.
For algorithm details, see :doc:`/explanation/what_enhancement_does`.
Args:
radius: Rolling-ball radius in pixels. Must be larger than the
typical colony diameter to avoid subtracting colony signal.
Typical range: 50--200. Default: 100.
kernel: Optional custom ball/shape array. When provided, overrides
``radius``. Default: ``None``.
nansafe: If ``True``, treat NaNs as missing data to avoid artifacts
when using masked images. Default: ``False``.
Returns:
Image: Input image with ``detect_mat`` background-subtracted.
``rgb`` and ``gray`` are unchanged.
Best For:
- Scanner vignetting, lid glare, or agar thickness variations.
- Flattening backgrounds to improve segmentation of dark colonies
on bright agar.
- Images with non-linear illumination gradients where Gaussian
subtraction leaves residual background.
Consider Also:
- :class:`SubtractGaussian` for faster Gaussian-based subtraction
with continuous sigma control.
- :class:`OpeningSubtractBg` for OpenCV-accelerated morphological
background removal in high-throughput pipelines.
- :class:`WhiteTophatEnhance` when you want to isolate small
bright structures rather than subtract background.
See Also:
:doc:`/tutorials/notebooks/03_enhancing_before_detection` for a
visual walkthrough of background subtraction on plate images.
:doc:`/explanation/what_enhancement_does` for background on
rolling-ball and other illumination correction strategies.
"""
[docs]
def __init__(
self, radius: int = 100, kernel: np.ndarray = None, nansafe: bool = False
):
"""
Parameters:
radius (int): Rolling-ball width (pixels). Use a value larger than
colony diameter to avoid removing colony signal. Default 100.
kernel (np.ndarray): Optional custom ball/shape; when provided it
overrides `width`.
nansafe (bool): If True, treat NaNs as missing data to avoid artifacts
when using masked images (e.g., outside the plate).
"""
self.radius: int = radius
self.kernel: np.ndarray = kernel
self.nansafe: bool = nansafe
def _operate(self, image: Image):
image.detect_mat[:] = image.detect_mat[:] - rolling_ball(
image=image.detect_mat[:],
radius=self.radius,
kernel=self.kernel,
nansafe=self.nansafe,
)
return image