Source code for ultrasound_metrics.metrics.cnr

"""
Contrast-to-Noise Ratio (CNR) metric for ultrasound image quality assessment.
"""

from typing import Callable

from array_api_compat import array_namespace
from array_api_compat import size as array_size
from beartype import beartype as typechecker
from jaxtyping import jaxtyped

from ultrasound_metrics._utils.array_api import ArrayAPIObj


[docs] def get_agg_func(xp: ArrayAPIObj, name: str) -> Callable[[ArrayAPIObj], ArrayAPIObj]: """ Get backend-agnostic aggregation function. Parameters ---------- xp Array API namespace. name Name of aggregation function ('MEAN' or 'MEDIAN'). Returns ------- callable Aggregation function. """ if name == "MEAN": return lambda arr: xp.mean(arr) elif name == "MEDIAN": return lambda arr: xp.median(arr) else: raise ValueError(f"Unknown aggregation function: {name}")
@jaxtyped(typechecker=typechecker)
[docs] def compute_cnr( values_signal: ArrayAPIObj, values_noise: ArrayAPIObj, fun_signal: str = "MEAN", fun_noise: str = "MEAN", *, use_signal_variance: bool = True, use_noise_variance: bool = True, ) -> float: """ Compute the contrast-to-noise ratio (CNR) between two regions. The CNR is commonly used in medical ultrasound to assess image quality [1]_. The CNR is computed as [2]_: .. math:: CNR = \\frac{|\\mu_i - \\mu_o|}{\\sqrt{\\sigma_i^2 + \\sigma_o^2}} where: .. math:: \\mu_i = E\\{|s_i|^2\\} \\mu_o = E\\{|s_o|^2\\} \\sigma_i^2 = E\\{(|s_i|^2 - \\mu_i)^2\\} \\sigma_o^2 = E\\{(|s_o|^2 - \\mu_o)^2\\} with :math:`s_i` and :math:`s_o` representing the signal values inside and outside the region of interest, respectively. Parameters ---------- values_signal Pixel values from the signal region (e.g., inside a lesion). values_noise Pixel values from the noise/background region (e.g., outside a lesion). fun_signal Aggregation function for the signal region ("MEAN" or "MEDIAN"). fun_noise Aggregation function for the noise region ("MEAN" or "MEDIAN"). use_signal_variance Whether to include signal variance in the denominator. use_noise_variance Whether to include noise variance in the denominator. Returns ------- float The contrast-to-noise ratio. References ---------- .. [1] Patterson, M. S., & Foster, F. S. (1983). The improvement and quantitative assessment of B-mode images produced by an annular array/cone hybrid. Ultrasonic Imaging, 5(3), 195-213. .. [2] A. Rodriguez-Molares, O. M. Hoel Rindal, J. D'hooge, S. -E. Måsøy, A. Austeng and H. Torp, "The Generalized Contrast-to-Noise Ratio," 2018 IEEE International Ultrasonics Symposium (IUS), Kobe, Japan, 2018, pp. 1-4, doi: 10.1109/ULTSYM.2018.8580101. """ xp = array_namespace(values_signal) if array_size(values_signal) == 0 or array_size(values_noise) == 0: raise ValueError("Input arrays must not be empty.") # Aggregation functions agg_signal = get_agg_func(xp, fun_signal) agg_noise = get_agg_func(xp, fun_noise) # Match the original: use squared values for mean and variance values_signal_sq = xp.abs(values_signal) ** 2 values_noise_sq = xp.abs(values_noise) ** 2 mu_signal = agg_signal(values_signal_sq) mu_noise = agg_noise(values_noise_sq) out = xp.abs(mu_signal - mu_noise) if use_noise_variance or use_signal_variance: sigma_signal = agg_signal((values_signal_sq - mu_signal) ** 2) sigma_noise = agg_noise((values_noise_sq - mu_noise) ** 2) denom = xp.sqrt(sigma_signal * int(use_signal_variance) + sigma_noise * int(use_noise_variance)) out = out / denom return float(out)