spacr.timeflows_qc¶
Check that timelapse annotation carries one label per object over time.
WHAT IT IS FOR¶
The timeflows plan rests on one claim about the
training data: an object keeps the SAME LABEL in every frame it appears in.
A time-flow head is trained to point from a pixel of an object in frame t
at that object’s centre in frame t+1, and the only thing that says which
object in t+1 is the same object is the label. If the annotation recycles
a label after its object dies or leaves, the model is trained to point at an
unrelated cell, and no later score catches it – the held-out check scrambles
the same wrong labels and agrees with itself.
So this module reads label stacks and produces a table. It answers the four questions step 1 of the plan asks, and it answers them as measurements rather than impressions:
Is a label ever reused? Every label that vanishes and comes back is listed with how far it moved across the gap, how much its mask overlaps the mask it had before the gap, and what a continuous object in the same data moves in one frame. A label that reappears on the other side of the field with no overlap is flagged; a label that flickers for one frame and returns where it was is reported as a gap and not as reuse.
How are divisions annotated? Two new ids, or one child inheriting the parent’s id. Both conventions exist in real annotation and the loss has to know which one it is being trained on.
What is the frame interval, and is it constant? Within a dataset and across datasets. A displacement field silently encodes pixels per frame, so a set imaged every 30 s mixed with one imaged every 10 min is two problems trained as one.
How far does an object move between frames? In pixels, and – the ratio that decides whether the task is easy or hard – in multiples of its own diameter. Displacement much smaller than the object is a field a small receptive field can carry. Displacement larger than the object means the vector has to point further than the head can see, and several identical cells are candidates at the far end of it.
WHY A SCRIPT AND NOT A LOOK¶
The defect this looks for is invisible by eye. A recycled id is a correct frame and a correct frame, and only the pair is wrong. The plan calls it the most common defect in tracking annotation and the easiest to miss, which is why the output here is a table with a verdict per check and an exit code, and why the detail tables behind every verdict are public: a flagged row is a row somebody has to read, not a number to average.
WHAT IT DOES NOT DO¶
It judges the ANNOTATION, not a tracker. Scoring a predicted tracking against
a ground truth is spacr.timeflows_baseline, which is step 2 of the same
plan. It holds no graphical code and opens no dialogs.
Functions¶
|
Audit several label stacks and compare their frame intervals. |
|
Answer step 1's four questions about one label stack, as a table. |
|
A distinct name for each stack path, as short as it can be. |
|
Per-frame displacement of every label that survives a frame. |
|
Find divisions and say which labelling convention they follow. |
|
Render a QC table as aligned text. |
|
Reduce frame timing to an interval and a verdict about its constancy. |
|
Measure every labelled object in every frame. |
|
Summarise where each label lives in the movie. |
|
Read a label stack from a file or a folder of frames. |
|
Run the QC over one or more label stacks and print the table. |
|
List every label that disappears and comes back, and judge each one. |
Module Contents¶
- spacr.timeflows_qc.audit_datasets(stacks, timestamps=None, frame_intervals=None, max_displacement=None)[source]¶
Audit several label stacks and compare their frame intervals.
- Parameters:
stacks – mapping of dataset name to label stack.
timestamps – optional mapping of dataset name to frame timestamps.
frame_intervals – optional mapping of dataset name to a stated interval.
max_displacement – passed to
recycled_labels().
- Returns:
DataFrameof every dataset’s rows, followed by oneframe_interval_across_datasetsrow reported under the dataset nameall. Two datasets imaged at different intervals are a different problem from one dataset imaged unevenly, and only this row sees it.
- spacr.timeflows_qc.audit_label_consistency(masks, dataset='dataset', timestamps=None, frame_interval=None, max_displacement=None)[source]¶
Answer step 1’s four questions about one label stack, as a table.
- Parameters:
masks – label stack,
(T, Y, X)or a sequence of 2-D frames.dataset – the name the rows are reported under.
timestamps – acquisition time of each frame, when it is known.
frame_interval – a stated frame interval, used when there are no timestamps.
max_displacement – passed to
recycled_labels().
- Returns:
DataFramewith the columnsdataset,check,value,verdictanddetail, one row per check.
- spacr.timeflows_qc.dataset_names(paths)[source]¶
A distinct name for each stack path, as short as it can be.
Named after the last path component, the fourteen Cell Tracking Challenge movies are all
TRA– they live at<movie>/01_GT/TRA– and keyed on that, each overwrote the one before, so only the last was audited (found 2026-09-21, the first run on real movies). A name that repeats takes in parent folders until every name is unique.- Parameters:
paths – the stack paths, in order.
- Returns:
one name per path, in the same order.
- spacr.timeflows_qc.displacement_table(frames_table)[source]¶
Per-frame displacement of every label that survives a frame.
Only CONSECUTIVE frames are measured. A label that reappears after a gap contributes nothing here, because the distance it covered is not a per-frame displacement and averaging it in would inflate the number this module exists to report.
- Parameters:
frames_table – the table
label_frames()returns.- Returns:
DataFramewithlabel,frame,displacementin pixels,diameterof the object in the earlier frame, anddisplacement_over_diameter.
- spacr.timeflows_qc.division_events(masks, child_overlap=CHILD_OVERLAP)[source]¶
Find divisions and say which labelling convention they follow.
A child is called an object’s child when at least
child_overlapof the child’s own area came from that object. Two or more children of one parent in one step is a division, and the convention is read off the ids: either one child carries the parent’s id forward, or both children are new.- Parameters:
masks – label stack,
(T, Y, X)or a sequence of 2-D frames.child_overlap – fraction of the child’s area that has to come from the parent. Default
CHILD_OVERLAP.
- Returns:
DataFramewithframe(the frame the children are in),parent_label,n_children,child_labelsandconvention, which is'child_inherits_parent'or'children_are_new'.
- spacr.timeflows_qc.format_qc_table(table, width=64)[source]¶
Render a QC table as aligned text.
- Parameters:
table – the table
audit_label_consistency()returns.width – how much of the detail sentence to print per row.
- Returns:
the table as a string, one line per row, with no trailing newline.
- spacr.timeflows_qc.frame_interval_summary(timestamps=None, frame_interval=None, tolerance=1e-06)[source]¶
Reduce frame timing to an interval and a verdict about its constancy.
- Parameters:
timestamps – acquisition time of each frame, in any one unit. Two frames are the minimum that says anything.
frame_interval – a stated interval, used when no timestamps are given. It is reported as stated and never contradicts a measurement.
tolerance – absolute spread between the largest and smallest interval that still counts as constant.
- Returns:
dict with
interval,spread,source('measured','stated'or'missing') andverdict.
- spacr.timeflows_qc.label_frames(masks)[source]¶
Measure every labelled object in every frame.
- Parameters:
masks – label stack,
(T, Y, X)or a sequence of 2-D frames.- Returns:
DataFramewith one row per object per frame and the columnsframe,label,area,y,xanddiameter, where the diameter is the area-equivalent circle diameter in pixels.
- spacr.timeflows_qc.label_spans(frames_table)[source]¶
Summarise where each label lives in the movie.
- Parameters:
frames_table – the table
label_frames()returns.- Returns:
DataFramewith one row per label carryinglabel,first_frame,last_frame,n_frames,n_missingandmissing_frames.n_missingcounts frames between the first and the last appearance in which the label is absent, which is the only kind of gap that can hide a recycled id.
- spacr.timeflows_qc.load_label_stack(path)[source]¶
Read a label stack from a file or a folder of frames.
- Parameters:
path –
.npyfile,.tif/.tifffile, or a folder holding one file per frame, read in sorted name order.- Returns:
(T, Y, X)integer array.- Raises:
ValueError – when the folder holds nothing readable, or the suffix is not one of the three above.
- spacr.timeflows_qc.main(argv=None)[source]¶
Run the QC over one or more label stacks and print the table.
- Parameters:
argv – command line arguments;
sys.argv[1:]whenNone.- Returns:
0when no check failed,1when one did. A failure here is a statement about the ANNOTATION, not about this program: it means the data does not support what the timeflows plan assumes about it.
- spacr.timeflows_qc.recycled_labels(masks, frames_table=None, max_displacement=None)[source]¶
List every label that disappears and comes back, and judge each one.
A recycled id – the same number given to an unrelated object after the first one left – is the defect the whole plan is exposed to, and it looks exactly like a tracking gap from one frame. What separates them is distance: a real object that was missed for a frame is found again near where it was, and usually still overlapping its own mask. So each gap is reported with the distance covered per missing frame, the overlap between the mask before the gap and the mask after it, and the distance a continuous object in the same data covers in one frame.
- Parameters:
masks – label stack,
(T, Y, X)or a sequence of 2-D frames.frames_table – the table
label_frames()returns, when it has already been measured. Measured here whenNone.max_displacement – distance per missing frame, in pixels, above which a gap is called reuse.
Nonemeasures it from the data asREUSE_DISPLACEMENT_FACTORtimes the 95th percentile of the continuous per-frame displacement, falling back to the median object diameter when nothing in the stack survives a frame.
- Returns:
DataFramewith one row per gap:label,gap_start,gap_end,gap_frames,displacement,displacement_per_frame,iou_across_gap,allowedandverdict('gap'or'reuse-suspected').