Source code for phenotypic.enhance._white_tophat_enhance
from __future__ import annotations
from typing import Annotated, 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 MorphologicalFiltering
from phenotypic.sdk_.typing_ import TuneSpec
[docs]
class WhiteTophatEnhance(MorphologicalFiltering):
"""Isolate small bright structures in ``detect_mat`` with the white top-hat transform.
Computes the white top-hat (original minus morphological opening) and
retains the result, extracting bright features smaller than the
structuring element while suppressing larger background structures.
Highlights small bright colonies, inocula, or specks against uneven
illumination.
For algorithm details, see :doc:`/explanation/what_enhancement_does`.
Best For:
- Isolating small bright colonies from larger background structures
or broad illumination gradients.
- Highlighting faint small colonies against uneven agar
illumination.
- Extracting early-stage inocula or pinpoints for quantification.
- Preprocessing before applying a detector tuned for small colony
phenotypes.
Consider Also:
- :class:`SubtractWhiteTophat` when the goal is to suppress small
bright artifacts rather than isolate them.
- :class:`SubtractOpening` for OpenCV-accelerated white top-hat
background subtraction that also preserves the surrounding
image context.
- :class:`FocusBlobLoG` for scale-invariant blob detection that
responds across a range of colony sizes.
Args:
shape: Footprint geometry for the structuring element. Accepted
values: ``'disk'`` (default) for isotropic extraction that
preserves rounded colony shapes; ``'diamond'`` for
computational efficiency; ``'square'`` to align with sensor
grid artifacts.
width: Maximum bright-object size in pixels targeted for
extraction. Set slightly larger than the maximum colony
diameter you want to isolate. ``None`` (default) auto-derives
a small value as approximately 0.4 % of the shorter image
dimension.
Returns:
Image: Input image with ``detect_mat`` replaced by the white
top-hat result, containing only the extracted small bright
structures. ``rgb`` and ``gray`` are unchanged.
Raises:
ValueError: If an unsupported footprint shape is provided.
See Also:
:doc:`/tutorials/notebooks/03_enhancing_before_detection` for a
visual walkthrough of morphological enhancement on plate images.
:doc:`/explanation/what_enhancement_does` for background on
top-hat transforms and feature isolation strategies.
"""
shape: str = "disk"
width: Annotated[int | None, TuneSpec(3, 15, step=2)] = None
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[:] = 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}")