Skip to content

Tracking parameters: what to measure, how to set them, what changes

This guide is for the two_phase tracker. Every value here is a line in the track: section of your parameters_*.yaml (or trajectories: in experiment.yaml for the last two). Changing a value in the yaml never needs a new software release. A release is only needed when a new option is added to the code.

1. The quick way: let the program measure your data

(Full description and a demo table on real and synthetic data: tracking_advice.)

# replace the path by YOUR run folder; --fps is the camera frame rate
uv run python scripts/tracking_advice.py ~/data/experiment/wp2/test --fps 5000

The path is a run folder (it has parameters_*.yaml, cal/ and res/run.zarr after the sequence phase). The program reads about 100 frames, measures the numbers in section 3, and prints for every parameter the value it recommends and the measurement behind it, then YAML lines you can paste. It writes nothing.

2. How the pieces fit together (what depends on what)

position noise (jitter) ──► size of the "kink" ──► confirmation tolerance (confirm_tol)
point density           ──► chance of a wrong neighbour ──► how tight things must be
camera matching quality ──► ray miss distance, brightness agreement ──► ghost marks (q_seed, q_young)
step per frame          ──► (large steps = frame skip / low frame rate) ──► blob_gate, fixed tolerance
frame rate + noise      ──► how long a window you need ──► smoothing window

Three words used below: - Step: how far a particle moves between two frames (mm). - Kink: how much that step changes from one frame to the next, |x(t+1) − 2 x(t) + x(t−1)|. With a fast camera, real accelerations are tiny, so the kink is almost entirely position noise. Its median is a direct measurement of your noise. - Ghost: a 3D point made from blobs of different particles. It is smooth and can form a long wrong trajectory; the tracker cannot tell by motion alone.

3. The numbers to measure (the advisor prints all of them)

Measurement How it is measured Typical value What it tells you
Density median number of other 3D points within 5 mm of a point 5–6 sparse, 10–11 dense many neighbours = wrong links are likelier
Median step median distance between linked points in consecutive frames 0.1 mm at 5000 fps large (≥ 0.2) = frame-skipped or slow camera
Median kink median of the kink over all linked triples 0.04–0.14 (grows with noise) the noise level
Kink / step the two above divided 0.4–0.9 high = noise dominates; low = real motion dominates
3-camera share share of points seen by only 3 cameras 35–40% (lv_multi 50%) more = more doubtful points
Flagged share share of points with ghost probability > 0.2 about 10% (lv_multi 18%) how many points the ghost rules will treat as doubtful

4. The parameters

Parameter What it does (one sentence) How to choose Higher value does Lower value does
q_seed (default 0.2) a point with ghost probability above this cannot start a trajectory 0.2 normally; 0.15 if 3-camera share > 45% or flagged > 14%; 0.3 if < 20% / < 4% keeps more points, more ghosts removes more ghosts, also ~1% real points
q_young (default 3) a trajectory with fewer points than this cannot continue onto a doubtful point 3 normally, 6 in accuracy mode removes more ghosts keeps more points
q_model (default rcm_blob) how the ghost probability is computed: rcm = ray miss distance + camera count; rcm_blob also uses blob brightness agreement rcm if blobs have no usable brightness – –
confirm_tol (mm/frame) a link survives only if the next step continues within this kink leave unset for sparse, noisy data (automatic); 0.3 for dense or frame-skipped data fewer cut links, longer tracks, more outliers kept cuts true links when the noise is larger than the tolerance
confirm_auto (default 8) with confirm_tol unset: tolerance = this × the median kink 8 looser tighter
confirm_auto_max_neighbours (7), confirm_auto_min_ratio (0.5), confirm_auto_radius (5 mm) the guard: the automatic tolerance is used only if the density is at most 7 neighbours and kink/step is at least 0.5 change only if the advisor's measurements are near the limit and you have tested – –
confirm_ends (default true) a track may not end by stepping onto a stranger keep true: it is the most valuable part of the confirmation – –
blob_gate (default off) a candidate whose blob brightness changed by more than this (log units) is rejected 0.5 for frame-skipped or low-frame-rate data (step ≥ 0.2 mm) looser stricter, cuts ~0.3% of real links at 0.5
v_max (mm/frame) search radius about 3× the typical step, or the largest real step more candidates loses fast particles
max_gap frames a track may be missing 2 (one missing frame) bridges longer dropouts, more mistakes –
trajectories.smoothing_window frames in the smoothing window about 4 ms of frames (21 at 5000 fps) smoother velocities, blurs real changes noisier velocities
trajectories.reconnect_gap (6; 0 = off) join broken pieces of a trajectory across up to gap − 1 missing frames, using a straight line from each side; tolerance trajectories.reconnect_tol (4 noise sigmas) 6 for all data; better in 8 of 9 benchmark cases joins more pieces, risk of a wrong join fewer joins
trajectories.smooth_filter_k (6; off = 0) drop points farther than k median residuals from the curve their neighbours define 6 when noise dominates (kink/step ≥ 0.5); off for frame-skipped data milder: keeps more points stricter: 4 gives −0.02 velocity error and drops 4% of the points
trajectories.trim_doubtful (off) cut up to 3 doubtful end points of a trajectory 0.3 when accuracy matters more than the number of points cuts fewer points cuts more (0.2: ~2% of points)

