# Capturing real data Everything measured in this project so far comes from one or two photographs. That is its largest weakness: the identity benchmark takes its gallery and its queries from the same photo, so every accuracy number is an in-distribution number. This is the procedure for replacing that with data from a real store, recorded on the device that will do the scanning. ## 1. Record a walk From `recognization`, run `./run-demo-server.sh stock-lopia` once to provision the ignored `config/local.env` token and the native apps' local LAN settings, then stop that server. Rebuild the app after provisioning. The token is a secret: keep `.demo-token`, `config/local.env`, and app-local settings out of commits and shared logs. For a recorded walk, start the same gallery backend on the launcher's port; `code/app.py` reads the token from `config/local.env`: ```bash cd product-recognition-demo/recognization # from the app repository root YOLO_OFFLINE=1 .venv/bin/python code/app.py \ --port 8099 --host 0.0.0.0 --stock-dir stock-lopia --identity gallery \ --live-conf 0.15 --classify-workers 8 \ --record-frames ~/dorayaki-data/storeA-visit1 \ --record-every 3 ``` This requires the local pinned DINOv2 file described in `../README.md` (`weights/dinov2_vits14_lvd142m.safetensors`, or `DORAYAKI_DINOV2_WEIGHTS`). A LAN bind without a token is refused. Record the second visit to a separate directory with the same catalog and model. Then run the app and scan. Each scan session becomes its own directory: ``` ~/dorayaki-data/storeA-visit1// 000000.jpg the frame, exactly as the client sent it 000001.jpg index.jsonl one line per recorded frame: {"frame": 123, "path": "000001.jpg", "at": 1699..., "summary": {...}, "tracks": [{"box": [...], "identity": {...}, "tag_identity": {...}, "tag": {...}}]} ``` Frames are sampled, not continuous, and the index keeps each frame's own number so the sampling is visible rather than silent. `/v1/health` reports how many frames have been written. ## 2. What to capture, and why Two visits to the same shelf are worth more than one long visit, because the benchmark needs a **cross-visit split**: the gallery comes from visit 1 and the test set from visit 2. That is the difference between "it works on this photo" and "it works on this shelf". Per shelf, capture: - a **slow walk along each rail** at working distance (about 1 m) — the live loop's real input; - a **close pass** (about 30 cm) over a few tags — the only distance where the barcode is decodable (measured: 0.63 px per module at 1 m needs ~3× more pixels); - the same shelf again **on a second visit**, ideally with something changed: a gap, a moved facing, a new product. ## 3. Turn a recording into labels The tag channel seeds the labels. A facing whose tag named a product, with a price the tag's own arithmetic corroborates, is a candidate label rather than a guess: ```bash python -c ' import json, sys sys.path.insert(0, "code") from record import labelling_candidates seeds = labelling_candidates(sys.argv[1]) print(len(seeds), "candidate labels") print(json.dumps(seeds[:3], ensure_ascii=False, indent=1)) ' "$HOME/dorayaki-data/storeA-visit1/REPLACE_WITH_SESSION/index.jsonl" ``` A human then confirms or corrects them in the app's confirmation screen — the same screen that closes the flywheel. Nothing here is a training set on its own; it is the raw material for one. ## 4. Replay a cross-visit benchmark Enroll the gallery from visit 1 only, then freeze that catalog. Confirm labels for visit 2 independently; `labels.json` beside its `index.jsonl` (or supplied with `--labels`) is a JSON list, or `{"labels": [...]}`. Each entry has `frame` (the number in `index.jsonl`), `box` (pixel coordinates `[x1, y1, x2, y2]`), and `sku_id` (a catalog SKU). These are confirmed annotations, not the server's predictions. For the second visit, run from `recognization` with the same frozen `stock-lopia` catalog: ```bash SESSION="$HOME/dorayaki-data/storeA-visit2/REPLACE_WITH_SESSION" YOLO_OFFLINE=1 .venv/bin/python code/eval_live.py "$SESSION" \ --stock-dir stock-lopia --labels "$SESSION/labels.json" \ --replay --identity gallery --device cpu ``` Replace `REPLACE_WITH_SESSION` with the actual session name. The evaluator replays recorded JPEGs through `LiveSession`, writes `eval_live.json` in that directory, and reports backend, loaded head, weight/catalog fingerprints, decision counts, and held-out labelled top-1 when valid labels exist. It rejects missing or unpinned DINOv2 weights rather than substituting matcher scores; labels copied from catalog templates are excluded as leakage. Without confirmed held-out visit-2 labels, counts are diagnostic only, **not gallery walk accuracy**. No held-out cross-visit result is available here yet.