Zura · Rejoin Tracked Shots
Stitching per-shot masks back together, and telling you where tracking failed
- masks
- tracking_segments
- tracked_mask
- tracking_coverage
Track a person across a cut and the tracker will happily follow the wrong human. It's the single most common failure in masked video work: frame 1 has your actor, frame 400 has a different actor in a different room, and the mask just keeps going. The fix is to break the clip at cuts and track each shot separately - which immediately creates a new problem, because now you have a list of masks instead of a video-length mask.
This node is the second half of that fix. Its twin, Zura · Reset Tracking at Cuts, detects the cuts and emits the per-shot batches; this one puts them back together in order.
How it works
It takes a list of per-shot masks and the manifest that describes their boundaries, validates them hard, concatenates them along the frame axis, and then does the part that earns it a place in the graph: coverage diagnostics.
For every frame it counts foreground pixels (mask > 0.5). If fewer than 0.1% of the raster is flagged, that frame is marked "near empty" and grouped into contiguous ranges. What comes out is a report of exactly where the mask went thin - which is what a failed track looks like from the outside.
The honesty here is worth quoting, because most nodes in this position would quietly fill the gap:
"Less than 0.1% of the raster is flagged for review, including tiny tracker remnants. This cannot distinguish valid cutaways from failed tracking."
Right. A person walking out of frame and a tracker losing them produce the same signal. The node declines to guess and hands you the ranges instead. The tracking_coverage output also carries cut_reselection_requires_review, which is true whenever there was more than one shot - a nudge that each cut is a fresh chance to have grabbed the wrong human.
What it explicitly does not do: fill missing tracking, interpolate, or infer identity. The mask you get out is the mask you put in, concatenated.
Inputs and outputs
masks-MASK, a list (the node declaresINPUT_IS_LIST, so wire the per-shot batches in via list mapping, not as one flat batch).tracking_segments-GENJ_TRACK_SEGMENTS, the manifest from the split node: frame count, raster, cut positions, segment boundaries.tracked_mask-MASK, one batch covering the whole source in shot order.tracking_coverage-GENJ_TRACK_COVERAGE, the diagnostics. Read it; it's the whole reason to use this over a plain concat.
Install
cd ComfyUI/custom_nodes && git clone https://github.com/ZURAVFX/ComfyUI_zura_nodes
pip install -r ComfyUI_zura_nodes/requirements.txt
Restart. No models, no weights, no GPU - this is validation, concatenation and statistics on CPU. The cut detection that feeds it runs on 32×32 thumbnails to keep it cheap even on long clips.
Common issues
"Native tracking must return exactly one mask batch for each shot." The list lengths don't match. Usually because one shot produced no output at all - a shot where the detector found nobody returns an empty branch rather than an empty mask.
"Tracked mask frame counts and raster must match their source shot." A tracker that resized or dropped frames. Check the native tracking node's settings; the mask must come back at the source raster, one frame per source frame.
"All tracked shot masks must use the same dtype and device." Mixed CPU/GPU masks, or a boolean mask from one branch and a float from another. The node is strict so the concatenation can't quietly promote anything.
"Tracking segments do not cover all source frames." The manifest is stale - you cut the clip after splitting. Re-split.
You concatenated them yourself and it looked fine. It often does, right up until a shot is 3 frames shorter than its neighbours and every subsequent frame in the edit is misaligned. The segment validation is the actual value; the concat is trivial.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| masks | MASK | — | |
| tracking_segments | GENJ_TRACK_SEGMENTS | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| tracked_mask | MASK | — |
| tracking_coverage | GENJ_TRACK_COVERAGE | — |