cozyclay 2.1.1 → 3.0.1

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