Source code for phenotypic.refine._white_tophat_modifier

from __future__ import annotations

from typing import Literal, 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 ObjectRefiner
from phenotypic.tools_.mixin import FootprintMixin


[docs] class WhiteTophat(ObjectRefiner, FootprintMixin): """Remove small bright mask structures using white tophat subtraction. Applies a white tophat transform to the binary mask to detect small bright features (glare bridges, dust speckles, thin connections) and subtracts them. Produces cleaner, more compact masks that better match colony boundaries under uneven illumination. Args: shape: Structuring element shape. ``"disk"`` preserves round features, ``"square"`` is more aggressive along axes, ``"diamond"`` provides a compromise. A NumPy array provides a custom element. Default: ``"disk"``. width: Footprint width in pixels. Larger values remove broader bright features but risk shrinking thin colony appendages. ``None`` auto-scales to ~0.4% of the smallest image dimension. Default: None. Returns: Image: Input image with ``objmask`` updated by subtracting the white tophat result. Best For: - Reducing glare-induced bridges between neighboring colonies. - Removing bright speckles or dust embedded in masks after thresholding. - Cleaning up thin bright connections that inflate colony perimeters. Consider Also: - :class:`MaskOpener` for general morphological opening that removes thin protrusions without tophat detection. - :class:`SmallObjectRemover` when small artifacts are entire disconnected objects rather than thin bridges. - :class:`GMMCoreExtractor` for intensity-based core extraction when halos are the primary artifact. See Also: :doc:`/how_to/notebooks/refine_noisy_boundaries` for tophat-based cleanup workflows. :doc:`/explanation/refinement_strategies` for a comparison of morphological refinement methods. """
[docs] def __init__(self, shape: Literal["disk", "square", "diamond"] | np.ndarray = "disk", width: int | None = None): """ Represents a structural element used to analyze and process images, specifically useful for microbial colony analysis on solid media agar. The class encapsulates the shape and size of the structural element. Structural elements are commonly used in morphological image processing tasks such as dilations, erosions, opening, and closing. These operations can enhance or isolate features of microbe colonies on agar plates, such as determining colony size, spacing, or detecting connections between colonies. Attributes: shape (Literal["disk", "square", "diamond"] | np.ndarray): Defines the shape of the structural element. Choosing "disk" may help preserve the rounded geometry of typical microbial colonies. "Square" and "diamond" shapes may be more useful for colonies that form irregular or grid-based patterns. Supplying a custom numpy array (np.ndarray) allows for complete customization of the structural element, which could be beneficial for non- standard colony morphologies. width (int | None): Specifies the size of the structural element by defining the width. Larger widths will create structural elements that can encompass larger colonies or areas of colonies, potentially aiding in operations designed to merge close colonies. Smaller widths will result in more localized structural elements, which can preserve fine details and delineate smaller colonies. A None value assumes a default or minimal size. """ self.shape = shape self.width = width
def _operate(self, image: Image) -> Image: white_tophat_results = white_tophat( image.objmask[:], footprint=FootprintMixin._make_footprint( shape=self.shape, width=self._get_footprint_width(array=image.objmask[:]), ), ) image.objmask[:] = image.objmask[:] & ~white_tophat_results return image def _get_footprint_width(self, array: np.ndarray) -> int: if self.width is None: return int(np.min(array.shape) * 0.004) else: return self.width