Source code for phenotypic.enhance._non_local_means
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from phenotypic._core._image import Image
from skimage.restoration import denoise_nl_means
from ..abc_ import ImageEnhancer
[docs]
class NonLocalMeansDenoiser(ImageEnhancer):
"""Denoise ``detect_mat`` with non-local means patch-based filtering.
Compares patches across the image to identify similar structures and
averages them, preserving thin colony boundaries and internal texture
better than simple Gaussian or median filtering. Particularly effective
at removing Gaussian noise and agar granularity.
For algorithm details, see :doc:`/explanation/what_enhancement_does`.
Args:
patch_size: Size of patches used for comparison in pixels. Larger
patches capture more structure but are slower. Typical range:
5--15. Default: 5.
search_dist: Maximum search distance for similar patches in pixels.
Larger values find more candidates at higher cost. Typical
range: 5--21. Default: 11.
h: Cut-off distance controlling smoothness. Rule of thumb:
``h`` ~= noise level (sigma). Higher values produce more
smoothing. Default: 0.5.
fast_mode: If ``True``, use faster variant with uniform spatial
weighting. If ``False`` (default), use original algorithm
with Gaussian spatial weighting.
sigma: Expected noise standard deviation. Values > 0 improve
patch weighting by accounting for expected noise variance.
Default: 0.0 (disabled).
Returns:
Image: Input image with ``detect_mat`` denoised via non-local
means filtering. ``rgb`` and ``gray`` are unchanged.
Best For:
- Scanner noise and agar granularity where colony edges must stay
sharp.
- Low-contrast or faint colonies where Gaussian blur would cause
loss of detail.
- Preserving colony texture and morphology during speckle and dust
removal.
- Pre-filtering before edge detection to avoid amplifying noise.
Consider Also:
- :class:`BM3DDenoiser` for state-of-the-art structured noise
removal at higher computational cost.
- :class:`BilateralDenoise` for faster edge-preserving denoising
without patch comparison.
- :class:`BayesShrinkEnhancer` for adaptive wavelet denoising with
spatially varying thresholds.
See Also:
:doc:`/tutorials/notebooks/03_enhancing_before_detection` for a
visual walkthrough of denoising pipelines on plate images.
:doc:`/how_to/notebooks/denoise_low_light` for non-local means
and other denoising strategies on low-light plate images.
"""
[docs]
def __init__(
self,
patch_size: int = 5,
search_dist: int = 11,
h: float = 0.5,
*,
fast_mode: bool = False,
sigma: float = 0.0,
):
"""
Parameters:
patch_size (int): Size of patches used for comparison. Larger patches capture
more structure but are slower. Start with 5-7 for agar plates; increase to 11-15
for heavily noisy images. Default: 7.
search_dist (int): Maximal distance in pixels to search for similar patches.
Larger values find more candidates at higher cost. Default: 11.
h (float): Cut-off distance controlling smoothness. Typical rule of thumb:
h ≈ sigma (noise level). Increase to ~1.5*sigma for more smoothing.
Default: 0.1.
fast_mode (bool): If True, use faster variant with uniform spatial weighting.
If False, use original algorithm with Gaussian spatial weighting (slower but
potentially better quality). Default: False.
sigma (float): Noise standard deviation. If provided (> 0), improves patch weighting
by accounting for expected noise variance. Start with estimate_sigma() output.
Default: 0.0 (disabled).
"""
self.patch_size = int(patch_size)
self.search_dist = int(search_dist)
self.h = float(h)
self.fast_mode = bool(fast_mode)
self.sigma = float(sigma)
def _operate(self, image: Image) -> Image:
"""Apply non-local means denoising to detection matrix."""
denoised = denoise_nl_means(
image=image.detect_mat[:],
patch_size=self.patch_size,
patch_distance=self.search_dist,
h=self.h,
fast_mode=self.fast_mode,
sigma=self.sigma,
preserve_range=True,
)
image.detect_mat[:] = denoised
return image