Source code for phenotypic.enhance._focus_edge_two_k_phase

# src/phenotypic/enhance/_focus_edge_two_k_phase.py
"""FocusEdgeTwoKPhase — two-scale-k phase-congruency hysteresis enhancer."""
from __future__ import annotations

from typing import TYPE_CHECKING, Annotated, Literal

from pydantic import Field

from phenotypic.abc_._enhance_markers._focus_edge import FocusEdge
from phenotypic.sdk_.mixin import NormalizedOutputMixin
from phenotypic.sdk_.typing_ import TuneSpec

from ._two_k_phase_kernel import two_k_phase

if TYPE_CHECKING:
    from phenotypic._core._image import Image


[docs] class FocusEdgeTwoKPhase(NormalizedOutputMixin, FocusEdge): """Branch-response enhancer via two-k phase-congruency hysteresis. Runs phase congruency at a strict k (clean seeds) and a loose k (full candidates), keeps loose candidates that touch a strict seed, and writes the loose-k magnitude gated by that mask into ``detect_mat``. Continuous output; the inoculum center hole is preserved (this is a pure branch enhancer — center filling belongs to the detector, not here). Assumes ``detect_mat`` is already illumination-flattened + contrast-stretched upstream (same contract as FocusEdgePhase). """ n_orient: Annotated[int, TuneSpec(4, 8)] = Field(8, ge=1) min_wavelength: Annotated[float, TuneSpec(2.0, 10.0)] = Field(5.0, ge=2.0) k_strict: Annotated[float, TuneSpec(4.0, 8.0)] = Field(6.0, ge=0.0) k_loose: Annotated[float, TuneSpec(3.5, 6.0)] = Field(4.5, ge=0.0) seed_thresh: Literal["otsu", "triangle"] = "otsu" cand_thresh: Literal["otsu", "triangle"] = "triangle" def _operate(self, image: "Image") -> "Image": gated, _loose = two_k_phase( image.detect_mat[:], k_strict=self.k_strict, k_loose=self.k_loose, seed_thresh=self.seed_thresh, cand_thresh=self.cand_thresh, n_orient=self.n_orient, min_wavelength=self.min_wavelength, ) image.detect_mat[:] = self._apply_norm(gated) return image