Teeth as an extracted blob contour, not a clipped band
The band filled the mouth because a band is the wrong reduction: the bright region is a blob, and reading it as "everything above a line" throws the shape away. Extracting a contour reintroduces the vertex-correspondence problem that made me avoid it, but for a blob there is a way out. Radial sampling from the centroid along N fixed directions makes vertex k always mean "the extent in direction k": correspondence holds by construction, the count is fixed, and temporal smoothing cannot reorder anything. It also yields a star-shaped reduction, which suits flat colour. Tongue rejection, which the band had no way to express: - pixels red relative to their own brightness are dropped (teeth are neutral) - component choice is biased toward the top of the cavity, since area alone picks the tongue when the mouth is wide - separate inner and outer controls: cavity erode pulls the sampled region off the lip edge, blob grow/erode resizes the found blob Also: a knob wired in app.js but missing from index.html threw during wiring and left a blank page with nothing useful in the console - which is exactly what happened to teethDwell in the previous commit. el() now names the missing id, and window.onerror surfaces it in the status line. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
c6daf827d1
commit
9a11eeabc1
7 changed files with 272 additions and 275 deletions
40
README.md
40
README.md
|
|
@ -65,25 +65,33 @@ is hand-drawn head plates, which this tool does not yet do.
|
|||
## Teeth
|
||||
|
||||
MediaPipe has no landmarks inside the lips: the inner ring bounds the cavity and
|
||||
everything within it is just pixels. So teeth come from the image — but tracing
|
||||
the bright blob would produce a new contour every frame with no vertex
|
||||
correspondence, which is precisely the boil the design exists to avoid.
|
||||
everything within it is just pixels. So teeth come from the image.
|
||||
|
||||
So the extraction yields a **scalar, not a shape**. The teeth polygon is the inner
|
||||
lip ring clipped to a horizontal line, and only that line's height is measured
|
||||
(Otsu threshold within the cavity, scanned from the top). The silhouette is
|
||||
therefore always the mouth's own shape — stable by construction — and the only
|
||||
per-frame variable is one number, which smooths trivially. It is also how the
|
||||
shape gets drawn by hand: a band bounded by the lip.
|
||||
The hazard is vertex correspondence — a traced contour reorders between frames
|
||||
and boils. The way out for a blob specifically is **radial sampling**: march
|
||||
outward from the blob's centroid along N fixed directions and take the last pixel
|
||||
inside. Vertex *k* is then always "the extent in direction *k*", so correspondence
|
||||
holds by construction, the vertex count is fixed, and temporal smoothing is well
|
||||
defined with no reordering possible. It also produces a star-shaped reduction,
|
||||
which is what flat blocks of colour want.
|
||||
|
||||
Presence uses hysteresis plus a minimum dwell, the same treatment plate selection
|
||||
gets, because a teeth block that blinks on and off for single frames is worse
|
||||
than one that is simply absent.
|
||||
The **teeth measurement** panel shows exactly what is sampled: region dimmed,
|
||||
kept pixels green, extracted contour amber. Tune against that, not the numbers.
|
||||
|
||||
**Tongue** would work the same way — a shape filling the lower cavity, gated on a
|
||||
redness rather than a brightness statistic. Not implemented, because it is not
|
||||
visible in the test footage: the cavity reads as dark with a bright upper-teeth
|
||||
band and nothing else.
|
||||
| Knob | What it does |
|
||||
| --- | --- |
|
||||
| teeth contrast | Gate on the separation between the cavity's dark and bright class means. Otsu always returns *some* threshold, so this is what stops it inventing teeth in a dark mouth. |
|
||||
| cavity erode | Pulls the sampled region in from the lip edge — MediaPipe's inner lip landmarks sit slightly outside the real opening, and lips are bright. |
|
||||
| blob grow/erode | Resizes the found blob. An open pass always runs first to despeckle. |
|
||||
| tongue reject | Drops pixels red relative to their own brightness. Teeth are near-neutral; tongue is not. |
|
||||
| prefer upper | Biases component choice toward the top of the cavity. Area alone picks the tongue when the mouth is wide. |
|
||||
| teeth vertices | Radial sample count. |
|
||||
| teeth avg ±f | Temporal average over the contour. |
|
||||
| teeth dwell | Frames a presence change must persist before it takes effect. |
|
||||
|
||||
**Tongue** as its own part would work the same way, gated on redness instead of
|
||||
brightness and biased low rather than high. Not implemented: it is not visible in
|
||||
the test footage, which reads as a dark cavity with a bright upper-teeth band.
|
||||
|
||||
## The plate is reference, not art
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue