File size: 2,820 Bytes
3972fea
 
 
 
110ba8b
3972fea
110ba8b
3972fea
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# Pose Driven 3D Characters

Stand in front of the camera and a rigged 3D character copies your pose in real time.

![Fig.1: Example 1](examples/POSE_DRIVEN_3D_EXAMPLE_1.png)

![Fig.2: Example 2](examples/POSE_DRIVEN_3D_EXAMPLE_2.png)

## Model

Two models, no training:

- **YOLO pose** (`models/yolo26s-pose.pt`) returns 17 body keypoints per person
  (shoulders, elbows, wrists, hips, knees, ankles, face), tracked across frames and
  smoothed with a one euro filter.
- **Rigged glTF characters** (`models/characters/*.glb`) carry their own skeletons:
  `Michelle` (Mixamo), `Rigged Figure` and `Cesium Man` (Khronos samples). Switch with
  `M` / `N`. The `.glb` files are read directly - meshes, skinning weights, bones and
  embedded textures - and drawn by a small software rasterizer, so there is no
  game engine and no GPU requirement.

## Logic

Each bone is aimed at the direction its keypoints describe. A bone points along
`parent.rotation @ child_offset`, so making that equal the target gives its local
rotation:

```
R = animated_parent.rotation^-1 @ swing(rest -> target) @ bind_parent.rotation
```

Both accumulated rotations are needed, animated **and** bind. Using the animated one on
both sides only works when the bind rotation is identity, which is never true once a rig
has an axis-conversion root, and it misaims every limb by up to 20 degrees.

The rest of it:

- **Depth.** One camera sees no depth, so a limb shorter than its own reference length
  must be angled toward or away from the lens; that missing length is recovered as a
  damped Z component.
- **Turning.** Shoulders narrower than the body's proportions mean the torso is turned.
  That yaw is folded into every bone's swing, because each driven bone's orientation is
  absolute - spinning one alone is undone by the next one down.
- **Mirroring.** The camera image is flipped, so left and right chains swap and the
  horizontal axis flips: raise your right arm and the character raises its right arm,
  facing you.
- **Speed.** All per-triangle work - projection, culling, lighting, colour - is batched
  in numpy, leaving one OpenCV polygon fill per triangle, and dense meshes are thinned
  at load to a triangle budget. Roughly 30-70 FPS depending on the character.

Views cycle with `K`: character only, character with a camera inset, or camera with a
character inset. Keypoints and the skeleton are drawn on the camera image.

Keys: `K` view, `M` / `N` model, `F` detail, `L` keypoint names, `Y` / `P` rotate view,
`R` reset view, `D` debug, `H` help, `Q` quit.

## Run

```bash
pip install -r requirements.txt
python src/app.py
```

```bash
python src/app.py --model "Rigged Figure"
python src/app.py --camera 1 --pose-size 448 --no-mirror
```

by Salimli Ayzek (Салимли Айзек): https://mathematiclove.github.io