Add barrier_picker_app — Dockerised web picker for barrier opening

A FastAPI app + plain HTML5 video page that replaces the matplotlib
picker. Browse to http://host:8000/, scrub through each video with
arrow keys (±5 s, ±1 s with Shift, ±0.1 s with Ctrl, ±1 frame with
,/.), and click one of three buttons:
  - All barriers open      — every ROI usable
  - Upper barrier opens    — ROIs 1,3,5 usable; lower row marked bad
  - Lower barrier opens    — ROIs 2,4,6 usable; upper row marked bad

The current playhead time is recorded as opening_s; bad_rois is set
accordingly. Also keyboard shortcuts (1/2/3 for the three modes,
s/u for skip/unusable). Refresh-safe: every submission persists to
data/metadata/barrier_opening.csv before advancing.

Server uses byte-range streaming so seeking inside long videos is
fast. Dockerfile + docker-compose.yml mount the data volume RO and
the metadata folder RW.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Giorgio Gilestro 2026-05-01 12:33:28 +01:00
parent 24403e0474
commit 1a7542def2
7 changed files with 611 additions and 5 deletions

View file

@ -0,0 +1,70 @@
# Cupido — web-based barrier-opening picker
A small FastAPI + HTML5-video app for annotating the barrier-opening
moment in each tracked recording. Lives in its own Docker container so
it can run on the lab server without polluting any existing
environment.
## What it does
For every video referenced by `all_video_info_merged.tsv` that has a
tracking DB on disk and isn't yet in `barrier_opening.csv`, it serves
a `<video>` element pre-loaded with that mp4. The analyst plays /
scrubs / pauses at the moment the barrier opens, then clicks one of:
| button | meaning | written to `bad_rois` |
|---|---|---|
| **All barriers open** | every ROI (1..6) is usable post-opening | _empty_ |
| **Upper barriers open** | only the top row opens (ROIs 1, 3, 5) | `2,4,6` |
| **Lower barriers open** | only the bottom row opens (ROIs 2, 4, 6) | `1,3,5` |
Plus a **Skip** (advance without saving) and **Unusable** (write
`opening_s = NaN`).
## Keyboard shortcuts
- <kbd>Space</kbd> — play / pause
- <kbd></kbd> / <kbd></kbd> — ±5 s
- <kbd>Shift</kbd>+arrows — ±1 s
- <kbd>Ctrl</kbd>+arrows — ±0.1 s
- <kbd>,</kbd> / <kbd>.</kbd> — ±1 frame
- <kbd>1</kbd> / <kbd>2</kbd> / <kbd>3</kbd> — All / Upper / Lower
- <kbd>s</kbd> — skip ; <kbd>u</kbd> — unusable
## Run locally (docker compose)
```bash
cd scripts/barrier_picker_app
docker compose up --build
```
Then browse to http://localhost:8000/.
The container mounts:
- `/mnt/data/projects/cupido` (data volume, read-only)
- `/mnt/ethoscope_data/videos` (source mp4s, read-only)
- `data/metadata/` from the repo (read-write — for persisting
`barrier_opening.csv`)
Adjust paths in `docker-compose.yml` if your layout differs.
## Run without Docker (development)
```bash
cd scripts/barrier_picker_app
pip install -r requirements.txt
python app.py # serves on http://localhost:8000
```
By default it expects:
- merged TSV at `/mnt/data/projects/cupido/all_video_info_merged.tsv`
- inventory at `/cupido/data/metadata/video_inventory.csv`
- writes to `/cupido/data/metadata/barrier_opening.csv`
Override via environment variables:
```bash
CUPIDO_DATA_VOLUME=/path/to/data \
CUPIDO_INVENTORY_CSV=$(pwd)/../../data/metadata/video_inventory.csv \
CUPIDO_OUTPUT_CSV=$(pwd)/../../data/metadata/barrier_opening.csv \
python app.py
```