No description
Find a file
Your Name a082208ad5 Frame removal for plates; mouth keeps every frame
Sparseness was being applied for two different reasons at once. Aesthetic
sparseness is set by the extraction rate; labour sparseness only binds on the
plate, because a human draws each one. The mouth is traced and therefore free,
and in limited animation lip sync is routinely the densest element - on 1s
while heads hold on 2s and 3s.

So: the mouth gets a key on every frame, and the frame strip is now the
editing surface for deciding which frames need their own plate drawing. All
frames start kept; delete the ones you don't want.

- strip of face-cropped thumbnails, keep/drop per frame, keyboard driven
- worksheet panel lists the drawings needed and the range each one holds
- Suggest runs error-tolerance decimation on head pose as a starting point
- export writes sparse plate keys + dense mouth keys, with a hold manifest
- smoothContours: bounded exception to "never smooth the contour", which held
  only while keys were sparse enough to reject detector noise by sampling
- averages are now a RADIUS in frames: 0 is off, 1 is +-1
- GPU delegate falls back to CPU instead of failing
- #synth / #frames autorun for headless smoke tests

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-24 14:51:15 -04:00
js Frame removal for plates; mouth keeps every frame 2026-09-24 14:51:15 -04:00
.gitignore roto: video -> take file builder with interactive tuning 2026-09-24 14:38:07 -04:00
extract.sh roto: video -> take file builder with interactive tuning 2026-09-24 14:38:07 -04:00
gl.html Frame removal for plates; mouth keeps every frame 2026-09-24 14:51:15 -04:00
index.html Frame removal for plates; mouth keeps every frame 2026-09-24 14:51:15 -04:00
probe.html Frame removal for plates; mouth keeps every frame 2026-09-24 14:51:15 -04:00
README.md roto: video -> take file builder with interactive tuning 2026-09-24 14:38:07 -04:00
selftest.html roto: video -> take file builder with interactive tuning 2026-09-24 14:38:07 -04:00

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

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:

./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

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.