cozyclay 2.1.0 → 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 (583) hide show
  1. package/CHANGELOG.md +65 -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 +22 -6
  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-BlT33CGf.js → first-shot-handoff-CC4xe4HM.js} +1 -1
  33. package/dist/assets/{landing-BSesrCpL.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/llms.txt +14 -0
  40. package/dist/sw.js +3 -6
  41. package/dist/workflow/index.html +5 -4
  42. package/mcp/LIVE-PROTOCOL.md +7 -47
  43. package/mcp/README.md +16 -26
  44. package/mcp/live-hub.mjs +6 -10
  45. package/mcp/package.json +1 -3
  46. package/mcp/tool-handlers.mjs +4 -168
  47. package/package.json +3 -22
  48. package/src/App.jsx +174 -2639
  49. package/src/agent-progress.js +79 -0
  50. package/src/agent-turn-mark.js +30 -0
  51. package/src/analytics.js +1 -19
  52. package/src/app-context.js +5 -0
  53. package/src/app-stage.jsx +2 -34
  54. package/src/asset-pane.jsx +6 -5
  55. package/src/camera-block.js +21 -0
  56. package/src/camera-direction.js +120 -0
  57. package/src/character-kind.js +3 -9
  58. package/src/command-bus.js +8 -6
  59. package/src/commands/cast.js +5 -34
  60. package/src/commands/elements/character.js +1 -1
  61. package/src/commands/elements/motion.js +1 -6
  62. package/src/commands/elements/object.js +6 -2
  63. package/src/commands/elements/shot.js +4 -1
  64. package/src/commands/elements.js +38 -14
  65. package/src/commands/index.js +3 -1
  66. package/src/commands/kits.js +37 -0
  67. package/src/commands/motion-timing.js +57 -0
  68. package/src/commands/motion.js +9 -152
  69. package/src/commands/motion3-compile.js +65 -0
  70. package/src/commands/motion3.js +207 -0
  71. package/src/commands/objects.js +6 -4
  72. package/src/commands/shot.js +49 -11
  73. package/src/commands/stage.js +14 -1
  74. package/src/commands/view.js +4 -1
  75. package/src/document-mirrors.js +42 -0
  76. package/src/document-store.js +4 -0
  77. package/src/domains/cast.js +19 -194
  78. package/src/domains/motion/motion-range-pin-rules.js +1 -1
  79. package/src/domains/motion/take-source.js +51 -0
  80. package/src/domains/motion.js +60 -1838
  81. package/src/domains/scenes.js +12 -55
  82. package/src/domains/shots.js +2 -2
  83. package/src/domains/stage.js +4 -2
  84. package/src/draft-ghost.js +46 -0
  85. package/src/draft-ghost.jsx +129 -0
  86. package/src/dualview.jsx +4 -3
  87. package/src/effects/EffectsLayer.jsx +112 -0
  88. package/src/effects/effect-meshes.js +232 -0
  89. package/src/effects/effect-record.js +103 -0
  90. package/src/effects/emitter.js +59 -0
  91. package/src/effects/simulate.js +261 -0
  92. package/src/effects/state.js +59 -0
  93. package/src/execution-telemetry.js +2 -3
  94. package/src/first-success-guide.jsx +5 -5
  95. package/src/ghost-set.js +257 -0
  96. package/src/held-object-edit.jsx +113 -0
  97. package/src/held-object-pose.js +175 -0
  98. package/src/hierarchy-model.js +1 -1
  99. package/src/hierarchy-panel.jsx +1 -1
  100. package/src/imported-mesh.jsx +103 -0
  101. package/src/kits/held.js +84 -0
  102. package/src/kits/index.js +70 -0
  103. package/src/kits/smoking.js +57 -0
  104. package/src/light-blockers.js +93 -0
  105. package/src/motion/beat-board-model.js +84 -0
  106. package/src/motion/beat-board.css +124 -0
  107. package/src/motion/beat-board.jsx +59 -0
  108. package/src/motion/center-of-mass.js +66 -0
  109. package/src/motion/fix-collisions.js +850 -0
  110. package/src/motion/physics-review.js +118 -0
  111. package/src/{ardy → motion}/physics-support.js +1 -76
  112. package/src/motion/rig-snapshot.js +22 -0
  113. package/src/motion/take-segments.jsx +140 -0
  114. package/src/{ardy → motion}/timeline-coordinates.js +0 -21
  115. package/src/{ardy → motion}/timeline.css +1 -2
  116. package/src/{ardy → motion}/timeline.jsx +48 -419
  117. package/src/motion/waypoints.js +76 -0
  118. package/src/motion3/beats.js +83 -0
  119. package/src/motion3/body.js +158 -0
  120. package/src/motion3/bones.js +47 -0
  121. package/src/motion3/carried.js +47 -0
  122. package/src/motion3/compile-job.js +75 -0
  123. package/src/motion3/compile-worker.js +10 -0
  124. package/src/motion3/compiler/anchors.js +105 -0
  125. package/src/motion3/compiler/arm-orient.js +128 -0
  126. package/src/motion3/compiler/channels.js +125 -0
  127. package/src/motion3/compiler/compile.js +205 -0
  128. package/src/motion3/compiler/gait-rhythm.js +97 -0
  129. package/src/motion3/compiler/grip.js +171 -0
  130. package/src/motion3/compiler/head-steady.js +48 -0
  131. package/src/motion3/compiler/hold.js +134 -0
  132. package/src/motion3/compiler/idle.js +113 -0
  133. package/src/motion3/compiler/limbs.js +170 -0
  134. package/src/motion3/compiler/math.js +72 -0
  135. package/src/motion3/compiler/pose.js +162 -0
  136. package/src/motion3/compiler/quat.js +49 -0
  137. package/src/motion3/compiler/skeleton.js +74 -0
  138. package/src/motion3/compiler/validate-common.js +32 -0
  139. package/src/motion3/compiler/validate-goals.js +118 -0
  140. package/src/motion3/compiler/validate-objects.js +143 -0
  141. package/src/motion3/compiler/validate-parts.js +116 -0
  142. package/src/motion3/compiler/validate.js +122 -0
  143. package/src/motion3/compiler/vocab.js +73 -0
  144. package/src/motion3/compiler/walk-pelvis.js +275 -0
  145. package/src/motion3/compiler/walk.js +207 -0
  146. package/src/motion3/compiler/wrist-clamp.js +32 -0
  147. package/src/motion3/delta.js +47 -0
  148. package/src/motion3/describe.js +174 -0
  149. package/src/motion3/docs.js +27 -0
  150. package/src/motion3/edit-carry.js +21 -0
  151. package/src/motion3/fix/ease.js +16 -0
  152. package/src/motion3/fix/ground.js +37 -0
  153. package/src/motion3/fix/index.js +52 -0
  154. package/src/motion3/fix/push.js +71 -0
  155. package/src/motion3/fix/reach.js +46 -0
  156. package/src/motion3/fix/retime.js +79 -0
  157. package/src/motion3/fix/self.js +211 -0
  158. package/src/motion3/fix/slide.js +70 -0
  159. package/src/motion3/fix/turn.js +51 -0
  160. package/src/motion3/follow.js +39 -0
  161. package/src/motion3/free-motion.js +173 -0
  162. package/src/motion3/grip-choice.js +130 -0
  163. package/src/motion3/hold-plan.js +157 -0
  164. package/src/motion3/json-patch.js +77 -0
  165. package/src/motion3/key-aim.js +105 -0
  166. package/src/motion3/key-edit.js +82 -0
  167. package/src/motion3/lenient.js +127 -0
  168. package/src/motion3/object-keys.js +236 -0
  169. package/src/motion3/object-pose.js +182 -0
  170. package/src/motion3/object-tracks.js +12 -0
  171. package/src/motion3/pipeline.js +102 -0
  172. package/src/motion3/playback.js +51 -0
  173. package/src/motion3/probes.js +91 -0
  174. package/src/motion3/qa/body-checks.js +34 -0
  175. package/src/motion3/qa/check-names.js +14 -0
  176. package/src/motion3/qa/checks.js +121 -0
  177. package/src/motion3/qa/contact-checks.js +51 -0
  178. package/src/motion3/qa/explain.js +80 -0
  179. package/src/motion3/qa/fmt.js +5 -0
  180. package/src/motion3/qa/foot-checks.js +72 -0
  181. package/src/motion3/qa/geometry.js +77 -0
  182. package/src/motion3/qa/goals.js +191 -0
  183. package/src/motion3/qa/ground.js +60 -0
  184. package/src/motion3/qa/hints-contact.js +179 -0
  185. package/src/motion3/qa/hints-goal.js +160 -0
  186. package/src/motion3/qa/hints-knee.js +24 -0
  187. package/src/motion3/qa/hints-motion.js +209 -0
  188. package/src/motion3/qa/hints-object.js +91 -0
  189. package/src/motion3/qa/index.js +42 -0
  190. package/src/motion3/qa/key-checks.js +82 -0
  191. package/src/motion3/qa/near.js +96 -0
  192. package/src/motion3/qa/object-checks.js +135 -0
  193. package/src/motion3/qa/patch.js +161 -0
  194. package/src/motion3/qa/severity.js +38 -0
  195. package/src/motion3/qa/spec-checks.js +65 -0
  196. package/src/motion3/qa/style.js +218 -0
  197. package/src/motion3/qa/wrist-checks.js +69 -0
  198. package/src/motion3/qa/wrist-obs.js +60 -0
  199. package/src/motion3/refs.js +120 -0
  200. package/src/motion3/repair-tune.js +121 -0
  201. package/src/motion3/repair.js +83 -0
  202. package/src/motion3/report.js +67 -0
  203. package/src/motion3/rigs/index.js +14 -0
  204. package/src/motion3/rigs/x-bot-tpose-body.json +1 -0
  205. package/src/motion3/rigs/x-bot-tpose.json +1 -0
  206. package/src/motion3/rigs/y-bot-tpose-body.json +1 -0
  207. package/src/motion3/rigs/y-bot-tpose.json +1 -0
  208. package/src/motion3/scene.js +60 -0
  209. package/src/motion3/schema/brief.js +89 -0
  210. package/src/motion3/schema/fields.js +44 -0
  211. package/src/motion3/schema/spec-examples.js +46 -0
  212. package/src/motion3/schema/topics.js +111 -0
  213. package/src/motion3/schema/types.js +147 -0
  214. package/src/motion3/search.js +117 -0
  215. package/src/motion3/second-hand.js +112 -0
  216. package/src/motion3/skills/basic.js +82 -0
  217. package/src/motion3/skills/book.js +76 -0
  218. package/src/motion3/skills/car.js +145 -0
  219. package/src/motion3/skills/frames-auto.js +29 -0
  220. package/src/motion3/skills/index.js +83 -0
  221. package/src/motion3/skills/info.js +65 -0
  222. package/src/motion3/skills/smoke-kit.js +109 -0
  223. package/src/motion3/skills/smoke.js +160 -0
  224. package/src/motion3/spec-store.js +59 -0
  225. package/src/motion3/take-edit.js +155 -0
  226. package/src/motion3/take.js +63 -0
  227. package/src/motion3/timeline.js +127 -0
  228. package/src/motion3/views.js +121 -0
  229. package/src/motion3/walk-plan.js +119 -0
  230. package/src/object-catalog.jsx +7 -1
  231. package/src/object-colors.js +98 -0
  232. package/src/object-gizmo-parts.js +115 -0
  233. package/src/object-gizmo.jsx +37 -122
  234. package/src/object-glow.js +139 -0
  235. package/src/object-hidden.js +33 -0
  236. package/src/object-look.js +196 -0
  237. package/src/object-size.js +10 -0
  238. package/src/object-travel.js +71 -6
  239. package/src/panels/CameraPanel.jsx +7 -7
  240. package/src/panels/HdrEnvironmentControls.jsx +59 -0
  241. package/src/panels/LightPanel.jsx +25 -4
  242. package/src/panels/ObjectLookPanel.jsx +88 -0
  243. package/src/panels/PoseImagePanel.jsx +1 -1
  244. package/src/panels/PosePanel.jsx +2 -17
  245. package/src/panels/RigControlPanel.jsx +5 -53
  246. package/src/panels/SubjectBox.jsx +4 -1
  247. package/src/panels/details.css +14 -0
  248. package/src/posestudio.jsx +4 -27
  249. package/src/project-browser.jsx +3 -4
  250. package/src/project-resources.js +18 -31
  251. package/src/project.js +13 -104
  252. package/src/props.jsx +25 -280
  253. package/src/rail-dot-drag.js +23 -0
  254. package/src/render-passes.js +10 -0
  255. package/src/resource-status.jsx +1 -0
  256. package/src/result-modal.jsx +4 -19
  257. package/src/room.jsx +20 -12
  258. package/src/scene-assets.js +8 -6
  259. package/src/scene-light-place.js +72 -0
  260. package/src/scene-lights.jsx +148 -0
  261. package/src/scene-objects.js +31 -139
  262. package/src/scenes.js +15 -105
  263. package/src/semantic-edit.js +2 -5
  264. package/src/set-pieces.jsx +183 -0
  265. package/src/settings-menu.jsx +3 -11
  266. package/src/shell/BottomDock.jsx +11 -53
  267. package/src/shell/CompositionGuideButton.jsx +20 -0
  268. package/src/shell/DetailsSlot.jsx +16 -200
  269. package/src/shell/GhostsMenuGroup.jsx +74 -0
  270. package/src/shell/LibrarySlot.jsx +1 -3
  271. package/src/shell/MenuBar.jsx +3 -3
  272. package/src/shell/PreferencesDialog.jsx +15 -264
  273. package/src/shell/PreferencesSlot.jsx +2 -8
  274. package/src/shell/ShotPreviewHeader.jsx +17 -0
  275. package/src/shell/TopBar.jsx +5 -83
  276. package/src/shell/ViewportToolbar.jsx +23 -93
  277. package/src/shell/agent-glass.css +19 -6
  278. package/src/shell/details-selection.js +42 -0
  279. package/src/shell/ghosts-menu.css +22 -0
  280. package/src/shell/glass-regions.css +29 -3
  281. package/src/shell/preferences.css +6 -1
  282. package/src/shell/viewport.css +12 -0
  283. package/src/shot-authoring.js +4 -7
  284. package/src/soft-shadows.js +37 -0
  285. package/src/stage-attach-frames.js +12 -12
  286. package/src/stage-canvas-rigs.jsx +3 -3
  287. package/src/stage-capture.jsx +42 -8
  288. package/src/stage-characters.jsx +22 -216
  289. package/src/stage-editor-gizmos.jsx +6 -6
  290. package/src/stage-environment.js +93 -0
  291. package/src/stage-hdr-builtins.js +107 -0
  292. package/src/stage-hdr-image.js +116 -0
  293. package/src/stage-hdr-library.js +70 -0
  294. package/src/stage-hdr-map.js +94 -0
  295. package/src/stage-hdr.js +130 -0
  296. package/src/stage-lighting.js +149 -0
  297. package/src/stage-occlusion.js +210 -0
  298. package/src/stage-sky.jsx +159 -0
  299. package/src/stage-startup-defaults.js +5 -9
  300. package/src/studio-actions.js +12 -14
  301. package/src/studio-agent-commands.js +46 -16
  302. package/src/studio-agent-motion.js +9 -9
  303. package/src/studio-agent-protocol.js +46 -73
  304. package/src/studio-app-binding.js +29 -45
  305. package/src/studio-asset-list.js +17 -0
  306. package/src/studio-contact-sheet.js +37 -5
  307. package/src/studio-elements.js +27 -7
  308. package/src/studio-entity-bounds.js +18 -0
  309. package/src/studio-error-codes.js +8 -0
  310. package/src/studio-export-framing.js +152 -0
  311. package/src/studio-lighting-check.js +103 -0
  312. package/src/studio-motion-visual.js +150 -0
  313. package/src/studio-schema-errors.js +80 -0
  314. package/src/studio-support-gap.js +74 -0
  315. package/src/styles.css +1 -19
  316. package/src/timeline-defaults.js +5 -0
  317. package/src/timeline-extent.js +8 -20
  318. package/src/tutorial-walk.js +17 -0
  319. package/src/ui-scrub.js +1 -1
  320. package/src/use-shot-camera-tracks.js +12 -0
  321. package/src/view-ghosts.js +97 -0
  322. package/src/view-ghosts.jsx +49 -0
  323. package/src/workflow/AgentPanel.jsx +47 -166
  324. package/src/workflow/AgentProgressCard.jsx +107 -0
  325. package/src/workflow/AgentReplyText.jsx +37 -0
  326. package/src/workflow/AgentTurnSteps.jsx +84 -0
  327. package/src/workflow/LoginPanel.jsx +140 -0
  328. package/src/workflow/WorkflowBuilder.jsx +7 -23
  329. package/src/workflow/agent-client.js +54 -51
  330. package/src/workflow/agent-panel.css +71 -1
  331. package/src/workflow/agent-progress-card.css +167 -0
  332. package/src/workflow/agent-progress-view.js +47 -0
  333. package/src/workflow/agent-turn-steps.css +113 -0
  334. package/src/workflow/agent-turn.js +191 -0
  335. package/src/workflow/login-client.js +34 -0
  336. package/src/workflow/motion-input.js +18 -31
  337. package/src/workflow/node-schema.js +1 -1
  338. package/src/workflow/reply-markdown.js +69 -0
  339. package/src/workflow/scene-asset-sync.js +8 -8
  340. package/tools/agent-session-stats.mjs +61 -0
  341. package/tools/dev-full.mjs +5 -56
  342. package/tools/eslint-baseline.json +12 -13
  343. package/tools/headless-studio.mjs +85 -0
  344. package/tools/knip-baseline.json +0 -179
  345. package/tools/loc-baseline.json +33 -83
  346. package/tools/motion3/extract-body.mjs +103 -0
  347. package/tools/motion3/extract-rig.mjs +85 -0
  348. package/tools/process-supervisor.mjs +0 -88
  349. package/tools/run-tests.mjs +9 -124
  350. package/bin/agent/motion-runtime.mjs +0 -76
  351. package/dist/ardy/cskel27-rest.json +0 -557
  352. package/dist/assets/analytics-B41FmPYI.js +0 -1
  353. package/dist/assets/app-Do-Og5Pu.css +0 -1
  354. package/dist/assets/app-DwP-tl2x.js +0 -4920
  355. package/dist/assets/config-Bx7pYsD9.js +0 -1
  356. package/dist/assets/config-DeoFTYzb.css +0 -1
  357. package/dist/assets/demo-Dt0qZfS7.js +0 -1
  358. package/dist/assets/scene-asset-sync-BCWDJB1d.js +0 -19
  359. package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
  360. package/dist/assets/ticket-jqMhqL88.js +0 -1
  361. package/dist/assets/workflow-Br-K7Srs.js +0 -179
  362. package/dist/demo/walk-then-stop.npz +0 -0
  363. package/mcp/ardy-prompts.mjs +0 -218
  364. package/src/ardy/auto-fix-panel.css +0 -108
  365. package/src/ardy/auto-physics.js +0 -1194
  366. package/src/ardy/client.js +0 -153
  367. package/src/ardy/convert.js +0 -313
  368. package/src/ardy/cskel27-neutral.js +0 -39
  369. package/src/ardy/cskel27.js +0 -77
  370. package/src/ardy/export.js +0 -165
  371. package/src/ardy/fix-collisions.js +0 -2257
  372. package/src/ardy/foot-lock.js +0 -230
  373. package/src/ardy/motion-calibration.js +0 -118
  374. package/src/ardy/motion-edit.js +0 -213
  375. package/src/ardy/motion-url.js +0 -55
  376. package/src/ardy/mp4-duration.js +0 -102
  377. package/src/ardy/npz.js +0 -595
  378. package/src/ardy/physics-panel.css +0 -21
  379. package/src/ardy/physics-panel.jsx +0 -89
  380. package/src/ardy/physics-refine.js +0 -111
  381. package/src/ardy/physics-review.js +0 -567
  382. package/src/ardy/physics-temporal.js +0 -54
  383. package/src/ardy/platform-fit-panel.css +0 -64
  384. package/src/ardy/platform-fit-panel.jsx +0 -41
  385. package/src/ardy/platform-fit.js +0 -326
  386. package/src/ardy/playback.js +0 -711
  387. package/src/ardy/pose-pin.js +0 -97
  388. package/src/ardy/prompt-clips.js +0 -30
  389. package/src/ardy/retime.js +0 -211
  390. package/src/ardy/root-drop.js +0 -369
  391. package/src/ardy/timeline-resize.js +0 -13
  392. package/src/ardy/to-cskel27.js +0 -225
  393. package/src/ardy/trim.js +0 -33
  394. package/src/ardy/waypoints.js +0 -369
  395. package/src/ardy-bridge-contract.js +0 -281
  396. package/src/domains/motion/motion-video-capture.js +0 -664
  397. package/src/i2v-motion-client.js +0 -77
  398. package/src/i2v-motion-studio.jsx +0 -180
  399. package/src/line-edit.js +0 -1348
  400. package/src/motion/generation.js +0 -105
  401. package/src/motion-readiness-ui.jsx +0 -67
  402. package/src/motion-readiness.js +0 -40
  403. package/src/motion-resources.js +0 -177
  404. package/src/motion-store.js +0 -65
  405. package/src/motion-trail.js +0 -601
  406. package/src/multimodel-ingest.js +0 -394
  407. package/src/panels/PromptBlocksPanel.jsx +0 -444
  408. package/src/panels/VideoCapturePanel.jsx +0 -181
  409. package/src/pose-extract/browser.js +0 -42
  410. package/src/pose-extract/detector.js +0 -100
  411. package/src/pose-extract/fit.js +0 -227
  412. package/src/pose-extract/image-frame.js +0 -51
  413. package/src/pose-extract/index.js +0 -58
  414. package/src/pose-extract/keyframes.js +0 -74
  415. package/src/pose-extract/landmarks.js +0 -162
  416. package/src/pose-extract/mirror.js +0 -153
  417. package/src/pose-extract/take.js +0 -131
  418. package/src/pose-extract/video-frames.js +0 -91
  419. package/src/take-recipe.js +0 -165
  420. package/src/trail-key-conflicts.js +0 -39
  421. package/src/trail-pick.js +0 -59
  422. package/src/use-i2v-capture-modal.js +0 -15
  423. package/tools/ardy/BRIDGE.md +0 -37
  424. package/tools/ardy/GVHMR-NPZ.md +0 -68
  425. package/tools/ardy/README.md +0 -124
  426. package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
  427. package/tools/ardy/artifacts.mjs +0 -34
  428. package/tools/ardy/bridge.mjs +0 -1732
  429. package/tools/ardy/bvh-cskel27.mjs +0 -1265
  430. package/tools/ardy/cclay_constrained_generate.py +0 -561
  431. package/tools/ardy/cclay_gvhmr_worker.py +0 -264
  432. package/tools/ardy/cclay_motion_edit.py +0 -510
  433. package/tools/ardy/cclay_sequence_generate.py +0 -705
  434. package/tools/ardy/cclay_worker.py +0 -304
  435. package/tools/ardy/dump-npz.py +0 -205
  436. package/tools/ardy/extract-rest.mjs +0 -299
  437. package/tools/ardy/extract.mjs +0 -507
  438. package/tools/ardy/footage.mjs +0 -467
  439. package/tools/ardy/gvhmr-floor.mjs +0 -102
  440. package/tools/ardy/gvhmr-to-cskel27.mjs +0 -21
  441. package/tools/ardy/gvhmr_fastpath.py +0 -168
  442. package/tools/ardy/gvhmr_trajectory.py +0 -240
  443. package/tools/ardy/merge-text-encoder.py +0 -160
  444. package/tools/ardy/mocap-metrics.mjs +0 -200
  445. package/tools/ardy/mocap-quality-gate.mjs +0 -110
  446. package/tools/ardy/motion-stabilize.mjs +0 -348
  447. package/tools/ardy/npz.mjs +0 -414
  448. package/tools/ardy/pose-to-npz.mjs +0 -106
  449. package/tools/ardy/prompt-limits.mjs +0 -5
  450. package/tools/ardy/run-edit-on-box.sh +0 -73
  451. package/tools/ardy/run-local.mjs +0 -233
  452. package/tools/ardy/run-on-box.sh +0 -593
  453. package/tools/ardy/run-sequence-on-box.sh +0 -177
  454. package/tools/ardy/runners/gvhmr-worker.mjs +0 -206
  455. package/tools/ardy/runners/index.mjs +0 -18
  456. package/tools/ardy/runners/local.mjs +0 -455
  457. package/tools/ardy/runners/proc.mjs +0 -125
  458. package/tools/ardy/runners/remote.mjs +0 -252
  459. package/tools/ardy/setup-local.mjs +0 -189
  460. package/tools/ardy/setup-text-encoder-on-box.sh +0 -93
  461. package/tools/ardy/setup-text-encoder.py +0 -306
  462. package/tools/ardy/smpl-cskel27.mjs +0 -178
  463. package/tools/ardy/visual-qa.mjs +0 -196
  464. package/tools/ardy/vq-car.mjs +0 -52
  465. package/tools/bench/EXP3.md +0 -61
  466. package/tools/bench/cclay_bench_extract_incam.py +0 -125
  467. package/tools/bench/cclay_bench_extract_obs.py +0 -204
  468. package/tools/bench/cclay_bench_runner.py +0 -42
  469. package/tools/bench/cube-contact.mjs +0 -162
  470. package/tools/bench/exp3.mjs +0 -152
  471. package/tools/bench/extract-bench-lib.mjs +0 -216
  472. package/tools/bench/extract-bench.mjs +0 -302
  473. package/tools/bench/fal-generate.mjs +0 -88
  474. package/tools/bench/fit/README.md +0 -189
  475. package/tools/bench/fit/camera.mjs +0 -59
  476. package/tools/bench/fit/contact.mjs +0 -419
  477. package/tools/bench/fit/footlock.mjs +0 -234
  478. package/tools/bench/fit/motion.mjs +0 -85
  479. package/tools/bench/fit/pin.mjs +0 -27
  480. package/tools/bench/fit/remote.mjs +0 -105
  481. package/tools/bench/fit-bench.mjs +0 -126
  482. package/tools/bench/fit-sanity.mjs +0 -45
  483. package/tools/bench/metrics.mjs +0 -313
  484. package/tools/bench/obs/depth.mjs +0 -258
  485. package/tools/bench/obs/extrinsics.mjs +0 -175
  486. package/tools/bench/obs/fit_mannequin_betas.py +0 -340
  487. package/tools/bench/obs/ground.mjs +0 -294
  488. package/tools/bench/obs/heading.mjs +0 -151
  489. package/tools/bench/obs/ladder.mjs +0 -555
  490. package/tools/bench/obs/mannequin-betas.json +0 -124
  491. package/tools/bench/obs/remote.mjs +0 -83
  492. package/tools/bench/obs/rest_joints.py +0 -52
  493. package/tools/bench/obs/ybot-targets.mjs +0 -49
  494. package/tools/bench/obs-bench.mjs +0 -520
  495. package/tools/bench/score.mjs +0 -343
  496. package/tools/bench/summarize.mjs +0 -86
  497. package/tools/demo-worker/README.md +0 -137
  498. package/tools/demo-worker/api-client.mjs +0 -233
  499. package/tools/demo-worker/generate.mjs +0 -301
  500. package/tools/demo-worker/index.mjs +0 -466
  501. package/tools/demo-worker/sign-core.mjs +0 -112
  502. package/tools/demo-worker/sign.mjs +0 -161
  503. package/tools/gt-render/browser.mjs +0 -159
  504. package/tools/gt-render/camera-math.mjs +0 -276
  505. package/tools/gt-render/page.mjs +0 -267
  506. package/tools/gt-render/render.mjs +0 -514
  507. package/tools/gt-render/scene-box.mjs +0 -31
  508. package/tools/gt-render/take-transform.mjs +0 -87
  509. package/tools/kimodo/compare-seam.mjs +0 -108
  510. package/tools/kimodo/constraints.mjs +0 -244
  511. package/tools/kimodo/edit.mjs +0 -259
  512. package/tools/kimodo/effector-constraints.mjs +0 -200
  513. package/tools/kimodo/generate.mjs +0 -646
  514. package/tools/kimodo/local-output.mjs +0 -265
  515. package/tools/kimodo/measure-pose.mjs +0 -65
  516. package/tools/kimodo/measure-preserve.mjs +0 -899
  517. package/tools/kimodo/measure-waypoints.mjs +0 -55
  518. package/tools/kimodo/mlx-generate.py +0 -111
  519. package/tools/kimodo/pose-constraints.mjs +0 -248
  520. package/tools/kimodo/preserve-mask.mjs +0 -543
  521. package/tools/kimodo/read-npz.mjs +0 -189
  522. package/tools/kimodo/run-edit-on-box.mjs +0 -155
  523. package/tools/kimodo/run-sequence-on-box.mjs +0 -178
  524. package/tools/kimodo/runner.mjs +0 -284
  525. package/tools/kimodo/seam-batch.mjs +0 -77
  526. package/tools/kimodo/setup-local.mjs +0 -30
  527. package/tools/kimodo/setup-on-box.sh +0 -339
  528. package/tools/kimodo/soma77-to-cskel27.mjs +0 -305
  529. package/tools/morphgs/assets/gen_truth.py +0 -39
  530. package/tools/morphgs/assets/mesh_ori_rig.txt +0 -27
  531. package/tools/morphgs/assets/playback-check.mjs +0 -30
  532. package/tools/morphgs/demo-gate.sh +0 -26
  533. package/tools/morphgs/fbx2morphgs.mjs +0 -68
  534. package/tools/morphgs/morphgs-to-cskel27.mjs +0 -105
  535. package/tools/morphgs/patches/preprocess_src-none-mode.patch +0 -76
  536. package/tools/morphgs/setup-on-cluster.sh +0 -126
  537. package/tools/projflow/driver.py +0 -1069
  538. package/tools/projflow/generate.mjs +0 -805
  539. package/tools/projflow/hml22-to-cskel27.mjs +0 -735
  540. package/tools/projflow/line-edit-job.mjs +0 -547
  541. package/tools/projflow/replay.mjs +0 -452
  542. package/tools/projflow/runner.mjs +0 -269
  543. package/tools/projflow/service.mjs +0 -742
  544. package/tools/track/DESIGN.md +0 -513
  545. package/tools/track/backfill-provenance.mjs +0 -116
  546. package/tools/track/budget.mjs +0 -8
  547. package/tools/track/check-rig.mjs +0 -209
  548. package/tools/track/diagnostics.schema.json +0 -60
  549. package/tools/track/export-rig.mjs +0 -256
  550. package/tools/track/fallback.mjs +0 -7
  551. package/tools/track/fk-parity-fixture.mjs +0 -161
  552. package/tools/track/gate.mjs +0 -349
  553. package/tools/track/masks.mjs +0 -318
  554. package/tools/track/metrics.mjs +0 -201
  555. package/tools/track/publish-obs.mjs +0 -108
  556. package/tools/track/py/check_env.py +0 -61
  557. package/tools/track/py/eval_lr.py +0 -229
  558. package/tools/track/py/lr_viterbi.py +0 -294
  559. package/tools/track/py/masks.py +0 -405
  560. package/tools/track/py/objective.py +0 -382
  561. package/tools/track/py/rig.py +0 -508
  562. package/tools/track/py/scene.py +0 -326
  563. package/tools/track/py/test_joint_indices.py +0 -123
  564. package/tools/track/py/test_lr_viterbi.py +0 -195
  565. package/tools/track/py/test_masks.py +0 -229
  566. package/tools/track/py/test_rig.py +0 -288
  567. package/tools/track/py/test_scene.py +0 -354
  568. package/tools/track/py/test_track.py +0 -328
  569. package/tools/track/py/track.py +0 -411
  570. package/tools/track/remote.mjs +0 -140
  571. package/tools/track/rig-dump.mjs +0 -227
  572. package/tools/track/run-box-tests.mjs +0 -14
  573. package/tools/track/run-box.mjs +0 -168
  574. package/tools/track/setup-box.sh +0 -59
  575. package/tools/track/study-2d.mjs +0 -728
  576. /package/src/{ardy → motion}/collision-blockers.js +0 -0
  577. /package/src/{ardy → motion}/ground.js +0 -0
  578. /package/src/{ardy → motion}/ik-drag.js +0 -0
  579. /package/src/{ardy → motion}/ik-key-json.js +0 -0
  580. /package/src/{ardy → motion}/ik.js +0 -0
  581. /package/src/{ardy → motion}/physics-surface.js +0 -0
  582. /package/src/{ardy → motion}/playback-clock.js +0 -0
  583. /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())