87 lines
3.4 KiB
Markdown
87 lines
3.4 KiB
Markdown
|
|
# roto
|
||
|
|
|
||
|
|
Video → **take file** builder for the Animator Pro rotoscope/puppet pipeline
|
||
|
|
described in `../docs/roto-puppet.md`.
|
||
|
|
|
||
|
|
This is the analysis and tuning half. It stabilises a face out of a clip, reduces
|
||
|
|
the lip contour to a handful of vertices, selects sparse keys on motion extremes,
|
||
|
|
and previews the result as flat indexed fills — so the *look and timing* can be
|
||
|
|
judged in seconds rather than through a minutes-long Animator Pro render. It
|
||
|
|
emits a `.take` file; nothing here touches Animator Pro.
|
||
|
|
|
||
|
|
All policy lives here. The take arrives at the renderer with its keys already
|
||
|
|
chosen.
|
||
|
|
|
||
|
|
## Run
|
||
|
|
|
||
|
|
```sh
|
||
|
|
python3 -m http.server 8777 # from this directory
|
||
|
|
# open http://127.0.0.1:8777
|
||
|
|
```
|
||
|
|
|
||
|
|
**Synthetic take** needs no video and exercises everything below detection.
|
||
|
|
|
||
|
|
For real footage:
|
||
|
|
|
||
|
|
```sh
|
||
|
|
./extract.sh /path/to/clip.mp4 24 # -> frames/0001.png …
|
||
|
|
```
|
||
|
|
|
||
|
|
then **Load frames**. MediaPipe's wasm is fetched from jsdelivr on first use;
|
||
|
|
`face_landmarker.task` is local.
|
||
|
|
|
||
|
|
Frames are pre-extracted rather than decoded in the page because browser video
|
||
|
|
seeking is approximate and `requestVideoFrameCallback` only delivers frames at
|
||
|
|
playback speed — neither gives a deterministic per-frame pass.
|
||
|
|
|
||
|
|
## Shooting for it
|
||
|
|
|
||
|
|
Near-frontal, good light, consistent scale, head reasonably still. Hold a neutral
|
||
|
|
closed mouth for a second at the top of the take: that frame is picked
|
||
|
|
automatically as the neutral and drives calibration and the placeholder plate.
|
||
|
|
|
||
|
|
Out-of-plane head rotation cannot be stabilised away by a 2D similarity
|
||
|
|
transform — the `residual` readout rises when it happens. The pipeline's answer
|
||
|
|
is hand-drawn head plates, which this tool does not yet do.
|
||
|
|
|
||
|
|
## Knobs
|
||
|
|
|
||
|
|
| Knob | What it does |
|
||
|
|
| --- | --- |
|
||
|
|
| vertices | Lip vertex budget. The reduction past what the footage supports *is* the style. |
|
||
|
|
| min hold | Minimum frames between keys. |
|
||
|
|
| change gate | Mean vertex movement required before a new key is accepted. |
|
||
|
|
| vel smoothing | Window on the velocity signal used to find extremes. |
|
||
|
|
| anchor smoothing | Window on the four similarity parameters. Smooths the *transform*, never the contour. |
|
||
|
|
| exposure | Grid that key frames snap onto. |
|
||
|
|
| closed-mouth cut | Aperture below which the mouth interior is emitted as `hidden`. |
|
||
|
|
|
||
|
|
Keys go on **velocity minima**, not distance thresholds: a threshold fires at the
|
||
|
|
frame it was crossed — partway through a transition — so poses land mushy and
|
||
|
|
late. The timeline shows the velocity curve, candidate minima, accepted keys
|
||
|
|
(`f`) and their pre-snap extremes (`src`); a large `f`/`src` gap means min-hold
|
||
|
|
and exposure are fighting.
|
||
|
|
|
||
|
|
## Tests
|
||
|
|
|
||
|
|
```sh
|
||
|
|
chromium --headless --virtual-time-budget=8000 --dump-dom \
|
||
|
|
http://127.0.0.1:8777/selftest.html | grep -oE '(PASS|FAIL) [0-9/]+'
|
||
|
|
```
|
||
|
|
|
||
|
|
Or open `selftest.html`. 29 assertions over the stages below detection.
|
||
|
|
|
||
|
|
The ring-simplicity check is the load-bearing one. Because `hold` parts *cut*
|
||
|
|
between poses instead of interpolating, a ring whose vertex order is wrong
|
||
|
|
self-intersects and renders as blocks meeting at corners — and it is invisible at
|
||
|
|
odd `verts/2` and obvious at even, so it needs an assertion rather than an
|
||
|
|
eyeball.
|
||
|
|
|
||
|
|
## Not done yet
|
||
|
|
|
||
|
|
Eyes and irises; hand-drawn head plates and per-plate mouth slots; real
|
||
|
|
performer→character calibration (currently identity, fitting the face oval to the
|
||
|
|
canvas); the override layer; anything on the Animator Pro side. The placeholder
|
||
|
|
plate is a frozen face-oval polygon — it exists so the mouth has a face to read
|
||
|
|
against, not to look good.
|