itasc.cellpose.flow_following

Flow-following cell segmentation, Qt-free, for the standalone distro.

When the standalone tool has both a nucleus and a cell channel, it can do better than independent native masks: it assigns each cell-foreground pixel to a nucleus, giving one cell per nucleus with the cell sharing the nucleus’ id. The cell’s extent is decided by Cellpose’s own flow field rather than by naive distance.

This is the proven two-phase algorithm (recovered from the pre-publication segmentation/flow_following.py):

  1. Integrate. Each foreground pixel is Euler-integrated along a blend of the Cellpose flow vector and an EDT gravity vector pointing toward the nearest nucleus (flow_weight mixes the two). A pixel that lands on a nucleus pixel during integration inherits that nucleus’ label immediately.

  2. Grow. Remaining foreground pixels are assigned by growing the labelled region outward in shells through the displaced (post-integration) positions, so labels chain-propagate along the flow topology. Pixels whose displaced position has no labelled neighbour within max_assign_radius stay background (orphans are dropped, not force-assigned).

The numba kernel is lazily JIT-compiled when numba is importable (it ships with the Cellpose stack) and falls back to the identical pure-Python function otherwise, so importing this module never requires numba.

Functions

flow_follow_frame(foreground_yx, dp_cyx, ...)

Assign one frame's cell foreground to nuclei via flow-following.

flow_follow_movie(foreground_tyx, dp_tcyx, ...)

Run flow_follow_frame() over a (T, Y, X) series.

Classes

FlowFollowingParams([fg_threshold, ...])

Parameters for flow-following cell assignment.

class itasc.cellpose.flow_following.FlowFollowingParams(fg_threshold=0.5, flow_weight=0.5, flow_step_scale=0.2, max_iterations=100, shell_width=5.0, max_assign_radius=30.0)[source]

Bases: object

Parameters for flow-following cell assignment.

Parameters:
fg_threshold: float = 0.5

Cell-foreground cutoff on the sigmoid probability (sigmoid(prob) > fg_threshold). Exposed knob — pixels below it are not assigned.

flow_weight: float = 0.5

Blend between the Cellpose flow vector (1) and the EDT gravity vector toward the nearest nucleus (0). 0.5 mixes both equally.

flow_step_scale: float = 0.2

Euler step size (multiplies the unit blended vector each iteration).

max_iterations: int = 100

Maximum integration steps per pixel.

shell_width: float = 5.0

Per-iteration capture distance when growing labels in shells (px).

max_assign_radius: float = 30.0

A displaced foreground pixel with no labelled pixel within this radius stays background — orphan foreground is dropped, not force-assigned.

itasc.cellpose.flow_following.flow_follow_frame(foreground_yx, dp_cyx, labels_yx, params)[source]

Assign one frame’s cell foreground to nuclei via flow-following.

foreground_yx is a bool mask, dp_cyx the Cellpose cell flow (2, Y, X) (dy, dx), labels_yx the tracked nucleus labels. Returns (Y, X) int32 cell labels carrying the nucleus ids (background 0).

Return type:

ndarray

Parameters:
itasc.cellpose.flow_following.flow_follow_movie(foreground_tyx, dp_tcyx, labels_tyx, params, *, progress_cb=None)[source]

Run flow_follow_frame() over a (T, Y, X) series.

dp_tcyx is (T, 2, Y, X). Cell labels inherit the (already tracked) nucleus ids per frame, so tracking the nuclei tracks the cells. Returns (T, Y, X) int32.

Return type:

ndarray

Parameters: