Source code for spindoctor.support.image_quality

"""Global image-quality helpers used during orchestrator preflight.

Three image-level checks happen before any technique runs.  This module owns
the implementations:

- ``saturation_mask``: pixels at or above the per-instrument full-well DN.
- ``cosmic_ray_mask``: single-pixel spikes above 5 sigma over a median-3x3
  neighborhood.

Each helper operates on the entire image and produces a boolean mask
consumed by feature extractors and the gradient computation.  Image-side
operations are global by design — no helper here crops to a predicted
position.
"""

import numpy as np
from scipy.ndimage import median_filter

from spindoctor.support.types import NDArrayBoolType, NDArrayFloatType

__all__ = [
    'cosmic_ray_mask',
    'saturation_mask',
]


[docs] def saturation_mask(image: NDArrayFloatType, *, full_well_dn: float) -> NDArrayBoolType: """Boolean mask with True where pixels are at or above the saturation DN. Parameters: image: 2-D float input array. full_well_dn: Per-instrument saturation DN. Pixels at or above this value are flagged as saturated. Returns: Boolean mask of the same shape as ``image``. Raises: TypeError: if ``image`` is not 2-D. """ if image.ndim != 2: raise TypeError(f'saturation_mask requires a 2-D image; got ndim={image.ndim}') return image >= full_well_dn
[docs] def cosmic_ray_mask( image: NDArrayFloatType, *, image_noise_sigma: float, k_sigma: float = 5.0, ) -> NDArrayBoolType: """Boolean mask flagging single-pixel spikes above a sigma threshold. Compares each pixel against a 3x3 median around it (reflect boundary handling); pixels whose excess over the local median exceeds ``k_sigma * image_noise_sigma`` are flagged. Suitable as a cheap pre-detection step that prevents single hot pixels from masquerading as star detections in pattern matching. Parameters: image: 2-D float input array. image_noise_sigma: Global MAD-based noise sigma (DN units); typically from ``estimate_image_noise_sigma``. k_sigma: Multiplier on ``image_noise_sigma`` (default 5). Returns: Boolean mask of the same shape as ``image``. Raises: TypeError: if ``image`` is not 2-D. ValueError: if ``image_noise_sigma`` is non-positive. """ if image.ndim != 2: raise TypeError(f'cosmic_ray_mask requires a 2-D image; got ndim={image.ndim}') if image_noise_sigma <= 0.0: raise ValueError(f'image_noise_sigma must be positive; got {image_noise_sigma}') a = image.astype(np.float64) med = median_filter(a, size=3, mode='reflect') threshold = k_sigma * image_noise_sigma mask: NDArrayBoolType = (a - med) > threshold return mask