viz.enhance.annular_normalize

viz.enhance.annular_normalize(
    image,
    center,
    *,
    radii,
    statistic='median',
    sigma=None,
    maxiters=1,
    clip_center='mean',
    clip_scale='std',
    mask=None,
    statistic_mask=None,
    return_stats=False,
)

Divide original pixels by statistics in explicit radial intervals.

Parameters

image : array - like

Nonempty, real 2-D image. Masked and nonfinite pixels are missing.

center : tuple of float

Zero-based (x, y) nucleus coordinates within the image.

radii : array - like

At least two finite, strictly increasing nonnegative bin edges in pixels. Each annulus includes its inner edge and excludes its outer edge. Include zero to normalize the central pixel too.

statistic : ('mean', 'median', 'mode') = "mean"

Divisor for each annulus. "mode" is 3*median - 2*mean. A callable receives finite retained pixels and returns a real scalar. Empty annuli skip the callable.

sigma : float, tuple of float, or None = None

Clipping threshold or (lower, upper) pair. None disables it. Rejected pixels remain in the output; only the divisor uses clipping.

maxiters : int or None = 1

Maximum clipping passes; 1 by default. None means convergence.

clip_center : ('mean', 'median') = "mean"

Location used for clipping bounds.

clip_scale : ('std', 'mad_std') = "std"

Sample deviation or Gaussian-consistent MAD used for clipping.

mask : array - like or None = None

Additional exclusion mask with the image shape.

statistic_mask : array - like or None = None

Exclude these pixels from each bin’s estimate, while dividing their original valid values by that estimate. The hard mask still excludes pixels from both estimation and output.

return_stats : bool = False

Return an EnhancementResult with .image and .stats when True. Default False returns only the image. Coverage is relative to in-frame pixel centers, not the area of a complete annulus.

Returns

: ndarray or EnhancementResult

Float64 image with unchanged shape. Missing pixels, pixels outside the radial intervals, and zero/nonfinite divisors produce NaN. The input is never modified.

Notes

Each finite pixel contributes once with equal weight, according to its center radius. There is no polar resampling or fractional area weighting. This is a different estimator from azimuthal_normalize, particularly near the nucleus, masks, and boundaries. Incomplete annuli use their available finite pixels and can therefore give biased divisors.

Pixel radii are computed once and grouped once, avoiding a separate full bounding-box scan for every annulus.

Examples

Normalize two annuli using a clipped mode estimate:

>>> data = np.ones((9, 9))
>>> result = annular_normalize(
...     data, (4, 4), radii=[0, 2, 4], statistic="mode", sigma=3
... )
>>> bool(result[4, 4] == 1.0)
True