Source code for playnano.processing.mask_generators

"""Module for masking features of AFM images in Numpy arrays."""

import logging

import numpy as np
from scipy import ndimage

from playnano.utils.versioning import versioned_filter

logger = logging.getLogger(__name__)


[docs] @versioned_filter("0.1.0") def mask_threshold(data: np.ndarray, threshold: float = 0.0) -> np.ndarray: """ Mask where data > threshold. Parameters ---------- data : numpy.ndarray Input 2D array. threshold : float, optional Threshold value. Pixels greater than this will be True. Default is 0.0. Returns ------- np.ndarray Boolean mask array. """ return (data > threshold) & np.isfinite(data)
[docs] @versioned_filter("0.1.0") def mask_below_threshold(data: np.ndarray, threshold: float = 0.0) -> np.ndarray: """ Mask where data < threshold. Parameters ---------- data : numpy.ndarray Input 2D array. threshold : float, optional Threshold value. Pixels less than this will be True. Default is 0.0. Returns ------- np.ndarray Boolean mask array. """ return (data < threshold) & np.isfinite(data)
[docs] @versioned_filter("0.1.0") def mask_mean_offset(data: np.ndarray, factor: float = 1.0) -> np.ndarray: """ Mask values greater than mean plus factor * standard deviation. Parameters ---------- data : numpy.ndarray Input 2D array. factor : float, optional Factor multiplied by the standard deviation to define the threshold. Default is 1.0. Returns ------- np.ndarray Boolean mask array. """ return (data - np.mean(data)) > factor * np.std(data)
[docs] @versioned_filter("0.1.0") def mask_morphological( data: np.ndarray, threshold: float = 0.0, structure_size: int = 3 ) -> np.ndarray: """ Apply threshold and morphological closing to mask foreground. Parameters ---------- data : numpy.ndarray Input 2D array. threshold : float, optional Threshold value. Default is 0.0. structure_size : int, optional Size of the structuring element for binary closing. Default is 3. Returns ------- np.ndarray Boolean mask array after morphological closing. """ binary = np.abs(data) > threshold structure = np.ones((structure_size, structure_size), dtype=bool) return ndimage.binary_closing(binary, structure=structure)
[docs] @versioned_filter("0.1.0") def mask_adaptive( data: np.ndarray, block_size: int = 15, offset: float = 0.0 ) -> np.ndarray: """ Adaptive local mean threshold per block. Parameters ---------- data : numpy.ndarray Input 2D array. block_size : int, optional Size of the local block. Default is 15. offset : float, optional Value added to local mean when thresholding. Default is 0.0. Returns ------- np.ndarray Boolean mask array where True indicates pixels above the threshold. """ h, w = data.shape mask = np.zeros_like(data, dtype=bool) for i in range(0, h, block_size): for j in range(0, w, block_size): block = data[i : i + block_size, j : j + block_size] local_mean = np.mean(block) mask_block = block > (local_mean + offset) mask[i : i + block_size, j : j + block_size] = mask_block return mask
[docs] def register_masking(): """ Return dictionary of available masking functions. Returns ------- dict Keys are function names (str), values are the corresponding callable functions. """ return { "mask_threshold": mask_threshold, "mask_below_threshold": mask_below_threshold, "mask_mean_offset": mask_mean_offset, "mask_morphological": mask_morphological, "mask_adaptive": mask_adaptive, }