motion-studio 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- motion_studio-0.1.0/LICENSE +21 -0
- motion_studio-0.1.0/MANIFEST.in +5 -0
- motion_studio-0.1.0/PKG-INFO +295 -0
- motion_studio-0.1.0/README.md +251 -0
- motion_studio-0.1.0/motion_studio/__init__.py +52 -0
- motion_studio-0.1.0/motion_studio/__main__.py +8 -0
- motion_studio-0.1.0/motion_studio/bundle.py +424 -0
- motion_studio-0.1.0/motion_studio/cli.py +459 -0
- motion_studio-0.1.0/motion_studio/config.py +58 -0
- motion_studio-0.1.0/motion_studio/core/__init__.py +8 -0
- motion_studio-0.1.0/motion_studio/core/plugins.py +285 -0
- motion_studio-0.1.0/motion_studio/core/types.py +72 -0
- motion_studio-0.1.0/motion_studio/library.py +188 -0
- motion_studio-0.1.0/motion_studio/plugins_builtin/__init__.py +8 -0
- motion_studio-0.1.0/motion_studio/plugins_builtin/_convert.py +108 -0
- motion_studio-0.1.0/motion_studio/plugins_builtin/corrector.py +79 -0
- motion_studio-0.1.0/motion_studio/plugins_builtin/corrector_impl.py +992 -0
- motion_studio-0.1.0/motion_studio/plugins_builtin/metrics.py +62 -0
- motion_studio-0.1.0/motion_studio/plugins_builtin/metrics_live.py +135 -0
- motion_studio-0.1.0/motion_studio/plugins_builtin/utils/__init__.py +74 -0
- motion_studio-0.1.0/motion_studio/plugins_builtin/utils/floor_utils.py +269 -0
- motion_studio-0.1.0/motion_studio/plugins_builtin/utils/metrics_utils.py +599 -0
- motion_studio-0.1.0/motion_studio/plugins_builtin/utils/motion_utils.py +880 -0
- motion_studio-0.1.0/motion_studio/server/__init__.py +1 -0
- motion_studio-0.1.0/motion_studio/server/api_bundle.py +649 -0
- motion_studio-0.1.0/motion_studio/server/api_motion.py +622 -0
- motion_studio-0.1.0/motion_studio/server/api_video.py +182 -0
- motion_studio-0.1.0/motion_studio/server/app.py +185 -0
- motion_studio-0.1.0/motion_studio/server/common.py +119 -0
- motion_studio-0.1.0/motion_studio/server/loaders.py +197 -0
- motion_studio-0.1.0/motion_studio/server/state.py +254 -0
- motion_studio-0.1.0/motion_studio/server/video_cache.py +283 -0
- motion_studio-0.1.0/motion_studio/smpl/__init__.py +5 -0
- motion_studio-0.1.0/motion_studio/smpl/convert.py +363 -0
- motion_studio-0.1.0/motion_studio/smpl/io.py +86 -0
- motion_studio-0.1.0/motion_studio/smpl/refit.py +370 -0
- motion_studio-0.1.0/motion_studio/static/app.js +4513 -0
- motion_studio-0.1.0/motion_studio/static/index.html +386 -0
- motion_studio-0.1.0/motion_studio/static/style.css +340 -0
- motion_studio-0.1.0/motion_studio/static/vendor/OrbitControls.js +1417 -0
- motion_studio-0.1.0/motion_studio/static/vendor/TransformControls.js +1573 -0
- motion_studio-0.1.0/motion_studio/static/vendor/three.module.js +53044 -0
- motion_studio-0.1.0/motion_studio.egg-info/PKG-INFO +295 -0
- motion_studio-0.1.0/motion_studio.egg-info/SOURCES.txt +60 -0
- motion_studio-0.1.0/motion_studio.egg-info/dependency_links.txt +1 -0
- motion_studio-0.1.0/motion_studio.egg-info/entry_points.txt +2 -0
- motion_studio-0.1.0/motion_studio.egg-info/requires.txt +23 -0
- motion_studio-0.1.0/motion_studio.egg-info/top_level.txt +1 -0
- motion_studio-0.1.0/pyproject.toml +84 -0
- motion_studio-0.1.0/setup.cfg +4 -0
- motion_studio-0.1.0/tests/test_api_surface.py +132 -0
- motion_studio-0.1.0/tests/test_bundle.py +153 -0
- motion_studio-0.1.0/tests/test_bundle_errors.py +88 -0
- motion_studio-0.1.0/tests/test_cli.py +299 -0
- motion_studio-0.1.0/tests/test_convert.py +149 -0
- motion_studio-0.1.0/tests/test_import_light.py +94 -0
- motion_studio-0.1.0/tests/test_info_routes.py +100 -0
- motion_studio-0.1.0/tests/test_io.py +144 -0
- motion_studio-0.1.0/tests/test_library.py +113 -0
- motion_studio-0.1.0/tests/test_plugin_contract.py +165 -0
- motion_studio-0.1.0/tests/test_plugins.py +276 -0
- motion_studio-0.1.0/tests/test_server.py +179 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Melissa Colin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: motion-studio
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A clean, installable multi-person SMPL motion editor with pluggable auto-correction and metrics.
|
|
5
|
+
Author-email: Melissa Colin <github@melissacolin.ai>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/melissa-colin/motion-studio
|
|
8
|
+
Project-URL: Repository, https://github.com/melissa-colin/motion-studio
|
|
9
|
+
Project-URL: Documentation, https://github.com/melissa-colin/motion-studio#readme
|
|
10
|
+
Project-URL: Issues, https://github.com/melissa-colin/motion-studio/issues
|
|
11
|
+
Keywords: smpl,motion,motion-capture,3d,animation,pose,dance,editor,visualization
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
19
|
+
Classifier: Intended Audience :: Science/Research
|
|
20
|
+
Classifier: Topic :: Multimedia :: Graphics :: 3D Modeling
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: numpy
|
|
25
|
+
Requires-Dist: flask>=3.0
|
|
26
|
+
Provides-Extra: smpl
|
|
27
|
+
Requires-Dist: torch; extra == "smpl"
|
|
28
|
+
Requires-Dist: smplx; extra == "smpl"
|
|
29
|
+
Provides-Extra: video
|
|
30
|
+
Requires-Dist: pillow; extra == "video"
|
|
31
|
+
Requires-Dist: torchvision; extra == "video"
|
|
32
|
+
Requires-Dist: librosa; extra == "video"
|
|
33
|
+
Requires-Dist: scipy; extra == "video"
|
|
34
|
+
Provides-Extra: all
|
|
35
|
+
Requires-Dist: torch; extra == "all"
|
|
36
|
+
Requires-Dist: smplx; extra == "all"
|
|
37
|
+
Requires-Dist: pillow; extra == "all"
|
|
38
|
+
Requires-Dist: torchvision; extra == "all"
|
|
39
|
+
Requires-Dist: librosa; extra == "all"
|
|
40
|
+
Requires-Dist: scipy; extra == "all"
|
|
41
|
+
Provides-Extra: dev
|
|
42
|
+
Requires-Dist: pytest; extra == "dev"
|
|
43
|
+
Dynamic: license-file
|
|
44
|
+
|
|
45
|
+
# Motion Studio
|
|
46
|
+
|
|
47
|
+
[](https://pypi.org/project/motion-studio/)
|
|
48
|
+
[](https://pypi.org/project/motion-studio/)
|
|
49
|
+
[](LICENSE)
|
|
50
|
+
[](https://github.com/melissa-colin/motion-studio/actions/workflows/ci.yml)
|
|
51
|
+
[](https://github.com/astral-sh/ruff)
|
|
52
|
+
[](https://github.com/psf/black)
|
|
53
|
+
|
|
54
|
+
A clean, installable **multi-person SMPL motion editor** for the browser, with
|
|
55
|
+
a 3D view (Three.js), a video/music background, frame-by-frame pose editing,
|
|
56
|
+
and **pluggable** auto-correction and metrics.
|
|
57
|
+
|
|
58
|
+
It started as an internal pose editor for the AIOZ-GDANCE dataset and was
|
|
59
|
+
rewritten as a small, modular, self-contained tool: no dependency on any
|
|
60
|
+
external research repo, a single save-file format, and a plugin contract so you
|
|
61
|
+
can drop in **your own** correction / metrics classes without touching the app.
|
|
62
|
+
|
|
63
|
+

|
|
64
|
+
|
|
65
|
+
*Editing a multi-person clip: the SMPL motion and the source video play in sync while you orbit the camera.*
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Features
|
|
70
|
+
|
|
71
|
+
- **3D editor**: skeleton + SMPL mesh, per-joint and whole-body editing, undo/redo.
|
|
72
|
+
- **Video & music background**: place the source video in the scene (position,
|
|
73
|
+
scale, opacity, time offset), server-side background removal, audio playback
|
|
74
|
+
synced to the timeline.
|
|
75
|
+
- **Floor**: estimate / edit / recompute the ground plane.
|
|
76
|
+
- **Auto-correction & metrics**: run a correction pipeline and score the motion;
|
|
77
|
+
both are **plugins** (built-in by default, swappable for your own).
|
|
78
|
+
- **One save file**: a `.motion` bundle holds the original motion, your edits,
|
|
79
|
+
the video, the music, placement parameters, comments and a metrics snapshot.
|
|
80
|
+
- **Two ways to load**: open a single SMPL `.pkl` (with optional video/music),
|
|
81
|
+
or point at a whole `GDance/` folder and convert it into the workspace.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Screenshots
|
|
86
|
+
|
|
87
|
+

|
|
88
|
+
*The 3D editor: multiple SMPL dancers (mesh + skeleton), per-joint and
|
|
89
|
+
whole-body editing, the automatic corrector (a plugin), and the live metrics
|
|
90
|
+
panel. One **Save (.motion)** button and an **Export (.pkl)** button.*
|
|
91
|
+
|
|
92
|
+

|
|
93
|
+
*The source video as a placeable background — position, scale, opacity, time
|
|
94
|
+
offset and server-side background removal — to check the motion against the
|
|
95
|
+
real footage.*
|
|
96
|
+
|
|
97
|
+

|
|
98
|
+
*The ground plane is estimated on load (RANSAC on foot contacts) and can be
|
|
99
|
+
edited or recomputed.*
|
|
100
|
+
|
|
101
|
+

|
|
102
|
+
*On startup the editor is empty (no clip auto-loaded, no pop-up). From the
|
|
103
|
+
**Clip** tab you choose: browse loaded clips, open a folder, or import a .pkl.*
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Install
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
pip install "motion-studio[all]" # recommended: full feature set
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`[all]` pulls in torch, smplx, torchvision, pillow, librosa and scipy — the SMPL
|
|
114
|
+
forward kinematics, pose refit, built-in corrector/metrics, server-side
|
|
115
|
+
background removal and music import. Narrower extras are available:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
pip install motion-studio # core only (numpy + flask): no SMPL/video
|
|
119
|
+
pip install "motion-studio[smpl]" # + torch, smplx
|
|
120
|
+
pip install "motion-studio[video]" # + pillow, torchvision, librosa, scipy
|
|
121
|
+
pip install "motion-studio[dev]" # + pytest, ruff, black, pre-commit
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Requires Python >= 3.9. The SMPL body models are **not** shipped; point the tool
|
|
125
|
+
at your local copy with `--smpl-dir`.
|
|
126
|
+
|
|
127
|
+
## Run
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
motion-studio --workspace ~/MotionStudio --smpl-dir ~/smpl/models --port 8815
|
|
131
|
+
# optional: import a raw dataset folder
|
|
132
|
+
motion-studio --data /path/to/GDance --smpl-dir ~/smpl/models
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Then open <http://127.0.0.1:8815>.
|
|
136
|
+
|
|
137
|
+
The **workspace** is the root folder that holds your saved `.motion` bundles,
|
|
138
|
+
like any desktop app keeps its documents.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Command line
|
|
143
|
+
|
|
144
|
+
The bare command (or `motion-studio serve`) starts the editor server. Two
|
|
145
|
+
headless subcommands run the same plugins from a terminal, no browser:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
motion-studio serve --smpl-dir ~/smpl/models # start the editor (default)
|
|
149
|
+
|
|
150
|
+
# Run the auto-corrector on a .motion and write the corrected bundle:
|
|
151
|
+
motion-studio correct in.motion -o out.motion
|
|
152
|
+
motion-studio correct in.motion -o out.motion --corrector ./my.py:MyCorrector
|
|
153
|
+
|
|
154
|
+
# Compute metrics on a .motion and print them:
|
|
155
|
+
motion-studio metrics in.motion
|
|
156
|
+
motion-studio metrics in.motion --metrics ./my.py:MyMetrics
|
|
157
|
+
|
|
158
|
+
motion-studio --version
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`--corrector` / `--metrics` take a plugin spec (`<module-or-path>:<Class>`),
|
|
162
|
+
the same as the server flags; omit them to use the built-in plugins.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Docker
|
|
167
|
+
|
|
168
|
+
A `Dockerfile` is included. Build the image, then run it with your SMPL models
|
|
169
|
+
mounted in and the port published:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
docker build -t motion-studio .
|
|
173
|
+
docker run -p 8815:8815 -v ~/smpl/models:/models -e SMPL_DIR=/models motion-studio
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Then open <http://127.0.0.1:8815>. Mount a host folder to `/workspace` (and set
|
|
177
|
+
`MOTION_STUDIO_HOME=/workspace`) to persist your `.motion` bundles across runs.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Python API
|
|
182
|
+
|
|
183
|
+
Motion Studio is also a library — import it and use the data model, bundle I/O
|
|
184
|
+
and plugin loaders directly, headless:
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
import motion_studio as ms
|
|
188
|
+
|
|
189
|
+
bundle = ms.load_bundle("session.motion") # -> Bundle (original/edited + media)
|
|
190
|
+
motion = bundle.original # -> Motion(poses, trans, betas, …)
|
|
191
|
+
|
|
192
|
+
corrector = ms.load_corrector(
|
|
193
|
+
"motion_studio.plugins_builtin.corrector:Corrector",
|
|
194
|
+
smpl_dir="~/smpl/models",
|
|
195
|
+
)
|
|
196
|
+
fixed = corrector.correct(motion)
|
|
197
|
+
|
|
198
|
+
ms.save_bundle("fixed.motion", original=motion, edited=fixed)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
The top-level surface (`__all__`) is: `Motion`, `Floor`, `load_bundle`,
|
|
202
|
+
`save_bundle`, `load_corrector`, `load_metrics`, `scan_dataset`, `Config`,
|
|
203
|
+
`__version__`. The library core imports with only numpy + flask (no torch).
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## The `.motion` save file
|
|
208
|
+
|
|
209
|
+
A `.motion` file is a single ZIP archive bundling an entire editing session:
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
manifest.json format/version, comments, video placement params,
|
|
213
|
+
metrics snapshot, timestamps
|
|
214
|
+
motion_original.npz the original SMPL motion (poses, trans, betas, fps, gender)
|
|
215
|
+
motion_edited.npz your edited motion (omitted if you have not edited)
|
|
216
|
+
video.mp4 the source video (optional)
|
|
217
|
+
music.<ext> the music track (optional)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Open a `.motion` to **continue** where you left off; the SMPL `.pkl` is written
|
|
221
|
+
out only when you click **Export**.
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Plugins: bring your own correction / metrics
|
|
226
|
+
|
|
227
|
+
Motion Studio depends only on a small contract, not on any concrete
|
|
228
|
+
implementation. A plugin is any class matching one of these protocols
|
|
229
|
+
(`motion_studio/core/plugins.py`):
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
class MotionCorrector(Protocol):
|
|
233
|
+
def __init__(self, *, smpl_dir, floor=None): ...
|
|
234
|
+
def correct(self, motion: Motion, log=print) -> Motion: ...
|
|
235
|
+
|
|
236
|
+
class MotionMetrics(Protocol):
|
|
237
|
+
def __init__(self, *, smpl_dir): ...
|
|
238
|
+
def compute(self, motion: Motion, floor: Floor) -> dict[str, float]: ...
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
`Motion` and `Floor` are the only types exchanged
|
|
242
|
+
(`motion_studio/core/types.py`):
|
|
243
|
+
|
|
244
|
+
```python
|
|
245
|
+
@dataclass
|
|
246
|
+
class Motion:
|
|
247
|
+
poses: np.ndarray # (N, T, 24, 3) axis-angle, z-up
|
|
248
|
+
trans: np.ndarray # (N, T, 3)
|
|
249
|
+
betas: np.ndarray | None
|
|
250
|
+
gender: str
|
|
251
|
+
fps: float
|
|
252
|
+
name: str
|
|
253
|
+
|
|
254
|
+
@dataclass
|
|
255
|
+
class Floor:
|
|
256
|
+
plane: tuple[float, float, float] # z = a*x + b*y + c
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Point the tool at your own classes (a dotted module **or** a file path,
|
|
260
|
+
followed by the class name); they are re-imported on every run, so edits take
|
|
261
|
+
effect on the next click:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
motion-studio --corrector ./my_corrector.py:MyCorrector \
|
|
265
|
+
--metrics ./my_metrics.py:MyMetrics
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
The built-in plugins live in `motion_studio/plugins_builtin/`.
|
|
269
|
+
|
|
270
|
+
**New to plugins?** See the full authoring guide in
|
|
271
|
+
[`docs/PLUGINS.md`](docs/PLUGINS.md) and the runnable, dependency-light
|
|
272
|
+
examples in [`examples/`](examples). For instance, the pure-numpy metrics
|
|
273
|
+
example (no torch / no SMPL needed) shows custom keys rendering end to end:
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
motion-studio --metrics ./examples/simple_metrics.py:SimpleMetrics
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Documentation
|
|
282
|
+
|
|
283
|
+
- [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — module map, data model,
|
|
284
|
+
request flow, the heavy-lock model, the `.motion` format.
|
|
285
|
+
- [`docs/PLUGINS.md`](docs/PLUGINS.md) — the plugin authoring guide.
|
|
286
|
+
- [`docs/USAGE.md`](docs/USAGE.md) — day-to-day editor usage.
|
|
287
|
+
- [`docs/API.md`](docs/API.md) — the HTTP API.
|
|
288
|
+
- [`CONTRIBUTING.md`](CONTRIBUTING.md) — dev install, tests, lint/format.
|
|
289
|
+
- [`SECURITY.md`](SECURITY.md) — the threat model and `--allow-remote` risk.
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## License
|
|
294
|
+
|
|
295
|
+
Released under the **MIT License** (see the `LICENSE` file in the repository).
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# Motion Studio
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/motion-studio/)
|
|
4
|
+
[](https://pypi.org/project/motion-studio/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://github.com/melissa-colin/motion-studio/actions/workflows/ci.yml)
|
|
7
|
+
[](https://github.com/astral-sh/ruff)
|
|
8
|
+
[](https://github.com/psf/black)
|
|
9
|
+
|
|
10
|
+
A clean, installable **multi-person SMPL motion editor** for the browser, with
|
|
11
|
+
a 3D view (Three.js), a video/music background, frame-by-frame pose editing,
|
|
12
|
+
and **pluggable** auto-correction and metrics.
|
|
13
|
+
|
|
14
|
+
It started as an internal pose editor for the AIOZ-GDANCE dataset and was
|
|
15
|
+
rewritten as a small, modular, self-contained tool: no dependency on any
|
|
16
|
+
external research repo, a single save-file format, and a plugin contract so you
|
|
17
|
+
can drop in **your own** correction / metrics classes without touching the app.
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+
|
|
21
|
+
*Editing a multi-person clip: the SMPL motion and the source video play in sync while you orbit the camera.*
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
- **3D editor**: skeleton + SMPL mesh, per-joint and whole-body editing, undo/redo.
|
|
28
|
+
- **Video & music background**: place the source video in the scene (position,
|
|
29
|
+
scale, opacity, time offset), server-side background removal, audio playback
|
|
30
|
+
synced to the timeline.
|
|
31
|
+
- **Floor**: estimate / edit / recompute the ground plane.
|
|
32
|
+
- **Auto-correction & metrics**: run a correction pipeline and score the motion;
|
|
33
|
+
both are **plugins** (built-in by default, swappable for your own).
|
|
34
|
+
- **One save file**: a `.motion` bundle holds the original motion, your edits,
|
|
35
|
+
the video, the music, placement parameters, comments and a metrics snapshot.
|
|
36
|
+
- **Two ways to load**: open a single SMPL `.pkl` (with optional video/music),
|
|
37
|
+
or point at a whole `GDance/` folder and convert it into the workspace.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Screenshots
|
|
42
|
+
|
|
43
|
+

|
|
44
|
+
*The 3D editor: multiple SMPL dancers (mesh + skeleton), per-joint and
|
|
45
|
+
whole-body editing, the automatic corrector (a plugin), and the live metrics
|
|
46
|
+
panel. One **Save (.motion)** button and an **Export (.pkl)** button.*
|
|
47
|
+
|
|
48
|
+

|
|
49
|
+
*The source video as a placeable background — position, scale, opacity, time
|
|
50
|
+
offset and server-side background removal — to check the motion against the
|
|
51
|
+
real footage.*
|
|
52
|
+
|
|
53
|
+

|
|
54
|
+
*The ground plane is estimated on load (RANSAC on foot contacts) and can be
|
|
55
|
+
edited or recomputed.*
|
|
56
|
+
|
|
57
|
+

|
|
58
|
+
*On startup the editor is empty (no clip auto-loaded, no pop-up). From the
|
|
59
|
+
**Clip** tab you choose: browse loaded clips, open a folder, or import a .pkl.*
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Install
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install "motion-studio[all]" # recommended: full feature set
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`[all]` pulls in torch, smplx, torchvision, pillow, librosa and scipy — the SMPL
|
|
70
|
+
forward kinematics, pose refit, built-in corrector/metrics, server-side
|
|
71
|
+
background removal and music import. Narrower extras are available:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pip install motion-studio # core only (numpy + flask): no SMPL/video
|
|
75
|
+
pip install "motion-studio[smpl]" # + torch, smplx
|
|
76
|
+
pip install "motion-studio[video]" # + pillow, torchvision, librosa, scipy
|
|
77
|
+
pip install "motion-studio[dev]" # + pytest, ruff, black, pre-commit
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Requires Python >= 3.9. The SMPL body models are **not** shipped; point the tool
|
|
81
|
+
at your local copy with `--smpl-dir`.
|
|
82
|
+
|
|
83
|
+
## Run
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
motion-studio --workspace ~/MotionStudio --smpl-dir ~/smpl/models --port 8815
|
|
87
|
+
# optional: import a raw dataset folder
|
|
88
|
+
motion-studio --data /path/to/GDance --smpl-dir ~/smpl/models
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Then open <http://127.0.0.1:8815>.
|
|
92
|
+
|
|
93
|
+
The **workspace** is the root folder that holds your saved `.motion` bundles,
|
|
94
|
+
like any desktop app keeps its documents.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Command line
|
|
99
|
+
|
|
100
|
+
The bare command (or `motion-studio serve`) starts the editor server. Two
|
|
101
|
+
headless subcommands run the same plugins from a terminal, no browser:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
motion-studio serve --smpl-dir ~/smpl/models # start the editor (default)
|
|
105
|
+
|
|
106
|
+
# Run the auto-corrector on a .motion and write the corrected bundle:
|
|
107
|
+
motion-studio correct in.motion -o out.motion
|
|
108
|
+
motion-studio correct in.motion -o out.motion --corrector ./my.py:MyCorrector
|
|
109
|
+
|
|
110
|
+
# Compute metrics on a .motion and print them:
|
|
111
|
+
motion-studio metrics in.motion
|
|
112
|
+
motion-studio metrics in.motion --metrics ./my.py:MyMetrics
|
|
113
|
+
|
|
114
|
+
motion-studio --version
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`--corrector` / `--metrics` take a plugin spec (`<module-or-path>:<Class>`),
|
|
118
|
+
the same as the server flags; omit them to use the built-in plugins.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Docker
|
|
123
|
+
|
|
124
|
+
A `Dockerfile` is included. Build the image, then run it with your SMPL models
|
|
125
|
+
mounted in and the port published:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
docker build -t motion-studio .
|
|
129
|
+
docker run -p 8815:8815 -v ~/smpl/models:/models -e SMPL_DIR=/models motion-studio
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Then open <http://127.0.0.1:8815>. Mount a host folder to `/workspace` (and set
|
|
133
|
+
`MOTION_STUDIO_HOME=/workspace`) to persist your `.motion` bundles across runs.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Python API
|
|
138
|
+
|
|
139
|
+
Motion Studio is also a library — import it and use the data model, bundle I/O
|
|
140
|
+
and plugin loaders directly, headless:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
import motion_studio as ms
|
|
144
|
+
|
|
145
|
+
bundle = ms.load_bundle("session.motion") # -> Bundle (original/edited + media)
|
|
146
|
+
motion = bundle.original # -> Motion(poses, trans, betas, …)
|
|
147
|
+
|
|
148
|
+
corrector = ms.load_corrector(
|
|
149
|
+
"motion_studio.plugins_builtin.corrector:Corrector",
|
|
150
|
+
smpl_dir="~/smpl/models",
|
|
151
|
+
)
|
|
152
|
+
fixed = corrector.correct(motion)
|
|
153
|
+
|
|
154
|
+
ms.save_bundle("fixed.motion", original=motion, edited=fixed)
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The top-level surface (`__all__`) is: `Motion`, `Floor`, `load_bundle`,
|
|
158
|
+
`save_bundle`, `load_corrector`, `load_metrics`, `scan_dataset`, `Config`,
|
|
159
|
+
`__version__`. The library core imports with only numpy + flask (no torch).
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## The `.motion` save file
|
|
164
|
+
|
|
165
|
+
A `.motion` file is a single ZIP archive bundling an entire editing session:
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
manifest.json format/version, comments, video placement params,
|
|
169
|
+
metrics snapshot, timestamps
|
|
170
|
+
motion_original.npz the original SMPL motion (poses, trans, betas, fps, gender)
|
|
171
|
+
motion_edited.npz your edited motion (omitted if you have not edited)
|
|
172
|
+
video.mp4 the source video (optional)
|
|
173
|
+
music.<ext> the music track (optional)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Open a `.motion` to **continue** where you left off; the SMPL `.pkl` is written
|
|
177
|
+
out only when you click **Export**.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Plugins: bring your own correction / metrics
|
|
182
|
+
|
|
183
|
+
Motion Studio depends only on a small contract, not on any concrete
|
|
184
|
+
implementation. A plugin is any class matching one of these protocols
|
|
185
|
+
(`motion_studio/core/plugins.py`):
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
class MotionCorrector(Protocol):
|
|
189
|
+
def __init__(self, *, smpl_dir, floor=None): ...
|
|
190
|
+
def correct(self, motion: Motion, log=print) -> Motion: ...
|
|
191
|
+
|
|
192
|
+
class MotionMetrics(Protocol):
|
|
193
|
+
def __init__(self, *, smpl_dir): ...
|
|
194
|
+
def compute(self, motion: Motion, floor: Floor) -> dict[str, float]: ...
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
`Motion` and `Floor` are the only types exchanged
|
|
198
|
+
(`motion_studio/core/types.py`):
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
@dataclass
|
|
202
|
+
class Motion:
|
|
203
|
+
poses: np.ndarray # (N, T, 24, 3) axis-angle, z-up
|
|
204
|
+
trans: np.ndarray # (N, T, 3)
|
|
205
|
+
betas: np.ndarray | None
|
|
206
|
+
gender: str
|
|
207
|
+
fps: float
|
|
208
|
+
name: str
|
|
209
|
+
|
|
210
|
+
@dataclass
|
|
211
|
+
class Floor:
|
|
212
|
+
plane: tuple[float, float, float] # z = a*x + b*y + c
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Point the tool at your own classes (a dotted module **or** a file path,
|
|
216
|
+
followed by the class name); they are re-imported on every run, so edits take
|
|
217
|
+
effect on the next click:
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
motion-studio --corrector ./my_corrector.py:MyCorrector \
|
|
221
|
+
--metrics ./my_metrics.py:MyMetrics
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
The built-in plugins live in `motion_studio/plugins_builtin/`.
|
|
225
|
+
|
|
226
|
+
**New to plugins?** See the full authoring guide in
|
|
227
|
+
[`docs/PLUGINS.md`](docs/PLUGINS.md) and the runnable, dependency-light
|
|
228
|
+
examples in [`examples/`](examples). For instance, the pure-numpy metrics
|
|
229
|
+
example (no torch / no SMPL needed) shows custom keys rendering end to end:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
motion-studio --metrics ./examples/simple_metrics.py:SimpleMetrics
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Documentation
|
|
238
|
+
|
|
239
|
+
- [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — module map, data model,
|
|
240
|
+
request flow, the heavy-lock model, the `.motion` format.
|
|
241
|
+
- [`docs/PLUGINS.md`](docs/PLUGINS.md) — the plugin authoring guide.
|
|
242
|
+
- [`docs/USAGE.md`](docs/USAGE.md) — day-to-day editor usage.
|
|
243
|
+
- [`docs/API.md`](docs/API.md) — the HTTP API.
|
|
244
|
+
- [`CONTRIBUTING.md`](CONTRIBUTING.md) — dev install, tests, lint/format.
|
|
245
|
+
- [`SECURITY.md`](SECURITY.md) — the threat model and `--allow-remote` risk.
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## License
|
|
250
|
+
|
|
251
|
+
Released under the **MIT License** (see the `LICENSE` file in the repository).
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Motion Studio: a clean, installable multi-person SMPL motion editor.
|
|
2
|
+
|
|
3
|
+
The names re-exported here are the package's stable public API. Everything in
|
|
4
|
+
this top-level namespace is torch-free and safe to import in a numpy+flask-only
|
|
5
|
+
("core") install: the heavy server stack and the built-in torch/SMPL plugins
|
|
6
|
+
are imported lazily, never at package import time.
|
|
7
|
+
|
|
8
|
+
Typical use::
|
|
9
|
+
|
|
10
|
+
import motion_studio as ms
|
|
11
|
+
|
|
12
|
+
bundle = ms.load_bundle("session.motion")
|
|
13
|
+
motion = bundle.original # ms.Motion
|
|
14
|
+
corrector = ms.load_corrector(ms.Config().corrector_spec,
|
|
15
|
+
smpl_dir="~/smpl/models")
|
|
16
|
+
fixed = corrector.correct(motion)
|
|
17
|
+
ms.save_motion_pkl(fixed, "fixed.pkl")
|
|
18
|
+
"""
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from .bundle import load_bundle, save_bundle
|
|
22
|
+
from .config import Config
|
|
23
|
+
from .core.plugins import (MotionCorrector, MotionMetrics, load_corrector,
|
|
24
|
+
load_metrics)
|
|
25
|
+
from .core.types import Floor, Motion
|
|
26
|
+
from .library import import_dataset, scan_dataset
|
|
27
|
+
from .smpl.io import load_motion_pkl, save_motion_pkl
|
|
28
|
+
|
|
29
|
+
__version__ = "0.1.0"
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
# Core data types.
|
|
33
|
+
"Motion",
|
|
34
|
+
"Floor",
|
|
35
|
+
# Plugin contracts + loaders.
|
|
36
|
+
"MotionCorrector",
|
|
37
|
+
"MotionMetrics",
|
|
38
|
+
"load_corrector",
|
|
39
|
+
"load_metrics",
|
|
40
|
+
# SMPL pkl I/O.
|
|
41
|
+
"load_motion_pkl",
|
|
42
|
+
"save_motion_pkl",
|
|
43
|
+
# .motion bundle I/O.
|
|
44
|
+
"save_bundle",
|
|
45
|
+
"load_bundle",
|
|
46
|
+
# Dataset discovery / import.
|
|
47
|
+
"scan_dataset",
|
|
48
|
+
"import_dataset",
|
|
49
|
+
# Configuration.
|
|
50
|
+
"Config",
|
|
51
|
+
"__version__",
|
|
52
|
+
]
|