Source code for phenotypic.enhance._enhance_local_contrast

from __future__ import annotations

from typing import TYPE_CHECKING, Annotated, Optional

if TYPE_CHECKING:
    from phenotypic._core._image import Image

from skimage.exposure import equalize_adapthist

from phenotypic.abc_ import ContrastAdjustment
from phenotypic.sdk_.typing_ import TuneSpec


[docs] class EnhanceLocalContrast(ContrastAdjustment): """Boost local contrast in ``detect_mat`` using adaptive histogram equalization. Divides ``detect_mat`` into tiles and equalizes the intensity histogram within each tile, with a clip limit that caps the redistribution gain and prevents excessive noise amplification. Faint colonies become more visible and easier to threshold, even when illumination varies across the plate. For a discussion of contrast enhancement strategies, see :doc:`/explanation/what_enhancement_does`. Best For: - Plates with faint or translucent colonies that blend into agar. - Images with uneven illumination such as vignetting or shadows from plate lids. - Pre-conditioning before global thresholding (Otsu, Triangle). - Early time-point plates where colonies are barely visible. Consider Also: - :class:`ContrastStretching` for a simpler global contrast adjustment when illumination is already uniform across the plate. - :class:`FlattenIllumination` when the primary problem is a large-scale illumination gradient rather than local contrast variation. - :class:`SharpenEdgeGauss` when edges need sharpening rather than contrast boosting. Args: kernel_size: Tile size for local equalization in pixels. Smaller tiles reveal tiny colonies and local features but amplify agar texture; larger tiles produce smoother results. ``None`` auto-selects a tile size of roughly one fifteenth of the image height. Default: ``None``. clip_limit: Maximum local contrast amplification factor. Typical range: 0.005--0.05. Lower values suppress noise amplification; higher values make faint colonies stand out more at the cost of amplifying local noise. Default: 0.01. Returns: Image: Input image with ``detect_mat`` contrast-enhanced. ``rgb`` and ``gray`` are unchanged. Raises: ValueError: If the ``detect_mat`` value range is invalid for equalization. References: [1] S. M. Pizer et al., "Adaptive histogram equalization and its variations," *Computer Vision, Graphics, and Image Processing*, vol. 39, no. 3, pp. 355--368, Sep. 1987. See Also: :doc:`/tutorials/notebooks/03_enhancing_before_detection` for a visual walkthrough of ``EnhanceLocalContrast`` before detection. :doc:`/how_to/notebooks/enhance_low_contrast` for a comparison of contrast enhancement methods on real plate images. """ kernel_size: Annotated[Optional[int], TuneSpec(tunable=False)] = None clip_limit: Annotated[float, TuneSpec(0.005, 0.05, log=True)] = 0.01 def _operate(self, image: Image) -> Image: try: image.detect_mat[:] = equalize_adapthist( image=image.detect_mat[:], kernel_size=self.kernel_size if self.kernel_size else self._auto_kernel_size(image), clip_limit=self.clip_limit, nbins=2 ** int(image.bit_depth), ) return image except RuntimeError as e: raise ValueError(f"Value Range: {image.detect_mat.val_range()}") from e @staticmethod def _auto_kernel_size(image: Image) -> int: return int(min(image.gray.shape[:1]) * (1.0 / 15.0))