5. What to expect (measured on synthetic data with known truth)

Velocity error relative to the true flow, lower is better; "perfect linker" is the best any tracker can do on the same 3D points.

Change Effect on the velocity error
Ghost rules on (q_seed 0.2, q_young 3) −0.020 (sparse) … −0.075 (4× density)
Brightness agreement (q_model: rcm_blob, instead of rcm) a further −0.005 … −0.039
Trim doubtful ends (0.3) + gap-aware smoother a further −0.01
Automatic confirmation tolerance, real noise level −0.018 (and more with more noise: −0.070 at 2× the real noise)
Reconnect broken pieces (gap 6, 4σ) −0.002 … −0.019 (+0.004 at 4× density)
Smoothness filter (k = 6) after reconnect a further −0.001 … −0.015, about 1% fewer points (skip ×8: +0.0075, so off there)
blob_gate: 0.5 on frame-skipped data −0.007 … −0.015
Accuracy mode (q_seed 0.15, q_young 6) a further −0.004 … −0.04, about 0.5–1% fewer points

6. How to check a setting on your own real data (no ground truth needed)

uv run python scripts/real_retrack.py NAME 300 q_seed=0.15 q_young=6     # a scratch copy
uv run python scripts/realism_metrics.py /private/tmp/claude-501/real_runs/NAME/res/run.zarr --last 300

Compare two settings with these numbers:

Number Better when Why
trajectories (tracks) lower fewer fragments and ghost pieces
mean_len higher longer tracks
n_ge50 (tracks of ≥ 50 frames) about equal long tracks must not be lost
never4_share lower long trajectories never seen by all 4 cameras are the best hint of ghosts
jitter_z_p90 lower scatter of the points around a smooth curve

Do not use jump_share to judge the confirmation tolerance. It counts large depth changes between frames; a looser tolerance keeps more of these noise outliers although the smoothed result is better (it reproduces identically on synthetic data, where the truth shows the looser setting wins). Judge a tolerance by the smoothed velocities, or on a synthetic copy of your data with known truth (scripts/synth_bench.py, section 7).

7. Testing a setting on synthetic data with known truth (optional)

uv run python scripts/synth_bench.py build --level L1 --sigma-px 0.08 --amp-jitter 0.18 --amp-flicker 0.05
uv run python scripts/synth_bench.py sweep --cases CASE --trackers "two_phase+q_seed=0.15+q_young=6" -j 3 --no-eval
uv run python scripts/synth_bench.py eval  --case CASE --trackers two_phase "two_phase+q_seed=0.15+q_young=6"

--sigma-px sets the position noise (0.08 matches real CompleteTest data); the other two options give realistic blob brightness. Never tune on real data; tune on the synthetic copy and check on the real data with section 6.

8. Limits to keep in mind

  • The numbers in sections 4–5 come from one flow field and one camera geometry. Where your measurements are near a limit (density 6–8, kink/step 0.4–0.6) test before trusting the automatic tolerance.
  • The automatic tolerance assumes a fast camera (kink = noise). With frame skipping the kink contains real motion and the guard switches it off.
  • Very short smoothing windows (about 9 frames) make the looser tolerance slightly worse (+0.008 at the real noise level).
  • See Point quality for how the ghost probability is computed.