Source code for itasc.contact_analysis.quantifiers.nucleus_dynamics

"""Nucleus-dynamics quantifier — the registry adapter over the dynamics core.

The nucleus twin of :mod:`.cell_dynamics`: it runs the same label-agnostic
:func:`build_track_dynamics` over the **nucleus** label stack instead of the cell
one, persisting ``4_contact_analysis/nucleus_dynamics.h5``. Nuclei are
compact, point-like centroids — the robust default for motility / MSD. The
object-key column stays ``cell_id`` (a nucleus is nucleus-seeded so it carries
its cell's shared track id), so the pooling / plotting layer treats it no
differently.
"""
from __future__ import annotations

from collections.abc import Callable, Mapping
from pathlib import Path

import numpy as np

from itasc.contact_analysis.dynamics import (
    TrackDynamics,
    build_track_dynamics,
    read_instantaneous_table,
    read_track_dynamics,
)
from itasc.contact_analysis.dynamics.kinematics import instantaneous_table
from itasc.contact_analysis.dynamics.trajectories import extract_trajectories
from itasc.contact_analysis.quantifier import PositionInputs, Quantifier


[docs] class NucleusDynamicsQuantifier(Quantifier): """Quantifies per-nucleus motion (speed, persistence, MSD, collective) from nucleus labels.""" quantity_id = "nucleus_dynamics" display_name = "Nucleus dynamics" requires = ("nucleus_labels_path",) # Pixel size + frame interval are global build params (see CellDynamics). required_build_params = { "pixel_size_um": "pixel size (µm/px)", "time_interval_s": "frame interval (s)", } default_output_name = "nucleus_dynamics.h5" # Instantaneous nucleus motion is keyed on the shared cell track id # (frame, cell_id). table_keys = ("frame", "cell_id")
[docs] def build( self, inputs: PositionInputs, output_path: Path, *, params: dict | None = None, progress_cb: Callable[[int, int, str], None] | None = None, ) -> Path: return build_track_dynamics( inputs.nucleus_labels_path, output_path, pixel_size_um=inputs.pixel_size_um, time_interval_s=inputs.time_interval_s, source_path=inputs.position_dir, params=params, quantity_id=self.quantity_id, progress_cb=progress_cb, )
[docs] def read(self, output_path: Path) -> TrackDynamics: return read_track_dynamics(output_path)
[docs] def object_table(self, output_path: Path) -> Mapping[str, np.ndarray]: return read_instantaneous_table(output_path)
[docs] def compute_object_table( self, inputs: PositionInputs, *, params: dict | None = None ) -> Mapping[str, np.ndarray]: trajectories = extract_trajectories( inputs.nucleus_labels_path, pixel_size_um=inputs.pixel_size_um ) return instantaneous_table(trajectories, time_interval_s=inputs.time_interval_s)