Video-to-Animation Research
Research repo archiving the attempts, data and findings from converting monocular videos of dancing people into bone animations driving a Ready Player Me / Mixamo avatar. Parked in April 2026; archived for future resume.
Goal
Given any video of a person dancing, produce a playable .glb animation on
an RPM-style avatar (e.g. Sofia, Avaturn, Rayan) that visually matches the
movement in the video. Quality is measured by a MediaPipe-based pose
similarity score (MP score, 0β100).
Target: MP β₯ 60. Best achieved: MP β 39.3 (wham-v18). Ceiling
appears to be driven by the extractor's own accuracy on dance motion, not
by the retargeting stage.
Pipeline overview
video.mp4
β
βββΊ [extractor] βββΊ pose sequence (SMPL, 3D joints, or rotations)
β ββ WHAM / SMPL-est-X / 4D-Humans / PromptHMR-Vid
β ββ GVHMR / TRAM / HybrIK
β ββ MediaPipe 3D
β
βββΊ [retarget] βββββΊ bone rotations on Mixamo skeleton
β ββ ARP (Auto-Rig Pro) β most used
β ββ analytical IK from 3D joints
β ββ MoCapAnything-style Procrustes+swing
β
βββΊ [post] βββββββββΊ smoothing / correction
β ββ SmoothNet
β ββ DNO / MotionFix refinement (not tried)
β ββ learned-correction (static Ξ per bone)
β
βββΊ [avatar bake] ββΊ final .glb with avatar mesh + animation
Alternative (Option C, MVP implemented): skip extraction entirely and
retrieve the closest match from a pre-made Mixamo library via MediaPipe
keypoints + DTW β scripts/dtw_match.py.
Approaches tried (chronological)
| # | Approach | MP score | Outcome |
|---|---|---|---|
| 1 | WHAM raw β bone rotations (no post) | 6.4 | broken bind-pose / twist |
| 2 | WHAM + ARP baseline (wham-v6) |
38.6 | OK but shoulders off |
| 3 | WHAM + ARP copy_bone_rest on shoulders (v18) |
39.3 | current champion |
| 4 | GT rendered β ARP mirror-rest (closure sanity) | 17.4 | pipeline loses ~60pt even with perfect input |
| 5 | Learned per-bone static correction Ξ | low | 30.5Β° residual is frame-dependent, can't be static |
| 6 | Analytical IK from 3D joints (shortest-arc) | 0 | axis convention / frame mismatch |
| 7 | MoCapAnything-style IK (Procrustes + swing, temporal warm-start) | not measured | math verified symbolically; visually off because WHAM-derived joints inherit WHAM error |
| 8 | SMPL-est-X β ARP (with hands) | similar | hand mesh clipping unresolved |
| 9 | 4D-Humans / HMR2.0 single-frame HD render | 0 | ViTDet detector failed on workbench renders |
| 10 | Retrieval β MediaPipe + DTW β library lookup | n/a | cluster correct (idleβidles, danceβdances); exact GT not always top-1 |
Key findings (the ones to remember)
Closure test failed (GT animation β our pipeline β compare to GT = MP 17.4). Any extraction approach is capped at ~60 MP because the pipeline itself drops ~60 points via avatar bind-pose, axis conventions, bone-roll mismatches.
Bind-pose mismatch dominates error. SMPL T-pose vs Sofia A-pose = 54.7Β° delta at the shoulder. Per-bone error breakdown from
bone_error_metric.py:- Hips: 170Β° (camera-frame/world-frame conversion)
- UpLegs: 170Β° (same)
- Shoulders: 125Β° (T-pose vs A-pose)
- Arms: 84Β°
- Spine: 10Β°
- Hands: 26Β°
"Fix the bind-pose" ideas that did NOT work:
- Re-rig Sofia as SMPL (manual Blender) β mesh distorted even with shape-key clean-up
- Swap to known T-pose avatar (avaturn/rayan) β didn't materially change score
- Learned static correction per bone (qlog mean) β error is frame-dependent, not static
MoCapAnything β free lunch without their model. The paper's real contribution is a trained 3D joint predictor (DINOv2 + 4D mesh). Their Β§3.5 IK recipe applied to WHAM-derived joints cannot exceed WHAM's ceiling, because those joints come from WHAM's own SMPL forward-kinematics. Code not released as of April 2026.
WHAM output convention (critical, repeatedly rediscovered):
poses_body= 23 SMPL-relative rotmats (body joints 1..23)poses_root_world= root rotation in world frametrans_world= root translationverts_cam= 6890 SMPL vertices in camera space- Default Y-down unless
return_y_up=True.yup2ydownflips Y-axis on root rotation and translation only β body-relative rots are frame-independent. - For Mixamo retargeting the fast path is
poses_body + poses_root_worlddirectly, not via joint positions.
Pipeline loses information even for the "identity" case (GT β extract β retarget β render β score vs GT = 17.4). Sources: (a) GLB animation sampling at 30fps, (b) armature empty-node vs pose-bone copy conversions, (c) rotation order / quaternion sign ambiguities.
HumanMM repo is empty (README only, no code). No forks/mirrors exist. PromptHMR-Vid already integrates TRAM and tests similarly to WHAM.
Approaches worth trying next
Priority order for future-me:
- MotionFix / DNO post-refinement over
wham-v18output. Diffusion-based motion denoising. Expected +5β10 MP. Low risk, well-documented. - Physics-based IK (joint limits + contact constraints). ECCV'24 "Biomechanically Accurate Neural IK". Useful if extraction is decent but poses look unnatural.
- Trained retarget model. Take our 5β6 annotated GT videos + WHAM
extractions β train a neural net that maps SMPL pose β Mixamo-correct
pose per bone. Feedback loop with Groq/LLaMA already memoised
(
project_groq_autooptimize.md). - Better extractor. Wait for HumanMM release (CVPR 2025 specialist on
multi-shot dance); or test GVHMR (script exists:
scripts/gvhmr_client.py); or PromptHMR-Vid tuning. - Retrieval (Option C2). MVP in this repo is C1 (one-shot nearest-neighbour). C2 adds a sliding DTW window with crossfades between clips β likely produces visually much better output than any single extractor on dances.
Directory layout
| Dir | Purpose |
|---|---|
scripts/ |
All Python/Blender scripts (77 files). Each targets a specific extractor or stage. See "Key scripts" below. |
extractions/ |
Per-extractor output data β {wham,4d-humans,gvhmr,mediapipe,nlf,prompthmr,prompthmr-vid,smplest-x,wham-joints3d}/ subdirs. |
artifacts/ |
Generated strategy GLBs playable in ui/strategies.html. Named ${anim}-${extractor}-${strategy}.glb. |
videos/ |
3 test videos: 01-idle.mp4, 02-dance1.mp4, 03-dance2.mp4 (rendered from F_Standing_Idle_001 / F_Dances_001 / F_Dances_005). |
ui/ |
Side-by-side comparison HTML (strategies.html). Loads original vs strategy GLB with Babylon.js. |
motion-library/ |
Library signatures (library.npz, 119 anims Γ 12 joints Γ T frames, body-local normalised) + per-video MediaPipe signatures + match results. |
mocapik/ |
GLBs from MoCapAnything-style IK experiment (procrustes + swing, baked on Avaturn). |
docs/ |
This README. |
autorig/ |
Auto-Rig Pro paid addon binaries (pinned versions). Private backup β do not redistribute. See autorig/README.md. |
Key scripts
Extraction:
wham_to_joints3d.pyβ convert WHAM pkl β per-frame 3D joint JSON.fourdh_to_json.pyβ same for 4D-Humans.hybrik_client.py,gvhmr_client.pyβ HTTP clients for remote extraction services.extract_video_signature.pyβ MediaPipe β 12-joint body-local npz.extract_library_signatures.pyβ Blender batch extractor for Mixamo library GLBs.
Retargeting:
arp_retarget.pyβ Auto-Rig Pro retargeting (champion path). Supports--mirror-target-restfor closure tests.mocapanything_ik.pyβ Procrustes + swing IK from 3D joints. Β§3.5 of arxiv 2512.10881.joints3d_to_animation.pyβ shortest-arc IK (failed, score 0).smpl_to_rpm_retarget.py,sofia_source_retarget.py,retarget_fk.py,rokoko_retarget.py,blender_retarget.pyβ alternative retargeters.apply_hybrik_correction.py,hybrik_calibration.pyβ HybrIK-specific post.
Diagnostics:
bone_error_metric.pyβ per-bone quaternion angular error GT vs extracted.closure_test.pyβ GT β our JSON format β compare; validates pipeline correctness.learn_correction.pyβ attempt to learn static per-bone Ξ (failed, 30.5Β° residual).compare_extraction.py,compare_mocap.py,compare_rotations.pyβ various side-by-side comparators.
Retrieval (Option C, MVP):
dtw_match.pyβ DTW matching with motion-energy penalty (fix for idle-bias). Results:01-idleβM_Standing_Idle_002(cost 0.25, correct cluster)02-dance1βF_Dances_006(cost 0.80, GT was F_Dances_001)03-dance2βM_Dances_003(cost 0.75, GT was F_Dances_005)
Rendering:
render_animated_avatar.pyβ HD render avatar + animation via workbench engine.render_wham_mesh.py,render_smpl_verts.pyβ render raw extractor output (no retarget).
Running experiments (reproduction)
Pre-req: Blender β₯ 4.5, Python β₯ 3.10, pip install mediapipe opencv-python numpy joblib.
# 1. Extract library signatures (one-time, ~3 min for 121 GLBs)
blender -b -P scripts/extract_library_signatures.py -- \
--lib-dir <path-to-rpm-library> --out motion-library/library.npz
# 2. Extract video signature
python3 scripts/extract_video_signature.py \
--video videos/02-dance1.mp4 \
--out motion-library/videos/02-dance1.npz \
--model <path-to>/pose_landmarker_heavy.task
# 3. Match
python3 scripts/dtw_match.py \
--library motion-library/library.npz \
--video motion-library/videos/02-dance1.npz \
--subsample 3 --top-k 10 --mode dtw
# 4. MoCapAnything IK from WHAM joints (alternative path)
blender -b -P scripts/mocapanything_ik.py -- \
--avatar <path-to>/avaturn.glb \
--joints extractions/wham-joints3d/02-dance1-joints3d.json \
--out mocapik/02-dance1-wham-mocapik-arp.glb
Related work / external deps (not stored here, reconstruct if needed)
Originally under tests/ml-mocap/{extern,humor,synsp,video2anim,data}/,
excluded to keep repo size down. Fetch from:
- WHAM: https://github.com/yohanshin/WHAM
- 4D-Humans / HMR2.0: https://github.com/shubham-goel/4D-Humans
- PromptHMR / PromptHMR-Vid: https://github.com/rawalkhirodkar/prompthmr
- GVHMR: https://github.com/zju3dv/GVHMR
- SMPL-est-X: https://github.com/SMPLCap/SMPLest-X
- HybrIK: https://github.com/jeffffffli/HybrIK
- MoCapAnything: https://animotionlab.github.io/MoCapAnything/ (code not released, Apr 2026)
- HumanMM: CVPR 2025 β repo README-only as of Apr 2026
- Auto-Rig Pro (Blender addon): paid, https://blendermarket.com/products/auto-rig-pro
Models required locally:
pose_landmarker_heavy.task(MediaPipe) β https://storage.googleapis.com/mediapipe-models/pose_landmarker/pose_landmarker_heavy/float16/latest/pose_landmarker_heavy.task- SMPL body model (
basicModel_neutral_lbs_10_207_0_v1.0.0.pkl) β https://smpl.is.tue.mpg.de/ - SMPL-X models β https://smpl-x.is.tue.mpg.de/
License
Internal research. MIT on original code in scripts/.
External test videos and Mixamo-derived GLBs retain their original licenses.
Status
Archived April 2026. Ceiling MP β 39 was not surpassed by any extraction-based pipeline tried here. See "Approaches worth trying next" when resuming.