cozyclay 2.1.1 → 3.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.
Files changed (577) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +12 -30
  3. package/bin/agent/agent-routes.mjs +40 -113
  4. package/bin/agent/agent-runner.mjs +9 -10
  5. package/bin/agent/cliproxy-provider.mjs +135 -0
  6. package/bin/agent/credential-store.mjs +30 -7
  7. package/bin/agent/pi-tools.mjs +4 -1
  8. package/bin/agent/provider-keys.mjs +32 -0
  9. package/bin/agent/provider-login.mjs +110 -0
  10. package/bin/agent/provider-routes.mjs +99 -0
  11. package/bin/agent/providers.mjs +19 -97
  12. package/bin/agent/script-tool.mjs +17 -4
  13. package/bin/agent/studio-motion-reference.mjs +23 -0
  14. package/bin/agent/studio-prompt.mjs +1 -1
  15. package/bin/agent/studio-rebase-text.mjs +32 -0
  16. package/bin/agent/studio-rebase.mjs +26 -39
  17. package/bin/agent/studio-tools.mjs +9 -28
  18. package/bin/agent/tool-arguments.mjs +49 -0
  19. package/bin/agent/turn-frames.mjs +66 -0
  20. package/bin/codex-auth.mjs +9 -2
  21. package/bin/cozyclay.mjs +10 -116
  22. package/bin/live/cli.mjs +3 -3
  23. package/bin/mcp-runtime.mjs +2 -12
  24. package/bin/package-signature.mjs +0 -4
  25. package/dist/app/index.html +7 -6
  26. package/dist/assets/EXRLoader-CvVIvGVE.js +1 -0
  27. package/dist/assets/analytics-C9sUnndd.js +1 -0
  28. package/dist/assets/app-CCh--1Hc.js +5087 -0
  29. package/dist/assets/app-f-fkV5uP.css +1 -0
  30. package/dist/assets/compile-worker-CkxiD6lH.js +5 -0
  31. package/dist/assets/fflate.module-KbGj5-H6.js +5 -0
  32. package/dist/assets/{first-shot-handoff-msLI71g1.js → first-shot-handoff-CC4xe4HM.js} +1 -1
  33. package/dist/assets/{landing-ChUvB_zL.js → landing-DjHD8u7k.js} +1 -1
  34. package/dist/assets/scene-asset-sync-Hx_FzdOH.js +20 -0
  35. package/dist/assets/scene-asset-sync-_S8NQl5B.css +1 -0
  36. package/dist/assets/workflow-C4yKH1p4.js +179 -0
  37. package/dist/cozyclay-package.json +1 -1
  38. package/dist/index.html +3 -3
  39. package/dist/sw.js +3 -6
  40. package/dist/workflow/index.html +5 -4
  41. package/mcp/LIVE-PROTOCOL.md +7 -47
  42. package/mcp/README.md +16 -26
  43. package/mcp/live-hub.mjs +6 -10
  44. package/mcp/package.json +1 -3
  45. package/mcp/tool-handlers.mjs +4 -168
  46. package/package.json +3 -22
  47. package/src/App.jsx +150 -2624
  48. package/src/agent-progress.js +79 -0
  49. package/src/agent-turn-mark.js +30 -0
  50. package/src/analytics.js +1 -19
  51. package/src/app-context.js +5 -0
  52. package/src/app-stage.jsx +2 -34
  53. package/src/asset-pane.jsx +6 -5
  54. package/src/camera-block.js +21 -0
  55. package/src/camera-direction.js +120 -0
  56. package/src/character-kind.js +3 -9
  57. package/src/command-bus.js +8 -6
  58. package/src/commands/cast.js +5 -34
  59. package/src/commands/elements/character.js +1 -1
  60. package/src/commands/elements/motion.js +1 -6
  61. package/src/commands/elements/object.js +6 -2
  62. package/src/commands/elements/shot.js +4 -1
  63. package/src/commands/elements.js +38 -14
  64. package/src/commands/index.js +3 -1
  65. package/src/commands/kits.js +37 -0
  66. package/src/commands/motion-timing.js +57 -0
  67. package/src/commands/motion.js +9 -152
  68. package/src/commands/motion3-compile.js +65 -0
  69. package/src/commands/motion3.js +207 -0
  70. package/src/commands/objects.js +6 -4
  71. package/src/commands/shot.js +49 -11
  72. package/src/commands/stage.js +14 -1
  73. package/src/commands/view.js +4 -1
  74. package/src/document-mirrors.js +42 -0
  75. package/src/document-store.js +4 -0
  76. package/src/domains/cast.js +19 -194
  77. package/src/domains/motion/motion-range-pin-rules.js +1 -1
  78. package/src/domains/motion/take-source.js +51 -0
  79. package/src/domains/motion.js +60 -1838
  80. package/src/domains/scenes.js +12 -55
  81. package/src/domains/shots.js +2 -2
  82. package/src/domains/stage.js +4 -2
  83. package/src/draft-ghost.js +46 -0
  84. package/src/draft-ghost.jsx +129 -0
  85. package/src/dualview.jsx +4 -3
  86. package/src/effects/EffectsLayer.jsx +112 -0
  87. package/src/effects/effect-meshes.js +232 -0
  88. package/src/effects/effect-record.js +103 -0
  89. package/src/effects/emitter.js +59 -0
  90. package/src/effects/simulate.js +261 -0
  91. package/src/effects/state.js +59 -0
  92. package/src/execution-telemetry.js +2 -3
  93. package/src/first-success-guide.jsx +5 -5
  94. package/src/ghost-set.js +257 -0
  95. package/src/held-object-edit.jsx +113 -0
  96. package/src/held-object-pose.js +175 -0
  97. package/src/hierarchy-model.js +1 -1
  98. package/src/hierarchy-panel.jsx +1 -1
  99. package/src/imported-mesh.jsx +103 -0
  100. package/src/kits/held.js +84 -0
  101. package/src/kits/index.js +70 -0
  102. package/src/kits/smoking.js +57 -0
  103. package/src/light-blockers.js +93 -0
  104. package/src/motion/beat-board-model.js +84 -0
  105. package/src/motion/beat-board.css +124 -0
  106. package/src/motion/beat-board.jsx +59 -0
  107. package/src/motion/center-of-mass.js +66 -0
  108. package/src/motion/fix-collisions.js +850 -0
  109. package/src/motion/physics-review.js +118 -0
  110. package/src/{ardy → motion}/physics-support.js +1 -76
  111. package/src/motion/rig-snapshot.js +22 -0
  112. package/src/motion/take-segments.jsx +140 -0
  113. package/src/{ardy → motion}/timeline-coordinates.js +0 -21
  114. package/src/{ardy → motion}/timeline.css +1 -2
  115. package/src/{ardy → motion}/timeline.jsx +42 -413
  116. package/src/motion/waypoints.js +76 -0
  117. package/src/motion3/beats.js +83 -0
  118. package/src/motion3/body.js +158 -0
  119. package/src/motion3/bones.js +47 -0
  120. package/src/motion3/carried.js +47 -0
  121. package/src/motion3/compile-job.js +75 -0
  122. package/src/motion3/compile-worker.js +10 -0
  123. package/src/motion3/compiler/anchors.js +105 -0
  124. package/src/motion3/compiler/arm-orient.js +128 -0
  125. package/src/motion3/compiler/channels.js +125 -0
  126. package/src/motion3/compiler/compile.js +205 -0
  127. package/src/motion3/compiler/gait-rhythm.js +97 -0
  128. package/src/motion3/compiler/grip.js +171 -0
  129. package/src/motion3/compiler/head-steady.js +48 -0
  130. package/src/motion3/compiler/hold.js +134 -0
  131. package/src/motion3/compiler/idle.js +113 -0
  132. package/src/motion3/compiler/limbs.js +170 -0
  133. package/src/motion3/compiler/math.js +72 -0
  134. package/src/motion3/compiler/pose.js +162 -0
  135. package/src/motion3/compiler/quat.js +49 -0
  136. package/src/motion3/compiler/skeleton.js +74 -0
  137. package/src/motion3/compiler/validate-common.js +32 -0
  138. package/src/motion3/compiler/validate-goals.js +118 -0
  139. package/src/motion3/compiler/validate-objects.js +143 -0
  140. package/src/motion3/compiler/validate-parts.js +116 -0
  141. package/src/motion3/compiler/validate.js +122 -0
  142. package/src/motion3/compiler/vocab.js +73 -0
  143. package/src/motion3/compiler/walk-pelvis.js +275 -0
  144. package/src/motion3/compiler/walk.js +207 -0
  145. package/src/motion3/compiler/wrist-clamp.js +32 -0
  146. package/src/motion3/delta.js +47 -0
  147. package/src/motion3/describe.js +174 -0
  148. package/src/motion3/docs.js +27 -0
  149. package/src/motion3/edit-carry.js +21 -0
  150. package/src/motion3/fix/ease.js +16 -0
  151. package/src/motion3/fix/ground.js +37 -0
  152. package/src/motion3/fix/index.js +52 -0
  153. package/src/motion3/fix/push.js +71 -0
  154. package/src/motion3/fix/reach.js +46 -0
  155. package/src/motion3/fix/retime.js +79 -0
  156. package/src/motion3/fix/self.js +211 -0
  157. package/src/motion3/fix/slide.js +70 -0
  158. package/src/motion3/fix/turn.js +51 -0
  159. package/src/motion3/follow.js +39 -0
  160. package/src/motion3/free-motion.js +173 -0
  161. package/src/motion3/grip-choice.js +130 -0
  162. package/src/motion3/hold-plan.js +157 -0
  163. package/src/motion3/json-patch.js +77 -0
  164. package/src/motion3/key-aim.js +105 -0
  165. package/src/motion3/key-edit.js +82 -0
  166. package/src/motion3/lenient.js +127 -0
  167. package/src/motion3/object-keys.js +236 -0
  168. package/src/motion3/object-pose.js +182 -0
  169. package/src/motion3/object-tracks.js +12 -0
  170. package/src/motion3/pipeline.js +102 -0
  171. package/src/motion3/playback.js +51 -0
  172. package/src/motion3/probes.js +91 -0
  173. package/src/motion3/qa/body-checks.js +34 -0
  174. package/src/motion3/qa/check-names.js +14 -0
  175. package/src/motion3/qa/checks.js +121 -0
  176. package/src/motion3/qa/contact-checks.js +51 -0
  177. package/src/motion3/qa/explain.js +80 -0
  178. package/src/motion3/qa/fmt.js +5 -0
  179. package/src/motion3/qa/foot-checks.js +72 -0
  180. package/src/motion3/qa/geometry.js +77 -0
  181. package/src/motion3/qa/goals.js +191 -0
  182. package/src/motion3/qa/ground.js +60 -0
  183. package/src/motion3/qa/hints-contact.js +179 -0
  184. package/src/motion3/qa/hints-goal.js +160 -0
  185. package/src/motion3/qa/hints-knee.js +24 -0
  186. package/src/motion3/qa/hints-motion.js +209 -0
  187. package/src/motion3/qa/hints-object.js +91 -0
  188. package/src/motion3/qa/index.js +42 -0
  189. package/src/motion3/qa/key-checks.js +82 -0
  190. package/src/motion3/qa/near.js +96 -0
  191. package/src/motion3/qa/object-checks.js +135 -0
  192. package/src/motion3/qa/patch.js +161 -0
  193. package/src/motion3/qa/severity.js +38 -0
  194. package/src/motion3/qa/spec-checks.js +65 -0
  195. package/src/motion3/qa/style.js +218 -0
  196. package/src/motion3/qa/wrist-checks.js +69 -0
  197. package/src/motion3/qa/wrist-obs.js +60 -0
  198. package/src/motion3/refs.js +120 -0
  199. package/src/motion3/repair-tune.js +121 -0
  200. package/src/motion3/repair.js +83 -0
  201. package/src/motion3/report.js +67 -0
  202. package/src/motion3/rigs/index.js +14 -0
  203. package/src/motion3/rigs/x-bot-tpose-body.json +1 -0
  204. package/src/motion3/rigs/x-bot-tpose.json +1 -0
  205. package/src/motion3/rigs/y-bot-tpose-body.json +1 -0
  206. package/src/motion3/rigs/y-bot-tpose.json +1 -0
  207. package/src/motion3/scene.js +60 -0
  208. package/src/motion3/schema/brief.js +89 -0
  209. package/src/motion3/schema/fields.js +44 -0
  210. package/src/motion3/schema/spec-examples.js +46 -0
  211. package/src/motion3/schema/topics.js +111 -0
  212. package/src/motion3/schema/types.js +147 -0
  213. package/src/motion3/search.js +117 -0
  214. package/src/motion3/second-hand.js +112 -0
  215. package/src/motion3/skills/basic.js +82 -0
  216. package/src/motion3/skills/book.js +76 -0
  217. package/src/motion3/skills/car.js +145 -0
  218. package/src/motion3/skills/frames-auto.js +29 -0
  219. package/src/motion3/skills/index.js +83 -0
  220. package/src/motion3/skills/info.js +65 -0
  221. package/src/motion3/skills/smoke-kit.js +109 -0
  222. package/src/motion3/skills/smoke.js +160 -0
  223. package/src/motion3/spec-store.js +59 -0
  224. package/src/motion3/take-edit.js +155 -0
  225. package/src/motion3/take.js +63 -0
  226. package/src/motion3/timeline.js +127 -0
  227. package/src/motion3/views.js +121 -0
  228. package/src/motion3/walk-plan.js +119 -0
  229. package/src/object-catalog.jsx +7 -1
  230. package/src/object-colors.js +98 -0
  231. package/src/object-gizmo-parts.js +115 -0
  232. package/src/object-gizmo.jsx +37 -122
  233. package/src/object-glow.js +139 -0
  234. package/src/object-hidden.js +33 -0
  235. package/src/object-look.js +196 -0
  236. package/src/object-size.js +10 -0
  237. package/src/object-travel.js +71 -6
  238. package/src/panels/CameraPanel.jsx +7 -7
  239. package/src/panels/HdrEnvironmentControls.jsx +59 -0
  240. package/src/panels/LightPanel.jsx +25 -4
  241. package/src/panels/ObjectLookPanel.jsx +88 -0
  242. package/src/panels/PosePanel.jsx +2 -17
  243. package/src/panels/RigControlPanel.jsx +5 -53
  244. package/src/panels/SubjectBox.jsx +4 -1
  245. package/src/panels/details.css +14 -0
  246. package/src/posestudio.jsx +4 -27
  247. package/src/project-browser.jsx +3 -4
  248. package/src/project-resources.js +18 -31
  249. package/src/project.js +13 -104
  250. package/src/props.jsx +25 -280
  251. package/src/rail-dot-drag.js +23 -0
  252. package/src/render-passes.js +10 -0
  253. package/src/resource-status.jsx +1 -0
  254. package/src/result-modal.jsx +4 -19
  255. package/src/room.jsx +20 -12
  256. package/src/scene-assets.js +8 -6
  257. package/src/scene-light-place.js +72 -0
  258. package/src/scene-lights.jsx +148 -0
  259. package/src/scene-objects.js +31 -139
  260. package/src/scenes.js +15 -105
  261. package/src/semantic-edit.js +2 -5
  262. package/src/set-pieces.jsx +183 -0
  263. package/src/settings-menu.jsx +3 -11
  264. package/src/shell/BottomDock.jsx +9 -51
  265. package/src/shell/DetailsSlot.jsx +16 -200
  266. package/src/shell/GhostsMenuGroup.jsx +74 -0
  267. package/src/shell/LibrarySlot.jsx +1 -3
  268. package/src/shell/MenuBar.jsx +3 -3
  269. package/src/shell/PreferencesDialog.jsx +15 -264
  270. package/src/shell/PreferencesSlot.jsx +2 -8
  271. package/src/shell/TopBar.jsx +5 -84
  272. package/src/shell/ViewportToolbar.jsx +10 -70
  273. package/src/shell/agent-glass.css +19 -6
  274. package/src/shell/details-selection.js +42 -0
  275. package/src/shell/ghosts-menu.css +22 -0
  276. package/src/shell/glass-regions.css +29 -3
  277. package/src/shell/preferences.css +6 -1
  278. package/src/shot-authoring.js +4 -7
  279. package/src/soft-shadows.js +37 -0
  280. package/src/stage-attach-frames.js +12 -12
  281. package/src/stage-canvas-rigs.jsx +3 -3
  282. package/src/stage-capture.jsx +42 -8
  283. package/src/stage-characters.jsx +22 -216
  284. package/src/stage-editor-gizmos.jsx +6 -6
  285. package/src/stage-environment.js +93 -0
  286. package/src/stage-hdr-builtins.js +107 -0
  287. package/src/stage-hdr-image.js +116 -0
  288. package/src/stage-hdr-library.js +70 -0
  289. package/src/stage-hdr-map.js +94 -0
  290. package/src/stage-hdr.js +130 -0
  291. package/src/stage-lighting.js +149 -0
  292. package/src/stage-occlusion.js +210 -0
  293. package/src/stage-sky.jsx +159 -0
  294. package/src/stage-startup-defaults.js +5 -9
  295. package/src/studio-actions.js +12 -14
  296. package/src/studio-agent-commands.js +19 -16
  297. package/src/studio-agent-motion.js +9 -9
  298. package/src/studio-agent-protocol.js +41 -71
  299. package/src/studio-app-binding.js +25 -45
  300. package/src/studio-asset-list.js +17 -0
  301. package/src/studio-contact-sheet.js +37 -5
  302. package/src/studio-elements.js +27 -7
  303. package/src/studio-entity-bounds.js +18 -0
  304. package/src/studio-error-codes.js +8 -0
  305. package/src/studio-export-framing.js +152 -0
  306. package/src/studio-lighting-check.js +103 -0
  307. package/src/studio-motion-visual.js +150 -0
  308. package/src/studio-schema-errors.js +80 -0
  309. package/src/studio-support-gap.js +74 -0
  310. package/src/styles.css +1 -19
  311. package/src/timeline-defaults.js +5 -0
  312. package/src/timeline-extent.js +8 -20
  313. package/src/tutorial-walk.js +17 -0
  314. package/src/ui-scrub.js +1 -1
  315. package/src/use-shot-camera-tracks.js +12 -0
  316. package/src/view-ghosts.js +97 -0
  317. package/src/view-ghosts.jsx +49 -0
  318. package/src/workflow/AgentPanel.jsx +47 -166
  319. package/src/workflow/AgentProgressCard.jsx +107 -0
  320. package/src/workflow/AgentReplyText.jsx +37 -0
  321. package/src/workflow/AgentTurnSteps.jsx +84 -0
  322. package/src/workflow/LoginPanel.jsx +140 -0
  323. package/src/workflow/WorkflowBuilder.jsx +7 -23
  324. package/src/workflow/agent-client.js +54 -51
  325. package/src/workflow/agent-panel.css +71 -1
  326. package/src/workflow/agent-progress-card.css +167 -0
  327. package/src/workflow/agent-progress-view.js +47 -0
  328. package/src/workflow/agent-turn-steps.css +113 -0
  329. package/src/workflow/agent-turn.js +191 -0
  330. package/src/workflow/login-client.js +34 -0
  331. package/src/workflow/motion-input.js +18 -31
  332. package/src/workflow/node-schema.js +1 -1
  333. package/src/workflow/reply-markdown.js +69 -0
  334. package/src/workflow/scene-asset-sync.js +8 -8
  335. package/tools/agent-session-stats.mjs +61 -0
  336. package/tools/dev-full.mjs +5 -56
  337. package/tools/eslint-baseline.json +12 -13
  338. package/tools/knip-baseline.json +0 -179
  339. package/tools/loc-baseline.json +31 -81
  340. package/tools/motion3/extract-body.mjs +103 -0
  341. package/tools/motion3/extract-rig.mjs +85 -0
  342. package/tools/process-supervisor.mjs +0 -88
  343. package/tools/run-tests.mjs +8 -124
  344. package/bin/agent/motion-runtime.mjs +0 -76
  345. package/dist/ardy/cskel27-rest.json +0 -557
  346. package/dist/assets/analytics-C9pMpW7s.js +0 -1
  347. package/dist/assets/app-B_2XB9rw.css +0 -1
  348. package/dist/assets/app-DAuvKPP8.js +0 -4920
  349. package/dist/assets/config-Bx7pYsD9.js +0 -1
  350. package/dist/assets/config-DeoFTYzb.css +0 -1
  351. package/dist/assets/demo-CvKuBgy5.js +0 -1
  352. package/dist/assets/scene-asset-sync-DKkpjKBV.js +0 -19
  353. package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
  354. package/dist/assets/ticket-BPk2Ni95.js +0 -1
  355. package/dist/assets/workflow-jJRBR94Z.js +0 -179
  356. package/dist/demo/walk-then-stop.npz +0 -0
  357. package/mcp/ardy-prompts.mjs +0 -218
  358. package/src/ardy/auto-fix-panel.css +0 -108
  359. package/src/ardy/auto-physics.js +0 -1194
  360. package/src/ardy/client.js +0 -153
  361. package/src/ardy/convert.js +0 -313
  362. package/src/ardy/cskel27-neutral.js +0 -39
  363. package/src/ardy/cskel27.js +0 -77
  364. package/src/ardy/export.js +0 -165
  365. package/src/ardy/fix-collisions.js +0 -2257
  366. package/src/ardy/foot-lock.js +0 -230
  367. package/src/ardy/motion-calibration.js +0 -118
  368. package/src/ardy/motion-edit.js +0 -213
  369. package/src/ardy/motion-url.js +0 -55
  370. package/src/ardy/mp4-duration.js +0 -102
  371. package/src/ardy/npz.js +0 -595
  372. package/src/ardy/physics-panel.css +0 -21
  373. package/src/ardy/physics-panel.jsx +0 -89
  374. package/src/ardy/physics-refine.js +0 -111
  375. package/src/ardy/physics-review.js +0 -567
  376. package/src/ardy/physics-temporal.js +0 -54
  377. package/src/ardy/platform-fit-panel.css +0 -64
  378. package/src/ardy/platform-fit-panel.jsx +0 -41
  379. package/src/ardy/platform-fit.js +0 -326
  380. package/src/ardy/playback.js +0 -711
  381. package/src/ardy/pose-pin.js +0 -97
  382. package/src/ardy/prompt-clips.js +0 -30
  383. package/src/ardy/retime.js +0 -211
  384. package/src/ardy/root-drop.js +0 -369
  385. package/src/ardy/timeline-resize.js +0 -13
  386. package/src/ardy/to-cskel27.js +0 -225
  387. package/src/ardy/trim.js +0 -33
  388. package/src/ardy/waypoints.js +0 -369
  389. package/src/ardy-bridge-contract.js +0 -281
  390. package/src/domains/motion/motion-video-capture.js +0 -664
  391. package/src/i2v-motion-client.js +0 -77
  392. package/src/i2v-motion-studio.jsx +0 -180
  393. package/src/line-edit.js +0 -1348
  394. package/src/motion/generation.js +0 -105
  395. package/src/motion-readiness-ui.jsx +0 -67
  396. package/src/motion-readiness.js +0 -40
  397. package/src/motion-resources.js +0 -177
  398. package/src/motion-store.js +0 -65
  399. package/src/motion-trail.js +0 -601
  400. package/src/multimodel-ingest.js +0 -394
  401. package/src/panels/PromptBlocksPanel.jsx +0 -444
  402. package/src/panels/VideoCapturePanel.jsx +0 -181
  403. package/src/pose-extract/browser.js +0 -42
  404. package/src/pose-extract/detector.js +0 -100
  405. package/src/pose-extract/fit.js +0 -227
  406. package/src/pose-extract/image-frame.js +0 -51
  407. package/src/pose-extract/index.js +0 -58
  408. package/src/pose-extract/keyframes.js +0 -74
  409. package/src/pose-extract/landmarks.js +0 -162
  410. package/src/pose-extract/mirror.js +0 -153
  411. package/src/pose-extract/take.js +0 -131
  412. package/src/pose-extract/video-frames.js +0 -91
  413. package/src/take-recipe.js +0 -165
  414. package/src/trail-key-conflicts.js +0 -39
  415. package/src/trail-pick.js +0 -59
  416. package/src/use-i2v-capture-modal.js +0 -15
  417. package/tools/ardy/BRIDGE.md +0 -37
  418. package/tools/ardy/GVHMR-NPZ.md +0 -68
  419. package/tools/ardy/README.md +0 -124
  420. package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
  421. package/tools/ardy/artifacts.mjs +0 -34
  422. package/tools/ardy/bridge.mjs +0 -1732
  423. package/tools/ardy/bvh-cskel27.mjs +0 -1265
  424. package/tools/ardy/cclay_constrained_generate.py +0 -561
  425. package/tools/ardy/cclay_gvhmr_worker.py +0 -264
  426. package/tools/ardy/cclay_motion_edit.py +0 -510
  427. package/tools/ardy/cclay_sequence_generate.py +0 -705
  428. package/tools/ardy/cclay_worker.py +0 -304
  429. package/tools/ardy/dump-npz.py +0 -205
  430. package/tools/ardy/extract-rest.mjs +0 -299
  431. package/tools/ardy/extract.mjs +0 -507
  432. package/tools/ardy/footage.mjs +0 -467
  433. package/tools/ardy/gvhmr-floor.mjs +0 -102
  434. package/tools/ardy/gvhmr-to-cskel27.mjs +0 -21
  435. package/tools/ardy/gvhmr_fastpath.py +0 -168
  436. package/tools/ardy/gvhmr_trajectory.py +0 -240
  437. package/tools/ardy/merge-text-encoder.py +0 -160
  438. package/tools/ardy/mocap-metrics.mjs +0 -200
  439. package/tools/ardy/mocap-quality-gate.mjs +0 -110
  440. package/tools/ardy/motion-stabilize.mjs +0 -348
  441. package/tools/ardy/npz.mjs +0 -414
  442. package/tools/ardy/pose-to-npz.mjs +0 -106
  443. package/tools/ardy/prompt-limits.mjs +0 -5
  444. package/tools/ardy/run-edit-on-box.sh +0 -73
  445. package/tools/ardy/run-local.mjs +0 -233
  446. package/tools/ardy/run-on-box.sh +0 -593
  447. package/tools/ardy/run-sequence-on-box.sh +0 -177
  448. package/tools/ardy/runners/gvhmr-worker.mjs +0 -206
  449. package/tools/ardy/runners/index.mjs +0 -18
  450. package/tools/ardy/runners/local.mjs +0 -455
  451. package/tools/ardy/runners/proc.mjs +0 -125
  452. package/tools/ardy/runners/remote.mjs +0 -252
  453. package/tools/ardy/setup-local.mjs +0 -189
  454. package/tools/ardy/setup-text-encoder-on-box.sh +0 -93
  455. package/tools/ardy/setup-text-encoder.py +0 -306
  456. package/tools/ardy/smpl-cskel27.mjs +0 -178
  457. package/tools/ardy/visual-qa.mjs +0 -196
  458. package/tools/ardy/vq-car.mjs +0 -52
  459. package/tools/bench/EXP3.md +0 -61
  460. package/tools/bench/cclay_bench_extract_incam.py +0 -125
  461. package/tools/bench/cclay_bench_extract_obs.py +0 -204
  462. package/tools/bench/cclay_bench_runner.py +0 -42
  463. package/tools/bench/cube-contact.mjs +0 -162
  464. package/tools/bench/exp3.mjs +0 -152
  465. package/tools/bench/extract-bench-lib.mjs +0 -216
  466. package/tools/bench/extract-bench.mjs +0 -302
  467. package/tools/bench/fal-generate.mjs +0 -88
  468. package/tools/bench/fit/README.md +0 -189
  469. package/tools/bench/fit/camera.mjs +0 -59
  470. package/tools/bench/fit/contact.mjs +0 -419
  471. package/tools/bench/fit/footlock.mjs +0 -234
  472. package/tools/bench/fit/motion.mjs +0 -85
  473. package/tools/bench/fit/pin.mjs +0 -27
  474. package/tools/bench/fit/remote.mjs +0 -105
  475. package/tools/bench/fit-bench.mjs +0 -126
  476. package/tools/bench/fit-sanity.mjs +0 -45
  477. package/tools/bench/metrics.mjs +0 -313
  478. package/tools/bench/obs/depth.mjs +0 -258
  479. package/tools/bench/obs/extrinsics.mjs +0 -175
  480. package/tools/bench/obs/fit_mannequin_betas.py +0 -340
  481. package/tools/bench/obs/ground.mjs +0 -294
  482. package/tools/bench/obs/heading.mjs +0 -151
  483. package/tools/bench/obs/ladder.mjs +0 -555
  484. package/tools/bench/obs/mannequin-betas.json +0 -124
  485. package/tools/bench/obs/remote.mjs +0 -83
  486. package/tools/bench/obs/rest_joints.py +0 -52
  487. package/tools/bench/obs/ybot-targets.mjs +0 -49
  488. package/tools/bench/obs-bench.mjs +0 -520
  489. package/tools/bench/score.mjs +0 -343
  490. package/tools/bench/summarize.mjs +0 -86
  491. package/tools/demo-worker/README.md +0 -137
  492. package/tools/demo-worker/api-client.mjs +0 -233
  493. package/tools/demo-worker/generate.mjs +0 -301
  494. package/tools/demo-worker/index.mjs +0 -466
  495. package/tools/demo-worker/sign-core.mjs +0 -112
  496. package/tools/demo-worker/sign.mjs +0 -161
  497. package/tools/gt-render/browser.mjs +0 -182
  498. package/tools/gt-render/camera-math.mjs +0 -276
  499. package/tools/gt-render/page.mjs +0 -267
  500. package/tools/gt-render/render.mjs +0 -514
  501. package/tools/gt-render/scene-box.mjs +0 -31
  502. package/tools/gt-render/take-transform.mjs +0 -87
  503. package/tools/kimodo/compare-seam.mjs +0 -108
  504. package/tools/kimodo/constraints.mjs +0 -244
  505. package/tools/kimodo/edit.mjs +0 -259
  506. package/tools/kimodo/effector-constraints.mjs +0 -200
  507. package/tools/kimodo/generate.mjs +0 -646
  508. package/tools/kimodo/local-output.mjs +0 -265
  509. package/tools/kimodo/measure-pose.mjs +0 -65
  510. package/tools/kimodo/measure-preserve.mjs +0 -899
  511. package/tools/kimodo/measure-waypoints.mjs +0 -55
  512. package/tools/kimodo/mlx-generate.py +0 -111
  513. package/tools/kimodo/pose-constraints.mjs +0 -248
  514. package/tools/kimodo/preserve-mask.mjs +0 -543
  515. package/tools/kimodo/read-npz.mjs +0 -189
  516. package/tools/kimodo/run-edit-on-box.mjs +0 -155
  517. package/tools/kimodo/run-sequence-on-box.mjs +0 -178
  518. package/tools/kimodo/runner.mjs +0 -284
  519. package/tools/kimodo/seam-batch.mjs +0 -77
  520. package/tools/kimodo/setup-local.mjs +0 -30
  521. package/tools/kimodo/setup-on-box.sh +0 -339
  522. package/tools/kimodo/soma77-to-cskel27.mjs +0 -305
  523. package/tools/morphgs/assets/gen_truth.py +0 -39
  524. package/tools/morphgs/assets/mesh_ori_rig.txt +0 -27
  525. package/tools/morphgs/assets/playback-check.mjs +0 -30
  526. package/tools/morphgs/demo-gate.sh +0 -26
  527. package/tools/morphgs/fbx2morphgs.mjs +0 -68
  528. package/tools/morphgs/morphgs-to-cskel27.mjs +0 -105
  529. package/tools/morphgs/patches/preprocess_src-none-mode.patch +0 -76
  530. package/tools/morphgs/setup-on-cluster.sh +0 -126
  531. package/tools/projflow/driver.py +0 -1069
  532. package/tools/projflow/generate.mjs +0 -805
  533. package/tools/projflow/hml22-to-cskel27.mjs +0 -735
  534. package/tools/projflow/line-edit-job.mjs +0 -547
  535. package/tools/projflow/replay.mjs +0 -452
  536. package/tools/projflow/runner.mjs +0 -269
  537. package/tools/projflow/service.mjs +0 -742
  538. package/tools/track/DESIGN.md +0 -513
  539. package/tools/track/backfill-provenance.mjs +0 -116
  540. package/tools/track/budget.mjs +0 -8
  541. package/tools/track/check-rig.mjs +0 -209
  542. package/tools/track/diagnostics.schema.json +0 -60
  543. package/tools/track/export-rig.mjs +0 -256
  544. package/tools/track/fallback.mjs +0 -7
  545. package/tools/track/fk-parity-fixture.mjs +0 -161
  546. package/tools/track/gate.mjs +0 -349
  547. package/tools/track/masks.mjs +0 -318
  548. package/tools/track/metrics.mjs +0 -201
  549. package/tools/track/publish-obs.mjs +0 -108
  550. package/tools/track/py/check_env.py +0 -61
  551. package/tools/track/py/eval_lr.py +0 -229
  552. package/tools/track/py/lr_viterbi.py +0 -294
  553. package/tools/track/py/masks.py +0 -405
  554. package/tools/track/py/objective.py +0 -382
  555. package/tools/track/py/rig.py +0 -508
  556. package/tools/track/py/scene.py +0 -326
  557. package/tools/track/py/test_joint_indices.py +0 -123
  558. package/tools/track/py/test_lr_viterbi.py +0 -195
  559. package/tools/track/py/test_masks.py +0 -229
  560. package/tools/track/py/test_rig.py +0 -288
  561. package/tools/track/py/test_scene.py +0 -354
  562. package/tools/track/py/test_track.py +0 -328
  563. package/tools/track/py/track.py +0 -411
  564. package/tools/track/remote.mjs +0 -140
  565. package/tools/track/rig-dump.mjs +0 -227
  566. package/tools/track/run-box-tests.mjs +0 -14
  567. package/tools/track/run-box.mjs +0 -168
  568. package/tools/track/setup-box.sh +0 -59
  569. package/tools/track/study-2d.mjs +0 -728
  570. /package/src/{ardy → motion}/collision-blockers.js +0 -0
  571. /package/src/{ardy → motion}/ground.js +0 -0
  572. /package/src/{ardy → motion}/ik-drag.js +0 -0
  573. /package/src/{ardy → motion}/ik-key-json.js +0 -0
  574. /package/src/{ardy → motion}/ik.js +0 -0
  575. /package/src/{ardy → motion}/physics-surface.js +0 -0
  576. /package/src/{ardy → motion}/playback-clock.js +0 -0
  577. /package/src/{ardy → motion}/range-pin.js +0 -0
