Source code for linumpy.stack_alignment.units
"""Unit conversion and centring for inter-slice shift fields."""
from collections.abc import Sequence
[docs]
def detect_shift_units(resolution: Sequence[float]) -> tuple[float, float]:
"""Detect whether resolution is in mm or µm and return (res_x_um, res_y_um).
OME-Zarr resolution can be reported in either mm (OME-NGFF standard)
or µm depending on the writer. Detects by magnitude:
- Values < 1.0 assumed to be mm (e.g. 0.01 mm = 10 µm)
- Values >= 1.0 assumed to be µm (e.g. 10 µm)
Parameters
----------
resolution : sequence
Resolution tuple/list (res_z, res_y, res_x) from read_omezarr.
Returns
-------
res_x_um, res_y_um : float
X and Y resolution in microns.
"""
res_x_raw = resolution[-1]
res_y_raw = resolution[-2] if len(resolution) >= 2 else res_x_raw
if res_x_raw < 1.0:
res_x_um = res_x_raw * 1000.0
res_y_um = res_y_raw * 1000.0
else:
res_x_um = float(res_x_raw)
res_y_um = float(res_y_raw)
return res_x_um, res_y_um
[docs]
def convert_shifts_to_pixels(cumsum_mm: dict, resolution_um: float | tuple[float, float]) -> dict:
"""Convert mm cumulative shifts to pixel shifts.
Parameters
----------
cumsum_mm : dict
Mapping from slice_id to (dx_mm, dy_mm).
resolution_um : float or (float, float)
Resolution in microns per pixel. Either a single value (isotropic XY)
or a (res_x_um, res_y_um) tuple for anisotropic XY.
Returns
-------
dict
Mapping from slice_id to (dx_px, dy_px).
"""
if isinstance(resolution_um, tuple):
mm_to_px_x = 1000.0 / resolution_um[0]
mm_to_px_y = 1000.0 / resolution_um[1]
else:
mm_to_px_x = mm_to_px_y = 1000.0 / resolution_um
return {slice_id: (dx_mm * mm_to_px_x, dy_mm * mm_to_px_y) for slice_id, (dx_mm, dy_mm) in cumsum_mm.items()}
[docs]
def center_shifts(cumsum_px: dict, slice_ids: list) -> dict:
"""Center shifts around the middle slice.
Subtracts the middle slice's cumulative shift from all slices,
preventing drift from pushing slices out of the output canvas.
Parameters
----------
cumsum_px : dict
Mapping from slice_id to (dx_px, dy_px).
slice_ids : list
Sorted list of slice IDs.
Returns
-------
dict
Centered cumulative shifts.
"""
if not slice_ids:
return cumsum_px
middle_idx = len(slice_ids) // 2
middle_id = slice_ids[middle_idx]
center_dx, center_dy = cumsum_px.get(middle_id, (0, 0))
return {slice_id: (dx - center_dx, dy - center_dy) for slice_id, (dx, dy) in cumsum_px.items()}