paul commited on
Commit
792cc2f
Β·
1 Parent(s): 7180508

Rewrite the model card: what it takes, what it returns, and how to run it

Browse files
Files changed (1) hide show
  1. README.md +146 -23
README.md CHANGED
@@ -1,39 +1,162 @@
1
  ---
2
- library_name: pytorch
3
  tags:
4
  - animation
5
  - character-animation
6
  - inverse-kinematics
7
  - pose-estimation
 
8
  license: other
9
  ---
10
 
11
- # Animatica Autoposer (AutoposerNet, SOMA-30)
12
 
13
- A neural autoposer: given a sparse set of effectors β€” positions, global rotations (6D), and
14
- look-at targets, each with a **tolerance** channel (metres of slack) β€” it produces a full-body
15
- pose for a 30-joint SOMA skeleton as draft joint positions plus per-joint 6D rotations for FK.
 
 
16
 
17
- - **Body-conditional**: takes a 29-d bone-length descriptor (z-scored), so the same effectors
18
- solve correctly on differently proportioned bodies.
19
- - **Floor-aware** (when `config.floor_ref` is true): y is absolute height above the ground;
20
- centroid-reference only x,z when preparing effector inputs.
21
- - Architecture: weighted-prototype ProtoRes (Oreshkin et al., ICLR 2022) with two-stage
22
- decoding (draft positions -> rotations), conditioning injected at the encoder (per effector)
23
- and the decoder embedding (zero-init).
24
 
25
- ## Usage
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
  ```python
28
- from autoposer import Autoposer
29
-
30
- ap = Autoposer.from_pretrained("Animatica-ai/autoposer") # or a local dir / training .pt
31
- # effectors x: [B, E, 7] = 6 value channels + 1 tolerance
32
- # weights: [B, E] encoder blending weights (1.0 = training convention)
33
- # jid / typ: [B, E] joint id (0..29) and effector type (0 pos / 1 rot / 2 look-at)
34
- # cond: [B, 29] z-scored bone lengths (required when config["cond_dim"] > 0)
35
- draft, rot6d = model(x, weights, jid, typ, cond=cond)
 
 
 
 
 
 
 
 
 
 
 
 
36
  ```
37
 
38
- `draft` is [B, 90] draft joint positions (root = first 3), `rot6d` is [B, 180] per-joint 6D
39
- rotations; run them through your SOMA-30 FK (with per-body neutral joints) for world positions.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ library_name: onnxruntime
3
  tags:
4
  - animation
5
  - character-animation
6
  - inverse-kinematics
7
  - pose-estimation
8
+ - onnx
9
  license: other
10
  ---
11
 
12
+ # Animatica Autoposer (SOMA-30)
13
 
