Source code for phenotypic.enhance._gray_opening

from __future__ import annotations

from typing import Annotated, Literal, TYPE_CHECKING

from skimage import morphology

if TYPE_CHECKING:
    from phenotypic._core._image import Image

from phenotypic.abc_ import MorphologicalFiltering
from phenotypic.sdk_.mixin import FootprintMixin
from phenotypic.sdk_.typing_ import TuneSpec


[docs] class GrayOpening(MorphologicalFiltering, FootprintMixin): """Remove small bright artefacts from ``detect_mat`` via morphological opening. Applies erosion followed by dilation with a structuring element, removing bright features whose spatial extent is smaller than the element while preserving larger colony structures. Effectively suppresses dust particles, small noise speckles, and tiny satellite colonies that would otherwise generate false detections. For algorithm details, see :doc:`/explanation/what_enhancement_does`. Best For: - Removing dust particles and small bright noise from plate scans before thresholding. - Suppressing tiny satellite colonies or debris that would be counted as false positives by downstream detectors. - Smoothing the detection surface before background subtraction to reduce artefact interference. Consider Also: - :class:`WhiteTophatEnhance` when the goal is to isolate and enhance small bright structures rather than suppress them. - :class:`SubtractWhiteTophat` for subtracting small bright artefacts from the image while retaining the broader background. - :class:`LocalEdgeDenoise` for noise reduction that preserves colony edges without assuming a specific feature size. Args: shape: Structuring element geometry. Accepted values: ``'square'`` (default, preserves axis-aligned edges), ``'diamond'`` (rounded at diagonals), ``'disk'`` (uniform circular neighbourhood). Default: ``'square'``. width: Diameter of the structuring element in pixels. Features smaller than ``width`` are removed. Typical range: 3--15. Default: 5. n_iter: Number of times to apply the opening in sequence. Repeated opening with a small element produces smoother suppression than a single pass with a larger element. Default: 1. Returns: Image: Input image with ``detect_mat`` morphologically opened. ``rgb`` and ``gray`` are unchanged. See Also: :doc:`/tutorials/notebooks/03_enhancing_before_detection` for a visual walkthrough of enhancement pipelines on plate images. :doc:`/explanation/what_enhancement_does` for how morphological filtering fits into the pipeline model. """ shape: Literal["square", "diamond", "disk"] = "square" # TODO: review bound (unverified vs literature) width: Annotated[int, TuneSpec(3, 15, step=2)] = 5 n_iter: Annotated[int, TuneSpec(1, 3)] = 1 def _operate(self, image: Image) -> Image: footprint = self._make_footprint(shape=self.shape, width=self.width) for _ in range(self.n_iter): image.detect_mat[:] = morphology.opening( image=image.detect_mat[:], footprint=footprint, ) return image