Source code for phenotypic.enhance._unsharp_mask
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from phenotypic._core._image import Image
from skimage.filters import unsharp_mask
from ..abc_ import ImageEnhancer
[docs]
class UnsharpMask(ImageEnhancer):
"""Sharpen colony edges in ``detect_mat`` with unsharp masking.
Subtracts a Gaussian-blurred copy from the original and scales the
difference to emphasize high-contrast boundaries. Makes soft or
indistinct colony edges more pronounced, improving thresholding and
edge-detection accuracy.
For algorithm details, see :doc:`/explanation/what_enhancement_does`.
Args:
radius: Standard deviation of the Gaussian blur in pixels.
Controls the scale of features enhanced. Small values
(0.5--2.0) sharpen fine details; larger values (5--15)
enhance broader features. Default: 2.0.
amount: Multiplier for the sharpening effect. Low values
(0.3--0.7) produce subtle enhancement; standard values
(1.0--1.5) give moderate sharpening; high values (2.0+)
create aggressive enhancement with risk of halo artifacts.
Default: 1.0.
preserve_range: Preserve the original pixel value range.
Default: ``False``.
n_iter: Number of successive sharpening passes. Multiple passes
compound the effect. Typical range: 1--3. Default: 1.
Returns:
Image: Input image with ``detect_mat`` sharpened via unsharp
masking. ``rgb`` and ``gray`` are unchanged.
Best For:
- Low-contrast colonies with soft, gradual edges (translucent
growth).
- Dense plates where colonies blend into background.
- Pre-threshold sharpening to improve segmentation accuracy.
- Slight scanner or lens blur that softens colony boundaries.
Consider Also:
- :class:`BilateralDenoise` for denoising before sharpening on
grainy images to avoid amplifying noise.
- :class:`LaplaceEnhancer` for second-derivative edge detection
that replaces rather than enhances the intensity profile.
- :class:`PhaseCongruencyEnhancer` for contrast-invariant edge
detection under uneven illumination.
See Also:
:doc:`/tutorials/notebooks/03_enhancing_before_detection` for a
visual walkthrough of edge sharpening on plate images.
:doc:`/explanation/what_enhancement_does` for background on
unsharp masking and sharpening strategies.
"""
[docs]
def __init__(
self,
radius: float = 2.0,
amount: float = 1.0,
preserve_range: bool = False,
n_iter: int = 1,
):
"""
Parameters:
radius (float): Standard deviation (sigma) of the Gaussian blur in pixels.
Defines the scale of features to enhance. Small values (0.5–2) sharpen
fine details (thin colony edges, small morphologies); larger values
(5–15) enhance broad features (large colonies, colony-background
separation). Must be > 0. For fungal colonies, keep below the typical
colony width to avoid merging adjacent colonies. Recommended: 2.0–3.0
for general-purpose use, 1.0 for high-density plates, 5.0+ for
emphasizing large-scale features on low-resolution images.
amount (float): Amplification factor for the sharpening effect. Controls
how much the edge enhancement contributes to the output. Typical range:
0.3–2.5. Low values (0.3–0.7) produce subtle enhancement suitable for
noisy images; standard values (1.0–1.5) give balanced sharpening;
high values (2.0+) create aggressive enhancement for very low-contrast
colonies. Can be negative to produce blurring instead. Excessive amounts
risk visible artifacts and noise amplification.
preserve_range (bool): If False (default), output may be rescaled if
necessary. If True, the original range of input values is preserved.
Keep as False for consistency with other enhancers.
n_iter (int): Number of successive unsharp mask passes to apply. Must be >= 1.
One pass (default) applies the filter once. Multiple passes (2+) compound
the sharpening effect for progressively more aggressive enhancement, but
at increased risk of noise amplification and halo artifacts.
"""
if radius <= 0:
raise ValueError("width must be > 0")
if n_iter < 1:
raise ValueError("n_iter must be >= 1")
self.radius = float(radius)
self.amount = float(amount)
self.preserve_range = bool(preserve_range)
self.n_iter = int(n_iter)
def _operate(self, image: Image) -> Image:
"""Apply unsharp masking to enhance colony edges in the detection matrix channel."""
for _ in range(self.n_iter):
image.detect_mat[:] = unsharp_mask(
image=image.detect_mat[:],
radius=self.radius,
amount=self.amount,
preserve_range=self.preserve_range,
channel_axis=None,
)
return image