@cueframe/kernel 0.1.0 → 0.1.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.
- package/LICENSE +661 -0
- package/dist/_members/composition/ops/compositionOps.d.ts +1 -0
- package/dist/_members/composition/projection/behind-readiness.d.ts +127 -0
- package/dist/_members/composition/projection/index.d.ts +2 -0
- package/dist/_members/composition/projection/reframe-readiness.d.ts +156 -0
- package/dist/_members/composition/validators/composition.d.ts +42 -25
- package/dist/_members/composition/validators/render.d.ts +32 -0
- package/dist/_members/core/render/compositionRender.d.ts +17 -1
- package/dist/_members/core/render/types.d.ts +31 -0
- package/dist/authoring/build-tools.js +6 -6
- package/dist/authoring/context.js +4 -4
- package/dist/authoring/tools.js +5 -5
- package/dist/authoring.js +11 -9
- package/dist/{chunk-ZB3C3HM7.js → chunk-7FO3ZJ43.js} +3 -0
- package/dist/{chunk-ODL7GQJG.js → chunk-AP6ZIVAV.js} +2 -2
- package/dist/{chunk-IBYQG46O.js → chunk-AQBREB5O.js} +1 -1
- package/dist/{chunk-YIOY2XEO.js → chunk-DK2ODBNT.js} +15 -5
- package/dist/{chunk-JRQVPFLF.js → chunk-HY3RIEZW.js} +1 -1
- package/dist/{chunk-GEA7E2FL.js → chunk-JJ6XD7NG.js} +151 -1
- package/dist/{chunk-COYMZ76S.js → chunk-MBCXCJOE.js} +1 -1
- package/dist/{chunk-37NJ27UU.js → chunk-OCFDUCF4.js} +3 -0
- package/dist/{chunk-VOO5FQSS.js → chunk-PDYO3DOJ.js} +5 -5
- package/dist/{chunk-MXFAADCQ.js → chunk-PODIYN2K.js} +9 -2
- package/dist/{chunk-NFEGPWGS.js → chunk-TOJEXFXL.js} +1 -1
- package/dist/{chunk-LM4WDUQJ.js → chunk-UHGSZZDI.js} +1 -1
- package/dist/{chunk-XWKZB5U5.js → chunk-ZWMV6DD2.js} +24 -1
- package/dist/compose/renderer.js +3 -3
- package/dist/compose.js +6 -6
- package/dist/composition/graphics-plan.js +1 -1
- package/dist/composition/ops.js +3 -3
- package/dist/composition/primitive-catalog.js +1 -1
- package/dist/composition/projection.js +19 -3
- package/dist/composition/validators.js +6 -4
- package/dist/composition-ops.js +8 -6
- package/dist/composition.js +24 -6
- package/dist/{context-OKX3HHFS.js → context-YWUZDG5X.js} +4 -4
- package/dist/core/render.js +3 -3
- package/dist/core/select.js +2 -2
- package/dist/core.js +3 -3
- package/dist/errors.js +1 -1
- package/dist/mcp/adapter.js +1 -0
- package/dist/media-model/testing.js +1071 -718
- package/dist/nle-export/server.js +328 -130
- package/dist/nle-export.js +7 -6
- package/package.json +13 -14
- package/dist/_members/account-model/index.js +0 -4
- package/dist/_members/account-model/testing/conversationStoreContract.js +0 -168
- package/dist/_members/account-model/testing/index.js +0 -7
- package/dist/_members/account-model/testing/restorePointStoreContract.js +0 -69
- package/dist/_members/account-model/types/index.js +0 -1
- package/dist/_members/account-model/validators/contextFile.js +0 -18
- package/dist/_members/account-model/validators/conversation.js +0 -95
- package/dist/_members/account-model/validators/dashboardInsight.js +0 -16
- package/dist/_members/account-model/validators/index.js +0 -7
- package/dist/_members/account-model/validators/sessionToken.js +0 -49
- package/dist/_members/account-model/validators/uploadToken.js +0 -16
- package/dist/_members/account-model/validators/userSettings.js +0 -12
- package/dist/_members/account-model/validators/workingComposition.js +0 -20
- package/dist/_members/brand-derivation/constants.js +0 -20
- package/dist/_members/brand-derivation/contrastSafe.js +0 -114
- package/dist/_members/brand-derivation/index.js +0 -1
- package/dist/_members/cloud-import/chunk-XKO6ZSWI.js +0 -162
- package/dist/_members/cloud-import/chunk-XKO6ZSWI.js.map +0 -1
- package/dist/_members/cloud-import/index.js +0 -241
- package/dist/_members/cloud-import/index.js.map +0 -1
- package/dist/_members/cloud-import/multipartUpload.js +0 -7
- package/dist/_members/cloud-import/multipartUpload.js.map +0 -1
- package/dist/_members/composition/audio/audioMix.js +0 -61
- package/dist/_members/composition/audio/index.js +0 -5
- package/dist/_members/composition/audio/mixStaging.js +0 -195
- package/dist/_members/composition/audio/musicPack.generated.js +0 -136
- package/dist/_members/composition/audio/musicPack.js +0 -36
- package/dist/_members/composition/audio/pack.generated.js +0 -603
- package/dist/_members/composition/audio/pack.js +0 -48
- package/dist/_members/composition/audio/palette.js +0 -134
- package/dist/_members/composition/autozoom/index.js +0 -83
- package/dist/_members/composition/brand-kit.js +0 -45
- package/dist/_members/composition/brief/contract.js +0 -209
- package/dist/_members/composition/color-grade.js +0 -80
- package/dist/_members/composition/depth.js +0 -8
- package/dist/_members/composition/fixtures/index.js +0 -143
- package/dist/_members/composition/fixtures/vaayaErrored.d.ts +0 -25
- package/dist/_members/composition/fixtures/vaayaErrored.js +0 -203
- package/dist/_members/composition/fonts.js +0 -162
- package/dist/_members/composition/graphics-plan/compile.js +0 -221
- package/dist/_members/composition/graphics-plan/index.js +0 -10
- package/dist/_members/composition/graphics-plan/instantiateMotif.js +0 -44
- package/dist/_members/composition/graphics-plan/motion.js +0 -66
- package/dist/_members/composition/graphics-plan/schemas.js +0 -261
- package/dist/_members/composition/graphics-plan/solve.js +0 -145
- package/dist/_members/composition/index.js +0 -5
- package/dist/_members/composition/invariants/clipPlacement.js +0 -69
- package/dist/_members/composition/invariants/composition.js +0 -28
- package/dist/_members/composition/invariants/errors.js +0 -10
- package/dist/_members/composition/invariants/etag.js +0 -38
- package/dist/_members/composition/invariants/ids.js +0 -33
- package/dist/_members/composition/invariants/index.js +0 -9
- package/dist/_members/composition/invariants/keyframes.js +0 -45
- package/dist/_members/composition/invariants/markers.js +0 -29
- package/dist/_members/composition/invariants/time.js +0 -20
- package/dist/_members/composition/invariants/tracks.js +0 -304
- package/dist/_members/composition/ops/compositionOps.js +0 -1056
- package/dist/_members/composition/ops/headlessStore.js +0 -94
- package/dist/_members/composition/ops/index.js +0 -4
- package/dist/_members/composition/ops/selectors.js +0 -118
- package/dist/_members/composition/ops/timeline-word.js +0 -1
- package/dist/_members/composition/ops/transcript.js +0 -77
- package/dist/_members/composition/ops/types.js +0 -16
- package/dist/_members/composition/primitive-catalog/catalogQuery.js +0 -150
- package/dist/_members/composition/primitive-catalog/disabled.js +0 -32
- package/dist/_members/composition/primitive-catalog/generated/meta.js +0 -15161
- package/dist/_members/composition/primitive-catalog/index.js +0 -46
- package/dist/_members/composition/primitive-catalog/paramValidation.js +0 -231
- package/dist/_members/composition/primitive-catalog/projection.js +0 -164
- package/dist/_members/composition/primitive-catalog/types.js +0 -14
- package/dist/_members/composition/projection/behind-placement.js +0 -62
- package/dist/_members/composition/projection/brandKit.js +0 -482
- package/dist/_members/composition/projection/cardTokens.js +0 -209
- package/dist/_members/composition/projection/composition-floor.js +0 -582
- package/dist/_members/composition/projection/cropProjection.js +0 -257
- package/dist/_members/composition/projection/index.js +0 -7
- package/dist/_members/composition/projection/occlusion-budget.js +0 -104
- package/dist/_members/composition/projection/safe-zones.js +0 -99
- package/dist/_members/composition/reframe/aspectLoss.js +0 -115
- package/dist/_members/composition/reframe/coverage.js +0 -62
- package/dist/_members/composition/reframe/derive.js +0 -73
- package/dist/_members/composition/reframe/index.js +0 -109
- package/dist/_members/composition/reframe/kenBurns.js +0 -64
- package/dist/_members/composition/reframe/shotScale.js +0 -176
- package/dist/_members/composition/reframe/trajectory.js +0 -124
- package/dist/_members/composition/timeline/compound.js +0 -134
- package/dist/_members/composition/timeline/index.js +0 -9
- package/dist/_members/composition/timeline/intent.js +0 -228
- package/dist/_members/composition/timeline/keyframes.js +0 -121
- package/dist/_members/composition/timeline/link.js +0 -59
- package/dist/_members/composition/timeline/occupancy.js +0 -36
- package/dist/_members/composition/timeline/placement.js +0 -74
- package/dist/_members/composition/timeline/plan.js +0 -31
- package/dist/_members/composition/timeline/resolve.js +0 -170
- package/dist/_members/composition/timeline/trims.js +0 -278
- package/dist/_members/composition/types/index.js +0 -1
- package/dist/_members/composition/validators/brandKit.js +0 -295
- package/dist/_members/composition/validators/composePlan.js +0 -113
- package/dist/_members/composition/validators/composition.js +0 -703
- package/dist/_members/composition/validators/font.js +0 -34
- package/dist/_members/composition/validators/graphicKinds.js +0 -30
- package/dist/_members/composition/validators/index.js +0 -9
- package/dist/_members/composition/validators/project.js +0 -29
- package/dist/_members/composition/validators/registry.js +0 -41
- package/dist/_members/composition/validators/render.js +0 -139
- package/dist/_members/composition/validators/session.js +0 -37
- package/dist/_members/composition/validators/webhook.js +0 -72
- package/dist/_members/composition-ops/alphaLayers.js +0 -141
- package/dist/_members/composition-ops/index.js +0 -637
- package/dist/_members/composition-ops/materializeGrid.js +0 -99
- package/dist/_members/composition-ops/opRecorder.js +0 -196
- package/dist/_members/composition-ops/recordingStore.js +0 -14
- package/dist/_members/composition-ops/replaceRegion.js +0 -102
- package/dist/_members/composition-ops/testFixtures.js +0 -19
- package/dist/_members/core/index.js +0 -5
- package/dist/_members/core/render/bookends.js +0 -53
- package/dist/_members/core/render/captions.js +0 -127
- package/dist/_members/core/render/compositionRender.js +0 -1249
- package/dist/_members/core/render/deriveTrajectory.js +0 -889
- package/dist/_members/core/render/emphasis.js +0 -16
- package/dist/_members/core/render/eval/cropBox.js +0 -139
- package/dist/_members/core/render/eval/inclusion.js +0 -96
- package/dist/_members/core/render/eval/index.js +0 -146
- package/dist/_members/core/render/eval/metrics.js +0 -94
- package/dist/_members/core/render/exportSettings.js +0 -65
- package/dist/_members/core/render/index.js +0 -20
- package/dist/_members/core/render/mapFaceToPerson.js +0 -73
- package/dist/_members/core/render/types.js +0 -8
- package/dist/_members/core/render/vcam/cameraPath.js +0 -248
- package/dist/_members/core/render/vcam/constants.js +0 -115
- package/dist/_members/core/render/vcam/errorCodes.js +0 -39
- package/dist/_members/core/render/vcam/framing.js +0 -136
- package/dist/_members/core/render/vcam/requiredRegion.js +0 -137
- package/dist/_members/core/render/vcam/shots.js +0 -53
- package/dist/_members/core/render/vcam/subjectSet.js +0 -388
- package/dist/_members/core/select/errorCodes.js +0 -38
- package/dist/_members/core/select/index.js +0 -26
- package/dist/_members/core/select/model.js +0 -18
- package/dist/_members/core/select/select.js +0 -244
- package/dist/_members/core/select/stabilize.js +0 -242
- package/dist/_members/core/types.js +0 -7
- package/dist/_members/errors/index.js +0 -1
- package/dist/_members/errors/registry.js +0 -560
- package/dist/_members/foundation/codes/index.js +0 -31
- package/dist/_members/foundation/index.js +0 -2
- package/dist/_members/foundation/primitives/index.js +0 -5
- package/dist/_members/mcp/adapter.js +0 -771
- package/dist/_members/mcp/burnedNames.js +0 -73
- package/dist/_members/mcp/core.js +0 -390
- package/dist/_members/mcp/curation.js +0 -792
- package/dist/_members/mcp/executor.js +0 -142
- package/dist/_members/mcp/format.js +0 -9
- package/dist/_members/mcp/generated/catalogPreviews.js +0 -95
- package/dist/_members/mcp/generated/harness.js +0 -81
- package/dist/_members/mcp/generated/operations.js +0 -131
- package/dist/_members/mcp/index.js +0 -50
- package/dist/_members/mcp/internalLoopTools.js +0 -109
- package/dist/_members/mcp/prompts.js +0 -45
- package/dist/_members/mcp/resources.js +0 -166
- package/dist/_members/mcp/server.js +0 -340
- package/dist/_members/mcp/wait.js +0 -378
- package/dist/_members/mcp/x402Meta.js +0 -66
- package/dist/_members/media-model/api.js +0 -175
- package/dist/_members/media-model/index.js +0 -1
- package/dist/_members/media-model/media-kind.js +0 -116
- package/dist/_members/media-model/testing/index.js +0 -1
- package/dist/_members/media-model/testing/mediaStoreContract.js +0 -82
- package/dist/_members/media-model/transcriptSearch.js +0 -70
- package/dist/_members/media-model/transcriptSearchCases.json +0 -79
- package/dist/_members/nle-export/compact-timeline.js +0 -73
- package/dist/_members/nle-export/compact-timeline.js.map +0 -1
- package/dist/_members/nle-export/composition-to-input.js +0 -147
- package/dist/_members/nle-export/composition-to-input.js.map +0 -1
- package/dist/_members/nle-export/fcpxml.js +0 -159
- package/dist/_members/nle-export/fcpxml.js.map +0 -1
- package/dist/_members/nle-export/index.js +0 -12
- package/dist/_members/nle-export/index.js.map +0 -1
- package/dist/_members/nle-export/premiere.js +0 -161
- package/dist/_members/nle-export/premiere.js.map +0 -1
- package/dist/_members/nle-export/server/index.js +0 -2
- package/dist/_members/nle-export/server/index.js.map +0 -1
- package/dist/_members/nle-export/server/pack-zip.js +0 -38
- package/dist/_members/nle-export/server/pack-zip.js.map +0 -1
- package/dist/_members/nle-export/srt.js +0 -50
- package/dist/_members/nle-export/srt.js.map +0 -1
- package/dist/_members/nle-export/types.js +0 -9
- package/dist/_members/nle-export/types.js.map +0 -1
- package/dist/_members/nle-export/xml/index.js +0 -11
- package/dist/_members/nle-export/xml/index.js.map +0 -1
- package/dist/_members/nle-export/xml-utils.js +0 -48
- package/dist/_members/nle-export/xml-utils.js.map +0 -1
- package/dist/_members/perception/activeSpeaker.js +0 -103
- package/dist/_members/perception/audioQuality.js +0 -76
- package/dist/_members/perception/energyBeat.js +0 -99
- package/dist/_members/perception/identityMatch.js +0 -91
- package/dist/_members/perception/index.js +0 -48
- package/dist/_members/perception/visualQuality.js +0 -95
- package/dist/_members/plane/conformance.js +0 -90
- package/dist/_members/plane/contract.js +0 -49
- package/dist/_members/plane/index.js +0 -16
- package/dist/_members/primitives/backgrounds/index.js +0 -32
- package/dist/_members/primitives/backgrounds/index.js.map +0 -1
- package/dist/_members/primitives/captions/index.js +0 -398
- package/dist/_members/primitives/captions/index.js.map +0 -1
- package/dist/_members/primitives/chunk-2LVE4UIT.js +0 -139
- package/dist/_members/primitives/chunk-2LVE4UIT.js.map +0 -1
- package/dist/_members/primitives/chunk-3IQUH6KK.js +0 -20
- package/dist/_members/primitives/chunk-3IQUH6KK.js.map +0 -1
- package/dist/_members/primitives/chunk-3OMURV6Q.js +0 -112
- package/dist/_members/primitives/chunk-3OMURV6Q.js.map +0 -1
- package/dist/_members/primitives/chunk-4IS6TMEG.js +0 -21
- package/dist/_members/primitives/chunk-4IS6TMEG.js.map +0 -1
- package/dist/_members/primitives/chunk-5MWGAEGQ.js +0 -1644
- package/dist/_members/primitives/chunk-5MWGAEGQ.js.map +0 -1
- package/dist/_members/primitives/chunk-7RRPFPQK.js +0 -15
- package/dist/_members/primitives/chunk-7RRPFPQK.js.map +0 -1
- package/dist/_members/primitives/chunk-DH5TRZK2.js +0 -125
- package/dist/_members/primitives/chunk-DH5TRZK2.js.map +0 -1
- package/dist/_members/primitives/chunk-E3KNUSFG.js +0 -2337
- package/dist/_members/primitives/chunk-E3KNUSFG.js.map +0 -1
- package/dist/_members/primitives/chunk-G2B3G4HK.js +0 -47
- package/dist/_members/primitives/chunk-G2B3G4HK.js.map +0 -1
- package/dist/_members/primitives/chunk-HFPFGZXT.js +0 -1494
- package/dist/_members/primitives/chunk-HFPFGZXT.js.map +0 -1
- package/dist/_members/primitives/chunk-HIWVFZVH.js +0 -19
- package/dist/_members/primitives/chunk-HIWVFZVH.js.map +0 -1
- package/dist/_members/primitives/chunk-IDAL6G3Y.js +0 -134
- package/dist/_members/primitives/chunk-IDAL6G3Y.js.map +0 -1
- package/dist/_members/primitives/chunk-KNC6JP5F.js +0 -34
- package/dist/_members/primitives/chunk-KNC6JP5F.js.map +0 -1
- package/dist/_members/primitives/chunk-KYRDVZST.js +0 -459
- package/dist/_members/primitives/chunk-KYRDVZST.js.map +0 -1
- package/dist/_members/primitives/chunk-LWLHS2NS.js +0 -3410
- package/dist/_members/primitives/chunk-LWLHS2NS.js.map +0 -1
- package/dist/_members/primitives/chunk-QAQ3P7DR.js +0 -8855
- package/dist/_members/primitives/chunk-QAQ3P7DR.js.map +0 -1
- package/dist/_members/primitives/chunk-ROV2ODWC.js +0 -81
- package/dist/_members/primitives/chunk-ROV2ODWC.js.map +0 -1
- package/dist/_members/primitives/chunk-TCRC2FH3.js +0 -183
- package/dist/_members/primitives/chunk-TCRC2FH3.js.map +0 -1
- package/dist/_members/primitives/chunk-US4YEDAA.js +0 -96
- package/dist/_members/primitives/chunk-US4YEDAA.js.map +0 -1
- package/dist/_members/primitives/chunk-UVKRO5ER.js +0 -13
- package/dist/_members/primitives/chunk-UVKRO5ER.js.map +0 -1
- package/dist/_members/primitives/chunk-W4EIIF2I.js +0 -10
- package/dist/_members/primitives/chunk-W4EIIF2I.js.map +0 -1
- package/dist/_members/primitives/chunk-WPGKIPCO.js +0 -3563
- package/dist/_members/primitives/chunk-WPGKIPCO.js.map +0 -1
- package/dist/_members/primitives/chunk-WZFLEGMD.js +0 -1435
- package/dist/_members/primitives/chunk-WZFLEGMD.js.map +0 -1
- package/dist/_members/primitives/chunk-XHU3WBEI.js +0 -33
- package/dist/_members/primitives/chunk-XHU3WBEI.js.map +0 -1
- package/dist/_members/primitives/chunk-YN7WTZZ7.js +0 -46
- package/dist/_members/primitives/chunk-YN7WTZZ7.js.map +0 -1
- package/dist/_members/primitives/effects/index.js +0 -30
- package/dist/_members/primitives/effects/index.js.map +0 -1
- package/dist/_members/primitives/hero-text/index.js +0 -14
- package/dist/_members/primitives/hero-text/index.js.map +0 -1
- package/dist/_members/primitives/index.js +0 -64
- package/dist/_members/primitives/index.js.map +0 -1
- package/dist/_members/primitives/layouts/index.js +0 -24
- package/dist/_members/primitives/layouts/index.js.map +0 -1
- package/dist/_members/primitives/lib/easings.js +0 -26
- package/dist/_members/primitives/lib/easings.js.map +0 -1
- package/dist/_members/primitives/lib/index.js +0 -74
- package/dist/_members/primitives/lib/index.js.map +0 -1
- package/dist/_members/primitives/lib/palette.js +0 -72
- package/dist/_members/primitives/lib/palette.js.map +0 -1
- package/dist/_members/primitives/lib/preview.js +0 -8
- package/dist/_members/primitives/lib/preview.js.map +0 -1
- package/dist/_members/primitives/lib/schema.js +0 -12
- package/dist/_members/primitives/lib/schema.js.map +0 -1
- package/dist/_members/primitives/lib/seeded-random.js +0 -14
- package/dist/_members/primitives/lib/seeded-random.js.map +0 -1
- package/dist/_members/primitives/lib/text/contrast.js +0 -12
- package/dist/_members/primitives/lib/text/contrast.js.map +0 -1
- package/dist/_members/primitives/lib/text/fill.js +0 -8
- package/dist/_members/primitives/lib/text/fill.js.map +0 -1
- package/dist/_members/primitives/lib/text/fitText.js +0 -10
- package/dist/_members/primitives/lib/text/fitText.js.map +0 -1
- package/dist/_members/primitives/lib/text/marker.js +0 -8
- package/dist/_members/primitives/lib/text/marker.js.map +0 -1
- package/dist/_members/primitives/lib/text/motion.js +0 -16
- package/dist/_members/primitives/lib/text/motion.js.map +0 -1
- package/dist/_members/primitives/lib/text/recolor.js +0 -8
- package/dist/_members/primitives/lib/text/recolor.js.map +0 -1
- package/dist/_members/primitives/lib/text/spec.js +0 -1
- package/dist/_members/primitives/lib/text/spec.js.map +0 -1
- package/dist/_members/primitives/lib/text/stage.js +0 -8
- package/dist/_members/primitives/lib/text/stage.js.map +0 -1
- package/dist/_members/primitives/lib/text/typeface.js +0 -8
- package/dist/_members/primitives/lib/text/typeface.js.map +0 -1
- package/dist/_members/primitives/lib/tokens.js +0 -24
- package/dist/_members/primitives/lib/tokens.js.map +0 -1
- package/dist/_members/primitives/lib/types.js +0 -1
- package/dist/_members/primitives/lib/types.js.map +0 -1
- package/dist/_members/primitives/params-required.json +0 -153
- package/dist/_members/primitives/scenes/index.js +0 -25
- package/dist/_members/primitives/scenes/index.js.map +0 -1
- package/dist/_members/primitives/subject-foreground/index.js +0 -30
- package/dist/_members/primitives/subject-foreground/index.js.map +0 -1
- package/dist/_members/primitives/text/index.js +0 -57
- package/dist/_members/primitives/text/index.js.map +0 -1
- package/dist/_members/primitives/transitions/index.js +0 -67
- package/dist/_members/primitives/transitions/index.js.map +0 -1
- package/dist/_members/primitives/ui-blocks/index.js +0 -76
- package/dist/_members/primitives/ui-blocks/index.js.map +0 -1
- package/dist/_members/primitives/version-map.json +0 -105
- package/dist/_members/primitives/workspace.js +0 -60
- package/dist/_members/primitives/workspace.js.map +0 -1
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* vcam/shots.ts — shot segmentation, the SOLE cut source.
|
|
3
|
-
*
|
|
4
|
-
* Phase 1 of the virtual-camera redesign (brain spec
|
|
5
|
-
* 2026-06-21-virtual-camera-reframe-design.md, "Shot/segment handling"):
|
|
6
|
-
* cuts come ONLY from the detector's `track.shotBoundaries` (HSV hard cuts),
|
|
7
|
-
* NEVER inferred from a face-center jump. The deleted `isJump`-as-cut inference
|
|
8
|
-
* is the false-cut snap (the 1.6-frame-widths/sec teleport) — structurally
|
|
9
|
-
* impossible once the only reset is a real detector boundary.
|
|
10
|
-
*
|
|
11
|
-
* Two scopes the selection math must NOT conflate:
|
|
12
|
-
* - **Identity scope = the authored segment** (cross-shot). A tracker id is
|
|
13
|
-
* stable within a shot; the same person is followed across cuts. The
|
|
14
|
-
* `MIN_SUBJECT_RESOLUTION` resolution gate lives at segment granularity.
|
|
15
|
-
* - **Camera re-lock scope = the shot.** The per-shot LOCKED subject set
|
|
16
|
-
* (quality gate) is computed per shot; deadzone / window / zoom-fit reset
|
|
17
|
-
* at a shot boundary (the latter two land in Phase 3/5).
|
|
18
|
-
*
|
|
19
|
-
* Pure, deterministic, no I/O. Inputs are already segment-local frame indices
|
|
20
|
-
* (the caller rebased the window-local `shotBoundaries` into segment offsets).
|
|
21
|
-
*/
|
|
22
|
-
/**
|
|
23
|
-
* Split `[0, segLen)` into shot ranges at the interior cut frames.
|
|
24
|
-
*
|
|
25
|
-
* `interiorCuts` are segment-local frame indices where a NEW shot begins — the
|
|
26
|
-
* SAME set `deriveTrajectory` already derives (`segCuts`: window-local
|
|
27
|
-
* `shotBoundaries` strictly inside the segment, rebased to segment offsets). A
|
|
28
|
-
* cut at frame `c` ends the prior shot at `c` and starts the next at `c`.
|
|
29
|
-
*
|
|
30
|
-
* Legacy tracks with no `shotBoundaries` → no interior cuts → ONE shot covering
|
|
31
|
-
* the whole segment (the safe hold). `shot_boundaries_missing` is NOT thrown —
|
|
32
|
-
* a one-shot segment is a legal, documented outcome, never a degrade.
|
|
33
|
-
*
|
|
34
|
-
* Out-of-range / duplicate / unsorted cuts are tolerated: cuts are sorted,
|
|
35
|
-
* deduped, and clamped to `(0, segLen)`; zero-length spans are dropped. A
|
|
36
|
-
* segment of length 0 yields no shots.
|
|
37
|
-
*/
|
|
38
|
-
export function segmentShots(interiorCuts, segLen) {
|
|
39
|
-
if (segLen <= 0)
|
|
40
|
-
return [];
|
|
41
|
-
const cuts = [...new Set(interiorCuts)]
|
|
42
|
-
.filter((c) => c > 0 && c < segLen)
|
|
43
|
-
.sort((a, b) => a - b);
|
|
44
|
-
const bounds = [0, ...cuts, segLen];
|
|
45
|
-
const shots = [];
|
|
46
|
-
for (let i = 0; i < bounds.length - 1; i++) {
|
|
47
|
-
const start = bounds[i];
|
|
48
|
-
const end = bounds[i + 1];
|
|
49
|
-
if (end - start > 0)
|
|
50
|
-
shots.push({ start, end });
|
|
51
|
-
}
|
|
52
|
-
return shots;
|
|
53
|
-
}
|
|
@@ -1,388 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* vcam/subjectSet.ts — the CHASE KILLER (Phase 2).
|
|
3
|
-
*
|
|
4
|
-
* The `all-faces` failure is the per-frame area-weighted centroid of EVERY
|
|
5
|
-
* detected face: any transient face (a crowd extra, a ByteTrack fragment, a
|
|
6
|
-
* background head) yanks the centroid, so the camera ping-pongs (the "chase").
|
|
7
|
-
* This module selects a STABLE subject set and frames the GROUP smoothly,
|
|
8
|
-
* beating a static center crop on jitter — the headline behavioral fix.
|
|
9
|
-
*
|
|
10
|
-
* Mode-isolated: this is consulted ONLY for the `all-faces` path in
|
|
11
|
-
* `deriveSegment`. `face` / `point` / `frame-center` / `active-speaker` keep
|
|
12
|
-
* their existing selection verbatim.
|
|
13
|
-
*
|
|
14
|
-
* Pipeline (brain spec 2026-06-21-virtual-camera-reframe-design.md, "Subject-of
|
|
15
|
-
* -interest selection"):
|
|
16
|
-
* 1. Per-shot LOCKED subject set (quality gate) — size-aware drop, NOT a flat
|
|
17
|
-
* persistence gate, so a dominant late entrant is kept and a transient
|
|
18
|
-
* small face is dropped.
|
|
19
|
-
* 2. Identity scope = the authored SEGMENT: union the per-shot locked sets →
|
|
20
|
-
* the segment candidate pool; rank by segment-wide dominance; take top-K.
|
|
21
|
-
* 3. Per-frame set-MEMBERSHIP hysteresis over the pool (add/drop debounce) so
|
|
22
|
-
* a member flickering at the gate boundary doesn't toggle the framing.
|
|
23
|
-
* 4. Emit, PER FRAME, the area-weighted centroid + union bbox over the framed
|
|
24
|
-
* members PRESENT this frame. The centroid is the raw crop target; the box
|
|
25
|
-
* is the RequiredRegion source. Transient entries/exits can't yank the
|
|
26
|
-
* camera (they're gated out of the framed set), but the framed group's own
|
|
27
|
-
* motion IS followed — Phase 3's deadzone + Hamming window in `cameraPath`
|
|
28
|
-
* turn this per-frame target into a held-then-glide path, so a walking
|
|
29
|
-
* subject stays in frame (the camera tracks) without chasing micro-jitter.
|
|
30
|
-
* (Phase 2 held this target static, which kept a moving subject framed only
|
|
31
|
-
* while it stayed near the centroid; Phase 3 lets the hold glide to re-
|
|
32
|
-
* contain the subject when it walks.)
|
|
33
|
-
*
|
|
34
|
-
* The per-subject `face_box_series` the floor reads is produced UNCHANGED by
|
|
35
|
-
* `perSubjectSeries` — this module only chooses WHERE the camera points.
|
|
36
|
-
*/
|
|
37
|
-
import { segmentShots } from './shots';
|
|
38
|
-
import { P_MIN, SIZE_MIN, SIZE_DOMINANT, MAX_SUBJECTS, DOMINANCE_WEIGHTS, ENTER_SEC, LEAVE_SEC, } from './constants';
|
|
39
|
-
import { scoreAndRank } from '../../select/select';
|
|
40
|
-
import { stabilizeSelectionHysteresis } from '../../select/stabilize';
|
|
41
|
-
/** Median of a numeric array (lower-median for even length, matching the
|
|
42
|
-
* detector-side convention used for `size_med`). Empty → fallback. */
|
|
43
|
-
function median(xs, fallback = 0) {
|
|
44
|
-
if (xs.length === 0)
|
|
45
|
-
return fallback;
|
|
46
|
-
const s = [...xs].sort((a, b) => a - b);
|
|
47
|
-
return s[Math.floor(s.length / 2)];
|
|
48
|
-
}
|
|
49
|
-
/** Per-id stats over one shot's frames (segment-local slice). */
|
|
50
|
-
function shotStats(perFrameTracked, srcW, srcH, perKeyframeSpeaker, shot) {
|
|
51
|
-
const shotLen = shot.end - shot.start;
|
|
52
|
-
// Accumulators per id.
|
|
53
|
-
const heights = new Map();
|
|
54
|
-
const centralities = new Map();
|
|
55
|
-
const present = new Map();
|
|
56
|
-
for (let i = shot.start; i < shot.end; i++) {
|
|
57
|
-
const tracked = i < perFrameTracked.length ? perFrameTracked[i] : {};
|
|
58
|
-
for (const [fid, b] of Object.entries(tracked)) {
|
|
59
|
-
if (!b || b.length < 4)
|
|
60
|
-
continue;
|
|
61
|
-
const [x1, y1, x2, y2] = b;
|
|
62
|
-
const hFrac = Math.max(0, y2 - y1) / srcH;
|
|
63
|
-
const cx = (x1 + x2) / 2 / srcW;
|
|
64
|
-
const cy = (y1 + y2) / 2 / srcH;
|
|
65
|
-
// Centrality: 1 at frame center, decreasing with normalized distance.
|
|
66
|
-
const dist = Math.hypot(cx - 0.5, cy - 0.5);
|
|
67
|
-
const centrality = Math.max(0, 1 - dist);
|
|
68
|
-
if (!heights.has(fid)) {
|
|
69
|
-
heights.set(fid, []);
|
|
70
|
-
centralities.set(fid, []);
|
|
71
|
-
present.set(fid, 0);
|
|
72
|
-
}
|
|
73
|
-
heights.get(fid).push(hFrac);
|
|
74
|
-
centralities.get(fid).push(centrality);
|
|
75
|
-
present.set(fid, present.get(fid) + 1);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
// Speaking fraction over this shot's keyframe picks.
|
|
79
|
-
const shotPicks = [];
|
|
80
|
-
for (const [kf, fid] of perKeyframeSpeaker) {
|
|
81
|
-
if (kf >= shot.start && kf < shot.end)
|
|
82
|
-
shotPicks.push(fid);
|
|
83
|
-
}
|
|
84
|
-
const speakCount = new Map();
|
|
85
|
-
for (const fid of shotPicks)
|
|
86
|
-
speakCount.set(fid, (speakCount.get(fid) ?? 0) + 1);
|
|
87
|
-
const out = new Map();
|
|
88
|
-
for (const fid of heights.keys()) {
|
|
89
|
-
out.set(fid, {
|
|
90
|
-
persistence: shotLen > 0 ? present.get(fid) / shotLen : 0,
|
|
91
|
-
sizeMed: median(heights.get(fid)),
|
|
92
|
-
centralityMed: median(centralities.get(fid)),
|
|
93
|
-
speaking: shotPicks.length > 0 ? (speakCount.get(fid) ?? 0) / shotPicks.length : 0,
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
return out;
|
|
97
|
-
}
|
|
98
|
-
/** The size-aware locked-set gate. Drop iff
|
|
99
|
-
* `(persistence < P_MIN AND sizeMed < SIZE_DOMINANT) OR (sizeMed < SIZE_MIN)`.
|
|
100
|
-
* Keeps a dominant late entrant (low persistence, large size); drops a
|
|
101
|
-
* transient small face. `speaking`/`centrality` feed dominance, NOT the gate. */
|
|
102
|
-
function passesGate(s) {
|
|
103
|
-
if (s.sizeMed < SIZE_MIN)
|
|
104
|
-
return false;
|
|
105
|
-
if (s.persistence < P_MIN && s.sizeMed < SIZE_DOMINANT)
|
|
106
|
-
return false;
|
|
107
|
-
return true;
|
|
108
|
-
}
|
|
109
|
-
/** Map a track's stats → the Selector core's signal envelope. The subjects
|
|
110
|
-
* Candidate adapter PRODUCES these `{size,centrality,persistence,speaking}`
|
|
111
|
-
* signals (the `shotStats`/`segStats` geometry lives HERE, in the adapter; the
|
|
112
|
-
* core only sees the absolute scalars — the rot-guard). Keys are in
|
|
113
|
-
* `DOMINANCE_WEIGHTS` order so `scoreAndRank`'s `Σ weights·signal` sums in the
|
|
114
|
-
* SAME float order as the old `dominance()`, preserving byte-equivalence. */
|
|
115
|
-
function statsToSignals(s) {
|
|
116
|
-
return {
|
|
117
|
-
size: { value: s.sizeMed },
|
|
118
|
-
centrality: { value: s.centralityMed },
|
|
119
|
-
persistence: { value: s.persistence },
|
|
120
|
-
speaking: { value: s.speaking },
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
function segStats(perFrameTracked, srcW, srcH, perKeyframeSpeaker, ids) {
|
|
124
|
-
const n = perFrameTracked.length;
|
|
125
|
-
const heights = new Map();
|
|
126
|
-
const widths = new Map();
|
|
127
|
-
const cxs = new Map();
|
|
128
|
-
const cys = new Map();
|
|
129
|
-
const centralities = new Map();
|
|
130
|
-
const present = new Map();
|
|
131
|
-
for (let i = 0; i < n; i++) {
|
|
132
|
-
const tracked = perFrameTracked[i] ?? {};
|
|
133
|
-
for (const [fid, b] of Object.entries(tracked)) {
|
|
134
|
-
if (!ids.has(fid) || !b || b.length < 4)
|
|
135
|
-
continue;
|
|
136
|
-
const [x1, y1, x2, y2] = b;
|
|
137
|
-
const hFrac = Math.max(0, y2 - y1) / srcH;
|
|
138
|
-
const wFrac = Math.max(0, x2 - x1) / srcW;
|
|
139
|
-
const cx = (x1 + x2) / 2 / srcW;
|
|
140
|
-
const cy = (y1 + y2) / 2 / srcH;
|
|
141
|
-
const centrality = Math.max(0, 1 - Math.hypot(cx - 0.5, cy - 0.5));
|
|
142
|
-
if (!heights.has(fid)) {
|
|
143
|
-
heights.set(fid, []);
|
|
144
|
-
widths.set(fid, []);
|
|
145
|
-
cxs.set(fid, []);
|
|
146
|
-
cys.set(fid, []);
|
|
147
|
-
centralities.set(fid, []);
|
|
148
|
-
present.set(fid, 0);
|
|
149
|
-
}
|
|
150
|
-
heights.get(fid).push(hFrac);
|
|
151
|
-
widths.get(fid).push(wFrac);
|
|
152
|
-
cxs.get(fid).push(cx);
|
|
153
|
-
cys.get(fid).push(cy);
|
|
154
|
-
centralities.get(fid).push(centrality);
|
|
155
|
-
present.set(fid, present.get(fid) + 1);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
const speakCount = new Map();
|
|
159
|
-
let totalPicks = 0;
|
|
160
|
-
for (const fid of perKeyframeSpeaker.values()) {
|
|
161
|
-
if (!ids.has(fid))
|
|
162
|
-
continue;
|
|
163
|
-
speakCount.set(fid, (speakCount.get(fid) ?? 0) + 1);
|
|
164
|
-
}
|
|
165
|
-
totalPicks += perKeyframeSpeaker.size;
|
|
166
|
-
const out = new Map();
|
|
167
|
-
for (const fid of heights.keys()) {
|
|
168
|
-
const wMed = median(widths.get(fid));
|
|
169
|
-
const hMed = median(heights.get(fid));
|
|
170
|
-
out.set(fid, {
|
|
171
|
-
sizeMed: hMed,
|
|
172
|
-
centralityMed: median(centralities.get(fid)),
|
|
173
|
-
persistence: n > 0 ? present.get(fid) / n : 0,
|
|
174
|
-
speaking: totalPicks > 0 ? (speakCount.get(fid) ?? 0) / totalPicks : 0,
|
|
175
|
-
reprCx: median(cxs.get(fid), 0.5),
|
|
176
|
-
reprCy: median(cys.get(fid), 0.5),
|
|
177
|
-
reprArea: Math.max(1e-6, wMed * hMed),
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
return out;
|
|
181
|
-
}
|
|
182
|
-
/**
|
|
183
|
-
* Select the stable subject set for an `all-faces` segment and emit the per-
|
|
184
|
-
* frame crop target. Pure over the segment-local slice.
|
|
185
|
-
*
|
|
186
|
-
* Empty locked set (no face survives the gate — a faceless / crowd-only clip) →
|
|
187
|
-
* center-hold (`[0.5,0.5]` every frame), a DEFINED outcome, never a throw
|
|
188
|
-
* (per spec fail-loud invariants).
|
|
189
|
-
*/
|
|
190
|
-
export function selectSubjectSet(perFrameTracked, srcW, srcH, fps, perKeyframeSpeaker, interiorCuts) {
|
|
191
|
-
const n = perFrameTracked.length;
|
|
192
|
-
const centerHold = () => ({
|
|
193
|
-
centers: Array.from({ length: n }, () => [0.5, 0.5]),
|
|
194
|
-
perFrameBoxes: Array.from({ length: n }, () => null),
|
|
195
|
-
framedIds: [],
|
|
196
|
-
resolvedFrames: 0,
|
|
197
|
-
});
|
|
198
|
-
if (n === 0 || srcW <= 0 || srcH <= 0)
|
|
199
|
-
return centerHold();
|
|
200
|
-
// 1. Per-shot LOCKED set (quality gate). Union across shots → segment pool.
|
|
201
|
-
const shots = segmentShots(interiorCuts, n);
|
|
202
|
-
const pool = new Set();
|
|
203
|
-
for (const shot of shots) {
|
|
204
|
-
const stats = shotStats(perFrameTracked, srcW, srcH, perKeyframeSpeaker, shot);
|
|
205
|
-
for (const [fid, s] of stats) {
|
|
206
|
-
if (passesGate(s))
|
|
207
|
-
pool.add(fid);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
if (pool.size === 0)
|
|
211
|
-
return centerHold();
|
|
212
|
-
// 2. Rank the pool by SEGMENT-WIDE dominance through the SELECTOR CORE's
|
|
213
|
-
// `Σ weights·signal` combinator (`scoreAndRank`) — `DOMINANCE_WEIGHTS` is
|
|
214
|
-
// `policy.weights`, the per-track segStats are the adapter-produced
|
|
215
|
-
// `{size,centrality,persistence,speaking}` signals. `scoreAndRank` sorts
|
|
216
|
-
// desc by score with the SAME stable id tiebreak as the old inline sort, so
|
|
217
|
-
// `framedIds` is byte-identical. Take top-K.
|
|
218
|
-
const seg = segStats(perFrameTracked, srcW, srcH, perKeyframeSpeaker, pool);
|
|
219
|
-
const ranked = scoreAndRank([...pool].map((id) => ({ id, signals: statsToSignals(seg.get(id)) })), DOMINANCE_WEIGHTS).map((s) => s.candidateId);
|
|
220
|
-
const framedIds = ranked.slice(0, MAX_SUBJECTS);
|
|
221
|
-
// 3. Per-frame set-MEMBERSHIP hysteresis over the framed pool — the SELECTOR
|
|
222
|
-
// CORE's `selection-hysteresis` stabilizer (`stabilizeSelectionHysteresis`,
|
|
223
|
-
// the `topK>1` per-id state machine). A member must be present for
|
|
224
|
-
// ENTER_FRAMES consecutive frames to enter the framed set and absent for
|
|
225
|
-
// LEAVE_FRAMES to leave, so a member flickering at the boundary doesn't
|
|
226
|
-
// toggle the framing. The deadzone (cameraPath = the geometric-deadzone
|
|
227
|
-
// stabilizer) stabilizes PAN; this stabilizes set MEMBERSHIP. Lifted
|
|
228
|
-
// verbatim, so `inThisFrame`/`everIn`/`usedFallback` are byte-identical.
|
|
229
|
-
const enterFrames = Math.max(1, Math.round(ENTER_SEC * fps));
|
|
230
|
-
const leaveFrames = Math.max(1, Math.round(LEAVE_SEC * fps));
|
|
231
|
-
const { inThisFrame, everIn, usedFallback } = stabilizeSelectionHysteresis(framedIds, (id, i) => !!(perFrameTracked[i] ?? {})[id], n, { enterFrames, leaveFrames });
|
|
232
|
-
let resolvedFrames = 0;
|
|
233
|
-
for (let i = 0; i < n; i++)
|
|
234
|
-
if (inThisFrame[i].length > 0)
|
|
235
|
-
resolvedFrames++;
|
|
236
|
-
// A short clip can end before any member clears the ENTER debounce; fall back
|
|
237
|
-
// to the full framed set so the group is still framed (never center-hold a
|
|
238
|
-
// clip that has a clear dominant subject just because it's brief). When that
|
|
239
|
-
// fallback fires, treat every present framed id as "in" for the box/centroid.
|
|
240
|
-
const groupIds = everIn.size > 0 ? [...everIn] : framedIds;
|
|
241
|
-
// 4. PER-FRAME tracking target + union box. For each frame, take the framed
|
|
242
|
-
// members PRESENT this frame, build their area-weighted centroid (the crop
|
|
243
|
-
// target that frame) and their union bbox (the RequiredRegion source). The
|
|
244
|
-
// camera now FOLLOWS the framed group as it moves — Phase 3's deadzone +
|
|
245
|
-
// Hamming window in cameraPath turn this raw per-frame target into a held-
|
|
246
|
-
// then-glide path. Frames where no framed member is present → null box +
|
|
247
|
-
// null center, gap-filled consistently below (so a leading warmup frame is
|
|
248
|
-
// byte-identical to the first resolved frame — no float-drifted seed).
|
|
249
|
-
const perFrameBoxes = new Array(n).fill(null);
|
|
250
|
-
const rawCenters = new Array(n).fill(null);
|
|
251
|
-
for (let i = 0; i < n; i++) {
|
|
252
|
-
const tracked = perFrameTracked[i] ?? {};
|
|
253
|
-
const ids = usedFallback ? framedIds.filter((id) => tracked[id]) : inThisFrame[i];
|
|
254
|
-
if (ids.length === 0)
|
|
255
|
-
continue; // null box + null center → gap-filled below
|
|
256
|
-
let x1 = Infinity;
|
|
257
|
-
let y1 = Infinity;
|
|
258
|
-
let x2 = -Infinity;
|
|
259
|
-
let y2 = -Infinity;
|
|
260
|
-
let area = 0;
|
|
261
|
-
let cxAcc = 0;
|
|
262
|
-
let cyAcc = 0;
|
|
263
|
-
for (const id of ids) {
|
|
264
|
-
const b = tracked[id];
|
|
265
|
-
const [bx1, by1, bx2, by2] = b;
|
|
266
|
-
const nx1 = bx1 / srcW;
|
|
267
|
-
const ny1 = by1 / srcH;
|
|
268
|
-
const nx2 = bx2 / srcW;
|
|
269
|
-
const ny2 = by2 / srcH;
|
|
270
|
-
if (nx1 < x1)
|
|
271
|
-
x1 = nx1;
|
|
272
|
-
if (ny1 < y1)
|
|
273
|
-
y1 = ny1;
|
|
274
|
-
if (nx2 > x2)
|
|
275
|
-
x2 = nx2;
|
|
276
|
-
if (ny2 > y2)
|
|
277
|
-
y2 = ny2;
|
|
278
|
-
const w = Math.max(0, nx2 - nx1);
|
|
279
|
-
const h = Math.max(0, ny2 - ny1);
|
|
280
|
-
const a = Math.max(1e-6, w * h);
|
|
281
|
-
cxAcc += ((nx1 + nx2) / 2) * a;
|
|
282
|
-
cyAcc += ((ny1 + ny2) / 2) * a;
|
|
283
|
-
area += a;
|
|
284
|
-
}
|
|
285
|
-
perFrameBoxes[i] = [x1, y1, x2, y2];
|
|
286
|
-
rawCenters[i] = area > 0 ? [cxAcc / area, cyAcc / area] : null;
|
|
287
|
-
}
|
|
288
|
-
// Gap-fill the per-frame centers for the SubjectSetResult.centers field (the
|
|
289
|
-
// diagnostic/legacy crop-target basis). Leading gap borrows the first known
|
|
290
|
-
// center; interior/trailing gaps hold the last known one. (cameraPath does its
|
|
291
|
-
// OWN shot-scoped forward-fill on the regions; this fill is only so `.centers`
|
|
292
|
-
// is a complete series for direct callers / the synthetic tests.) When NO
|
|
293
|
-
// frame ever resolved → the held representative centroid → else source center.
|
|
294
|
-
const firstKnown = rawCenters.find((c) => c !== null) ?? null;
|
|
295
|
-
let fallback = [0.5, 0.5];
|
|
296
|
-
if (firstKnown === null) {
|
|
297
|
-
let totalArea = 0;
|
|
298
|
-
let reprCxAcc = 0;
|
|
299
|
-
let reprCyAcc = 0;
|
|
300
|
-
for (const id of groupIds) {
|
|
301
|
-
const s = seg.get(id);
|
|
302
|
-
reprCxAcc += s.reprCx * s.reprArea;
|
|
303
|
-
reprCyAcc += s.reprCy * s.reprArea;
|
|
304
|
-
totalArea += s.reprArea;
|
|
305
|
-
}
|
|
306
|
-
if (totalArea > 0)
|
|
307
|
-
fallback = [reprCxAcc / totalArea, reprCyAcc / totalArea];
|
|
308
|
-
}
|
|
309
|
-
const centers = new Array(n);
|
|
310
|
-
let last = firstKnown ?? fallback;
|
|
311
|
-
for (let i = 0; i < n; i++) {
|
|
312
|
-
if (rawCenters[i] !== null)
|
|
313
|
-
last = rawCenters[i];
|
|
314
|
-
centers[i] = [last[0], last[1]];
|
|
315
|
-
}
|
|
316
|
-
return {
|
|
317
|
-
centers,
|
|
318
|
-
perFrameBoxes,
|
|
319
|
-
framedIds: groupIds,
|
|
320
|
-
resolvedFrames: resolvedFrames > 0 ? resolvedFrames : n,
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
/**
|
|
324
|
-
* Return the largest face this frame (the active-speaker degrade + the empty
|
|
325
|
-
* authored-id fallback — mirror of `resolveSubjectFaces`'s largest-face path).
|
|
326
|
-
*/
|
|
327
|
-
function largestFaceId(tracked) {
|
|
328
|
-
let bestArea = -1;
|
|
329
|
-
let bestId = null;
|
|
330
|
-
for (const [id, b] of Object.entries(tracked)) {
|
|
331
|
-
if (!b || b.length < 4)
|
|
332
|
-
continue;
|
|
333
|
-
const area = Math.max(0, b[2] - b[0]) * Math.max(0, b[3] - b[1]);
|
|
334
|
-
if (area > bestArea) {
|
|
335
|
-
bestArea = area;
|
|
336
|
-
bestId = id;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
return bestId;
|
|
340
|
-
}
|
|
341
|
-
/**
|
|
342
|
-
* The `topK=1` subjects adapter for `face` / `active-speaker`. Per frame, resolve
|
|
343
|
-
* the single chosen subject and emit its source-normalized box. Pure over the
|
|
344
|
-
* segment-local slice.
|
|
345
|
-
*
|
|
346
|
-
* - `face` (authored id): the box of that STABLE tracker id this frame; absent →
|
|
347
|
-
* null (no silent snap to a different person — the roster rule). The empty /
|
|
348
|
-
* non-`face-` sentinel id resolves to the largest face (legacy callers).
|
|
349
|
-
* - `active-speaker`: the box of the per-keyframe speaker pick when tracked this
|
|
350
|
-
* frame; otherwise the largest face this frame (the ASD degrade, mirror of
|
|
351
|
-
* `deriveSegment`'s active-speaker path).
|
|
352
|
-
*/
|
|
353
|
-
export function selectSingleSubjectBoxes(perFrameTracked, srcW, srcH, perKeyframeSpeaker, pick, nearestSpeaker) {
|
|
354
|
-
const n = perFrameTracked.length;
|
|
355
|
-
const perFrameBoxes = new Array(n).fill(null);
|
|
356
|
-
if (srcW <= 0 || srcH <= 0)
|
|
357
|
-
return { perFrameBoxes, resolvedFrames: 0 };
|
|
358
|
-
const authoredFaceId = pick.kind === 'face' && pick.authoredFaceId.startsWith('face-') ? pick.authoredFaceId : null;
|
|
359
|
-
let resolvedFrames = 0;
|
|
360
|
-
for (let i = 0; i < n; i++) {
|
|
361
|
-
const tracked = perFrameTracked[i] ?? {};
|
|
362
|
-
if (Object.keys(tracked).length === 0)
|
|
363
|
-
continue;
|
|
364
|
-
let chosenId = null;
|
|
365
|
-
if (pick.kind === 'active-speaker' && perKeyframeSpeaker.size > 0) {
|
|
366
|
-
// The per-keyframe speaker pick, when tracked this frame (mirror of
|
|
367
|
-
// deriveSegment's active-speaker nearest-keyframe lookup).
|
|
368
|
-
const sp = nearestSpeaker(i);
|
|
369
|
-
if (sp && tracked[sp])
|
|
370
|
-
chosenId = sp;
|
|
371
|
-
}
|
|
372
|
-
if (chosenId === null) {
|
|
373
|
-
// face: the authored id (when present this frame); else (active-speaker with
|
|
374
|
-
// no ASD pick, or the largest-face sentinel) the largest face — the existing
|
|
375
|
-
// degrade.
|
|
376
|
-
chosenId = authoredFaceId !== null ? (tracked[authoredFaceId] ? authoredFaceId : null) : largestFaceId(tracked);
|
|
377
|
-
}
|
|
378
|
-
if (chosenId === null)
|
|
379
|
-
continue;
|
|
380
|
-
const b = tracked[chosenId];
|
|
381
|
-
if (!b || b.length < 4)
|
|
382
|
-
continue;
|
|
383
|
-
const [x1, y1, x2, y2] = b;
|
|
384
|
-
perFrameBoxes[i] = [x1 / srcW, y1 / srcH, x2 / srcW, y2 / srcH];
|
|
385
|
-
resolvedFrames++;
|
|
386
|
-
}
|
|
387
|
-
return { perFrameBoxes, resolvedFrames };
|
|
388
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* select/errorCodes.ts — the Selector error vocabulary, as VALUES.
|
|
3
|
-
*
|
|
4
|
-
* Every code the Selector core / adapters / lowerings can throw (now or in a
|
|
5
|
-
* later phase) is listed here as a literal value, and the registry-membership CI
|
|
6
|
-
* test (`__tests__/selectorErrorRegistry.test.ts`) imports THIS list and asserts
|
|
7
|
-
* each exists in `@cueframe/errors`'s `ERROR_CODES`. Importing the codes as VALUES
|
|
8
|
-
* (not grepping literals) means a template/variable code can't slip past — an
|
|
9
|
-
* unregistered code would make the CFERR decode fall back to `internal_error` and
|
|
10
|
-
* 500 (the `clip_source_track_mismatch` landmine).
|
|
11
|
-
*
|
|
12
|
-
* Phase 1 (this phase) registers all of these even though only the CORE codes
|
|
13
|
-
* (`selector_signal_absent`, `selector_override_conflict`) are thrown today; the
|
|
14
|
-
* adapter/lowering codes (`selector_timebase_undefined`, `selector_group_unsynced`,
|
|
15
|
-
* `selector_segment_out_of_media`) are thrown by the Phase-2 switch adapters and
|
|
16
|
-
* are registered now so they can never 500 when that phase lands. Phase 3
|
|
17
|
-
* (active-speaker) adds `selector_speaking_double_count` — the structural double-
|
|
18
|
-
* count guard (§11-L5): weighting both `activeSpeaker` and `speaking` fails loud.
|
|
19
|
-
* Phase 6 (cross-source identity) adds `selector_identity_detector_version_mismatch`
|
|
20
|
-
* — a `lockPersonId` match over a STALE-detector-version embedding fails loud (the
|
|
21
|
-
* 3-stamp lesson: a person-lock cosine over embeddings from a different
|
|
22
|
-
* `DETECTOR_VERSION` than the reference is meaningless; refuse rather than guess).
|
|
23
|
-
*/
|
|
24
|
-
/** The error codes the Selector layer is allowed to throw. Each MUST be a row in
|
|
25
|
-
* `@cueframe/errors` `ERROR_CODES` (asserted in CI). */
|
|
26
|
-
export const SELECTOR_ERROR_CODES = [
|
|
27
|
-
// CORE (thrown in Phase 1):
|
|
28
|
-
'selector_signal_absent',
|
|
29
|
-
'selector_override_conflict',
|
|
30
|
-
// ADAPTER / LOWERING (registered now, thrown in Phase 2 switch):
|
|
31
|
-
'selector_timebase_undefined',
|
|
32
|
-
'selector_group_unsynced',
|
|
33
|
-
'selector_segment_out_of_media',
|
|
34
|
-
// ADAPTER (Phase 3 active-speaker — the structural double-count guard, §11-L5):
|
|
35
|
-
'selector_speaking_double_count',
|
|
36
|
-
// ADAPTER (Phase 6 cross-source identity — the stale-embedding version guard):
|
|
37
|
-
'selector_identity_detector_version_mismatch',
|
|
38
|
-
];
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @cueframe/core/select — the durable Selector core (selector-abstraction spec).
|
|
3
|
-
*
|
|
4
|
-
* The candidate-agnostic data model + the ~40-line `select()` scoring contract +
|
|
5
|
-
* the per-kind `stabilize` seam. Reframe is its first instance (the subjects
|
|
6
|
-
* adapter lives in `render/vcam/subjectSet.ts`, which calls `scoreAndRank` +
|
|
7
|
-
* `stabilizeSelectionHysteresis` and lowers via the sealed `cameraPath`).
|
|
8
|
-
*
|
|
9
|
-
* Switch (Phase 2) is the second instance — same core, new candidate adapter +
|
|
10
|
-
* the incumbent-challenger/presence-gate stabilizers + the cut/overlay lowering.
|
|
11
|
-
*/
|
|
12
|
-
export { HOLD } from './model';
|
|
13
|
-
export { select, scoreAndRank } from './select';
|
|
14
|
-
export { stabilizeSelectionHysteresis, stabilizeIncumbentChallenger, stabilizePresenceGate, cameraPath, } from './stabilize';
|
|
15
|
-
export { SELECTOR_ERROR_CODES } from './errorCodes';
|
|
16
|
-
/** The default prominence weights — vcam's `DOMINANCE_WEIGHTS`, re-exported as the
|
|
17
|
-
* canonical "dominant-subject" `policy.weights` for the SCORE combinator. The
|
|
18
|
-
* switch instance's video-on-prominence path uses these directly (spec §9.1 P2 —
|
|
19
|
-
* "video-switch on prominence = vcam dominance, exists"); reframe uses the same
|
|
20
|
-
* constant. Re-exporting it here keeps the scoring default in the select surface.
|
|
21
|
-
* (This import is in index.ts, NOT select.ts — the rot-guard scans select.ts.) */
|
|
22
|
-
export { DOMINANCE_WEIGHTS } from '../render/vcam/constants';
|
|
23
|
-
/** The Selector engine version (spec §8). DISTINCT from BAKE_VERSION (the crop
|
|
24
|
-
* lowering stamp) and the per-signal stamps. Bump when the scoring/stabilizer
|
|
25
|
-
* math changes in a way that changes the Advisory for fixed inputs. */
|
|
26
|
-
export const SELECTOR_VERSION = 'selector-core-v1-2026.06.21';
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* select/model.ts — the candidate-agnostic data model for the Selector core.
|
|
3
|
-
*
|
|
4
|
-
* Four nouns (selector-abstraction spec §1): Candidate, Signal, SelectorPolicy,
|
|
5
|
-
* Advisory. The CORE (`select.ts`) scores over these and reads ONLY `candidateId`,
|
|
6
|
-
* `Signal.value/confidence`, and `policy` — NEVER `Candidate.kind`,
|
|
7
|
-
* `Candidate.timebase`, or any geometry (the §8 rot-guard, enforced by a test AND
|
|
8
|
-
* by `select()` receiving a projected `CoreCandidate`, not the full `Candidate`).
|
|
9
|
-
*
|
|
10
|
-
* Plain TS (no zod): the core package has no zod dependency and the vcam math it
|
|
11
|
-
* generalizes is plain TS. The spec's zod schemas describe the SHAPE; the fail-
|
|
12
|
-
* loud invariants live in `select.ts` as explicit guards (no silent fallbacks —
|
|
13
|
-
* `feedback_never_add_fallbacks`).
|
|
14
|
-
*/
|
|
15
|
-
/** The held/abstain sentinel — first-class, not a magic empty (spec §1.4 step 3).
|
|
16
|
-
* Lowers per kind: sources → stay on incumbent/primary, no cut; subjects →
|
|
17
|
-
* center-hold the crop. */
|
|
18
|
-
export const HOLD = '__hold__';
|