mocapgod/docs/B_ENGINE_SETUP.md
type-two 35403f8f05 Lane B: tick Lane B done, session-5 completion summary + Lane C handoff
PROGRESS.md: Lane B checked off with the session-5 outcome; SESSION 5 DONE block
(all 6 orders) + explicit Lane C handoff (BVH axes, skel_to_mixamorig map, camera
root_space, --from-params). B_ENGINE_SETUP.md: pose_engine.py usage + the engine
facts learned (SKEL 46-q, rest-aware FK ~4cm, Y-down camera fix, mobilenet detector).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 10:42:28 +10:00

88 lines
6.1 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Lane B engine — HSMR install & smoke-test state (session 3, 2026-07-17)
Engine per Decision 1 = **Path B: HSMR → SKEL**, run local on **ultra**, MPS. Installed under
`.engine/HSMR` (gitignored). This doc = how it's wired + how to run once SKEL lands.
## State
-`uv venv` py3.10 at `.engine/HSMR/.venv`; torch **2.13.0**, `mps.is_available() == True`.
- ✅ Ungated weights in `.engine/HSMR/data_inputs/`: HSMR-ViTH ckpt
(`released_models/HSMR-ViTH-r1d1/checkpoints/hsmr.ckpt`), ViTPose backbone, 3 regressors.
-**Real person detector** (torchvision Faster R-CNN, no detectron2) — checked by
`.engine/HSMR/test_detector.py`, green **today with no SKEL**: 7/7 demo images → 15 patches
@256×256 through HSMR's own cropper.
- ✅ Smoke-test runner `.engine/HSMR/smoke_test.py`**PASSED 2026-07-17** (session 5): SKEL
v1.1.1 in place, device=mps, 263 frames → 1315 patches, recovery 31.2 fps, **nan=False**,
`data_outputs/smoke/params.npz` written (poses (N,46), betas (N,10), cam_t (N,3)).
- ✅ Install script (re-runnable): **`./setup_hsmr.sh`** at the repo root (tracked in git).
`./setup_hsmr.sh --patch` re-applies the ARM/headless fixes onto an existing/re-cloned HSMR.
-**The ARM fixes are now durable in git** (session 5, was: "on ultra's disk only"). Canonical
copies live in **`engine_patches/`** (`_headless.py`, `test_detector.py`, `smoke_test.py`,
`lib/modeling/pipelines/vitdet/__init__.py`); `setup_hsmr.sh` overlays them onto the gitignored
`.engine/HSMR` clone. Verified: fresh clone + `--patch``test_detector.py` green, no hand edits.
## Apple-Silicon blockers — all three resolved (session 4, "Lane B detector")
- **detectron2 won't build on ARM Mac** → **RESOLVED.** Replaced its whole ViTDet detector with
a torchvision Faster R-CNN drop-in in `lib/modeling/pipelines/vitdet/__init__.py` — same
`build_detector(...) -> (dets, downsample_ratios)` contract, torchvision's person class (1)
remapped to HSMR's human id (0), boxes as lurb, ratio 1.0. This also killed a **load-time
landmine**: the old `import detectron2` there exploded anything importing `lib.kits.hsmr_demo`
(why session 3 had to stub it). Detector pinned to **CPU** — cheap next to the ViT-H pass and
it dodges MPS RoIAlign/NMS gaps; an in-file `# ponytail` note marks the mobilenet swap if
per-frame detection ever dominates on long videos.
- **pyrender is dead headless on macOS** (`py_renderer` forces `PYOPENGL_PLATFORM=egl`, no EGL on
Mac; `pyrender` is even referenced in a type annotation, so a plain import-guard won't do).
Centralized the permissive `pyrender`/`OpenGL` fake into **`.engine/HSMR/_headless.py`** — every
entrypoint does `import _headless` before any `lib.*` import. QC still happens in Blender (fleet
renderer + Lane C's tool); the runner dumps `params.npz` (SKEL poses/betas/cam_t — the Path-B
payload) and `mesh_qc.npz` (verts/faces for Blender).
- **chumpy failed to build** → **NOT a blocker (verified).** The runtime `SKELWrapper` loads its
pkls with plain `pickle.load(encoding='latin1')` — no chumpy import. chumpy only appears in
SKEL's offline model-*build* script (`merge_smplh_mano.py`), which we never run. Ignore the WARN.
## John's one step — drop the SKEL files (login + licence, so it's yours to do)
Register **skel.is.tue.mpg.de** → Download → **"SKEL and BSM models"** → `skel_models_v1.1.zip`
(**must be v1.1.1** — check `changelog_v1.1.1.txt` inside; older = silent incompatibility).
Unzip, then:
```
mv /path/to/skel_models_v1.1 ~/Documents/MOCAPGOD/.engine/HSMR/data_inputs/body_models/skel
```
so that `…/data_inputs/body_models/skel/skel_male.pkl` exists.
## Run the smoke test (once SKEL is in place)
```
cd ~/Documents/MOCAPGOD/.engine/HSMR && source .venv/bin/activate
PYTHONPATH=. python smoke_test.py -i data_inputs/demo/example_videos/gymnasts.mp4 # or .../example_imgs
```
Outputs → `data_outputs/smoke/`: `params.npz` (rotations), `mesh_qc.npz` (Blender QC), and
console device/fps/NaN. Answers the Lane B smoke questions: **MPS forward OK? timing (<2×
realtime)? pose sane?**
HSMR is a **per-frame** recoverer root is **camera-space `cam_t`, not world-grounded**.
Fine for in-place DJ moves; locomotion (walk) needs Lane C's foot-contact root derivation or a
grounding pass. This is the treaty's global-trajectory caveat, carried into Path B.
## Run the pose engine (B2/B3, session 5) — the real service
`pose_engine.py` (repo root, tracked) wraps the engine into the pipeline. Run under the engine venv:
```
cd ~/Documents/MOCAPGOD
.engine/HSMR/.venv/bin/python pose_engine.py # drain queue/ → out/<name>/
.engine/HSMR/.venv/bin/python pose_engine.py queue/clip.mp4 # one clip
.engine/HSMR/.venv/bin/python pose_engine.py <clip> --from-params # re-run SKEL→BVH, skip HMR
.engine/HSMR/.venv/bin/python pose_engine.py --selfcheck # BVH-math self-test, no engine
```
Per clip `out/<name>/`: `motion.bvh` (SKEL-24, **Y-up, metres, faces Z**), `motion.json`
(sidecar, `root_space:"camera"`), `params.npz` (raw poses/betas/cam_t for QC / `--from-params`).
Heartbeat: `~/.jobs/pose_engine`. Models build **once** per invocation (queue amortizes); a bad
clip is logged and skipped, not fatal.
Key engine facts learned this session (for whoever touches B again):
- **SKEL emits `poses` = (F, 46)** biomechanical q, NOT SMPL's 72 axis-angles. Don't decode q by
hand run `SKEL.forward()` and consume `joints_ori` (F,24,3,3 global orientations) + `joints_tpose`.
- BVH is rigid-bone: rest-aware FK (`offset=rest_J[j]-rest_J[parent]`, `W=Rg@rest_R^T`,
`local=W[parent]^T@W[j]`) reconstructs SKEL joints to ~4 cm; the residual is SKEL's coupled
knee/ankle translations, which a rotation-only BVH cannot carry (accept it).
- **HSMR camera frame is Y-DOWN** (OpenCV; verified head.y < pelvis.y). `pose_engine` applies
`diag(1,-1,-1)` to reach the treaty's Y-up / faces-Z. Undo/adjust there if a future model differs.
- Detector is **mobilenet** now (was R50) CPU detection was the wall-clock hog. It self-caps input
to ~640 px internally, so pre-downscaling does nothing; `--detect-every` is the real throughput knob.