Counts deadlift reps from a side-on clip, times each pull off the barbell plate, and calls each rep's back straight or rounded, with the probability live beside the video. Three models, one VLM Run Gateway, no weights to download:
-
Get an API key at app.vlm.run/sign-in.
-
Set it in a .envat the repo root:cp ../.env.example ../.env # paste your key after VLMRUN_API_KEY=
-
Create the conda environment: conda env create -f environment.yml conda activate deadlift
-
Add side-on deadlift clips to data/input/. Film side-on, with the whole lifter and the near plate in shot.
-
Run it from this directory: python main.py # every clip in data/input/ python main.py data/input/my_lift.mov # just this one python main.py --li "Your Name" --x @yourhandle # with your credit in the corner The settings that change from run to run are flags, and each overrides its config.pyvalue for that run only:python main.py -hlists them. Everything else stays inconfig.py.
run.json in each output folder records what the run cost, per model; the back
reads are most of it.
- Bar height and reps. SAM 3.1 tracks the plate, and its own size is the
ruler, so height comes out in meters. This assumes an Olympic plate, 45 cm
across (PLATE_DIAMETER_CM). A rep is counted with the same hysteresis as chin_ups.
- Hip hinge. The shoulder–hip–knee angle from ViTPose, drawn on the lifter.
It cross-checks the plate's reps and takes over if the plate track fails
(REP_SOURCE).
- Back position. Every frame, cropped to the lifter, is one System One
choicequestion. The answer comes back as exactly two probabilities,{"straight": 0.05, "rounded": 0.95}, with no text to parse. The question asks about the spine's shape, not how far the torso leans, since a flat back leaning over the bar otherwise reads as rounded.
- Each rep's verdict is the mean P(rounded) over its first pull, from the bar leaving the floor to about the knee, where rounding shows.
- HDR clips. The displayed video is tone-mapped (TONEMAP,autoby default). The models see that tone-mapped video only whenTONEMAP_INFERENCEis on; otherwise they see the plain conversion. Tone-mapping needs an ffmpeg withlibplacebo, which the conda environment has; without it the run warns and carries on untoned. On macOS, libplacebo also needs a Vulkan driver (conda install -c conda-forge moltenvk); without it an HDR conversion fails.
Every setting, prompt included, is in config.py.
One timestamped directory per run under data/output/:
20260929-134948/
├── <clip>_deadlift.mp4 # overlay + live panel
├── timeline.png # each rep's timed pull on bar height, hip angle, P(rounded)
├── reps.json # per-rep timings and verdicts
├── reads.json # every System One read
├── summary.txt # the rep report
└── run.json # config, provenance, cost
Gateway replies are cached in data/cache/, so a re-render costs nothing, and
data/ is gitignored.