Source code for playnano.processing.stack_edit

"""
Functions for editing AFM image stacks by removing or selecting frames.

These functions are designed to be called by the ProcessingPipeline, which
handles provenance tracking and updates to the AFMImageStack object. The
functions here operate purely on data arrays or frame index lists.

Only 'drop_frames' performs actual stack edits.
Other registered stack_edit functions return indices to drop, which are
then passed to 'drop_frames' to ensure consistent provenance tracking.

Functions
---------
- drop_frames : Remove specific frames from a 3D array.
- drop_frame_range : Generate a list of frame indices to drop within a given range.
- select_frames : Generate a list of frame indices to drop, keeping only the selected
  frames.
"""

from typing import Callable

import numpy as np

from playnano.utils.versioning import versioned_filter


[docs] @versioned_filter("0.1.0") def drop_frames(data: np.ndarray, indices_to_drop: list[int]) -> np.ndarray: """ Remove specific frames from a 3D array. Parameters ---------- data : np.ndarray 3D array of shape (n_frames, height, width) representing the image stack. indices_to_drop : list of int List of frame indices to remove from the stack. Returns ------- np.ndarray New array with the specified frames removed. Raises ------ ValueError If any provided indices are out of bounds or if `data` is not 3D. Notes ----- - The function does not modify the input array in place. - The ProcessingPipeline is responsible for updating metadata and provenance. """ if data.ndim != 3: raise ValueError(f"Expected a 3D array, got {data.ndim}D.") n_frames = data.shape[0] indices_to_drop = sorted(set(indices_to_drop)) if any(i < 0 or i >= n_frames for i in indices_to_drop): raise ValueError( f"Indices out of range for {n_frames} frames: {indices_to_drop}" ) keep_mask = np.ones(n_frames, dtype=bool) keep_mask[indices_to_drop] = False return data[keep_mask]
[docs] @versioned_filter("0.1.0") def drop_frame_range(data: np.ndarray, start: int, end: int) -> list[int]: """ Generate indices to drop within a given range of frames. Parameters ---------- data : np.ndarray 3D array of shape (n_frames, height, width) representing the image stack. start : int Starting index of the range (inclusive). end : int Ending index of the range (exclusive). Returns ------- list of int List of indices that should be dropped. Raises ------ ValueError If the range is invalid or out of bounds. """ if data.ndim != 3: raise ValueError(f"Expected a 3D array, got {data.ndim}D.") n_frames = data.shape[0] if start < 0 or end > data.shape[0] or start >= end: raise ValueError(f"Invalid range: start={start}, end={end}, total={n_frames}") return list(range(start, end))
[docs] @versioned_filter("0.1.0") def select_frames(data: np.ndarray, keep_indices: list[int]) -> list[int]: """ Generate a list of frame indices to drop, keeping only the selected frames. Parameters ---------- data : np.ndarray 3D array of shape (n_frames, height, width) representing the image stack. keep_indices : list of int Indices of frames to retain in the stack. Returns ------- list of int List of frame indices that should be dropped. Raises ------ ValueError If `keep_indices` contains out-of-range values. """ if data.ndim != 3: raise ValueError(f"Expected a 3D array, got {data.ndim}D.") n_frames = data.shape[0] keep_indices = sorted(set(keep_indices)) if any(i < 0 or i >= n_frames for i in keep_indices): raise ValueError(f"Invalid frame indices: {keep_indices}") all_indices = set(range(n_frames)) drop_indices = sorted(all_indices - set(keep_indices)) return drop_indices
[docs] def register_stack_edit_processing() -> dict[str, Callable]: """ Return a dictionary of registered stack editing processing filters. Keys are names of the operations, values are the functions themselves. drop_frames is the operational function takes a 3D stack (n_frames, H, W) and a list of indicies and returns a ndarray. drop_frame_range and select_frames are helper functions that return lists of indices to drop which can be passed to drop_frames. """ return { "drop_frames": drop_frames, "drop_frame_range": drop_frame_range, "select_frames": select_frames, }