14
+ A neural autoposer for full-body character rigs. Give it a handful of handles β€” where a hand
15
+ should be, where the feet are planted, which way the head looks β€” and it returns a complete,
16
+ natural pose for a 30-joint skeleton. It is what makes posing in the
17
+ [Animatica](https://animatica.ai) Blender add-on feel like moving a character rather than
18
+ rotating thirty bones.
19
 
20
+ It is a **poser, not an animator**: one pose per call, a few milliseconds each, no motion model
21
+ and no temporal state. Ask it once per frame and you have an editable animation; ask it once and
22
+ you have a pose.
 
 
 
 
23
 
24
+ ## What you give it
25
+
26
+ Any number of effectors, in any combination. Each carries a **tolerance** β€” metres of slack β€” so
27
+ a handle can be an exact pin or a loose suggestion the model may overrule to keep the body
28
+ plausible.
29
+
30
+ | Effector | Type id | Value | Meaning |
31
+ |---|---|---|---|
32
+ | Position | `0` | xyz | put this joint here |
33
+ | Rotation | `1` | 6D rotation | orient this joint like this |
34
+ | Look-at | `2` | xyz + direction | aim this joint at this point |
35
+
36
+ Plus a **body descriptor**: 29 bone lengths, z-scored. The same effectors solve correctly on a
37
+ child, a heavyset adult, or a giant, because proportion is an input rather than an assumption.
38
+
39
+ Three position effectors is the sparsest input it was trained to answer well; one or two will
40
+ return something, but the body below them is mostly prior.
41
+
42
+ ## What you get back
43
+
44
+ - **draft joint positions** β€” `[B, 90]`, the 30 joints as xyz, root first
45
+ - **6D rotations** β€” `[B, 180]`, per joint, for FK on your own skeleton
46
+
47
+ Run the rotations through SOMA-30 forward kinematics with your body's neutral joints to get world
48
+ positions. The bundled IK pass (below) then pins the effectors exactly and keeps the feet out of
49
+ the floor.
50
+
51
+ ## Use it in Blender
52
+
53
+ The Animatica add-on downloads this repository on first use and needs no token or manual setup β€”
54
+ handles appear on your rig and you drag them. That is the shortest path to using this model, and
55
+ the one it is tuned for.
56
+
57
+ ## Use it directly (ONNX)
58
+
59
+ `onnx/` is the runtime bundle: two graphs and the metadata that describes them.
60
 
61
  ```python
62
+ import json, numpy as np, onnxruntime as ort
63
+ from huggingface_hub import snapshot_download
64
+
65
+ d = snapshot_download("Animatica-ai/autoposer", allow_patterns=["onnx/*"])
66
+ meta = json.load(open(f"{d}/onnx/meta.json"))
67
+ poser = ort.InferenceSession(f"{d}/onnx/poser.onnx")
68
+
69
+ E = 3 # three handles: hips and both hands
70
+ val = np.zeros((1, E, 7), np.float32) # 6 value channels + 1 tolerance
71
+ val[0, 0, :3], val[0, 0, 6] = (0.0, 0.95, 0.0), 0.005
72
+ val[0, 1, :3], val[0, 1, 6] = (0.35, 1.20, 0.20), 0.005
73
+ val[0, 2, :3], val[0, 2, 6] = (-0.35, 1.20, 0.20), 0.005
74
+
75
+ draft, rot6d = poser.run(None, {
76
+ "val": val,
77
+ "weights": np.ones((1, E), np.float32), # encoder blending weights
78
+ "joint_id": np.array([[0, 13, 19]], np.int64), # Hips, LeftHand, RightHand
79
+ "eff_type": np.zeros((1, E), np.int64), # all positions
80
+ "cond": np.zeros((1, 29), np.float32), # z-scored bone lengths
81
+ })
82
  ```
83
 
84
+ Two details the graph will not tell you, and which decide whether the output is a pose or a heap:
85
+
86
+ - **Reference frame.** Position and look-at values are expressed relative to the centroid of the
87
+ position targets. This checkpoint is floor-referenced (`floor_ref: true`), so **y stays
88
+ absolute** β€” the ground is a real height, not something relative β€” and the centroid is zeroed
89
+ on y before subtracting.
90
+ - **Conditioning.** `cond` is the 29 bone lengths, z-scored with `blen_mean` / `blen_std` from
91
+ `meta.json` and clamped to Β±4.5. Zeros mean "the average body the statistics describe", which
92
+ is why the snippet above works at all.
93
+
94
+ ### The IK pass
95
+
96
+ `onnx/ik.onnx` is a damped least-squares refinement that runs after the network: it pins
97
+ effectors to the tolerance you asked for, holds a floor plane, and rolls the toes rather than
98
+ sinking the feet. Twelve iterations over a damping ladder, accepting per step. The network gets
99
+ the pose right; this makes the handles exact.
100
+
101
+ `meta.json` carries a **selftest** β€” recorded inputs and expected outputs β€” so a miscompiled
102
+ graph is detectable. It matters more than it sounds: a bad kernel fusion does not raise, it
103
+ quietly returns a wrong pose. Run the IK graph on `selftest.inputs` and compare against
104
+ `selftest.expect`; agreement should be well under half a centimetre.
105
+
106
+ ## Use it directly (PyTorch)
107
+
108
+ `model.safetensors` + `config.json` are the training weights, for research use and for anyone who
109
+ wants to fine-tune. `config.json` records the skeleton (`njoints`, `ux`/`ux_names`), the
110
+ conditioning width (`cond_dim: 29`), floor referencing, and the training step.
111
+
112
+ ## The skeleton
113
+
114
+ 30 joints, root first:
115
+
116
+ ```
117
+ Hips Spine1 Spine2 Chest Neck1 Neck2 Head Jaw LeftEye RightEye
118
+ LeftShoulder LeftArm LeftForeArm LeftHand LeftHandThumbEnd LeftHandMiddleEnd
119
+ RightShoulder RightArm RightForeArm RightHand RightHandThumbEnd RightHandMiddleEnd
120
+ LeftLeg LeftShin LeftFoot LeftToeBase
121
+ RightLeg RightShin RightFoot RightToeBase
122
+ ```
123
+
124
+ `meta.json` has the parent list, the neutral (rest) joint positions, the four foot joints used by
125
+ the floor term, and the effector joint ids.
126
+
127
+ ## How it works
128
+
129
+ Each effector is embedded from its value, tolerance, joint id and type, and the set is blended by
130
+ weight into a single pose code β€” so the model takes any number of handles in any order, rather
131
+ than a fixed rig of them. Two-stage decoding follows: draft joint positions first, then per-joint
132
+ 6D rotations, which is what keeps limbs attached to a body of the right proportions instead of
133
+ merely near the handles. The body descriptor is injected twice, at the encoder alongside each
134
+ effector and again at the decoder embedding (zero-initialised, so it starts as a no-op and is
135
+ learned into).
136
+
137
+ ## Files
138
+
139
+ | File | Size | What |
140
+ |---|---|---|
141
+ | `onnx/poser.onnx` | 142 MB | the network |
142
+ | `onnx/ik.onnx` | 1.9 MB | the IK refinement pass |
143
+ | `onnx/meta.json` | 19 KB | skeleton, normalisation stats, checksums, selftest |
144
+ | `model.safetensors` | β€” | PyTorch weights |
145
+ | `config.json` | β€” | training configuration |
146
+
147
+ ## Limitations
148
+
149
+ - **One pose at a time.** There is no temporal model: consecutive frames are consistent because
150
+ consecutive effectors are, not because the model remembers anything.
151
+ - **SOMA-30 only.** Other skeletons need retargeting onto these 30 joints first.
152
+ - **Hands and face are along for the ride.** Fingers are a thumb and middle tip per hand; jaw and
153
+ eyes are in the skeleton but are not meaningfully posed.
154
+ - **Fewer than three position effectors** leaves most of the body to the prior. It will answer;
155
+ it just has little to answer from.
156
+ - **Floor-referenced.** Feed it absolute heights. A pose built in a different vertical frame will
157
+ come back crouching or floating.
158
+
159
+ ---
160
+
161
+ Made by [Animatica](https://animatica.ai). Found something odd? Tell us β€” the model and the
162
+ add-on move together, and a pose that comes out wrong is usually interesting.