Source code for vista.algorithms.background_removal.static_median_background_removal

"""Static (non-sliding) median background removal for VISTA

This module computes a single median image from a user-specified set of
background frames (typically frames without transient events) and subtracts
it from each target frame.

Compared to the sliding ``TemporalMedian`` variant, this is appropriate for
non-moving transient events where one or more quiescent periods can be used
to model the background.
"""
import numpy as np


[docs] def static_median_background_removal(background_images, target_images, callback=None): """ Subtract a median background image, computed from a fixed set of frames, from each target frame. The median is computed pixelwise over ``background_images`` (typically a quiescent period without transient events). The same median image is subtracted from every frame in ``target_images``. Parameters ---------- background_images : numpy.ndarray 3D array of shape (num_background_frames, height, width) used to compute the median background. target_images : numpy.ndarray 3D array of shape (num_target_frames, height, width) to which the background removal is applied. Must share height and width with ``background_images``. callback : callable, optional Called after each target frame with (frame_processed, total_frames). Should return False to cancel processing. Returns ------- tuple of (numpy.ndarray, numpy.ndarray) (background, foreground) arrays with the shape and dtype of ``target_images``. The background array contains the same median image broadcast to each target frame. Raises ------ InterruptedError If the callback returns False (user cancellation). ValueError If the height/width of ``background_images`` and ``target_images`` do not match, or if ``background_images`` is empty. """ if background_images.ndim != 3 or target_images.ndim != 3: raise ValueError("background_images and target_images must be 3D arrays.") if (background_images.shape[1] != target_images.shape[1] or background_images.shape[2] != target_images.shape[2]): raise ValueError( "background_images and target_images must have the same height and width." ) if background_images.shape[0] == 0: raise ValueError("background_images must contain at least one frame.") num_tgt = target_images.shape[0] median_image = np.median(background_images, axis=0).astype(target_images.dtype) background = np.empty_like(target_images) foreground = np.empty_like(target_images) for t in range(num_tgt): background[t] = median_image foreground[t] = target_images[t] - median_image if callback is not None: if not callback(t + 1, num_tgt): raise InterruptedError("Processing cancelled by user") return background, foreground