Registered photo underlay as the plate reference

The generated face oval was never going to be good enough to draw from:
MediaPipe's face oval is the FACE boundary, cut at the hairline and excluding
hair, ears, jaw and neck, so it is an egg by construction. Segmentation would
give a real head outline but costs a 16MB model and per-frame inference for a
shape that gets replaced by a drawing anyway.

So the plate layer becomes switchable, and the useful modes are photographic:
the source frame mapped into raster space through the same transform chain the
contours go through. Registration is the whole point - the head sits still and
a drawing traced from the underlay is already aligned to the mouth. An
unregistered underlay would be decoration.

- underlay.js: pixel->raster affine (a general affine, since MediaPipe
  normalises x by width and y by height), registered draw, palette posterise
- plate modes: photo / photo dim / posterized / oval / oval+photo / none, B cycles
- worksheet cells are registered composites rather than raw crops
- Save frame 4x writes a 1280x800 PNG to draw on
- selftest: FACE_OVAL simplicity, which was never asserted; a wrong ordering
  there reads as a lumpy plate rather than an obvious bowtie

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Your Name 2026-09-24 14:57:59 -04:00
parent 5ac919b9d6
commit de90bfd907
6 changed files with 191 additions and 20 deletions

View file

@ -68,6 +68,15 @@
<button id="btn-keepall">Keep all</button>
<button id="btn-suggest">Suggest</button>
<input type="text" id="takename" value="line_01" size="9" title="take name">
<select id="plateMode" title="plate representation (B to cycle)">
<option value="photo-dim">photo dim</option>
<option value="photo">photo</option>
<option value="posterize">posterized</option>
<option value="oval" selected>oval</option>
<option value="oval+photo">oval + photo</option>
<option value="off">none</option>
</select>
<button id="btn-saveframe">Save frame 4x</button>
<button id="btn-export">Export .take</button>
<span id="status"></span>
</header>
@ -88,7 +97,9 @@
<h2>flat render — 320×200 indexed</h2>
<canvas id="cv-render"></canvas>
<div class="legend" id="framelabel"></div>
<div class="legend">audio drives the clock — dropped frames, never drift</div>
<div class="legend">audio drives the clock — dropped frames, never drift<br>
plate representation: <kbd>B</kbd> cycles · photo modes are <b>registered</b>
into raster space, so tracing them lands on the mouth</div>
</div>
</div>
@ -97,7 +108,7 @@
<div id="strip"></div>
<input type="range" id="scrub" min="0" max="0" value="0" style="width:100%;margin-top:10px">
<div class="legend">
<kbd>←</kbd> <kbd>→</kbd> step · <kbd>X</kbd> delete · <kbd>K</kbd> keep ·
<kbd>←</kbd> <kbd>→</kbd> step · <kbd>X</kbd> delete · <kbd>K</kbd> keep · <kbd>B</kbd> background ·
click to select · double-click or shift-click to toggle ·
the mouth keeps <b>every</b> frame regardless
</div>
@ -131,7 +142,7 @@
</div>
<div class="panel">
<h2>drawings needed — one per kept frame, with the range it holds</h2>
<h2>drawings needed — registered reference per kept frame, with the range it holds</h2>
<div id="sheet"></div>
</div>
</main>