Views
No views yet
mobilefacenet, OpenFace, etc.) and emits 6DoF head
pose calibrated to img2pose's coordinate frame. Designed for py-feat
pipelines that use a face detector without a built-in pose head (e.g.
RetinaFace in py-feat ≥ 0.7).py-feat's v0.6 production pipeline used img2pose as its face detector,
which multi-tasks face localization with 6DoF head pose regression — so
pose came "for free" from the detector. In v0.7 the default face detector
became RetinaFace (much higher WIDERFACE Hard AP) which only detects
faces. To preserve the Fex schema (pitch, roll, yaw, x, y,
z columns), py-feat distills img2pose's pose regression into a small
MLP that operates entirely on already-computed landmarks.Linear(136→512) → LayerNorm → GELU → Dropout(0.15) → Linear(512→256) → LayerNorm → GELU → Dropout → Linear(256→128) → LayerNorm → GELU → Dropout → Linear(128→6)feat.utils.face_pose_mlp.normalize_landmarks).[Pitch, Roll, Yaw, X, Y, Z]. The MLP emits
z-scored values; the loader de-normalizes using mean/std stored in
the sidecar pose_mlp_v2.json. Angles are radians, calibrated to
img2pose's coordinate frame.img2pose (Albiero et al., 2021). The MLP is trained to
match img2pose's regressed [Pitch, Roll, Yaw, X, Y, Z] outputs.n_clips = 35,445,
n_train_frames = 2,783,134, n_val_frames = 154,619. Frames with
FaceScore < 0.8 or |pose| > 75° are dropped (filters bad teacher
signal on degenerate poses).lr=1e-3, batch_size=1024.pose_mlp_v2.json
for per-epoch history).| Axis | MAE (°) |
|---|---|
| Pitch | 2.66 |
| Roll | 2.34 |
| Yaw | 1.58 |
| Aspect | v1 | v2 |
|---|---|---|
| Hidden | 256→128→64 | 512→256→128 |
| Activation | Linear → ReLU → Dropout | Linear → LayerNorm → GELU → Dropout |
| Dropout | 0.10 | 0.15 |
| Training frames | 569,678 | 2,783,134 |
| Epochs | 30 | 40 |
| Best val loss | 0.0809 | 0.0777 |
| Roll MAE (°) | 2.530 | 2.335 |
py-feat with a face detector that doesn't predict pose
(face_model='retinaface' in feat.Detector, MediaPipe in
feat.MPDetector).L2CS-Net for gaze.feat.Detector when
face_model != 'img2pose'. To call it directly:1import torch
2from feat.utils.face_pose_mlp import pose_from_landmarks_mlp
3
4# 68 (x, y) landmarks in image-pixel coordinates, e.g. from mobilefacenet.
5landmarks = torch.tensor([
6 # ... [68, 2] ...
7], dtype=torch.float32).unsqueeze(0) # [1, 68, 2]
8
9pose = pose_from_landmarks_mlp(landmarks) # [1, 6]: (Pitch, Roll, Yaw, X, Y, Z)
10print(pose)FEAT_POSE_MLP_PATH environment variablemodels/pose_mlp_v2.safetensors in the repopy-feat/pose_mlp_v2)py-feat pipeline
but may need recalibration to compare with other tools.py-feat and this pose-MLP, please cite both py-feat and
img2pose:1@article{cheong2023pyfeat,
2 title={Py-Feat: Python Facial Expression Analysis Toolbox},
3 author={Cheong, Jin Hyun and Jolly, Eshin and Xie, Tiankang and Byrne, Sophie and Kenney, Matthew and Chang, Luke J.},
4 journal={Affective Science},
5 volume={4},
6 pages={781--796},
7 year={2023}
8}
9
10@inproceedings{albiero2021img2pose,
11 title={img2pose: Face Alignment and Detection via 6DoF, Face Pose Estimation},
12 author={Albiero, Vítor and Chen, Xingyu and Yin, Xi and Pang, Guan and Hassner, Tal},
13 booktitle={Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)},
14 pages={7617--7627},
15 year={2021}
16}
17
18@inproceedings{zhu2022celebvhq,
19 title={CelebV-HQ: A Large-Scale Video Facial Attributes Dataset},
20 author={Zhu, Hao and Wu, Wayne and Zhu, Wentao and Jiang, Liming and Tang, Siwei and Zhang, Li and Liu, Ziwei and Loy, Chen Change},
21 booktitle={Proceedings of the European Conference on Computer Vision (ECCV)},
22 year={2022}
23}img2pose) is BSD-3, and the
training corpus (CelebV-HQ) is released for non-commercial research
use — please honor each upstream license if you re-train or
re-distribute.pose_mlp_v2.safetensors — model weights (1 MB)pose_mlp_v2.json — architecture, output-normalization stats, training
history, validation MAE per epochREADME.md — this card