Source code for phenotypic.refine._skeletonize

from __future__ import annotations

from typing import Literal, TYPE_CHECKING

if TYPE_CHECKING:
    from phenotypic._core._image import Image

from skimage.morphology import skeletonize

from phenotypic.abc_ import ObjectRefiner


[docs] class Skeletonize(ObjectRefiner): """Reduce object masks to single-pixel-wide skeletons via medial axis thinning. Compresses each object region to its medial axis (centerline), preserving topological connectivity while discarding boundary and interior pixels. Useful for distilling colony morphology to its core branching structure for filament or spreading phenotype analysis. Args: method: Thinning algorithm. ``"zhang"`` is fast and optimized for clean 2D masks. ``"lee"`` is more robust to noise and works on 2D/3D. ``None`` auto-selects based on dimensionality. Default: None. Returns: Image: Input image with ``objmask`` replaced by the single-pixel-wide skeleton. Raises: ValueError: If an invalid ``method`` is provided. Best For: - Extracting colony centerlines for elongation or orientation analysis. - Analyzing branching patterns in filamentous fungi or spreading bacterial phenotypes. - Simplifying masks for spatial graph analysis or hyphae tracking. - Reducing boundary noise before measuring advanced morphological features. Consider Also: - :class:`Thinning` for iterative boundary peeling with control over the number of iterations. - :class:`MaskGradient` when you need boundary outlines rather than medial axes. - :class:`MaskEroder` for uniform inward shrinking that preserves filled regions. See Also: :doc:`/how_to/notebooks/refine_noisy_boundaries` for skeleton-based analysis workflows. :doc:`/explanation/refinement_strategies` for a comparison of morphological refinement methods. """
[docs] def __init__(self, method: Literal["zhang", "lee"] | None = None): """Initialize the skeletonizer. Args: method (Literal["zhang", "lee"] | None): Algorithm for skeletonization. - "zhang": Optimized for 2D images; fast, produces thin skeletons. Best for well-defined colony boundaries. - "lee": Works on 2D/3D; more robust to noisy or irregular boundaries. Slightly slower but preserves topology better on challenging images. - None: Automatically selects Zhang for 2D and Lee for 3D. Choosing the right method depends on image quality: clean, binary masks benefit from Zhang; noisier masks or fungal hyphae benefit from Lee. """ super().__init__() self.method: Literal["zhang", "lee"] | None = method
def _operate(self, image: Image) -> Image: image.objmask[:] = skeletonize(image.objmask.copy(), method=self.method) return image