Source code for vista.algorithms.detectors.threshold

"""Simple threshold detector algorithm for finding bright blobs in imagery"""
import numpy as np
from skimage.measure import label, regionprops


[docs] class SimpleThreshold: """ Detector that uses a fixed threshold to find blobs. Uses regionprops to identify connected regions above or below threshold, or both, filtered by area, and returns weighted centroids as detections. """ name = "Simple Threshold"
[docs] def __init__(self, threshold: float, min_area: int = 1, max_area: int = 1000, detection_mode: str = 'above'): """ Initialize the Simple Threshold detector. Parameters ---------- threshold : float Intensity threshold for detection. min_area : int, optional Minimum detection area in pixels. Default is 1. max_area : int, optional Maximum detection area in pixels. Default is 1000. detection_mode : {'above', 'below', 'both'}, optional Detection mode controlling how threshold is applied: - 'above': Detect pixels > threshold (default) - 'below': Detect pixels < -threshold (negative values) - 'both': Detect pixels where ``|pixel|`` > threshold (absolute value) """ self.threshold = threshold self.min_area = min_area self.max_area = max_area self.detection_mode = detection_mode
[docs] def __call__(self, image): """ Process a single image and return detections. Parameters ---------- image : ndarray 2D numpy array representing a single image frame to process. Returns ------- rows : ndarray Array of row coordinates (y-positions) for detection centroids. columns : ndarray Array of column coordinates (x-positions) for detection centroids. Raises ------ ValueError If detection_mode is not 'above', 'below', or 'both'. Notes ----- The detector: 1. Applies threshold based on detection_mode to create a binary mask 2. Labels connected components in the binary mask 3. Filters regions by area (min_area <= area <= max_area) 4. Computes weighted centroids for qualifying regions 5. Returns centroid coordinates as (rows, columns) tuple """ # Apply threshold based on detection mode if self.detection_mode == 'above': # Detect pixels brighter than threshold binary = image > self.threshold elif self.detection_mode == 'below': # Detect pixels darker than threshold (for negative values) binary = image < -self.threshold elif self.detection_mode == 'both': # Detect pixels with large absolute values (far from zero in either direction) binary = np.abs(image) > self.threshold else: raise ValueError(f"Invalid detection_mode: {self.detection_mode}. " f"Must be 'above', 'below', or 'both'.") # Label connected components labeled = label(binary) # Get region properties regions = regionprops(labeled, intensity_image=image) # Filter by area and extract weighted centroids rows = [] columns = [] for region in regions: if self.min_area <= region.area <= self.max_area: # Use weighted centroid (intensity-weighted) and account for center of pixel being at 0.5, 0.5 centroid = region.weighted_centroid rows.append(centroid[0] + 0.5) columns.append(centroid[1] + 0.5) # Convert to numpy arrays rows = np.array(rows) columns = np.array(columns) return rows, columns