region_maps¶
feat.utils.region_maps
¶
Facial AU / ARKit-blendshape region overlays on the MediaPipe-478 mesh.
This is the dense-mesh, non-overlapping successor to the dlib-68 muscle
polygons in feat.plotting.draw_muscles and the overlapping muscle map in
feat.utils.muscle_to_landmark. It ships two maps:
au_region_map.json— the 20 FACS AUs, left+right merged per AU.blendshape_region_map.json— the spatially-distinct ARKit blendshapes, Left/Right kept independent (ARKit pre-splits...Left/...Right).
Both are built by a winner-take-all geodesic-Voronoi partition of the mesh:
each vertex is assigned to the single nearest region seed by walking the mesh
surface (Dijkstra over the triangle-edge graph, capped at TIGHTNESS of the
face span), with the eye/mouth aperture rings walling growth off. The partition
is non-overlapping by construction, which fixes the heavy region overlap of
the geodesic-grow muscle map (where 323/394 covered verts sat in >=2 muscles).
The AU <-> muscle <-> blendshape correspondence is grounded in: * FACS (Ekman & Friesen) — AU -> muscle. * Melinda Ozel's ARKit-to-FACS cheat sheet (melindaozel.com/arkit-to-facs-cheat-sheet). * pooyadeperson's "Ultimate Guide to ARKit's 52 Facial Blendshapes" (anatomy refs).
Rendering smooths the coarse 468-vertex mesh by midpoint-subdividing it
SUBDIV_LEVELS times before filling triangles. The subdivision topology and
the dense per-vertex region labels are fixed in index space, so the same assets
overlay a live detected 478-mesh — only the dense vertex positions are
recomputed per frame (dense_positions). See feat.plotting.plot_face_regions.
build_au_region_map()
¶
{AU: {muscles, mp478_vertices, n_vertices}} — muscle partition merged
to AU (left+right share an AU).
Source code in feat/utils/region_maps.py
build_blendshape_region_map()
¶
{blendshape: {au, muscle, side, mp478_vertices, n_vertices}}.
L/R pairs share one symmetric mesh FEATURE (their seeds merged) grown by the geodesic-Voronoi partition; each sided blendshape is then the half of that feature on its side of the facial midline (center shapes keep the whole, bilateral feature). So L/R divide cleanly down the middle — no winner-take- all asymmetry, no shared midline seam. Non-overlapping by construction.
Source code in feat/utils/region_maps.py
dense_positions(V, parents)
¶
Rebuild subdivided vertex positions for an arbitrary base mesh V
(e.g. a detected 478-mesh) given the parents list from subdivide.
V supplies the original vertices; midpoints are filled by averaging.
Source code in feat/utils/region_maps.py
geodesic_voronoi(seeds_by_region, adj, xy, aperture, max_geo)
¶
Winner-take-all surface partition: assign each vertex to the nearest
seed's region by Dijkstra over the edge graph (euclidean edge weights on
xy), capped at max_geo, never crossing aperture verts.
Returns {vertex_index: region_name}. Non-overlapping by construction —
a vertex is claimed once, by whichever region reaches it first/closest.
Source code in feat/utils/region_maps.py
load_au_region_map()
¶
Bundled non-overlapping AU region map (20 AUs, L+R merged). Each value:
{"muscles": [...], "mp478_vertices": [...], "n_vertices": int}.
load_blendshape_region_map()
¶
Bundled non-overlapping blendshape region map (L/R independent). Each
value: {"au", "muscle", "side", "mp478_vertices", "n_vertices"}.
project_xy(V)
¶
Frontal x/y projection, flipped so the forehead (v10) is above the chin
(v152). V may be the canonical mesh or a detected mesh (N>=153, x/y in
cols 0/1). Used both for geodesic distances and for plotting.
Source code in feat/utils/region_maps.py
render_assets(kind='au')
¶
Cached rendering assets for kind in {"au", "blendshape"}:
{"parents": [(a,b)...], "tris": dense_tris[K,3],
"region_verts": {region: set(dense vert ids)}, "axis": float, "n_base": 468}
Regions are resolved on the SUBDIVIDED canonical mesh (smooth boundaries),
keyed by AU for "au" and by FEATURE (L/R merged) for "blendshape" —
the renderer splits a feature into Left/Right by the midline axis using
triangle centroids. parents/tris/region_verts are fixed in index
space, so a deformed 478-mesh just needs dense_positions(mesh, parents).
Source code in feat/utils/region_maps.py
subdivide(V, tris, aperture, levels)
¶
levels rounds of 1-to-4 midpoint subdivision. Original vertices keep
their indices (so seeds and stored vertex ids stay valid). Returns
(V_dense, tris_dense, aperture_dense, parents) where parents lists,
in creation order, the (a, b) parent pair of each appended midpoint — so
a deformed mesh's dense positions can be rebuilt with dense_positions.
A midpoint inherits aperture status only if BOTH parents are aperture.