// 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]); }