Particle-tracking velocimetry (PTV) pipeline for white spheres floating on a river surface, imaged from overhead. The footage comes from a laboratory floodplain flume experiment. Built on trackpy (Crocker–Grier centroid detection + linking).
Status: first scaffold. No footage yet — the pipeline runs end-to-end on a synthetic frame generator so the plumbing can be exercised and refined before real data arrives. Tuning parameters are placeholders to be set from a calibration frame and the known flow speed.
Given a frame sequence (video or image folder) it produces:
- Per-frame detections —
detections.csv(frame, x, y, mass, size, ecc) - Linked trajectories —
trajectories.csv(particle, frame, x, y) - Surface velocities —
velocities.csv(particle, frame, vx_px, vy_px, speed_px), in px/frame. Real-world units are not assumed:PX_TO_Mis a clearly-marked TODO to be set once ground-control / orthorectification info exists. - QA figures — detection overlay, mass histogram, trajectory overlay.
ptv_pipeline/
ingest.py # frame loading, channel selection (blue / inv-red), CLAHE
detect.py # tp.locate / tp.batch, mass/size/ecc filtering, glint premask
link.py # tp.link, velocity prediction, filter_stubs
postprocess.py # velocity computation, PX_TO_M scale placeholder, drift removal
qa_plots.py # detection overlay, mass histogram, trajectory overlay
run_pipeline.py # CLI entry point stitching the above together
make_synthetic.py # synthetic test frames (NOT real data) for smoke-testing
conda env create -f environment.yml
conda activate floodplain-flume-ptvOn synthetic test data (verifies the pipeline executes):
python make_synthetic.py --outdir synthetic_frames --n-frames 40 --n-spheres 60
python -m ptv_pipeline.run_pipeline \
--input synthetic_frames/ --diameter 11 --minmass 400 \
--search-range 15 --memory 2 --fps 30 --outdir out/On real footage (once available), the same command with scene-specific tuning:
python -m ptv_pipeline.run_pipeline \
--input footage.mp4 --channel blue --diameter <odd px> \
--minmass <from mass histogram> --separation <min sphere spacing> \
--max-ecc 0.3 --search-range <flow_px_per_frame> --memory 2 \
--predict --fps <frame rate> --outdir out/- Channel — white spheres on warm sand: prefer the blue channel or inverted red over naive RGB→gray to maximise contrast. Compare both.
- diameter — measure a sphere against a scale reference; pass an odd integer.
- minmass — read the valley in the mass histogram (
qa_mass_histogram.png) between glints/ripple-sparkle (low mass) and real spheres (high mass). - search_range — surface velocity × frame interval, in pixels.
- memory — 1–3 frames to bridge glare washout / wave-shadow occlusion; higher risks mislinking in shear.
- max-ecc / size — reject elongated glints and sand glints breaking the surface; real spheres are near-circular.
- Frame rate and approximate surface velocity (sets
search_range) - Camera fixed vs. moving (whether drift removal is needed)
- Ground-control points / known distances for px→m conversion
- Sphere diameter in pixels (calibration frame)
- Whether sun and shade coexist in-frame (whether CLAHE is needed)