cozyclay 1.9.0 → 2.0.0
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.
- package/CHANGELOG.md +171 -0
- package/bin/agent/agent-routes.mjs +74 -70
- package/bin/agent/agent-runner.mjs +10 -3
- package/bin/agent/motion-runtime.mjs +32 -260
- package/bin/agent/providers.mjs +46 -3
- package/bin/agent/session-store.mjs +41 -5
- package/bin/agent/studio-prompt.mjs +1 -1
- package/bin/agent/studio-tools.mjs +48 -11
- package/bin/cozyclay.mjs +26 -8
- package/bin/live/cli.mjs +22 -3
- package/bin/mcp-runtime.mjs +35 -20
- package/bin/telemetry-state.mjs +5 -2
- package/bin/update-check.mjs +15 -0
- package/dist/app/index.html +6 -6
- package/dist/assets/analytics-MVZOEW8O.js +1 -0
- package/dist/assets/app-C1sdVbg4.css +1 -0
- package/dist/assets/app-CA55tcI3.js +4876 -0
- package/dist/assets/{demo-B-eg1CT8.js → demo-ByeYp-dY.js} +1 -1
- package/dist/assets/{first-shot-handoff-CGgnvNu8.js → first-shot-handoff-DgT28ZiV.js} +1 -1
- package/dist/assets/{landing-IbRp3FnQ.js → landing-BaXkpFip.js} +1 -1
- package/dist/assets/shot-prompt-CbqWcWYi.css +1 -0
- package/dist/assets/shot-prompt-Yrt99wZx.js +17 -0
- package/dist/assets/{ticket-BgIzOBOf.js → ticket-BcmsnHn5.js} +1 -1
- package/dist/assets/workflow-DRdwoDBT.css +1 -0
- package/dist/assets/workflow-dNPGY9xz.js +179 -0
- package/dist/cozyclay-package.json +1 -1
- package/dist/fonts/IBM-Plex-Sans-OFL.txt +92 -0
- package/dist/fonts/JetBrains-Mono-OFL.txt +93 -0
- package/dist/fonts/README.md +13 -9
- package/dist/fonts/ibm-plex-sans-400-latin.woff2 +0 -0
- package/dist/fonts/ibm-plex-sans-500-latin.woff2 +0 -0
- package/dist/fonts/ibm-plex-sans-600-latin.woff2 +0 -0
- package/dist/fonts/jetbrains-mono-400-latin.woff2 +0 -0
- package/dist/fonts/jetbrains-mono-500-latin.woff2 +0 -0
- package/dist/index.html +7 -7
- package/dist/privacy/index.html +13 -8
- package/dist/sitemap.xml +6 -6
- package/dist/workflow/index.html +5 -5
- package/mcp/LIVE-PROTOCOL.md +1 -1
- package/mcp/live-hub.mjs +35 -93
- package/mcp/mesh-file.mjs +48 -0
- package/mcp/server.mjs +6 -77
- package/mcp/tool-handlers.mjs +224 -377
- package/package.json +2 -1
- package/src/App.jsx +1273 -9075
- package/src/analytics.js +393 -9
- package/src/app-context.js +211 -0
- package/src/app-stage.jsx +25 -24
- package/src/ardy/auto-fix-panel.css +108 -0
- package/src/ardy/collision-blockers.js +9 -3
- package/src/ardy/fix-collisions.js +10 -66
- package/src/ardy/ground.js +4 -4
- package/src/ardy/ik-drag.js +101 -0
- package/src/ardy/ik-key-json.js +43 -0
- package/src/ardy/ik.js +122 -76
- package/src/ardy/physics-panel.css +2 -0
- package/src/ardy/physics-panel.jsx +17 -5
- package/src/ardy/platform-fit-panel.css +64 -0
- package/src/ardy/platform-fit-panel.jsx +40 -0
- package/src/ardy/platform-fit.js +326 -0
- package/src/ardy/playback.js +166 -20
- package/src/ardy/range-pin.js +299 -0
- package/src/ardy/timeline-coordinates.js +26 -0
- package/src/ardy/timeline.css +1199 -0
- package/src/ardy/timeline.jsx +149 -41
- package/src/ardy/waypoints.js +2 -2
- package/src/asset-pane.css +462 -0
- package/src/asset-pane.jsx +461 -232
- package/src/command-bus.js +349 -0
- package/src/commands/ai.js +31 -0
- package/src/commands/cast.js +140 -0
- package/src/commands/elements/character.js +13 -0
- package/src/commands/elements/motion.js +14 -0
- package/src/commands/elements/object.js +22 -0
- package/src/commands/elements/scene.js +16 -0
- package/src/commands/elements/shot.js +9 -0
- package/src/commands/elements/stage.js +11 -0
- package/src/commands/elements.js +155 -0
- package/src/commands/export.js +20 -0
- package/src/commands/index.js +40 -0
- package/src/commands/motion.js +231 -0
- package/src/commands/objects.js +174 -0
- package/src/commands/project.js +53 -0
- package/src/commands/scene.js +81 -0
- package/src/commands/shared.js +19 -0
- package/src/commands/shot.js +103 -0
- package/src/commands/stage.js +17 -0
- package/src/commands/view.js +42 -0
- package/src/document-store.js +144 -0
- package/src/domains/cast.js +1007 -0
- package/src/domains/motion.js +3219 -0
- package/src/domains/objects.js +932 -0
- package/src/domains/scenes.js +817 -0
- package/src/domains/shots.js +404 -0
- package/src/domains/stage.js +93 -0
- package/src/dualview.jsx +113 -57
- package/src/facing-marks.js +3 -0
- package/src/first-success-guide.jsx +14 -14
- package/src/grid-view.js +18 -5
- package/src/hierarchy-model.js +24 -16
- package/src/hierarchy-panel.css +386 -0
- package/src/hierarchy-panel.jsx +150 -21
- package/src/{fal-motion-client.js → i2v-motion-client.js} +13 -13
- package/src/i2v-motion-studio.jsx +180 -0
- package/src/ik-camera.js +3 -0
- package/src/main.jsx +6 -0
- package/src/motion/generation.js +105 -0
- package/src/motion-readiness-ui.jsx +4 -4
- package/src/motion-readiness.js +11 -0
- package/src/motion-trail.js +366 -9
- package/src/object-gizmo.jsx +7 -1
- package/src/otio.js +2 -1
- package/src/panels/CameraPanel.jsx +60 -0
- package/src/panels/CharacterTransformPanel.jsx +48 -0
- package/src/panels/EnvironmentPanel.jsx +40 -0
- package/src/panels/Foldout.jsx +32 -0
- package/src/panels/LightPanel.jsx +24 -0
- package/src/panels/ObjectTransformPanel.jsx +554 -0
- package/src/panels/PosePanel.jsx +97 -0
- package/src/panels/ProjectPanel.jsx +33 -0
- package/src/panels/PromptBlocksPanel.jsx +442 -0
- package/src/panels/PropsPanel.jsx +70 -0
- package/src/panels/ReferenceImageField.jsx +91 -0
- package/src/panels/RigControlPanel.jsx +78 -0
- package/src/panels/RigPanel.jsx +37 -0
- package/src/panels/SubjectBox.jsx +47 -0
- package/src/panels/SubjectsPanel.jsx +39 -0
- package/src/panels/VideoCapturePanel.jsx +180 -0
- package/src/panels/details.css +1171 -0
- package/src/panels/motion.css +197 -0
- package/src/panels/pose.css +39 -0
- package/src/planview.jsx +60 -31
- package/src/posestudio.jsx +59 -4
- package/src/project-browser.css +1073 -0
- package/src/project-browser.jsx +284 -123
- package/src/range-pin-object-transform.js +19 -0
- package/src/range-pin-panel.css +518 -0
- package/src/range-pin-panel.jsx +344 -0
- package/src/result-modal.jsx +5 -5
- package/src/room.jsx +79 -51
- package/src/scene-objects.js +37 -1
- package/src/scenes.js +5 -0
- package/src/semantic-edit.js +2 -0
- package/src/settings-menu.jsx +39 -105
- package/src/shell/BottomDock.jsx +450 -0
- package/src/shell/DetailsSlot.jsx +513 -0
- package/src/shell/LibrarySlot.jsx +86 -0
- package/src/shell/MenuBar.jsx +601 -0
- package/src/shell/OutlinerSlot.jsx +50 -0
- package/src/shell/PreferencesDialog.jsx +538 -0
- package/src/shell/PreferencesSlot.jsx +40 -0
- package/src/shell/StatusBar.jsx +125 -0
- package/src/shell/StudioShell.jsx +92 -0
- package/src/shell/TopBar.jsx +129 -0
- package/src/shell/ViewportToolbar.jsx +656 -0
- package/src/shell/agent-glass.css +296 -0
- package/src/shell/dock.css +93 -0
- package/src/shell/glass-regions.css +985 -0
- package/src/shell/glass.css +687 -0
- package/src/shell/log-store.js +52 -0
- package/src/shell/mode.css +9 -0
- package/src/shell/preferences.css +651 -0
- package/src/shell/shell.css +282 -0
- package/src/shell/studio-shell-context.js +19 -0
- package/src/shell/topbar.css +435 -0
- package/src/shell/viewport.css +451 -0
- package/src/store/authored-intent.js +22 -0
- package/src/store/runtime-adapters.js +92 -0
- package/src/store/scene-stage.js +21 -0
- package/src/store/use-document-store.js +14 -0
- package/src/studio-actions.js +210 -0
- package/src/studio-agent-commands.js +41 -271
- package/src/studio-agent-context.js +39 -10
- package/src/studio-agent-motion.js +122 -373
- package/src/studio-agent-protocol.js +67 -31
- package/src/studio-app-binding.js +379 -0
- package/src/studio-contact-sheet.js +73 -0
- package/src/studio-elements.js +58 -51
- package/src/styles/themes.css +200 -0
- package/src/styles/tokens.css +69 -0
- package/src/styles.css +460 -1742
- package/src/theme.js +44 -0
- package/src/timeline-extent.js +16 -0
- package/src/trail-key-conflicts.js +39 -0
- package/src/trail-pick.js +59 -0
- package/src/ui.jsx +8 -8
- package/src/use-case-question.jsx +66 -0
- package/src/workflow/AgentPanel.jsx +82 -69
- package/src/workflow/agent-client.js +12 -2
- package/src/workflow/agent-panel.css +34 -32
- package/src/workflow/cozy-scene-node.css +2 -0
- package/src/workflow/workflow.css +2 -0
- package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
- package/tools/ardy/bridge.mjs +164 -146
- package/tools/ardy/visual-qa.mjs +5 -5
- package/tools/bench/EXP3.md +61 -0
- package/tools/bench/cclay_bench_extract_incam.py +125 -0
- package/tools/bench/cclay_bench_extract_obs.py +204 -0
- package/tools/bench/cclay_bench_runner.py +42 -0
- package/tools/bench/cube-contact.mjs +162 -0
- package/tools/bench/exp3.mjs +152 -0
- package/tools/bench/extract-bench-lib.mjs +216 -0
- package/tools/bench/extract-bench.mjs +302 -0
- package/tools/bench/fal-generate.mjs +88 -0
- package/tools/bench/fit/README.md +189 -0
- package/tools/bench/fit/camera.mjs +59 -0
- package/tools/bench/fit/contact.mjs +419 -0
- package/tools/bench/fit/footlock.mjs +234 -0
- package/tools/bench/fit/motion.mjs +85 -0
- package/tools/bench/fit/pin.mjs +27 -0
- package/tools/bench/fit/remote.mjs +105 -0
- package/tools/bench/fit-bench.mjs +126 -0
- package/tools/bench/fit-sanity.mjs +45 -0
- package/tools/bench/metrics.mjs +313 -0
- package/tools/bench/obs/depth.mjs +258 -0
- package/tools/bench/obs/extrinsics.mjs +175 -0
- package/tools/bench/obs/fit_mannequin_betas.py +340 -0
- package/tools/bench/obs/ground.mjs +294 -0
- package/tools/bench/obs/heading.mjs +151 -0
- package/tools/bench/obs/ladder.mjs +555 -0
- package/tools/bench/obs/mannequin-betas.json +124 -0
- package/tools/bench/obs/remote.mjs +83 -0
- package/tools/bench/obs/rest_joints.py +52 -0
- package/tools/bench/obs/ybot-targets.mjs +49 -0
- package/tools/bench/obs-bench.mjs +520 -0
- package/tools/bench/score.mjs +343 -0
- package/tools/bench/summarize.mjs +86 -0
- package/tools/dev/pages/privacy.html +13 -8
- package/tools/gt-render/browser.mjs +159 -0
- package/tools/gt-render/camera-math.mjs +276 -0
- package/tools/gt-render/page.mjs +267 -0
- package/tools/gt-render/render.mjs +514 -0
- package/tools/gt-render/scene-box.mjs +31 -0
- package/tools/gt-render/take-transform.mjs +87 -0
- package/tools/morphgs/assets/gen_truth.py +39 -0
- package/tools/morphgs/assets/mesh_ori_rig.txt +27 -0
- package/tools/morphgs/assets/playback-check.mjs +30 -0
- package/tools/morphgs/demo-gate.sh +26 -0
- package/tools/morphgs/fbx2morphgs.mjs +68 -0
- package/tools/morphgs/morphgs-to-cskel27.mjs +105 -0
- package/tools/morphgs/patches/preprocess_src-none-mode.patch +76 -0
- package/tools/morphgs/setup-on-cluster.sh +126 -0
- package/tools/qa/css-rule-usage.mjs +283 -0
- package/tools/qa/studio-control-count.mjs +70 -32
- package/tools/run-tests.mjs +134 -12
- package/tools/track/DESIGN.md +513 -0
- package/tools/track/backfill-provenance.mjs +116 -0
- package/tools/track/budget.mjs +8 -0
- package/tools/track/check-rig.mjs +209 -0
- package/tools/track/diagnostics.schema.json +60 -0
- package/tools/track/export-rig.mjs +256 -0
- package/tools/track/fallback.mjs +7 -0
- package/tools/track/fk-parity-fixture.mjs +161 -0
- package/tools/track/gate.mjs +349 -0
- package/tools/track/masks.mjs +318 -0
- package/tools/track/metrics.mjs +201 -0
- package/tools/track/publish-obs.mjs +108 -0
- package/tools/track/py/check_env.py +61 -0
- package/tools/track/py/eval_lr.py +229 -0
- package/tools/track/py/lr_viterbi.py +294 -0
- package/tools/track/py/masks.py +405 -0
- package/tools/track/py/objective.py +382 -0
- package/tools/track/py/rig.py +508 -0
- package/tools/track/py/scene.py +326 -0
- package/tools/track/py/test_joint_indices.py +123 -0
- package/tools/track/py/test_lr_viterbi.py +195 -0
- package/tools/track/py/test_masks.py +229 -0
- package/tools/track/py/test_rig.py +288 -0
- package/tools/track/py/test_scene.py +354 -0
- package/tools/track/py/test_track.py +328 -0
- package/tools/track/py/track.py +411 -0
- package/tools/track/remote.mjs +140 -0
- package/tools/track/rig-dump.mjs +227 -0
- package/tools/track/run-box-tests.mjs +14 -0
- package/tools/track/run-box.mjs +168 -0
- package/tools/track/setup-box.sh +59 -0
- package/tools/track/study-2d.mjs +728 -0
- package/dist/assets/analytics-B1hnH66c.js +0 -1
- package/dist/assets/app-BWusbqgO.js +0 -4861
- package/dist/assets/app-qKDo4PBX.css +0 -1
- package/dist/assets/shot-prompt-CHUtw6af.js +0 -17
- package/dist/assets/shot-prompt-C_g2BHVc.css +0 -1
- package/dist/assets/workflow-CVwzMhz2.js +0 -179
- package/dist/assets/workflow-DsudxKHF.css +0 -1
- package/src/fal-motion-studio.jsx +0 -180
- package/src/scene-history.js +0 -129
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "obs.mannequin-betas.v1",
|
|
3
|
+
"method": {
|
|
4
|
+
"model": "smpl-x supermotion neutral",
|
|
5
|
+
"optimizer": "projected Adam warm start + bounded SLSQP minimax",
|
|
6
|
+
"adam_steps": 5000,
|
|
7
|
+
"adam_lr": 0.005,
|
|
8
|
+
"minimax_steps": 1500,
|
|
9
|
+
"minimax_starts": 3,
|
|
10
|
+
"minimax_success": true,
|
|
11
|
+
"minimax_status": 0,
|
|
12
|
+
"beta_limit": 3.0,
|
|
13
|
+
"smooth_max_weight": 0.0,
|
|
14
|
+
"smooth_max_temperature": 0.002,
|
|
15
|
+
"l2_prior": 1e-05,
|
|
16
|
+
"device": "cpu",
|
|
17
|
+
"source": "rendered rig rest pose (public/models/y-bot-tpose.fbx), segments: thigh_left, thigh_right, shin_left, shin_right, foot_left, foot_right, pelvis_width, spine_length, upper_arm_left, upper_arm_right, forearm_left, forearm_right, shoulder_width"
|
|
18
|
+
},
|
|
19
|
+
"betas": [
|
|
20
|
+
0.9879521131515503,
|
|
21
|
+
-0.43811583518981934,
|
|
22
|
+
0.23403924703598022,
|
|
23
|
+
1.1289187669754028,
|
|
24
|
+
0.4586992859840393,
|
|
25
|
+
-0.2744547724723816,
|
|
26
|
+
-2.9971723556518555,
|
|
27
|
+
-3.0,
|
|
28
|
+
2.999577045440674,
|
|
29
|
+
2.7470200061798096
|
|
30
|
+
],
|
|
31
|
+
"target_geometry": {
|
|
32
|
+
"ybot_leg_height_m": 0.9650828829792554,
|
|
33
|
+
"canonical_cskel27_leg_height_m": 0.9544128,
|
|
34
|
+
"playback_scale": 1.0111797358326036,
|
|
35
|
+
"rest_contract": "public/ardy/cskel27-rest.json"
|
|
36
|
+
},
|
|
37
|
+
"residuals": {
|
|
38
|
+
"thigh_left": {
|
|
39
|
+
"target_m": 0.4059943638074366,
|
|
40
|
+
"fitted_m": 0.39750587940216064,
|
|
41
|
+
"residual_m": -0.008488484405275942,
|
|
42
|
+
"abs_residual_m": 0.008488484405275942
|
|
43
|
+
},
|
|
44
|
+
"thigh_right": {
|
|
45
|
+
"target_m": 0.4059943859177966,
|
|
46
|
+
"fitted_m": 0.4050813913345337,
|
|
47
|
+
"residual_m": -0.0009129945832628827,
|
|
48
|
+
"abs_residual_m": 0.0009129945832628827
|
|
49
|
+
},
|
|
50
|
+
"shin_left": {
|
|
51
|
+
"target_m": 0.42099025912196575,
|
|
52
|
+
"fitted_m": 0.4308183193206787,
|
|
53
|
+
"residual_m": 0.009828060198712962,
|
|
54
|
+
"abs_residual_m": 0.009828060198712962
|
|
55
|
+
},
|
|
56
|
+
"shin_right": {
|
|
57
|
+
"target_m": 0.42099023874729363,
|
|
58
|
+
"fitted_m": 0.43444767594337463,
|
|
59
|
+
"residual_m": 0.013457437196081001,
|
|
60
|
+
"abs_residual_m": 0.013457437196081001
|
|
61
|
+
},
|
|
62
|
+
"foot_left": {
|
|
63
|
+
"target_m": 0.15721559146548023,
|
|
64
|
+
"fitted_m": 0.14614197611808777,
|
|
65
|
+
"residual_m": -0.011073615347392457,
|
|
66
|
+
"abs_residual_m": 0.011073615347392457
|
|
67
|
+
},
|
|
68
|
+
"foot_right": {
|
|
69
|
+
"target_m": 0.15721558809599626,
|
|
70
|
+
"fitted_m": 0.14439848065376282,
|
|
71
|
+
"residual_m": -0.012817107442233439,
|
|
72
|
+
"abs_residual_m": 0.012817107442233439
|
|
73
|
+
},
|
|
74
|
+
"pelvis_width": {
|
|
75
|
+
"target_m": 0.18248906135565482,
|
|
76
|
+
"fitted_m": 0.15615954995155334,
|
|
77
|
+
"residual_m": -0.026329511404101474,
|
|
78
|
+
"abs_residual_m": 0.026329511404101474
|
|
79
|
+
},
|
|
80
|
+
"spine_length": {
|
|
81
|
+
"target_m": 0.3518987587650434,
|
|
82
|
+
"fitted_m": 0.3289218842983246,
|
|
83
|
+
"residual_m": -0.02297687446671881,
|
|
84
|
+
"abs_residual_m": 0.02297687446671881
|
|
85
|
+
},
|
|
86
|
+
"upper_arm_left": {
|
|
87
|
+
"target_m": 0.20827565969956363,
|
|
88
|
+
"fitted_m": 0.23210719227790833,
|
|
89
|
+
"residual_m": 0.023831532578344694,
|
|
90
|
+
"abs_residual_m": 0.023831532578344694
|
|
91
|
+
},
|
|
92
|
+
"upper_arm_right": {
|
|
93
|
+
"target_m": 0.2082745012754011,
|
|
94
|
+
"fitted_m": 0.2391563355922699,
|
|
95
|
+
"residual_m": 0.030881834316868806,
|
|
96
|
+
"abs_residual_m": 0.030881834316868806
|
|
97
|
+
},
|
|
98
|
+
"forearm_left": {
|
|
99
|
+
"target_m": 0.27614463806176104,
|
|
100
|
+
"fitted_m": 0.2709249258041382,
|
|
101
|
+
"residual_m": -0.005219712257622855,
|
|
102
|
+
"abs_residual_m": 0.005219712257622855
|
|
103
|
+
},
|
|
104
|
+
"forearm_right": {
|
|
105
|
+
"target_m": 0.2761446380616249,
|
|
106
|
+
"fitted_m": 0.26336047053337097,
|
|
107
|
+
"residual_m": -0.012784167528253954,
|
|
108
|
+
"abs_residual_m": 0.012784167528253954
|
|
109
|
+
},
|
|
110
|
+
"shoulder_width": {
|
|
111
|
+
"target_m": 0.3752159881593781,
|
|
112
|
+
"fitted_m": 0.34457817673683167,
|
|
113
|
+
"residual_m": -0.030637811422546457,
|
|
114
|
+
"abs_residual_m": 0.030637811422546457
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"summary": {
|
|
118
|
+
"segment_count": 13,
|
|
119
|
+
"max_abs_beta": 3.0,
|
|
120
|
+
"max_abs_residual_m": 0.030881834316868806,
|
|
121
|
+
"rms_residual_m": 0.018625148220087758,
|
|
122
|
+
"max_bone_length_residual_m": 0.030881834316868806
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
3
|
+
import { appendFileSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { assertGpuIdle } from "../fit/remote.mjs";
|
|
6
|
+
|
|
7
|
+
const sshOptions = ["-o", "BatchMode=yes", "-o", "ConnectTimeout=15"];
|
|
8
|
+
const port = process.env.CCLAY_EXTRACT_SSH_PORT;
|
|
9
|
+
const sshFlags = port ? [...sshOptions, "-p", port] : sshOptions;
|
|
10
|
+
const scpFlags = port ? [...sshOptions, "-P", port] : sshOptions;
|
|
11
|
+
const here = fileURLToPath(new URL("../", import.meta.url));
|
|
12
|
+
let injectedExec = null;
|
|
13
|
+
|
|
14
|
+
const quote = value => `'${String(value).replaceAll("'", "'\\''")}'`;
|
|
15
|
+
|
|
16
|
+
function realExec(program, args, log) {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
const child = spawn(program, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
19
|
+
let output = "";
|
|
20
|
+
const onData = chunk => { output += chunk; if (log) appendFileSync(log, chunk); };
|
|
21
|
+
child.stdout.on("data", onData); child.stderr.on("data", onData);
|
|
22
|
+
child.once("error", reject);
|
|
23
|
+
child.once("close", code => code === 0 ? resolve(output) : reject(new Error(`${program} exited ${code}: ${output.slice(-3000)}`)));
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Test hook: replace ssh/scp execution with (program, args, log) => Promise. */
|
|
28
|
+
export function setObsExec(exec) { injectedExec = exec; }
|
|
29
|
+
|
|
30
|
+
function cameraK(camera) {
|
|
31
|
+
const value = typeof camera === "string" ? JSON.parse(readFileSync(camera, "utf8")) : camera;
|
|
32
|
+
const K = value?.K ?? value?.intrinsics?.K ?? value?.intrinsics;
|
|
33
|
+
if (!Array.isArray(K) || K.length !== 3 || K.some(row => !Array.isArray(row) || row.length !== 3)) {
|
|
34
|
+
throw new Error("camera must contain a 3x3 K array");
|
|
35
|
+
}
|
|
36
|
+
return K;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function run(program, args, log) {
|
|
40
|
+
return (injectedExec ?? realExec)(program, args, log);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function extractObs({ host, video, camera, output, detector = "palette", keypoints = "vitpose", betas, log, force = false }) {
|
|
44
|
+
if (!host) throw new Error("missing host");
|
|
45
|
+
if (!video || !camera || !output) throw new Error("video, camera, and output are required");
|
|
46
|
+
if (!["palette", "yolo"].includes(detector)) throw new Error(`invalid detector: ${detector}`);
|
|
47
|
+
if (!["vitpose", "hybrid"].includes(keypoints)) throw new Error(`invalid keypoints: ${keypoints}`);
|
|
48
|
+
const exec = (program, args) => run(program, args, log);
|
|
49
|
+
if (!injectedExec) await assertGpuIdle(host);
|
|
50
|
+
else await exec("ssh", [...sshFlags, host, "python3 -c 'import sys; print(\"GPU idle for GVHMR\")'"]);
|
|
51
|
+
|
|
52
|
+
const remote = `/tmp/cclay-obs-${Date.now()}-${randomBytes(5).toString("hex")}`;
|
|
53
|
+
const localK = `${output}.K-${process.pid}.json`;
|
|
54
|
+
const K = cameraK(camera);
|
|
55
|
+
writeFileSync(localK, JSON.stringify(K));
|
|
56
|
+
let failure;
|
|
57
|
+
try {
|
|
58
|
+
await exec("ssh", [...sshFlags, host, `umask 077 && mkdir ${quote(remote)}`]);
|
|
59
|
+
await exec("scp", [...scpFlags, `${here}/cclay_bench_extract_obs.py`, `${host}:${remote}/cclay_bench_extract_obs.py`]);
|
|
60
|
+
await exec("scp", [...scpFlags, video, `${host}:${remote}/video.mp4`]);
|
|
61
|
+
await exec("scp", [...scpFlags, localK, `${host}:${remote}/K.json`]);
|
|
62
|
+
const args = [
|
|
63
|
+
`${remote}/cclay_bench_extract_obs.py`,
|
|
64
|
+
"cclay_gvhmr_extract.py",
|
|
65
|
+
`${remote}/video.mp4`, `${remote}/obs.npz`, "--K-json", `${remote}/K.json`,
|
|
66
|
+
"--detector", detector, "--keypoints", keypoints, "--out-root", `${remote}/cache`, "--check-pp",
|
|
67
|
+
];
|
|
68
|
+
if (betas) {
|
|
69
|
+
await exec("scp", [...scpFlags, betas, `${host}:${remote}/betas.json`]);
|
|
70
|
+
args.push("--betas-json", `${remote}/betas.json`);
|
|
71
|
+
}
|
|
72
|
+
await exec("ssh", [...sshFlags, host, `set -e; cd ~/cclay-ingest/GVHMR; .venv/bin/python ${args.map(quote).join(" ")}`]);
|
|
73
|
+
await exec("scp", [...scpFlags, `${host}:${remote}/obs.npz`, output]);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
failure = error;
|
|
76
|
+
} finally {
|
|
77
|
+
try { unlinkSync(localK); } catch {}
|
|
78
|
+
try { await exec("ssh", [...sshFlags, host, `rm -rf ${quote(remote)}`]); }
|
|
79
|
+
catch (error) { failure = failure ? new AggregateError([failure, error], `remote cleanup failed: ${remote}`) : error; }
|
|
80
|
+
}
|
|
81
|
+
if (failure) throw failure;
|
|
82
|
+
return { output, remote };
|
|
83
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""SMPL rest joints for one betas vector (box helper for tools/bench/obs-bench.mjs).
|
|
3
|
+
|
|
4
|
+
Run from ~/cclay-ingest/GVHMR with its venv:
|
|
5
|
+
.venv/bin/python rest_joints.py '<json list of 10 betas>'
|
|
6
|
+
|
|
7
|
+
Reproduces cclay_gvhmr_extract.py main()'s ``smpl_rest_joints`` exactly:
|
|
8
|
+
SMPL-X ``make_smplx("supermotion")`` zero pose for ``betas`` -> smplx2smpl
|
|
9
|
+
sparse vertices -> neutral SMPL J-regressor, pelvis moved to the origin.
|
|
10
|
+
Also reports the rest mesh's lowest vertex height (pelvis-relative), which
|
|
11
|
+
gives the body's sole clearance under each foot joint. CPU only: the GPU is
|
|
12
|
+
reserved for serial GVHMR extraction, so CUDA is hidden before torch loads.
|
|
13
|
+
"""
|
|
14
|
+
import json
|
|
15
|
+
import os
|
|
16
|
+
import sys
|
|
17
|
+
|
|
18
|
+
os.environ["CUDA_VISIBLE_DEVICES"] = ""
|
|
19
|
+
|
|
20
|
+
import numpy as np
|
|
21
|
+
import torch
|
|
22
|
+
|
|
23
|
+
from hmr4d.utils.smplx_utils import make_smplx
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def main():
|
|
27
|
+
assert not torch.cuda.is_available(), "rest_joints.py must not see the GPU"
|
|
28
|
+
betas = np.asarray(json.loads(sys.argv[1]), dtype=np.float32)
|
|
29
|
+
if betas.shape != (10,) or not np.isfinite(betas).all():
|
|
30
|
+
raise ValueError("expected 10 finite betas")
|
|
31
|
+
smplx = make_smplx("supermotion")
|
|
32
|
+
sparse = torch.load("hmr4d/utils/body_model/smplx2smpl_sparse.pt", map_location="cpu")
|
|
33
|
+
regressor = torch.load("hmr4d/utils/body_model/smpl_neutral_J_regressor.pt", map_location="cpu")
|
|
34
|
+
with torch.no_grad():
|
|
35
|
+
rest = smplx(betas=torch.from_numpy(betas)[None], body_pose=torch.zeros(1, 63),
|
|
36
|
+
global_orient=torch.zeros(1, 3), transl=torch.zeros(1, 3))
|
|
37
|
+
verts = torch.matmul(sparse, rest.vertices[0])
|
|
38
|
+
joints = torch.einsum("jv,vi->ji", regressor, verts)
|
|
39
|
+
pelvis = joints[0].clone()
|
|
40
|
+
joints = joints - pelvis
|
|
41
|
+
min_y = float((verts[:, 1] - pelvis[1]).min())
|
|
42
|
+
print(json.dumps({
|
|
43
|
+
"schema": "obs.rest-joints.v1",
|
|
44
|
+
"betas": [float(v) for v in betas],
|
|
45
|
+
"restJoints": joints.numpy().astype(np.float32).tolist(),
|
|
46
|
+
"restMinVertexY": min_y,
|
|
47
|
+
"model": "make_smplx('supermotion') zero pose -> smplx2smpl_sparse -> smpl_neutral_J_regressor; pelvis at origin",
|
|
48
|
+
}))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
if __name__ == "__main__":
|
|
52
|
+
main()
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Body-size targets for fit_mannequin_betas.py, measured on the character that
|
|
3
|
+
// is actually rendered: the y-bot FBX rig in its rest (T) pose, at the Studio's
|
|
4
|
+
// 0.01 armature scale. Only the character model is read (no motion, no video).
|
|
5
|
+
// Usage: node tools/bench/obs/ybot-targets.mjs [model] > targets.json
|
|
6
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
7
|
+
import { dirname, join } from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import * as THREE from "three";
|
|
10
|
+
import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader.js";
|
|
11
|
+
|
|
12
|
+
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "../../..");
|
|
13
|
+
|
|
14
|
+
// Segment name -> Mixamo bone pair, matching fit_mannequin_betas.py SEGMENTS
|
|
15
|
+
// (SMPL joint pairs there). Upper arm: the SMPL spine3 -> shoulder span maps to
|
|
16
|
+
// Spine2 -> Arm on the rig, the same visible shoulder reach.
|
|
17
|
+
export const RIG_SEGMENTS = {
|
|
18
|
+
thigh_left: ["LeftUpLeg", "LeftLeg"], thigh_right: ["RightUpLeg", "RightLeg"],
|
|
19
|
+
shin_left: ["LeftLeg", "LeftFoot"], shin_right: ["RightLeg", "RightFoot"],
|
|
20
|
+
foot_left: ["LeftFoot", "LeftToeBase"], foot_right: ["RightFoot", "RightToeBase"],
|
|
21
|
+
pelvis_width: ["LeftUpLeg", "RightUpLeg"], spine_length: ["Hips", "Spine2"],
|
|
22
|
+
upper_arm_left: ["Spine2", "LeftArm"], upper_arm_right: ["Spine2", "RightArm"],
|
|
23
|
+
forearm_left: ["LeftForeArm", "LeftHand"], forearm_right: ["RightForeArm", "RightHand"],
|
|
24
|
+
shoulder_width: ["LeftArm", "RightArm"],
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export function ybotTargets(model = "y-bot-tpose") {
|
|
28
|
+
const path = ["public", "dist"].map((d) => join(ROOT, d, "models", `${model}.fbx`)).find(existsSync);
|
|
29
|
+
if (!path) throw new Error(`character model ${model}.fbx not found under public/ or dist/models`);
|
|
30
|
+
const bytes = readFileSync(path);
|
|
31
|
+
const rig = new FBXLoader().parse(bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength), "");
|
|
32
|
+
rig.scale.setScalar(0.01);
|
|
33
|
+
rig.updateMatrixWorld(true);
|
|
34
|
+
const bones = new Map();
|
|
35
|
+
rig.traverse((o) => { if (o.isBone) bones.set(o.name.replace(/^mixamorig:?/, ""), o); });
|
|
36
|
+
const at = (name) => {
|
|
37
|
+
const bone = bones.get(name);
|
|
38
|
+
if (!bone) throw new Error(`${model}: no bone ${name}`);
|
|
39
|
+
return new THREE.Vector3().setFromMatrixPosition(bone.matrixWorld);
|
|
40
|
+
};
|
|
41
|
+
const targets = Object.fromEntries(Object.entries(RIG_SEGMENTS).map(([name, [a, b]]) => [name, at(a).distanceTo(at(b))]));
|
|
42
|
+
// Standing height: the skinned mesh's rest bounding box (sole to crown).
|
|
43
|
+
const box = new THREE.Box3();
|
|
44
|
+
rig.traverse((o) => { if (o.isMesh) { o.geometry.computeBoundingBox(); box.union(o.geometry.boundingBox.clone().applyMatrix4(o.matrixWorld)); } });
|
|
45
|
+
targets.total_height = box.max.y - box.min.y;
|
|
46
|
+
return { schema: "obs.ybot-targets.v1", model: path.slice(ROOT.length + 1), scale: 0.01, pose: "FBX rest (T) pose", targets };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (import.meta.url === `file://${process.argv[1]}`) console.log(JSON.stringify(ybotTargets(process.argv[2]), null, 1));
|