Skip to content

Add emotion_to_mesh and bs_to_mesh PLS visualization models - #369

Open
ljchang wants to merge 4 commits into
mainfrom
feat/emotion-bs-to-mesh-pls
Open

Add emotion_to_mesh and bs_to_mesh PLS visualization models#369
ljchang wants to merge 4 commits into
mainfrom
feat/emotion-bs-to-mesh-pls

Conversation

@ljchang

@ljchang ljchang commented Jun 27, 2026

Copy link
Copy Markdown
Member

Summary

Adds emotion → mesh and blendshape → mesh PLS visualization models as companions to the existing au_to_mesh model. They predict the 478-vertex MediaPipe FaceMesh from 7 emotion probabilities or 52 blendshapes (+ pose), trained on the same CelebV-HQ Detectorv2/MPDetector predictions and aligned into the same au_to_mesh v5 canonical frame, so all three render coherently.

Weights are hosted on the HF Hub:

API (in feat.plotting)

  • PLSFeatMeshModel — generic feature+pose → mesh wrapper (mirrors PLSAUMeshModel)
  • load_emotion_face_mesh_model() / load_blendshape_face_mesh_model() — lazy hf_hub_download from the repos above, with a feature_columns drift guard
  • predict_face_mesh_from_features(feats, model)(478, 3) / (n, 478, 3)
  • plot_face_mesh(...) gains emotion= and blendshapes= kwargs (mutually exclusive with au= / mesh=)
from feat.plotting import load_blendshape_face_mesh_model, predict_face_mesh_from_features, plot_face_mesh
import numpy as np
plot_face_mesh(blendshapes=np.eye(52)[25])     # jawOpen
plot_face_mesh(emotion=np.eye(7)[3])           # happiness

Models

Recipe matches au_to_mesh (Umeyama-Procrustes absolute coords, [feat | pose | feat×pose], full-rank linear ≡ full-rank PLS, linearized to coef/intercept, pose absorbed at inference). Trained on 633,207 frames / 34,854 videos.

model OOS variance-weighted R² (3-fold GroupKFold by video)
emotion → mesh 0.206
blendshape → mesh 0.639

Tests

feat/tests/test_feat_mesh_viz.py — 13 offline (stubbed) + 2 network. Existing test_face_mesh_viz.py (au_to_mesh) still passes.

ljchang added 4 commits June 27, 2026 08:48
Companions to the AU->mesh model: predict the 478-vertex MediaPipe FaceMesh
from 7 emotion probabilities or 52 blendshapes + pose, trained on the same
CelebV-HQ Detectorv2/MPDetector predictions and aligned into the shared
au_to_mesh v5 canonical frame so all three render coherently.

- PLSFeatMeshModel + load_emotion_face_mesh_model / load_blendshape_face_mesh_model
  (lazy hf_hub_download from py-feat/emotion_to_mesh and py-feat/bs_to_mesh)
- predict_face_mesh_from_features; plot_face_mesh gains emotion=/blendshapes=
- tests (offline stubs + network)

OOS variance-weighted R2 (3-fold GroupKFold by video, 633K frames/34.8K videos):
emotion 0.206, blendshapes 0.639.
- predict(): clear ValueError on 0-d/scalar input (was cryptic IndexError);
  slice deployed coef feature rows instead of zero-padding pose channels
- plot_face_mesh(emotion=/blendshapes=): accept a single-face (1, n) row
- extract shared _flat_to_mesh() helper (AU + feature paths)
- tests for the above
The au/emotion/blendshape/mesh inputs are now validated together, so the
error text changed from 'either au or mesh' to 'at most one of ...'.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant