Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🎭 Mocapy

Markerless motion capture β€” video or webcam β†’ animated VRM avatar

Turn any video or live webcam feed into full-body animation β€” body, fingers, head, eye gaze, and facial expression β€” and export it as BVH (Blender / Unity) plus a per-frame facial-morph sidecar.

No game engine. No plugins. No GPU required.

Python MediaPipe License


Mocapy demo β€” input video on the left, live pose + hands + face-mesh skeleton on the right

Left: source footage Β· Right: Mocapy's live pose + hand + face-mesh solve


  πŸŽ₯ video / webcam
        β”‚
        β–Ό
  🧠 MediaPipe        pose (33) + hands (21Γ—2) + face mesh (478) + 52 blendshapes
        β”‚
        β–Ό
  🦴 solver           bone quaternions + facial morphs
        β”‚
        β–Ό
  πŸ“¦ export           BVH  Β·  .morphs.json     β†’ any humanoid VRM rig

Works on any VRM (0.x or 1.x) with standard humanoid bone names. Or skip the rig entirely with - and use the bundled default skeleton.


✨ Features

🦴 Full-body solve Torso, arms, legs, head/neck, 24 finger bones + thumbs per hand
πŸ‘€ Face & gaze Iris-driven eye gaze + 52 ARKit blendshapes β†’ facial morph weights + 12 VRM presets
🎯 Two detection backends cv2 + MediaPipe Tasks, or a WASM tasks-vision bridge in headless Chromium for higher-fidelity legs
πŸ’» Holistic (CPU-only) mode One combined pose+face+hands model that runs with no GPU β€” works on low-end machines
🟦 Live skeleton preview Realtime overlay window (pose + hands + face mesh + bounding boxes)
πŸ›‘οΈ Robustness layers Leg-collision gating, finger depth-recovery + smoothing, plant-adaptive foot grounding, torso de-wobble
πŸ“€ Exports BVH + a per-frame .morphs.json facial sidecar
πŸ” Detect once, retarget many A shared frames.json lets you re-solve one capture onto many rigs without re-detecting

πŸš€ Quick start

# 1. install (editable)
pip install -e .

# 2. download the MediaPipe models into models/  (see Installation below)

# 3a. detect:  video β†’ frames.json   (add --preview for the live overlay window)
mocapy-detect  fixtures/test.mp4  frames.json

# 3b. solve:   frames.json β†’ BVH     ( '-' = bundled default skeleton, no VRM needed )
mocapy-solve   frames.json  assets/AliciaSolid.vrm  out.bvh
mocapy-solve   frames.json  -  out.bvh

Drop out.bvh into Blender (File β†’ Import β†’ Motion Capture (.bvh)) and hit play. ▢️


πŸ“¦ Installation

1. Prerequisites

Requirement Why
Python 3.10 – 3.12 (3.11 recommended) mediapipe 0.10.x ships wheels for these versions
Miniconda / Anaconda (recommended) Isolates mediapipe's native deps from system Python
Webcam (optional) Only for live capture
~150 MB free disk Source + models + sample fixtures

πŸͺŸπŸŽπŸ§ Windows Β· macOS Β· Linux all work identically. Shell snippets use Unix-style paths; on Windows PowerShell, swap / β†’ \.

2. Create the environment

conda env create -f environment.yml
conda activate mocapy
Prefer plain venv / pip instead of conda?
python -m venv .venv && source .venv/bin/activate      # Windows: .venv\Scripts\activate
pip install "numpy>=1.26,<2.0" "opencv-python>=4.8" "mediapipe>=0.10.21"

⚠️ numpy must be <2.0. numpy 2.x crashes inside the VRM skeleton loader on some builds. Both environment.yml and pyproject.toml pin this for you.

3. Install Mocapy

pip install -e .

This registers three CLI entrypoints: mocapy-detect, mocapy-solve, and mocapy-webcam.

4. Download the MediaPipe models

The .task model files (~50 MB total) are not committed. Download them from MediaPipe's official model repository into a models/ folder at the repo root:

models/
β”œβ”€β”€ pose_landmarker_full.task
β”œβ”€β”€ pose_landmarker_heavy.task     # optional β€” only for --pose-model heavy / --strong
β”œβ”€β”€ hand_landmarker.task
β”œβ”€β”€ face_landmarker.task
└── holistic_landmarker.task       # optional β€” only for --holistic

5. (Optional) Set up the WASM bridge backend

The higher-fidelity --backend bridge path runs the browser-grade tasks-vision models in headless Chromium. It needs Node.js + ffprobe (from FFmpeg) on your PATH, plus a one-time install:

cd node_bridge
npm install        # pulls puppeteer's headless Chromium + @mediapipe/tasks-vision

The default cv2 backend needs none of this β€” bridge is purely opt-in.


🎬 Usage

Mocapy runs in two stages around a shared frames.json. This split lets you detect once and re-solve onto many avatars.

Stage A β€” Detect (mocapy-detect)

mocapy-detect <video> [frames.json] [options]
Flag Description
--backend {cv2,bridge} Detector. cv2 (default) or bridge (WASM in Chromium β€” best legs)
--pose-model {full,heavy} heavy is steadier on legs, ~3Γ— slower
--hand-conf FLOAT Hand-detection confidence (bridge). Lower (e.g. 0.3) catches faster hands
--strong Preset = --pose-model heavy --hand-conf 0.3 (best limbs + fingers)
--holistic One combined CPU-only model (bridge) β€” for machines with no GPU
--preview Open a live overlay window (pose + hands + face mesh + boxes)
--max-frames N Stop after N frames
--no-leg-stabilize Disable leg-collision gating
--no-hand-stabilize Disable finger depth-recovery + smoothing
# fast, no extra deps
mocapy-detect  fixtures/test.mp4  frames.json

# highest-fidelity legs, with a live preview window
mocapy-detect  fixtures/test.mp4  frames.json  --backend bridge --strong --preview

# CPU-only, low-end machine
mocapy-detect  fixtures/test.mp4  frames.json  --backend bridge --holistic

Stage A (live) β€” Webcam (mocapy-webcam)

Writes the same frames.json, so Stage B is unchanged.

mocapy-webcam --device 0 --duration 30 --output frames.json
Flag Default Description
--device INT 0 Webcam index (try 1/2 for multiple cams)
--duration SEC 10 Max recording length
--output PATH frames.json Where to write the capture
--width / --height 1280 / 720 Capture resolution
--target-fps FPS 30 Throttle rate (60 fps cams drop every other frame)
--mirror / --no-mirror on Mirror-image capture (selfie convention)
--no-preview β€” Headless (no window) β€” for scripts/servers
--pose-model {full,heavy} full heavy is more accurate, ~3Γ— slower

Stop a webcam capture with q or ESC (not Ctrl-C) so frames.json is flushed.

Stage B β€” Solve to BVH (mocapy-solve)

mocapy-solve <frames.json> <vrm|-> <out.bvh> [options]

Pass a VRM path to retarget onto that rig, or - to use the bundled default skeleton (no VRM file needed). Writes the BVH plus a <out>.bvh.morphs.json sidecar.

Flag Description
--fov FLOAT Camera field of view for depth back-projection (tune root Z motion)
--mirror Left/right-mirror the solved output (retarget to opposite handedness)
--ground Enable foot-grounding (drives hips-Y from the planted foot)
--no-foot-stabilize Disable plant-adaptive foot jitter hold
mocapy-solve  frames.json  assets/AliciaSolid.vrm  out.bvh           # onto a rig
mocapy-solve  frames.json  -  out.bvh                                 # bundled skeleton
mocapy-solve  frames.json  assets/cj.vrm  out.bvh  --mirror --ground  # mirrored + grounded

πŸ“€ Outputs

File Plays in Contains
.bvh Blender, Unity, Maya, MotionBuilder Full bone hierarchy + per-frame rotations + root translation
.bvh.morphs.json (sidecar) Per-frame facial morph weights to pair with the BVH

The first BVH frame is a clean rest pose (identity rotations), so the rig holds its A-pose until you press play β€” convenient for Mixamo-style retargeting.


πŸ—‚οΈ Project layout

mocapy/                the library
β”œβ”€β”€ pipeline.py          orchestration: solve_frame β†’ solve_sequence β†’ BVH
β”œβ”€β”€ detect/              MediaPipe Tasks + OneEuro filters + WASM bridge backend
β”œβ”€β”€ solve/               torso Β· arms Β· legs Β· head Β· wrist Β· fingers Β· eyes Β· morphs Β· blend
β”œβ”€β”€ vrm/skeleton.py      VRM / glTF rest-pose parser
└── export/              bvh.py Β· morphs.py
node_bridge/           tasks-vision (WASM) detection bridge + realtime preview
tools/                 webcam capture Β· Blender add-on Β· demo-GIF generator
models/                MediaPipe .task files (download separately, ~50 MB)
assets/                example VRM avatars
fixtures/              sample video + reference captures
media/                 README demo GIF

Set MOCAPY_ROOT to relocate fixtures/ / assets/ / models/.


🩹 Troubleshooting

Symptom Fix
No module named mediapipe Activate the env: conda activate mocapy
No module named 'mocapy' Run pip install -e . from the repo root
Crash inside load_skeleton (float scalar) numpy is 2.x β†’ pip install "numpy>=1.26,<2.0"
Webcam returns no frames Wrong --device β€” try 1 or 2
Body "marches in place" Capture was un-mirrored β€” re-record (webcam mirror is on by default)
Hands don't curl Hands were out of frame / too far / too dark β€” get closer, brighten the room
UnicodeEncodeError printing 手捩 Windows: set PYTHONIOENCODING=utf-8
--backend bridge fails to start Need Node.js + ffprobe on PATH and npm install in node_bridge/

πŸ“„ License

See LICENSE. Built on Google's MediaPipe for landmark detection.


Made with 🦴 and a lot of quaternions.

About

Markerless motion capture: video/webcam -> VRM avatar bones -> BVH

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages