spacr.spacrops
Module Contents
- class spacr.spacrops.spacrStitcher(detector: str = 'ORB', nfeatures: int = 6000, max_keypoints: int | None = 2000, downsample: float = 0.5, ransac_thresh_px: float = 3.0, allow_scale: bool = False, allow_rotation: bool = False, outline_source: str = 'otsu', canny: Tuple[int, int] = (40, 120), blur_sigma: float = 0.0, dilate_ksize: int = 0, line_thickness: int = 1, outline_alpha: float = 1.0, outdir: str = './sbs_out', save_qc: bool = True, save_stitched_default: bool = True, all_scores: bool = False, score_threshold: float | None = None, verbose: bool = False, feature_cache_mode: str = 'disk', feature_cache_dir: str | None = None, max_ram_features: int = 256, n_workers_features: int | None = None, pair_batch_size: int = 8000, stream_csv: bool = True, opencv_threads: int = 1, arr_axes: str = 'AUTO', mip: bool = False, z_index: int = 0, t_index: int = 0, squeeze_singleton: bool = True)[source]
Pairwise stitcher with downsampled scoring and whole-well mosaic assembly.
- Robustness features for very large datasets:
feature_cache_mode: “disk” (default) writes DS features to disk with an LRU RAM cap
pair_batch_size: limit number of concurrent pair futures
stream_csv: write pairwise rows immediately (low RAM)
opencv_threads: limit OpenCV internal threading to avoid oversubscription
Transform control
- allow_scale: if True → use RANSAC affine (rotation+scale+translation)
if False → enforce unit scale (see allow_rotation)
- allow_rotation: if True → rotation+translation
if False → translation-only
Mosaic
Uses topology-aware pruning + maximum spanning tree on pairwise scores to build a cohesive grid. Exports mosaic image and a mosaic.csv manifest.
Axis & Z handling
- arr_axesstr in {“AUTO”} or a string over {T,C,Z,Y,X} (e.g. “CZYX”, “CYX”, “ZYX”).
Determines how to interpret multi-dimensional TIFFs.
mip : bool. If True and Z exists, perform max-intensity projection over Z. z_index : int (if mip=False) choose Z slice index. t_index : int choose time index if T exists.
- prepare_features(paths: List[str], channel_index: int, num_workers: int | None = None)[source]
Precompute features. In ‘disk’ mode, every computed feature is flushed to disk and only an LRU-sized subset is kept in RAM. In ‘ram’ mode, behaves like before.
- stitch_pair(pathA: str, pathB: str, channel_index: int = 0, score_threshold: float | None = None, save_stitched: bool | None = None, force_no_qc: bool = False, qc_only_if_score_ge: float | None = None) Dict | None[source]
- run_folder(folder: str, csv_path: str, *, channel_index: int = 0, exts: Tuple[str, Ellipsis] = ('.tif', '.tiff'), recursive: bool = False, same_well_only: bool = True, max_site_gap: int = 3, n_workers: int = 8, stitch: bool = True, score_threshold: float | None = None, meta_regex: str | re.Pattern | None = None, mosaic: bool = False, mosaic_out: str | None = None, mosaic_min_score: float | None = None, mosaic_csv_out: str | None = None, mosaic_all_channels: bool = False, mosaic_channel_count: int | None = None, mosaic_channel_index_order: List[int] | None = None, qc_pairs_threshold: int = 1000, qc_only_above_threshold_when_many: bool = True) str[source]
When number of candidate pairs exceeds qc_pairs_threshold, QC plotting is suppressed during the first (threaded) scoring pass and, once a threshold is known, QC is produced only for pairs with score >= threshold (in the second pass).
- build_multichannel_mosaic_from_manifest(manifest_csv: str, out_tif: str, out_png: str | None = None, channel_indices: List[int] | None = None, blend: str = 'max', tmp_dir: str | None = None, preview_downsample: int = 8)[source]
Build a CYX BigTIFF mosaic from a ‘mosaic.csv’ manifest written by spacrStitcher.render_mosaic_from_csv(…).
- Expected CSV columns (per tile row):
path, H, W, M00, M01, M02, M10, M11, M12, canvas_x, canvas_y, best_pair_score
Notes
Uses the 2x3 affine in the manifest (already includes global offset).
If channel_indices is None, infers the channel count from the first valid tile.
blend=”max” takes per-pixel max across overlapping tiles; blend=”overwrite” writes the latest tile over earlier ones where coverage>0.
If tmp_dir is provided (or available from self.feature_cache_dir), the output workspace uses a disk memmap to reduce RAM.
- mosaic_all_channels_from_csv_v1(csv_path: str, out_tif: str, *, min_score: float | None = None, channel_count: int | None = None, channel_index_order: List[int] | None = None, angle_tol_deg: float = 30.0, step_tol_frac: float = 0.25, rot_tol_deg: float = 5.0, scale_tol: float = 0.03, cap_one_per_dir: bool = True, out_csv: str | None = None) str[source]
Build a CYX mosaic by reusing pairwise transforms computed on (typically) the nuclei channel.
- Parameters:
csv_path (str) – Pairwise results CSV produced by run_folder(…).
out_tif (str) – Output TIFF path (saved with axes=’CYX’).
min_score (Optional[float]) – Minimum pairwise score to keep when building the mosaic graph. If None, auto-knee.
channel_count (Optional[int]) – If provided, force this many channels to be mosaicked (0..channel_count-1). Otherwise inferred as the minimum channel count across nodes.
channel_index_order (Optional[List[int]]) – Optional explicit channel indices to mosaic (e.g., [0,3,1]). Overrides channel_count if given.
angle_tol_deg – Same gating params as single-channel mosaic.
step_tol_frac – Same gating params as single-channel mosaic.
rot_tol_deg – Same gating params as single-channel mosaic.
scale_tol – Same gating params as single-channel mosaic.
cap_one_per_dir – Same gating params as single-channel mosaic.
out_csv (Optional[str]) – If provided, write a manifest with per-node canvas transforms.
- Returns:
Path to the saved multi-channel mosaic TIFF (CYX).
- Return type:
str
- render_mosaic_from_csv_v1(csv_path: str, out_tif: str, out_png: str | None = None, channel_index: int = 0, min_score: float | None = None, *, angle_tol_deg: float = 30.0, step_tol_frac: float = 0.25, rot_tol_deg: float = 5.0, scale_tol: float = 0.03, cap_one_per_dir: bool = True, out_csv: str | None = None) Tuple[str, str | None][source]
- render_mosaic_from_csv(csv_path: str, out_tif: str, out_png: str | None = None, channel_index: int = 0, min_score: float | None = None, *, angle_tol_deg: float = 30.0, step_tol_frac: float = 0.25, rot_tol_deg: float = 5.0, scale_tol: float = 0.03, cap_one_per_dir: bool = True, out_csv: str | None = None) Tuple[str, str | None][source]
- mosaic_all_channels_from_csv(csv_path: str, out_tif: str | None, *, min_score: float | None = None, channel_count: int | None = None, channel_index_order: List[int] | None = None, angle_tol_deg: float = 30.0, step_tol_frac: float = 0.25, rot_tol_deg: float = 5.0, scale_tol: float = 0.03, cap_one_per_dir: bool = True, out_csv: str | None = None) str | None[source]
Build a CYX mosaic by reusing pairwise transforms computed on (typically) the nuclei channel.
If out_csv is not None and out_tif is None, run in “manifest-only” mode: compute transforms + canvas geometry + per-node canvas transforms and write the manifest CSV, but DO NOT render/write the mosaic TIFF.
- class spacr.spacrops.StitchedMultiAligner(detector: str = 'ORB', nfeatures: int = 6000, max_keypoints: int | None = 2000, downsample: float = 0.5, ransac_thresh_px: float = 3.0, allow_scale: bool = False, allow_rotation: bool = False, outdir: str = './align_out', opencv_threads: int = 1, arr_axes: str = 'AUTO', mip: bool = False, z_index: int = 0, t_index: int = 0, squeeze_singleton: bool = True)[source]
Align an arbitrary number of stitched mosaics (multi-channel, arbitrary axes) to a common reference using the nuclei/ Hoechst channel. Saves a channel-concatenated aligned stack and a CSV manifest describing the mapping from input channels to output channels and the estimated transforms/scores.
Output image axes: CYX (channels stacked in the order inputs are provided).
- Parameters:
detector ({"ORB","SIFT"}) – Feature detector for keypoint matching.
nfeatures (int) – Feature budget for detector.
max_keypoints (Optional[int]) – Hard cap on kept keypoints after detection (by detector’s internal ranking).
downsample (float in (0,1]) – Downsample factor for feature/score pass.
ransac_thresh_px (float) – Reprojection threshold (pixels) for affine estimation (downsampled space).
allow_scale (bool) – If False, constrain to rotation+translation (or translation only if allow_rotation=False).
allow_rotation (bool) – If False, constrain to translation only.
outdir (str) – Output directory for images/csv.
opencv_threads (int) – Limit OpenCV internal threading (avoid oversubscription).
reading) (# Axis/Z/time handling (for TIFF)
arr_axes ("AUTO" or a string over {T,C,Z,Y,X})
mip (bool) – If True and Z exists, max-project Z.
z_index (int) – If mip=False, choose Z slice.
t_index (int) – Choose T index if T exists.
squeeze_singleton (bool) – Squeeze 1-length axes after slicing.
Notes
Alignment is done to the first image in paths (reference).
For each image, you can provide a per-image nuclei channel index via nuclei_channel_indices. If None, defaults to 0 for all.
- class spacr.spacrops.FOVAlignAndCropper(detector: str = 'ORB', nfeatures: int = 6000, max_keypoints: int | None = 2000, downsample: float = 0.5, ransac_thresh_px: float = 3.0, allow_scale: bool = False, allow_rotation: bool = False, outdir: str = './fov_out', opencv_threads: int = 1, arr_axes: str = 'AUTO', mip: bool = False, z_index: int = 0, t_index: int = 0, squeeze_singleton: bool = True, folder_image_scale: float = 1.0)[source]
Align each image in a folder (arbitrary channels) to a stitched mosaic (arbitrary channels) using the Hoechst/nuclei channel, then extract the FOV region from the mosaic at the aligned location. For each input FOV, saves:
- a .npy array with shape (C_fov + C_mosaic, H_fov, W_fov):
[FOV channels stacked; mosaic channels warped into FOV frame and stacked]
a CSV row with file paths, transform, score, and the mosaic-space top-left of the aligned FOV bbox.
- Parameters:
detector – Same semantics as in StitchedMultiAligner.
nfeatures – Same semantics as in StitchedMultiAligner.
max_keypoints – Same semantics as in StitchedMultiAligner.
downsample – Same semantics as in StitchedMultiAligner.
ransac_thresh_px – Same semantics as in StitchedMultiAligner.
allow_scale – Same semantics as in StitchedMultiAligner.
allow_rotation – Same semantics as in StitchedMultiAligner.
outdir – Same semantics as in StitchedMultiAligner.
opencv_threads – Same semantics as in StitchedMultiAligner.
arr_axes – Same TIFF axis handling semantics as in StitchedMultiAligner.
mip – Same TIFF axis handling semantics as in StitchedMultiAligner.
z_index – Same TIFF axis handling semantics as in StitchedMultiAligner.
t_index – Same TIFF axis handling semantics as in StitchedMultiAligner.
squeeze_singleton – Same TIFF axis handling semantics as in StitchedMultiAligner.
Notes
Alignment is (FOV -> mosaic). Extraction uses the inverse transform to warp the mosaic into the FOV frame, guaranteeing the combined array has the FOV’s native (H_fov, W_fov).
- run(stitched_path: str, folder: str, *, stitched_nuclei_idx: int = 0, fov_nuclei_idx: int = 0, exts: Tuple[str, Ellipsis] = ('.tif', '.tiff'), recursive: bool = False, csv_path: str | None = None, npy_dir: str | None = None, folder_image_scale: float | None = None) str[source]
Align each FOV in folder to the stitched mosaic at stitched_path using the nuclei channel, then save a stacked array [FOV channels; mosaic channels warped into FOV] to .npy and a CSV row.
- Known scale handling:
If the FOV magnification differs from the mosaic, set folder_image_scale to the FOV→mosaic pixel-scale factor (e.g., mosaic 10× vs FOV 20× -> 0.5; mosaic 20× vs FOV 10× -> 2.0).
- Returns:
Path to the CSV manifest.
- Return type:
str
- spacr.spacrops.align_image_to_stitch(stitch_dst_root: str, align_src: str, *, meta_regex: str = '(?P<mag>\\d+X)_c(?P<chan>\\d+)_?(?P<well>[A-H]\\d{1,2}).*?Site[-_](?P<site>\\d+)\\.(?:tif|tiff)$', well_group: str = 'well', channel_index: int = 0, relative_scale: float = 2.0, downsample: float = 0.5, nfeatures: int = 4000, ransac_thresh_px: float = 3.0, allow_scale: bool = False, allow_rotation: bool = False, qc_outlines: bool = False, recursive_align_src: bool = True, exts: tuple = ('.tif', '.tiff')) Dict[str, Dict[str, str]][source]
For each WELL that already has a stitched mosaic under stitch_dst_root/<WELL>/_stitch/mosaic_allc.tif, align all 20× images from align_src (grouped by WELL via meta_regex) to that mosaic using FOVAlignAndCropper, and write per-well crop manifests and .npy crops.
- Returns:
{“mosaic”: <path>, “align_folder”: <per-well link>, “manifest_csv”: <path>} }
- Return type:
{ WELL