@@ -1,405 +0,0 @@
1
- #!/usr/bin/env python3
2
- """SAM2 video masks of the one person in a bench clip (tracker silhouette evidence).
3
-
4
- Inputs are what a real user has: the video and GVHMR's obs NPZ (``bbx_xys`` =
5
- centre x, centre y, square size in image pixels; ``kp2d`` ViTPose confidences).
6
- No plate, truth mask or colour cue is read here.
7
-
8
- Prompting:
9
- * anchor = first frame whose obs box is valid and trusted (mean ViTPose
10
- confidence >= TRUST_CONF; frame 0 on every clean clip). If no frame is
11
- trusted the most confident valid frame is used; no valid box at all raises
12
- ``no-person-prompt``. The anchor gets the obs box plus positive points at the
13
- confident ViTPose shoulder/hip midpoints, nose, wrists and (at >= ANKLE_TRUST)
14
- ankles inside the box (``--prompt joints``); frames before it are tracked
15
- backwards from it. Measured on the 11 shaded truth clips (median of per-item
16
- median IoU): box + centre point 0.000 (the centre of GVHMR's loose square box
17
- lands on the floor between the legs), box only 0.000, torso midpoints 0.889,
18
- joints 0.930, all 17 joints 0.899; without ankles (``upper``) the grey skin
19
- set falls to 0.886. The part-coloured mannequin needs points on the head and
20
- limbs or SAM2 returns a sub-part.
21
- * re-prompt: when the mask area changes by more than REPROMPT_CHANGE relative to
22
- the previous frame in tracking order, that frame is re-prompted from its own
23
- obs box (if trusted), at most once per frame, and tracking resumes from it.
24
- * chunked propagation: at most ``--chunk-frames`` frames are loaded per SAM2
25
- state (frames offloaded to CPU); the next chunk is seeded with the previous
26
- chunk's boundary mask. This bounds GPU memory for 15 s (362 frame) clips.
27
-
28
- Output ``<out>/masks.npz``: ``prob`` float16 T x H/2 x W/2 (sigmoid of the SAM2
29
- logits, area-downsampled), ``reprompted`` int32 frame list, ``area`` int32 full
30
- resolution foreground pixels per frame, ``anchor`` int32, ``chunks`` int32
31
- (direction, start, end) rows with direction 1 forward / -1 backward. A summary
32
- is written to ``<out>/masks.json``.
33
- """
34
- from __future__ import annotations
35
-
36
- import argparse
37
- import contextlib
38
- import json
39
- import os
40
- import sys
41
- import tempfile
42
- import time
43
-
44
- import numpy as np
45
-
46
- REPROMPT_CHANGE = 0.4
47
- TRUST_CONF = 0.5
48
- DEFAULT_CHUNK_FRAMES = 180
49
- DEFAULT_MAX_RESERVED_MIB = 5632
50
- CONFIG = "configs/sam2.1/sam2.1_hiera_s.yaml"
51
- CHECKPOINT = os.path.expanduser("~/cclay-ingest/cozyfit/checkpoints/sam2.1_hiera_small.pt")
52
- OBJ_ID = 1
53
-
54
-
55
- class NoPersonPrompt(RuntimeError):
56
- """The obs carries no usable person box to prompt SAM2 with."""
57
-
58
- def __init__(self, detail: str):
59
- super().__init__(f"no-person-prompt: {detail}")
60
-
61
-
62
- def box_from_xys(xys, width: int, height: int):
63
- """obs ``bbx_xys`` row (centre x, centre y, size) -> clipped xyxy box, or None."""
64
- xys = np.asarray(xys, dtype=np.float64)
65
- if xys.shape != (3,) or not np.all(np.isfinite(xys)) or xys[2] <= 1.0:
66
- return None
67
- cx, cy, size = xys
68
- half = size / 2.0
69
- x0, x1 = np.clip([cx - half, cx + half], 0.0, width - 1.0)
70
- y0, y1 = np.clip([cy - half, cy + half], 0.0, height - 1.0)
71
- if x1 - x0 < 2.0 or y1 - y0 < 2.0:
72
- return None
73
- return np.array([x0, y0, x1, y1], dtype=np.float32)
74
-
75
-
76
- def box_centre(box) -> np.ndarray:
77
- return np.array([[(box[0] + box[2]) / 2.0, (box[1] + box[3]) / 2.0]], dtype=np.float32)
78
-
79
-
80
- PROMPT_MODES = ("centre", "box", "torso", "upper", "joints", "all")
81
- DEFAULT_PROMPT = "joints"
82
- TORSO_PAIRS = ((5, 6), (11, 12)) # COCO shoulders, hips
83
- EXTREMITIES = (0, 9, 10, 15, 16) # COCO nose, wrists, ankles
84
- UPPER = (0, 9, 10) # COCO nose, wrists
85
- ANKLES = (15, 16)
86
- # Floor-standing props hide the feet first; a hidden ankle still scores 0.46-0.71
87
- # (fal stepup/handon) against >= 0.76 when visible, and a click on the prop pulls
88
- # the prop into the person mask.
89
- ANKLE_TRUST = 0.75
90
-
91
-
92
- def prompt_points(mode: str, box, kp=None, trust: float = TRUST_CONF):
93
- """Positive points accompanying the box prompt of one frame (None = box only).
94
-
95
- centre: the box centre; torso: shoulder and hip midpoints whose two ViTPose
96
- joints are both confident and inside the box.
97
- """
98
- if mode not in PROMPT_MODES:
99
- raise ValueError(f"unknown prompt mode {mode}")
100
- if box is None or mode == "box":
101
- return None
102
- if mode == "centre":
103
- return box_centre(box)
104
- if kp is None:
105
- return None
106
- kp = np.asarray(kp, dtype=np.float64)
107
- candidates = []
108
- for a, b in TORSO_PAIRS:
109
- if kp[a, 2] >= trust and kp[b, 2] >= trust:
110
- candidates.append((kp[a, :2] + kp[b, :2]) / 2.0)
111
- extra = {"upper": UPPER, "joints": EXTREMITIES, "all": range(len(kp))}.get(mode, ())
112
- candidates += [kp[j, :2] for j in extra if kp[j, 2] >= (max(trust, ANKLE_TRUST) if j in ANKLES else trust)]
113
- points = [(x, y) for x, y in candidates if box[0] <= x <= box[2] and box[1] <= y <= box[3]]
114
- return np.array(points, dtype=np.float32) if points else None
115
-
116
-
117
- def frame_confidence(kp2d, frames: int) -> np.ndarray:
118
- """Mean ViTPose confidence per frame; all ones when the obs has no kp2d."""
119
- if kp2d is None:
120
- return np.ones(frames, dtype=np.float64)
121
- kp2d = np.asarray(kp2d, dtype=np.float64)
122
- if kp2d.ndim != 3 or kp2d.shape[0] != frames or kp2d.shape[2] < 3:
123
- raise ValueError(f"bad-obs: kp2d shape {kp2d.shape} for {frames} frames")
124
- conf = kp2d[:, :, 2].mean(axis=1)
125
- return np.where(np.isfinite(conf), conf, 0.0)
126
-
127
-
128
- def prompt_boxes(bbx_xys, width: int, height: int):
129
- """Validated per-frame prompt boxes; raises no-person-prompt when none exists."""
130
- if bbx_xys is None:
131
- raise NoPersonPrompt("obs has no bbx_xys")
132
- bbx = np.asarray(bbx_xys, dtype=np.float64)
133
- if bbx.ndim != 2 or bbx.shape[0] == 0 or bbx.shape[1] != 3:
134
- raise NoPersonPrompt(f"obs bbx_xys is empty or malformed (shape {bbx.shape})")
135
- boxes = [box_from_xys(row, width, height) for row in bbx]
136
- if all(box is None for box in boxes):
137
- raise NoPersonPrompt(f"none of {len(boxes)} obs boxes is a valid person box")
138
- return boxes
139
-
140
-
141
- def choose_anchor(boxes, conf, trust: float = TRUST_CONF) -> int:
142
- valid = np.array([box is not None for box in boxes])
143
- if not valid.any():
144
- raise NoPersonPrompt("no valid obs box")
145
- trusted = np.flatnonzero(valid & (np.asarray(conf) >= trust))
146
- if trusted.size:
147
- return int(trusted[0])
148
- return int(np.flatnonzero(valid)[np.argmax(np.asarray(conf)[valid])])
149
-
150
-
151
- def should_reprompt(prev_area: int, area: int, threshold: float = REPROMPT_CHANGE) -> bool:
152
- """Re-prompt when the mask area jumps by more than ``threshold`` of the previous frame's."""
153
- return prev_area > 0 and abs(area - prev_area) > threshold * prev_area
154
-
155
-
156
- def reprompt_frames(areas, eligible=None, threshold: float = REPROMPT_CHANGE):
157
- """Frames the rule fires on for a fixed area sequence (tracking order = index order)."""
158
- areas = [int(a) for a in areas]
159
- eligible = [True] * len(areas) if eligible is None else list(eligible)
160
- return [t for t in range(1, len(areas)) if eligible[t] and should_reprompt(areas[t - 1], areas[t], threshold)]
161
-
162
-
163
- def plan_chunks(frames: int, anchor: int, chunk_frames: int):
164
- """(direction, start, end) inclusive ranges in processing order.
165
-
166
- Forward chunks walk anchor..T-1, backward chunks anchor..0; neighbouring
167
- chunks share their boundary frame, which seeds the later chunk.
168
- """
169
- if chunk_frames < 2:
170
- raise ValueError("chunk-frames must be >= 2")
171
- if not 0 <= anchor < frames:
172
- raise ValueError(f"anchor {anchor} outside 0..{frames - 1}")
173
- chunks = []
174
- start = anchor
175
- while True:
176
- end = min(start + chunk_frames - 1, frames - 1)
177
- chunks.append((1, start, end))
178
- if end == frames - 1:
179
- break
180
- start = end
181
- end = anchor
182
- while end > 0:
183
- start = max(end - chunk_frames + 1, 0)
184
- chunks.append((-1, start, end))
185
- end = start
186
- return chunks
187
-
188
-
189
- def read_video(path: str):
190
- import cv2
191
-
192
- capture = cv2.VideoCapture(path)
193
- if not capture.isOpened():
194
- raise RuntimeError(f"cannot open video {path}")
195
- frames = []
196
- while True:
197
- ok, frame = capture.read()
198
- if not ok:
199
- break
200
- frames.append(frame)
201
- capture.release()
202
- if not frames:
203
- raise RuntimeError(f"no frames decoded from {path}")
204
- return frames
205
-
206
-
207
- def log(message: str) -> None:
208
- print(f"[masks] {message}", flush=True)
209
-
210
-
211
- def track_clip(predictor, open_chunk, count: int, prob_hw, boxes, trusted, anchor: int, chunks,
212
- threshold: float = REPROMPT_CHANGE, points=None):
213
- """Propagate one object over ``chunks`` with the online re-prompt rule.
214
-
215
- ``predictor`` follows the SAM2 video predictor API; ``open_chunk(start, end)``
216
- is a context manager yielding an inference state over frames start..end.
217
- Returns (prob float16 T x h x w, area int64 T, reprompted frame list).
218
- """
219
- import torch
220
- import torch.nn.functional as F
221
-
222
- ph, pw = prob_hw
223
- prob = np.zeros((count, ph, pw), dtype=np.float16)
224
- area = np.full(count, -1, dtype=np.int64)
225
- reprompted: list[int] = []
226
- boundary: dict = {} # full-res bool masks: chunk boundary frames and the anchor
227
- done = 0
228
-
229
- def record(t: int, logits) -> int:
230
- nonlocal done
231
- mask = logits[0, 0] > 0
232
- small = F.interpolate(torch.sigmoid(logits.float()), size=(ph, pw), mode="area")[0, 0]
233
- prob[t] = small.float().cpu().numpy().astype(np.float16)
234
- if area[t] < 0:
235
- done += 1
236
- if done % 20 == 0 or done == count:
237
- log(f"{done} / {count}")
238
- area[t] = int(mask.sum().item())
239
- boundary[t] = mask.cpu()
240
- return int(area[t])
241
-
242
- def prompt_box(state, local: int, t: int):
243
- positive = None if points is None else points[t]
244
- if positive is None:
245
- predictor.add_new_points_or_box(state, frame_idx=local, obj_id=OBJ_ID, box=boxes[t])
246
- else:
247
- predictor.add_new_points_or_box(state, frame_idx=local, obj_id=OBJ_ID, box=boxes[t], points=positive,
248
- labels=np.ones(len(positive), dtype=np.int32))
249
-
250
- for index, (direction, start, end) in enumerate(chunks):
251
- reverse = direction < 0
252
- seed = end if reverse else start
253
- log(f"chunk {index + 1}/{len(chunks)} {'backward' if reverse else 'forward'} {start}-{end} seed={seed}")
254
- with open_chunk(start, end) as state:
255
- seed_local = seed - start
256
- if seed in boundary and bool(boundary[seed].any()):
257
- predictor.add_new_mask(state, frame_idx=seed_local, obj_id=OBJ_ID, mask=boundary[seed])
258
- elif boxes[seed] is not None:
259
- if seed != anchor:
260
- reprompted.append(seed)
261
- log(f"reprompt f{seed}: empty seed mask")
262
- prompt_box(state, seed_local, seed)
263
- else:
264
- predictor.add_new_mask(state, frame_idx=seed_local, obj_id=OBJ_ID, mask=boundary[seed])
265
- resume = seed_local
266
- while True:
267
- restart = None
268
- for local, _, logits in predictor.propagate_in_video(state, start_frame_idx=resume, reverse=reverse):
269
- t = start + local
270
- if t == seed and area[t] >= 0:
271
- continue # boundary frame already recorded by the previous chunk
272
- new_area = record(t, logits)
273
- prev = t + 1 if reverse else t - 1
274
- if (t != seed and 0 <= prev < count and area[prev] >= 0 and t not in reprompted
275
- and trusted[t] and boxes[t] is not None
276
- and should_reprompt(int(area[prev]), new_area, threshold)):
277
- log(f"reprompt f{t}: area {int(area[prev])} -> {new_area}")
278
- reprompted.append(t)
279
- restart = local
280
- break
281
- if restart is None:
282
- break
283
- # Forget the tracked output so the obs box is an initial conditioning
284
- # prompt (fresh segmentation), not a correction of the drifted mask.
285
- state["output_dict_per_obj"][0]["non_cond_frame_outputs"].pop(restart, None)
286
- state["frames_tracked_per_obj"][0].pop(restart, None)
287
- area[start + restart] = -1
288
- done -= 1
289
- prompt_box(state, restart, start + restart)
290
- resume = restart
291
- for t in list(boundary):
292
- if t not in (start, end, anchor):
293
- del boundary[t]
294
- if (area < 0).any():
295
- raise RuntimeError(f"frames without a mask: {np.flatnonzero(area < 0).tolist()[:10]}")
296
- return prob, area, sorted(reprompted)
297
-
298
-
299
- def segment(video: str, obs: str, chunk_frames: int = DEFAULT_CHUNK_FRAMES, checkpoint: str = CHECKPOINT,
300
- max_reserved_mib: int = DEFAULT_MAX_RESERVED_MIB, threshold: float = REPROMPT_CHANGE,
301
- prompt: str = DEFAULT_PROMPT):
302
- """Run SAM2 over the clip; returns (arrays, summary)."""
303
- if prompt not in PROMPT_MODES:
304
- raise ValueError(f"unknown prompt mode {prompt}")
305
- data = np.load(obs)
306
- bbx = data["bbx_xys"] if "bbx_xys" in data.files else None
307
- kp2d = data["kp2d"] if "kp2d" in data.files else None
308
- # Validate the prompt before decoding video or touching the GPU.
309
- if bbx is None or np.asarray(bbx).ndim != 2 or np.asarray(bbx).shape[0] == 0:
310
- prompt_boxes(bbx, 1, 1)
311
- started = time.time()
312
- frames = read_video(video)
313
- count = len(frames)
314
- height, width = frames[0].shape[:2]
315
- if len(bbx) != count:
316
- raise ValueError(f"frame-count-mismatch: video {count} frames, obs {len(bbx)}")
317
- boxes = prompt_boxes(bbx, width, height)
318
- conf = frame_confidence(kp2d, count)
319
- anchor = choose_anchor(boxes, conf)
320
- chunks = plan_chunks(count, anchor, chunk_frames)
321
- points = [prompt_points(prompt, boxes[t], None if kp2d is None else kp2d[t]) for t in range(count)]
322
- anchor_points = None if points[anchor] is None else points[anchor].round(1).tolist()
323
- log(f"video {width}x{height} frames={count} anchor={anchor} conf={conf[anchor]:.2f} box={boxes[anchor].round(1).tolist()} "
324
- f"prompt={prompt} points={anchor_points} chunks={len(chunks)}")
325
-
326
- import cv2
327
- import torch
328
- from sam2.build_sam import build_sam2_video_predictor
329
-
330
- if not torch.cuda.is_available():
331
- raise RuntimeError("CUDA is unavailable")
332
- total_mib = torch.cuda.get_device_properties(0).total_memory / 2**20
333
- # Hard cap: an allocation past the budget raises OOM instead of silently exceeding it.
334
- torch.cuda.set_per_process_memory_fraction(min(1.0, max_reserved_mib / total_mib), 0)
335
- torch.cuda.reset_peak_memory_stats()
336
- torch.backends.cuda.matmul.allow_tf32 = True
337
- torch.backends.cudnn.allow_tf32 = True
338
- predictor = build_sam2_video_predictor(CONFIG, checkpoint, device="cuda")
339
- load_s = time.time() - started
340
-
341
- @contextlib.contextmanager
342
- def jpeg_chunk(start: int, end: int):
343
- with tempfile.TemporaryDirectory(prefix="cozyfit-masks-") as tmp:
344
- for local, t in enumerate(range(start, end + 1)):
345
- cv2.imwrite(os.path.join(tmp, f"{local:05d}.jpg"), frames[t], [cv2.IMWRITE_JPEG_QUALITY, 95])
346
- state = predictor.init_state(tmp, offload_video_to_cpu=True)
347
- try:
348
- yield state
349
- finally:
350
- del state
351
- torch.cuda.empty_cache()
352
-
353
- with torch.inference_mode(), torch.autocast("cuda", dtype=torch.bfloat16):
354
- prob, area, reprompted = track_clip(predictor, jpeg_chunk, count, (height // 2, width // 2),
355
- boxes, conf >= TRUST_CONF, anchor, chunks, threshold, points)
356
- ph, pw = prob.shape[1:]
357
- torch.cuda.synchronize()
358
- peak = torch.cuda.max_memory_reserved() / 2**20
359
- seconds = time.time() - started
360
- arrays = {
361
- "prob": prob,
362
- "reprompted": np.array(sorted(reprompted), dtype=np.int32),
363
- "area": area.astype(np.int32),
364
- "anchor": np.array(anchor, dtype=np.int32),
365
- "chunks": np.array(chunks, dtype=np.int32).reshape(-1, 3),
366
- }
367
- summary = {
368
- "frames": count, "width": width, "height": height, "probShape": [count, ph, pw],
369
- "anchor": anchor, "anchorConfidence": float(conf[anchor]), "chunks": [list(c) for c in chunks],
370
- "chunkFrames": chunk_frames, "reprompted": sorted(reprompted), "repromptChange": threshold,
371
- "trustConfidence": TRUST_CONF, "prompt": prompt, "emptyFrames": int((area == 0).sum()),
372
- "seconds": round(seconds, 3), "modelLoadSeconds": round(load_s, 3), "peakReservedMiB": round(peak, 1),
373
- "maxReservedMiB": max_reserved_mib, "config": CONFIG, "checkpoint": os.path.basename(checkpoint),
374
- "torch": torch.__version__,
375
- }
376
- return arrays, summary
377
-
378
-
379
- def main(argv=None) -> int:
380
- parser = argparse.ArgumentParser(description=__doc__.split("\n")[0])
381
- parser.add_argument("--video", required=True)
382
- parser.add_argument("--obs", required=True)
383
- parser.add_argument("--out", required=True)
384
- parser.add_argument("--chunk-frames", type=int, default=DEFAULT_CHUNK_FRAMES)
385
- parser.add_argument("--checkpoint", default=CHECKPOINT)
386
- parser.add_argument("--max-reserved-mib", type=int, default=DEFAULT_MAX_RESERVED_MIB)
387
- parser.add_argument("--prompt", choices=PROMPT_MODES, default=DEFAULT_PROMPT)
388
- args = parser.parse_args(argv)
389
- try:
390
- arrays, summary = segment(args.video, args.obs, args.chunk_frames, args.checkpoint, args.max_reserved_mib,
391
- prompt=args.prompt)
392
- except NoPersonPrompt as error:
393
- print(str(error), file=sys.stderr, flush=True)
394
- return 3
395
- os.makedirs(args.out, exist_ok=True)
396
- np.savez_compressed(os.path.join(args.out, "masks.npz"), **arrays)
397
- with open(os.path.join(args.out, "masks.json"), "w") as handle:
398
- json.dump(summary, handle, indent=1)
399
- log(f"done frames={summary['frames']} seconds={summary['seconds']} peakReservedMiB={summary['peakReservedMiB']} "
400
- f"reprompted={len(summary['reprompted'])} emptyFrames={summary['emptyFrames']}")
401
- return 0
402
-
403
-
404
- if __name__ == "__main__":
405
- raise SystemExit(main())