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

@ -80,6 +80,17 @@ export function run() {
ok(`${label} ring is simple at every vertex budget`, !worst, worst || '');
}
// FACE_OVAL traversal: never checked before, and a wrong ordering here shows up
// as a lumpy plate rather than an obvious bowtie, so it needs asserting.
{
let bad = null;
for (let f = 0; f < dense.length && !bad; f++) {
const h = ringSelfIntersections(FACE_OVAL.map((i) => dense[f][i]));
if (h.length) bad = `frame ${f} edges ${JSON.stringify(h[0])}`;
}
ok('FACE_OVAL is a simple ring on every frame', !bad, bad || '');
}
// similarity fit recovers a known transform
const src = [{ x: 0, y: 0 }, { x: 1, y: 0 }, { x: 0, y: 1 }, { x: 2, y: 3 }];
const truth = { s: 1.7, theta: 0.6, tx: 4, ty: -2 };