Second garment-only render pass per view at the identical paused animation time; the analyzer intersects so body-key pixels split into exposed_skin (no garment behind — informational: collars, sleeveless arms) vs clip_through (garment behind — gating). Highlights differ: lime exposed, red clip. Peasant re-run: 72 captures, 56 clip-through flags — real collar micro-clips under crouch/walk plus suspected 1px boundary artifacts; gate threshold + garment-mask dilation are the tuning knobs, to be calibrated against the first real modern garments. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
39 lines
2.4 KiB
Markdown
39 lines
2.4 KiB
Markdown
# garment-qa — chromakey garment-clipping QA (T-1089)
|
||
|
||
Automatically detects clothing clip-through with a **two-pass depth-proximity**
|
||
chromakey. Per view (clips × sampled frames × 4 camera yaws), the capture scene renders
|
||
the SAME frozen animation frame twice: **pass A** paints the body segments a garment
|
||
*claims to cover* (coverage.json `hides`) flat **unshaded magenta**, garment + head/hands
|
||
normal; **pass B** is identical except the garment is flat **cyan** and every garment
|
||
vertex is nudged `clip_epsilon_m` (default 3 cm) **toward the camera**. A body-key pixel
|
||
that is magenta in A but cyan in B means the ε-shifted cloth now covers it — the body sat
|
||
within ε *in front of* the cloth = poking through. This separates a true clip from a limb
|
||
merely crossing in front of the torso, an open collar, or a bare arm over background
|
||
(all of which stay magenta because the cloth behind them is > ε away).
|
||
|
||
**Run:** `tooling/garment-qa/run-garment-qa [config.json]` (defaults to
|
||
`configs/peasant.json`). It launches Godot (`~/bin/godot4`, `opengl3`, `xvfb-run` when
|
||
headless) on the capture scene, then runs the analyzer.
|
||
|
||
**Config** (path passed to the scene via `GARMENT_QA_CONFIG`): `garments`
|
||
(`{item_id,slot}` catalogue items, or `{glb,covers[]}` for a raw WIP garment),
|
||
`body_types`, `clips`, `frames_per_clip`, `yaws`, `head_id/hair_id/eyebrow_id/skin_tone`,
|
||
`out_dir`.
|
||
|
||
**Read the report:** `<out_dir>/report.json` — the same summary prints to stdout. Two
|
||
metrics per capture:
|
||
- **`clip_through_pixels`** / `largest_clip_component` — key pixels the ε-shifted cloth
|
||
covers (skin ≤ ε in front of cloth). **This is the real defect and it gates:** a capture
|
||
`fail`s when its largest connected clip blob ≥ `--min-pixels` (default 8, tolerating AA
|
||
edges). `clip_through_failures` + the `by_group` `clipfail`/`worstClip` columns roll it up.
|
||
- **`exposed_skin_pixels`** — key pixels the shift did NOT cover (skin well in front of any
|
||
cloth: collar/sleeve/ankle gaps, a limb crossing the torso). Informational; does not gate.
|
||
|
||
Tune sensitivity with `clip_epsilon_m` in the config (smaller = only tighter pokes count)
|
||
and `--min-pixels` on the analyzer. Failing/flagged frames are copied to
|
||
`<out_dir>/failures/*_HL.png` with **exposed_skin lime**, **clip_through filled red**, and
|
||
each clip blob boxed.
|
||
|
||
**Note:** the Godot scene lives at `client/tools/garment_qa/chromakey_scene.gd` (not here)
|
||
because Godot `res://` paths cannot leave the client project root.
|