Source code for phenotypic.enhance._subtract_white_tophat

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from phenotypic._core._image import Image

import numpy as np
from skimage.morphology import white_tophat

from phenotypic.abc_ import ImageEnhancer


[docs] class SubtractWhiteTophat(ImageEnhancer): """Suppress small bright artifacts in ``detect_mat`` by subtracting the white top-hat. Computes the white top-hat (original minus morphological opening) and subtracts it from the image, removing small bright blobs such as dust specks, glare highlights, and condensation artifacts while preserving larger colony structures. For algorithm details, see :doc:`/explanation/what_enhancement_does`. Args: shape: Footprint geometry. ``'diamond'`` (default) or ``'disk'`` provide isotropic behavior; ``'square'`` can align with sensor grid artifacts. width: Maximum bright-object size (pixels) targeted for removal. Set slightly smaller than the smallest colonies to preserve them. ``None`` (default) derives a small value from image dimensions. Returns: Image: Input image with ``detect_mat`` smoothed by subtracting the white top-hat. ``rgb`` and ``gray`` are unchanged. Best For: - Removing small bright artifacts that could be mistaken for tiny colonies. - Reducing glare highlights on shiny plates before thresholding. - Cleaning up dust and condensation artifacts that confuse detection. Consider Also: - :class:`WhiteTophatEnhance` when you want to isolate (not suppress) small bright structures. - :class:`GrayOpening` for morphological smoothing that removes small bright features without explicit subtraction. - :class:`RankMedianEnhancer` for impulsive noise removal via median filtering. See Also: :doc:`/tutorials/notebooks/03_enhancing_before_detection` for a visual walkthrough of artifact removal on plate images. """
[docs] def __init__(self, shape: str = "diamond", width: int = None): """ Parameters: shape (str): Footprint geometry controlling which bright features are removed. 'diamond' or 'disk' provide isotropic behavior on plates; 'square' can align with sensor grid artifacts. Advanced: 'sphere' or 'cube' for volumetric data. width (int | None): Maximum bright-object width (in pixels) targeted for removal. Set slightly smaller than the smallest colonies to avoid suppressing real colonies. None picks a small default based on image dimensions. """ self.shape = shape self.width = width
def _operate(self, image: Image) -> Image: white_tophat_results = white_tophat( image.detect_mat[:], footprint=self._get_footprint( self._get_footprint_width(detection_matrix=image.detect_mat[:]), ), ) image.detect_mat[:] = image.detect_mat[:] - white_tophat_results return image def _get_footprint_width(self, detection_matrix: np.ndarray) -> int: if self.width is None: return int(np.min(detection_matrix.shape) * 0.004) else: return self.width def _get_footprint(self, width: int) -> np.ndarray: match self.shape: # Use shared ImageEnhancer utility for common 2D shapes case "disk" | "square" | "diamond": return self._make_footprint(shape=self.shape, width=width) case _: raise ValueError(f"Unsupported footprint shape: {self.shape}")