arthur/js/landmarks.js

55 lines
2.3 KiB
JavaScript
Raw Normal View History

// MediaPipe FaceLandmarker index tables.
// Ring arrays are ORDERED traversals, not raw connection sets: vertex position
// within a ring is the vertex's identity, and every downstream stage depends on
// that ordering being stable. See docs/roto-puppet.md, "Fixed topology".
// Rigid landmarks for the similarity fit. Eye corners, nose bridge, nose tip.
// Nothing here may be a feature that moves under performance: including the
// mouth or brows bleeds performance into the stabilization.
export const RIGID = [33, 133, 362, 263, 168, 6, 1];
// Outer lip ring, clockwise from the right corner over the top.
// index 0 = right corner, 5 = top centre, 10 = left corner, 15 = bottom centre.
export const LIPS_OUTER = [
61, 185, 40, 39, 37, 0, 267, 269, 270, 409,
291, 375, 321, 405, 314, 17, 84, 181, 91, 146,
];
// Inner lip ring, same orientation and the same four cardinal positions.
export const LIPS_INNER = [
78, 191, 80, 81, 82, 13, 312, 311, 310, 415,
308, 324, 318, 402, 317, 14, 87, 178, 88, 95,
];
// Inner upper / lower lip centres. Their separation is the aperture signal that
// decides whether the mouth interior is present at all.
export const APERTURE = [13, 14];
// Face oval, used only to derive the placeholder plate in v1.
export const FACE_OVAL = [
10, 338, 297, 332, 284, 251, 389, 356, 454, 323, 361, 288,
397, 365, 379, 378, 400, 377, 152, 148, 176, 149, 150, 136,
172, 58, 132, 93, 234, 127, 162, 21, 54, 103, 67, 109,
];
// Eye corners, for the calibration box and for reporting fit residual.
export const EYE_INNER = [133, 362];
// Pick `n` slots from a ring of `len` by even spacing. Returns RING POSITIONS,
// not landmark ids: positions are the vertex identity downstream, and mapping ids
// back to positions with indexOf would silently pick the wrong slot if a table
// ever repeated an id.
//
// For even n this naturally lands on the cardinal positions (corners and lip
// centres) of a 20-point ring. Fixed indices, never adaptive decimation: the
// vertex at slot k means the same thing on every frame of the shot.
export function subsampleSlots(len, n) {
const out = [];
for (let k = 0; k < n; k++) out.push(Math.round((k * len) / n) % len);
return out;
}
export function subsampleRing(ring, n) {
return subsampleSlots(ring.length, n).map((s) => ring[s]);
}