Source code for pyfli.data_cc.preprocessing

"""
Apply threshold masks and shared boolean masks to one or more data arrays.

This module belongs to :mod:`pyfli.data_cc` and is part of PyFLI array preprocessing
helpers for normalization, masking, ROI extraction, and IRF alignment. Public API
includes classes :class:`DataPreprocessing`.
"""

from typing import Any

import numpy as np


[docs] class DataPreprocessing: # Supports: # - 2D data : (H, W) # - 3D data : (H, W, T) # - multiple inputs (decay, irf, background, etc.) """ Apply reusable masks and threshold filters to one or more aligned data arrays. It is useful before fitting, visualization, and statistical comparison steps that need shared valid-pixel selection. Parameters ---------- *data : Any Additional positional values accepted by the object. mask : np.ndarray | None Boolean mask selecting valid pixels or samples. """ def __init__(self, *data: Any, mask: np.ndarray | None = None) -> None: self.data = data self.mask = mask
[docs] def threshold_masking( self, lower: np.ndarray | None = None, upper: np.ndarray | None = None, data_index: int = 0, ) -> np.ndarray: """ Generates a mask based on intensity thresholds. If lower and upper are both None, a mask of all ones is returned. Parameters ---------- lower : float, optional Minimum intensity threshold. upper : float, optional Maximum intensity threshold. data_index : int Index of the dataset to use for generating the mask. """ arr = self.data[data_index] # Calculate intensity map if arr.ndim == 3: intensity = np.sum(arr, axis=-1) elif arr.ndim == 2: intensity = arr else: raise ValueError("Data must be 2D or 3D") # Initialize mask with all True (1s) # If lower=None and upper=None, this remains all True. mask = np.ones(intensity.shape, dtype=bool) # Apply lower bound if provided if lower is not None: mask &= intensity >= lower # Apply upper bound if provided if upper is not None: mask &= intensity <= upper self.mask = mask return mask
[docs] def apply_mask(self, mask: np.ndarray | None = None) -> tuple[Any, ...]: """ Apply mask. Parameters ---------- mask : np.ndarray | None Boolean or labeled mask selecting pixels for the operation. Returns ------- tuple[Any, ...] Tuple containing masked data and mask metadata. """ if mask is None: mask = self.mask if mask is None: raise ValueError( "Mask not provided. Generate one or pass it as an argument." ) masked_outputs = [] for arr in self.data: if arr.ndim == 3: # Use np.newaxis to broadcast (H, W) mask to (H, W, T) data mask_expanded = mask[..., np.newaxis] masked = arr * mask_expanded elif arr.ndim == 2: masked = arr * mask else: raise ValueError("Data must be 2D or 3D") masked_outputs.append(masked) return tuple(masked_outputs)