@camstack/addon-post-analysis 1.2.290 → 1.2.292
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/dist/{clip-model-registry--sFeijO7.js → clip-model-registry-CTY5co6P.js} +35 -4
- package/dist/{clip-model-registry-CoYBCvtr.mjs → clip-model-registry-Cudz--Ql.mjs} +35 -4
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-r3ZZOSzj.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CBHxwCSj.mjs} +1 -1
- package/dist/pipeline-analytics/{hostInit-PoeIZAFx.mjs → hostInit-DSKT2dE4.mjs} +1 -1
- package/dist/pipeline-analytics/index.js +670 -83
- package/dist/pipeline-analytics/index.mjs +670 -83
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_clip_model_registry = require("../clip-model-registry
|
|
5
|
+
const require_clip_model_registry = require("../clip-model-registry-CTY5co6P.js");
|
|
6
6
|
let node_fs = require("node:fs");
|
|
7
7
|
node_fs = require_clip_model_registry.__toESM(node_fs, 1);
|
|
8
8
|
let node_path = require("node:path");
|
|
@@ -39677,7 +39677,12 @@ var RebuildInputSchema = require_clip_model_registry.object({
|
|
|
39677
39677
|
* Milliseconds between tracks; omit for the default, `0` to run flat out.
|
|
39678
39678
|
* A rebuild shares hub-main's only thread with every live request.
|
|
39679
39679
|
*/
|
|
39680
|
-
pacingMs: require_clip_model_registry.number().int().nonnegative().optional()
|
|
39680
|
+
pacingMs: require_clip_model_registry.number().int().nonnegative().optional(),
|
|
39681
|
+
/**
|
|
39682
|
+
* Re-embed ONLY tracks already in the pinned space — to redo vectors cut
|
|
39683
|
+
* from the wrong box (D664). Requires `maxTracks`.
|
|
39684
|
+
*/
|
|
39685
|
+
force: require_clip_model_registry.boolean().optional()
|
|
39681
39686
|
});
|
|
39682
39687
|
var RebuildStartedSchema = require_clip_model_registry.object({
|
|
39683
39688
|
started: require_clip_model_registry.boolean(),
|
|
@@ -39697,7 +39702,20 @@ var RebuildStatusSchema = require_clip_model_registry.object({
|
|
|
39697
39702
|
scanned: require_clip_model_registry.number(),
|
|
39698
39703
|
rebuilt: require_clip_model_registry.number(),
|
|
39699
39704
|
missingKeyFrame: require_clip_model_registry.number(),
|
|
39705
|
+
/** No object event at all — its own number since D664. */
|
|
39706
|
+
missingEvent: require_clip_model_registry.number(),
|
|
39700
39707
|
missingBbox: require_clip_model_registry.number(),
|
|
39708
|
+
/** Which rule placed each judged track's box (D664). */
|
|
39709
|
+
boxSource: require_clip_model_registry.object({
|
|
39710
|
+
subjectBox: require_clip_model_registry.number(),
|
|
39711
|
+
positions: require_clip_model_registry.number(),
|
|
39712
|
+
event: require_clip_model_registry.number(),
|
|
39713
|
+
refused: require_clip_model_registry.number()
|
|
39714
|
+
}),
|
|
39715
|
+
alreadyInSpace: require_clip_model_registry.number(),
|
|
39716
|
+
/** Whether the current / last pass was a `force` pass (D664). */
|
|
39717
|
+
force: require_clip_model_registry.boolean().nullable(),
|
|
39718
|
+
forcedInSpace: require_clip_model_registry.number(),
|
|
39701
39719
|
/** Refused by the executing node (unreadable key frame / bad box), not broken. */
|
|
39702
39720
|
notRunnable: require_clip_model_registry.number(),
|
|
39703
39721
|
/** The pass STOPPED: no node in the cluster could serve the pinned model. */
|
|
@@ -40972,7 +40990,7 @@ function toPixelBox(b) {
|
|
|
40972
40990
|
h: b.height
|
|
40973
40991
|
};
|
|
40974
40992
|
}
|
|
40975
|
-
function refused(reason, detail) {
|
|
40993
|
+
function refused$1(reason, detail) {
|
|
40976
40994
|
return {
|
|
40977
40995
|
kind: "refused",
|
|
40978
40996
|
reason,
|
|
@@ -41000,8 +41018,8 @@ function comparableFaces(input) {
|
|
|
41000
41018
|
/** Refuse before choosing when nothing in the photo can become the sample. */
|
|
41001
41019
|
function precondition(input) {
|
|
41002
41020
|
const enrollable = input.faces.filter((f) => f.enrollable && f.embedding !== void 0);
|
|
41003
|
-
if (enrollable.length === 0) return refused("no-enrollable-face", `${input.faces.length} face(s) found, none enrollable (${input.faces.map((f) => f.refusedReason ?? "unknown").join(", ")})`);
|
|
41004
|
-
if (!enrollable.some((f) => isEnrollableIn(f, input.modelId))) return refused("model-mismatch", `faces were embedded by ${[...new Set(enrollable.map((f) => f.modelId))].join(", ")}, the old sample by ${input.modelId} — re-embed the gallery first`);
|
|
41021
|
+
if (enrollable.length === 0) return refused$1("no-enrollable-face", `${input.faces.length} face(s) found, none enrollable (${input.faces.map((f) => f.refusedReason ?? "unknown").join(", ")})`);
|
|
41022
|
+
if (!enrollable.some((f) => isEnrollableIn(f, input.modelId))) return refused$1("model-mismatch", `faces were embedded by ${[...new Set(enrollable.map((f) => f.modelId))].join(", ")}, the old sample by ${input.modelId} — re-embed the gallery first`);
|
|
41005
41023
|
return null;
|
|
41006
41024
|
}
|
|
41007
41025
|
function chooseByIou(input, oldBox, scored) {
|
|
@@ -41010,13 +41028,13 @@ function chooseByIou(input, oldBox, scored) {
|
|
|
41010
41028
|
face,
|
|
41011
41029
|
iou: iou$3(box, toPixelBox(face.bbox))
|
|
41012
41030
|
})).filter((c) => c.iou >= PHOTO_REENROL_MIN_IOU);
|
|
41013
|
-
if (overlapping.length === 0) return refused("no-iou-match", `no face overlaps the old box at IoU ≥ ${PHOTO_REENROL_MIN_IOU}`);
|
|
41014
|
-
if (overlapping.length > 1) return refused("ambiguous", `${overlapping.length} faces overlap the old box (IoU ${overlapping.map((c) => c.iou.toFixed(2)).join(", ")})`);
|
|
41031
|
+
if (overlapping.length === 0) return refused$1("no-iou-match", `no face overlaps the old box at IoU ≥ ${PHOTO_REENROL_MIN_IOU}`);
|
|
41032
|
+
if (overlapping.length > 1) return refused$1("ambiguous", `${overlapping.length} faces overlap the old box (IoU ${overlapping.map((c) => c.iou.toFixed(2)).join(", ")})`);
|
|
41015
41033
|
const [only] = overlapping;
|
|
41016
|
-
if (only === void 0) return refused("no-iou-match", "no overlapping face");
|
|
41017
|
-
if (!isEnrollableIn(only.face, input.modelId)) return refused("no-enrollable-face", `the face on the old box is not enrollable (${only.face.refusedReason ?? `model ${only.face.modelId ?? "none"}`})`);
|
|
41034
|
+
if (only === void 0) return refused$1("no-iou-match", "no overlapping face");
|
|
41035
|
+
if (!isEnrollableIn(only.face, input.modelId)) return refused$1("no-enrollable-face", `the face on the old box is not enrollable (${only.face.refusedReason ?? `model ${only.face.modelId ?? "none"}`})`);
|
|
41018
41036
|
const cosine = scored.find((s) => s.face === only.face)?.cosine;
|
|
41019
|
-
if (cosine === void 0 || cosine < .5) return refused("below-floor", `the overlapping face scores cosine ${cosine?.toFixed(3) ?? "n/a"} < ${PHOTO_REENROL_COSINE_FLOOR}`);
|
|
41037
|
+
if (cosine === void 0 || cosine < .5) return refused$1("below-floor", `the overlapping face scores cosine ${cosine?.toFixed(3) ?? "n/a"} < ${PHOTO_REENROL_COSINE_FLOOR}`);
|
|
41020
41038
|
return {
|
|
41021
41039
|
kind: "chosen",
|
|
41022
41040
|
face: only.face,
|
|
@@ -41027,10 +41045,10 @@ function chooseByIou(input, oldBox, scored) {
|
|
|
41027
41045
|
}
|
|
41028
41046
|
function chooseByCosine(input, scored) {
|
|
41029
41047
|
const [best, runnerUp] = [...scored].toSorted((a, b) => b.cosine - a.cosine);
|
|
41030
|
-
if (best === void 0) return refused("model-mismatch", "no face has a comparable vector");
|
|
41031
|
-
if (best.cosine < .5) return refused("below-floor", `best cosine ${best.cosine.toFixed(3)} < ${PHOTO_REENROL_COSINE_FLOOR}`);
|
|
41032
|
-
if (runnerUp !== void 0 && runnerUp.cosine >= .5 && best.cosine - runnerUp.cosine < .1) return refused("ambiguous", `two faces clear the floor within ${PHOTO_REENROL_AMBIGUITY_MARGIN}: ${best.cosine.toFixed(3)} vs ${runnerUp.cosine.toFixed(3)}`);
|
|
41033
|
-
if (!isEnrollableIn(best.face, input.modelId)) return refused("ambiguous", `the closest face (cosine ${best.cosine.toFixed(3)}) is not enrollable (${best.face.refusedReason ?? "unknown"}) — the enrollable ones may be someone else`);
|
|
41048
|
+
if (best === void 0) return refused$1("model-mismatch", "no face has a comparable vector");
|
|
41049
|
+
if (best.cosine < .5) return refused$1("below-floor", `best cosine ${best.cosine.toFixed(3)} < ${PHOTO_REENROL_COSINE_FLOOR}`);
|
|
41050
|
+
if (runnerUp !== void 0 && runnerUp.cosine >= .5 && best.cosine - runnerUp.cosine < .1) return refused$1("ambiguous", `two faces clear the floor within ${PHOTO_REENROL_AMBIGUITY_MARGIN}: ${best.cosine.toFixed(3)} vs ${runnerUp.cosine.toFixed(3)}`);
|
|
41051
|
+
if (!isEnrollableIn(best.face, input.modelId)) return refused$1("ambiguous", `the closest face (cosine ${best.cosine.toFixed(3)}) is not enrollable (${best.face.refusedReason ?? "unknown"}) — the enrollable ones may be someone else`);
|
|
41034
41052
|
return {
|
|
41035
41053
|
kind: "chosen",
|
|
41036
41054
|
face: best.face,
|
|
@@ -47036,6 +47054,42 @@ function pairRiders(detections) {
|
|
|
47036
47054
|
return pairs;
|
|
47037
47055
|
}
|
|
47038
47056
|
//#endregion
|
|
47057
|
+
//#region src/pipeline-analytics/pipeline/tracker/low-score-continuation.ts
|
|
47058
|
+
var LOW_SCORE_CONTINUATION_FLOOR_DEFAULT = .35;
|
|
47059
|
+
var LOW_SCORE_CONTINUATION_CLASSES = new Set(["person", "animal"]);
|
|
47060
|
+
/** `[x1, y1, x2, y2]` (the discard trail's tuple) → the tracker's rectangle. */
|
|
47061
|
+
function toRect(bbox) {
|
|
47062
|
+
const [x1, y1, x2, y2] = bbox;
|
|
47063
|
+
return {
|
|
47064
|
+
x: x1,
|
|
47065
|
+
y: y1,
|
|
47066
|
+
w: x2 - x1,
|
|
47067
|
+
h: y2 - y1
|
|
47068
|
+
};
|
|
47069
|
+
}
|
|
47070
|
+
/**
|
|
47071
|
+
* The continuation candidates of one frame. `floor === null` means the tier is
|
|
47072
|
+
* off for this camera and nothing is selected.
|
|
47073
|
+
*/
|
|
47074
|
+
function selectLowScoreContinuations(discarded, floor) {
|
|
47075
|
+
if (floor === null || discarded === void 0) return [];
|
|
47076
|
+
const out = [];
|
|
47077
|
+
for (const d of discarded) {
|
|
47078
|
+
if (d.reason !== "below-threshold") continue;
|
|
47079
|
+
if (d.score < floor) continue;
|
|
47080
|
+
if (!LOW_SCORE_CONTINUATION_CLASSES.has(d.macroClass)) continue;
|
|
47081
|
+
const bbox = toRect(d.bbox);
|
|
47082
|
+
if (bbox.w <= 0 || bbox.h <= 0) continue;
|
|
47083
|
+
out.push({
|
|
47084
|
+
class: d.macroClass,
|
|
47085
|
+
originalClass: d.macroClass,
|
|
47086
|
+
score: d.score,
|
|
47087
|
+
bbox
|
|
47088
|
+
});
|
|
47089
|
+
}
|
|
47090
|
+
return out;
|
|
47091
|
+
}
|
|
47092
|
+
//#endregion
|
|
47039
47093
|
//#region src/pipeline-analytics/pipeline/track-id.ts
|
|
47040
47094
|
/**
|
|
47041
47095
|
* A track's identity: short, random, and mintable inside a frame.
|
|
@@ -47184,6 +47238,30 @@ function reacquireReachPx(bbox, elapsedMs) {
|
|
|
47184
47238
|
const elapsed = Math.max(0, elapsedMs);
|
|
47185
47239
|
return clampRange(diag * REACH_DIAGONALS_PER_SEC * (elapsed / 1e3), diag * REACH_MIN_DIAGONALS, diag * REACH_MAX_DIAGONALS);
|
|
47186
47240
|
}
|
|
47241
|
+
/**
|
|
47242
|
+
* Resurrection reach, in box DIAGONALS per second of gap (D665).
|
|
47243
|
+
*
|
|
47244
|
+
* A corpse is reached only after `maxMissedMs` (4 s) without a box, so the
|
|
47245
|
+
* rescue rate above — which is at its 1.5-diagonal ceiling by 1 s — says
|
|
47246
|
+
* nothing useful here. A subject the detector has not boxed for four seconds
|
|
47247
|
+
* is, on this fleet's evidence, one that stood still or stood half-hidden,
|
|
47248
|
+
* not one striding across the frame (a striding person is detected): 0.25
|
|
47249
|
+
* diagonals per second is a slow walk for a mid-field person and a pause for
|
|
47250
|
+
* a near one. At the 4 s floor of the graveyard band it gives 1.0 diagonal;
|
|
47251
|
+
* camera 590's refused returns sat at 1.05–1.82× the old flat 0.75 reach,
|
|
47252
|
+
* i.e. 0.79–1.37 diagonals, which this covers from about 5.5 s of gap.
|
|
47253
|
+
*/
|
|
47254
|
+
var RESURRECTION_REACH_DIAGONALS_PER_SEC = .25;
|
|
47255
|
+
/** Never tighter than the flat `rescueCentroidFactor` default it replaces. */
|
|
47256
|
+
var RESURRECTION_REACH_MIN_DIAGONALS = .75;
|
|
47257
|
+
/**
|
|
47258
|
+
* How far from a corpse's last box a detection may sit and still resurrect it
|
|
47259
|
+
* on the proximity clause, for a corpse last seen `gapMs` ago.
|
|
47260
|
+
*/
|
|
47261
|
+
function resurrectionReachPx(bbox, gapMs) {
|
|
47262
|
+
const diag = Math.hypot(bbox.w, bbox.h);
|
|
47263
|
+
return clampRange(diag * RESURRECTION_REACH_DIAGONALS_PER_SEC * (Math.max(0, gapMs) / 1e3), diag * RESURRECTION_REACH_MIN_DIAGONALS, diag * 2);
|
|
47264
|
+
}
|
|
47187
47265
|
//#endregion
|
|
47188
47266
|
//#region src/pipeline-analytics/pipeline/tracker/rescue-growth.ts
|
|
47189
47267
|
/**
|
|
@@ -47332,7 +47410,10 @@ var DEFAULT_TRACKER_CONFIG = {
|
|
|
47332
47410
|
byteTrackLowIouThreshold: .2,
|
|
47333
47411
|
minSpawnAreaEnabled: true,
|
|
47334
47412
|
classSpawnAreaFracs: { person: 5e-4 },
|
|
47335
|
-
classGroupAssoc: true
|
|
47413
|
+
classGroupAssoc: true,
|
|
47414
|
+
lowScoreContinuationEnabled: true,
|
|
47415
|
+
lowScoreContinuationMaxMs: 8e3,
|
|
47416
|
+
resurrectionTimeScaled: true
|
|
47336
47417
|
};
|
|
47337
47418
|
/** Macro-classes the duplicate resolver may collapse across (people in
|
|
47338
47419
|
* non-upright poses misdetected as animals). Any other cross-class pair is
|
|
@@ -47340,6 +47421,15 @@ var DEFAULT_TRACKER_CONFIG = {
|
|
|
47340
47421
|
var PERSON_CLASS = "person";
|
|
47341
47422
|
var ANIMAL_CLASS = "animal";
|
|
47342
47423
|
var MAX_PATH_LENGTH = 300;
|
|
47424
|
+
function emptyLowScoreStats() {
|
|
47425
|
+
return {
|
|
47426
|
+
offered: 0,
|
|
47427
|
+
continued: 0,
|
|
47428
|
+
resumed: 0,
|
|
47429
|
+
sustainCapDrops: 0,
|
|
47430
|
+
shadowed: 0
|
|
47431
|
+
};
|
|
47432
|
+
}
|
|
47343
47433
|
/** No stationary-held detections on this frame — the default for every caller
|
|
47344
47434
|
* that has no gate (replay, tests, the non-pipeline planes). */
|
|
47345
47435
|
var EMPTY_INDEX_SET = /* @__PURE__ */ new Set();
|
|
@@ -47411,6 +47501,8 @@ var SortTracker = class SortTracker {
|
|
|
47411
47501
|
trackLossSampler = new TrackLossSampler();
|
|
47412
47502
|
/** Sink for fragmentation diagnostics — see {@link setSpawnBesideCoastingObserver}. */
|
|
47413
47503
|
spawnBesideObserver = null;
|
|
47504
|
+
/** Running counts behind {@link drainLowScoreStats} (D665). */
|
|
47505
|
+
lowScoreStats = emptyLowScoreStats();
|
|
47414
47506
|
constructor(config = {}) {
|
|
47415
47507
|
this.config = {
|
|
47416
47508
|
...DEFAULT_TRACKER_CONFIG,
|
|
@@ -47471,7 +47563,7 @@ var SortTracker = class SortTracker {
|
|
|
47471
47563
|
if (SortTracker.spawnBesideFamily(track.class) !== family) continue;
|
|
47472
47564
|
const tc = bboxCentroid(track.bbox);
|
|
47473
47565
|
const dist = Math.hypot(tc.x - dc.x, tc.y - dc.y);
|
|
47474
|
-
const reach = this.reacquireReach(track, timestamp,
|
|
47566
|
+
const reach = this.reacquireReach(track, timestamp, "rescue");
|
|
47475
47567
|
if (dist > reach * 2) continue;
|
|
47476
47568
|
if (dist < bestDist) {
|
|
47477
47569
|
best = track;
|
|
@@ -47518,7 +47610,7 @@ var SortTracker = class SortTracker {
|
|
|
47518
47610
|
explainDecline(track, det, detections, frameArea, timestamp, centroidDist) {
|
|
47519
47611
|
if (track.class !== det.class) return det.nativeRecovery !== void 0 ? "native-measured-class" : "cross-class";
|
|
47520
47612
|
if (iou$2(track.bbox, det.bbox) >= this.config.rescueIouThreshold) return "already-claimed";
|
|
47521
|
-
if (centroidDist <= this.reacquireReach(track, timestamp,
|
|
47613
|
+
if (centroidDist <= this.reacquireReach(track, timestamp, "rescue")) {
|
|
47522
47614
|
if (!this.isPlausibleBirth(det, frameArea)) return "already-claimed";
|
|
47523
47615
|
if (!this.config.reacquireTimeScaled) return "plausible-birth-veto";
|
|
47524
47616
|
return this.isUnambiguousReach(track, det, detections) ? "already-claimed" : "crowd-guard";
|
|
@@ -47973,34 +48065,38 @@ var SortTracker = class SortTracker {
|
|
|
47973
48065
|
* this gate crossed the group, and it is where a `person` at 0.86 took over
|
|
47974
48066
|
* an animal's id (camera 615, 17:11:01).
|
|
47975
48067
|
*/
|
|
47976
|
-
looseMatch(track, det, timestamp,
|
|
48068
|
+
looseMatch(track, det, timestamp, pass = "rescue") {
|
|
47977
48069
|
if (track.class !== det.class) return null;
|
|
47978
48070
|
if (iou$2(track.bbox, det.bbox) >= this.config.rescueIouThreshold) return "iou";
|
|
47979
48071
|
const tc = bboxCentroid(track.bbox);
|
|
47980
48072
|
const dc = bboxCentroid(det.bbox);
|
|
47981
|
-
if (Math.hypot(tc.x - dc.x, tc.y - dc.y) <= this.reacquireReach(track, timestamp,
|
|
48073
|
+
if (Math.hypot(tc.x - dc.x, tc.y - dc.y) <= this.reacquireReach(track, timestamp, pass)) return "centroid";
|
|
47982
48074
|
if (growthAdjacentMatch(track.bbox, det.bbox, this.growthContext(track, timestamp))) return "growth";
|
|
47983
48075
|
return null;
|
|
47984
48076
|
}
|
|
47985
48077
|
/**
|
|
47986
48078
|
* The proximity budget the `centroid` clause is measured against.
|
|
47987
48079
|
*
|
|
47988
|
-
* On the RESCUE pass
|
|
47989
|
-
*
|
|
47990
|
-
*
|
|
47991
|
-
*
|
|
47992
|
-
*
|
|
47993
|
-
* On the RESURRECTION pass it
|
|
47994
|
-
*
|
|
47995
|
-
*
|
|
47996
|
-
*
|
|
47997
|
-
*
|
|
47998
|
-
*
|
|
47999
|
-
*
|
|
48000
|
-
|
|
48001
|
-
|
|
48080
|
+
* On the RESCUE pass it is {@link reacquireReachPx} — a multiple of the
|
|
48081
|
+
* track's diagonal that grows with the elapsed gap and stops at a ceiling,
|
|
48082
|
+
* because a live coasting track's whereabouts genuinely become less certain
|
|
48083
|
+
* the longer it has gone unseen (D399).
|
|
48084
|
+
*
|
|
48085
|
+
* On the RESURRECTION pass it is {@link resurrectionReachPx} (D665): the same
|
|
48086
|
+
* idea on the graveyard's own time scale — 0.25 diagonals per second of gap,
|
|
48087
|
+
* never below the flat 0.75 it replaces, never above 2. Until D665 this pass
|
|
48088
|
+
* kept the flat `rescueCentroidFactor × diagonal` on the argument that it
|
|
48089
|
+
* exists for "a flickering stationary object", and a stationary subject does
|
|
48090
|
+
* not travel further because it blinked for longer. True of the object it
|
|
48091
|
+
* was written for; not of the pass as it runs: on camera 590 (2026-09-27)
|
|
48092
|
+
* 38% of all re-splits were PEOPLE returning after 4–12 s, further than the
|
|
48093
|
+
* flat gate, with frames flowing the whole time. `resurrectionTimeScaled`
|
|
48094
|
+
* off restores the flat gate.
|
|
48095
|
+
*/
|
|
48096
|
+
reacquireReach(track, timestamp, pass) {
|
|
48002
48097
|
const diag = Math.hypot(track.bbox.w, track.bbox.h);
|
|
48003
|
-
if (
|
|
48098
|
+
if (pass === "resurrection") return this.config.resurrectionTimeScaled ? resurrectionReachPx(track.bbox, timestamp - track.lastSeen) : this.config.rescueCentroidFactor * diag;
|
|
48099
|
+
if (!this.config.reacquireTimeScaled) return this.config.rescueCentroidFactor * diag;
|
|
48004
48100
|
return reacquireReachPx(track.bbox, timestamp - track.lastSeen);
|
|
48005
48101
|
}
|
|
48006
48102
|
/**
|
|
@@ -48050,6 +48146,23 @@ var SortTracker = class SortTracker {
|
|
|
48050
48146
|
nearestPx: nearest
|
|
48051
48147
|
};
|
|
48052
48148
|
}
|
|
48149
|
+
/**
|
|
48150
|
+
* The resurrection counterpart of {@link isUnambiguousReach}: no other
|
|
48151
|
+
* same-class subject is live or resurrectable beside `corpse`
|
|
48152
|
+
* ({@link sameClassPeerCount} over the SURVIVING set, which is what the crowd
|
|
48153
|
+
* guard counts), and `det` is the only box of that class on the frame —
|
|
48154
|
+
* the sub-floor `lowScore` boxes included.
|
|
48155
|
+
*/
|
|
48156
|
+
isSoleSubjectResurrection(corpse, det, detections, surviving, lowScore) {
|
|
48157
|
+
if (this.sameClassPeerCount(corpse, surviving) > 0) return false;
|
|
48158
|
+
for (const other of detections) if (other !== det && other.class === corpse.class) return false;
|
|
48159
|
+
for (const other of lowScore) {
|
|
48160
|
+
if (other.class !== corpse.class) continue;
|
|
48161
|
+
if (iou$2(other.bbox, det.bbox) >= this.config.byteTrackLowIouThreshold) continue;
|
|
48162
|
+
return false;
|
|
48163
|
+
}
|
|
48164
|
+
return true;
|
|
48165
|
+
}
|
|
48053
48166
|
isUnambiguousReach(track, det, detections) {
|
|
48054
48167
|
if (this.sameClassPeerCount(track, this.tracks) > 0) return false;
|
|
48055
48168
|
for (const other of detections) {
|
|
@@ -48204,6 +48317,64 @@ var SortTracker = class SortTracker {
|
|
|
48204
48317
|
used.add(pair.detIdx);
|
|
48205
48318
|
}
|
|
48206
48319
|
}
|
|
48320
|
+
/** A CONFIDENT box fed `track` (match, resurrection): the emitted box
|
|
48321
|
+
* follows it again, and a hold that ends this way is an id the tier kept. */
|
|
48322
|
+
markConfident(track, bbox, timestamp) {
|
|
48323
|
+
track.confidentBbox = bbox;
|
|
48324
|
+
track.confidentSeenAt = timestamp;
|
|
48325
|
+
if (track.lowScoreHeld) {
|
|
48326
|
+
track.lowScoreHeld = false;
|
|
48327
|
+
this.lowScoreStats.resumed++;
|
|
48328
|
+
}
|
|
48329
|
+
}
|
|
48330
|
+
/**
|
|
48331
|
+
* The low-score continuation stage (D665): greedy, highest IoU first, of the
|
|
48332
|
+
* sub-floor boxes against the CONFIRMED tracks nothing claimed this frame,
|
|
48333
|
+
* same class, on the predicted box at `byteTrackLowIouThreshold`.
|
|
48334
|
+
*
|
|
48335
|
+
* A bound box moves the track's association state only — `bbox`, `lastSeen`,
|
|
48336
|
+
* `age` — so the next confident box meets the subject where it is. Not
|
|
48337
|
+
* `hits` (it confirms nothing), not `score`, `class`, the vote, the path or
|
|
48338
|
+
* the velocity (it describes nothing downstream may act on). A track past
|
|
48339
|
+
* `lowScoreContinuationMaxMs` since its last confident box is not offered.
|
|
48340
|
+
*/
|
|
48341
|
+
continueOnLowScore(established, lowScore, timestamp, matchedTracks, matched) {
|
|
48342
|
+
const claimed = [...matched.values()].map((d) => d.bbox);
|
|
48343
|
+
const candidates = lowScore.filter((det) => {
|
|
48344
|
+
const shadow = claimed.some((b) => iou$2(b, det.bbox) >= this.config.byteTrackLowIouThreshold);
|
|
48345
|
+
if (shadow) this.lowScoreStats.shadowed++;
|
|
48346
|
+
return !shadow;
|
|
48347
|
+
});
|
|
48348
|
+
const pairs = [];
|
|
48349
|
+
for (const track of established) {
|
|
48350
|
+
if (matchedTracks.has(track)) continue;
|
|
48351
|
+
if (timestamp - track.confidentSeenAt > this.config.lowScoreContinuationMaxMs) continue;
|
|
48352
|
+
const pbox = this.predicted(track, timestamp);
|
|
48353
|
+
for (const det of candidates) {
|
|
48354
|
+
if (det.class !== track.class) continue;
|
|
48355
|
+
const score = iou$2(pbox, det.bbox);
|
|
48356
|
+
if (score >= this.config.byteTrackLowIouThreshold) pairs.push({
|
|
48357
|
+
track,
|
|
48358
|
+
det,
|
|
48359
|
+
score
|
|
48360
|
+
});
|
|
48361
|
+
}
|
|
48362
|
+
}
|
|
48363
|
+
pairs.sort((a, b) => b.score - a.score);
|
|
48364
|
+
const usedTracks = /* @__PURE__ */ new Set();
|
|
48365
|
+
const usedDets = /* @__PURE__ */ new Set();
|
|
48366
|
+
for (const pair of pairs) {
|
|
48367
|
+
if (usedTracks.has(pair.track) || usedDets.has(pair.det)) continue;
|
|
48368
|
+
usedTracks.add(pair.track);
|
|
48369
|
+
usedDets.add(pair.det);
|
|
48370
|
+
pair.track.bbox = pair.det.bbox;
|
|
48371
|
+
pair.track.lastSeen = timestamp;
|
|
48372
|
+
pair.track.lastSeenFrameSeq = this.frameSeq;
|
|
48373
|
+
pair.track.age = 0;
|
|
48374
|
+
pair.track.lowScoreHeld = true;
|
|
48375
|
+
this.lowScoreStats.continued++;
|
|
48376
|
+
}
|
|
48377
|
+
}
|
|
48207
48378
|
update(detections, timestamp, frameContext) {
|
|
48208
48379
|
this.frameSeq++;
|
|
48209
48380
|
const frameArea = frameContext ? frameContext.frameWidth * frameContext.frameHeight : 0;
|
|
@@ -48261,6 +48432,9 @@ var SortTracker = class SortTracker {
|
|
|
48261
48432
|
this.rescueAssociate(established, PROXIMITY, detections, lowSet, frameArea, timestamp, used, matchedTracks, matched, continuationOnly);
|
|
48262
48433
|
this.rescueAssociate(provisional, new Set([...OVERLAP, ...PROXIMITY]), detections, lowSet, frameArea, timestamp, used, matchedTracks, matched, continuationOnly);
|
|
48263
48434
|
if (this.config.byteTrackEnabled && lowIdxs.length > 0) for (const tier of [established, provisional]) this.greedyAssociate(tier, detections, lowIdxs, this.config.byteTrackLowIouThreshold, timestamp, used, matchedTracks, matched, continuationOnly);
|
|
48435
|
+
const lowScore = frameContext?.lowScore ?? [];
|
|
48436
|
+
this.lowScoreStats.offered += lowScore.length;
|
|
48437
|
+
if (this.config.lowScoreContinuationEnabled && lowScore.length > 0) this.continueOnLowScore(established, lowScore, timestamp, matchedTracks, matched);
|
|
48264
48438
|
for (const [track, det] of matched) {
|
|
48265
48439
|
if (track.class !== det.class) this.reportRefusal("cross-class-absorbed", det, {
|
|
48266
48440
|
against: track,
|
|
@@ -48291,6 +48465,7 @@ var SortTracker = class SortTracker {
|
|
|
48291
48465
|
track.hits++;
|
|
48292
48466
|
track.lastSeen = timestamp;
|
|
48293
48467
|
track.lastSeenFrameSeq = this.frameSeq;
|
|
48468
|
+
this.markConfident(track, det.bbox, timestamp);
|
|
48294
48469
|
if (dtMs > 0) {
|
|
48295
48470
|
track.velocity = {
|
|
48296
48471
|
dx: (newCenter.x - prevCenter.x) / dtMs,
|
|
@@ -48315,10 +48490,15 @@ var SortTracker = class SortTracker {
|
|
|
48315
48490
|
}
|
|
48316
48491
|
const occluded = this.config.occlusionEnabled && occluders.some((ob) => containment(this.predicted(track, timestamp), ob.bbox) >= this.config.occlusionContainment);
|
|
48317
48492
|
track.age++;
|
|
48318
|
-
|
|
48493
|
+
const coastMs = timestamp - track.lastSeen;
|
|
48494
|
+
const limitMs = occluded ? this.config.occlusionMaxMissedMs : this.config.maxMissedMs;
|
|
48495
|
+
const heldTooLong = track.lowScoreHeld && timestamp - track.confidentSeenAt > this.config.lowScoreContinuationMaxMs;
|
|
48496
|
+
if (heldTooLong) this.lowScoreStats.sustainCapDrops++;
|
|
48497
|
+
if (coastMs > limitMs || heldTooLong) {
|
|
48319
48498
|
track.lost = true;
|
|
48320
48499
|
track.lostAt = timestamp;
|
|
48321
48500
|
track.resurrectable = true;
|
|
48501
|
+
track.lowScoreHeld = false;
|
|
48322
48502
|
this.lostTracks.push(track);
|
|
48323
48503
|
} else surviving.push(track);
|
|
48324
48504
|
}
|
|
@@ -48329,15 +48509,17 @@ var SortTracker = class SortTracker {
|
|
|
48329
48509
|
let bestScore = -1;
|
|
48330
48510
|
let bestGate = "iou";
|
|
48331
48511
|
let bestPeers = 0;
|
|
48512
|
+
let bestVetoLifted = false;
|
|
48332
48513
|
let refused;
|
|
48333
48514
|
let refusedGate = "iou";
|
|
48334
48515
|
let refusedScore = -1;
|
|
48335
48516
|
for (const lost of this.lostTracks) {
|
|
48336
48517
|
if (!lost.resurrectable) continue;
|
|
48337
48518
|
if (timestamp - lost.lostAt > this.config.resurrectionWindowMs) continue;
|
|
48338
|
-
const gate = this.looseMatch(lost, det, timestamp,
|
|
48519
|
+
const gate = this.looseMatch(lost, det, timestamp, "resurrection");
|
|
48339
48520
|
if (gate === null) continue;
|
|
48340
|
-
|
|
48521
|
+
const vetoLifted = gate === "centroid" && this.isPlausibleBirth(det, frameArea);
|
|
48522
|
+
if (vetoLifted && !(this.config.resurrectionTimeScaled && this.isSoleSubjectResurrection(lost, det, detections, surviving, lowScore))) continue;
|
|
48341
48523
|
const score = iou$2(lost.bbox, det.bbox);
|
|
48342
48524
|
const peers = this.sameClassPeerCount(lost, surviving);
|
|
48343
48525
|
if (this.config.resurrectionCrowdGuard && peers > 0 && score < this.config.resurrectionCrowdedIouThreshold) {
|
|
@@ -48353,13 +48535,14 @@ var SortTracker = class SortTracker {
|
|
|
48353
48535
|
bestScore = score;
|
|
48354
48536
|
bestGate = gate;
|
|
48355
48537
|
bestPeers = peers;
|
|
48538
|
+
bestVetoLifted = vetoLifted;
|
|
48356
48539
|
}
|
|
48357
48540
|
}
|
|
48358
48541
|
if (!best) {
|
|
48359
48542
|
if (refused !== void 0) this.reportBind("resurrection-refused", refusedGate, refused, det, timestamp);
|
|
48360
48543
|
continue;
|
|
48361
48544
|
}
|
|
48362
|
-
resurrectedScene.set(best, bestPeers > 0 ? "multi-subject" : "sole-subject");
|
|
48545
|
+
resurrectedScene.set(best, bestPeers > 0 ? "multi-subject" : bestVetoLifted ? "sole-subject-veto-lifted" : "sole-subject");
|
|
48363
48546
|
this.reportBind("resurrection", bestGate, best, det, timestamp);
|
|
48364
48547
|
this.lostTracks = this.lostTracks.filter((t) => t !== best);
|
|
48365
48548
|
best.bbox = det.bbox;
|
|
@@ -48373,6 +48556,7 @@ var SortTracker = class SortTracker {
|
|
|
48373
48556
|
best.lost = false;
|
|
48374
48557
|
best.lastSeen = timestamp;
|
|
48375
48558
|
best.lastSeenFrameSeq = this.frameSeq;
|
|
48559
|
+
this.markConfident(best, det.bbox, timestamp);
|
|
48376
48560
|
best.velocity = {
|
|
48377
48561
|
dx: 0,
|
|
48378
48562
|
dy: 0
|
|
@@ -48449,7 +48633,10 @@ var SortTracker = class SortTracker {
|
|
|
48449
48633
|
classVotes: new Map([[det.class, det.score > 0 ? det.score : Number.EPSILON]]),
|
|
48450
48634
|
emitted: false,
|
|
48451
48635
|
lossReported: false,
|
|
48452
|
-
lastNativeRecovery: det.nativeRecovery
|
|
48636
|
+
lastNativeRecovery: det.nativeRecovery,
|
|
48637
|
+
confidentBbox: det.bbox,
|
|
48638
|
+
confidentSeenAt: timestamp,
|
|
48639
|
+
lowScoreHeld: false
|
|
48453
48640
|
});
|
|
48454
48641
|
}
|
|
48455
48642
|
this.tracks = surviving;
|
|
@@ -48457,11 +48644,12 @@ var SortTracker = class SortTracker {
|
|
|
48457
48644
|
return this.tracks.filter((t) => this.isConfirmedForEmit(t)).map((t) => {
|
|
48458
48645
|
t.emitted = true;
|
|
48459
48646
|
const scene = resurrectedScene.get(t);
|
|
48647
|
+
const held = t.lowScoreHeld;
|
|
48460
48648
|
return {
|
|
48461
48649
|
class: this.resolveReportedClass(t),
|
|
48462
48650
|
originalClass: t.originalClass,
|
|
48463
48651
|
score: t.score,
|
|
48464
|
-
bbox: t.bbox,
|
|
48652
|
+
bbox: held ? t.confidentBbox : t.bbox,
|
|
48465
48653
|
trackId: t.id,
|
|
48466
48654
|
trackAge: t.hits,
|
|
48467
48655
|
velocity: {
|
|
@@ -48469,8 +48657,9 @@ var SortTracker = class SortTracker {
|
|
|
48469
48657
|
dy: t.velocity.dy * t.emitIntervalMs
|
|
48470
48658
|
},
|
|
48471
48659
|
path: [...t.path],
|
|
48472
|
-
matchedThisFrame: t.lastSeen === timestamp,
|
|
48473
|
-
...scene !== void 0 ? { resurrectionScene: scene } : {}
|
|
48660
|
+
matchedThisFrame: t.lastSeen === timestamp && !held,
|
|
48661
|
+
...scene !== void 0 ? { resurrectionScene: scene } : {},
|
|
48662
|
+
...held ? { heldByLowScore: true } : {}
|
|
48474
48663
|
};
|
|
48475
48664
|
});
|
|
48476
48665
|
}
|
|
@@ -48484,6 +48673,16 @@ var SortTracker = class SortTracker {
|
|
|
48484
48673
|
this.tracks = this.tracks.filter((t) => t.id !== trackId);
|
|
48485
48674
|
this.lostTracks = this.lostTracks.filter((t) => t.id !== trackId);
|
|
48486
48675
|
}
|
|
48676
|
+
/**
|
|
48677
|
+
* What the low-score continuation stage did since the last call, and reset
|
|
48678
|
+
* (D665). The tracker has no `deviceId` and no logger; its owner reads this
|
|
48679
|
+
* on its own cadence and logs it per camera.
|
|
48680
|
+
*/
|
|
48681
|
+
drainLowScoreStats() {
|
|
48682
|
+
const out = this.lowScoreStats;
|
|
48683
|
+
this.lowScoreStats = emptyLowScoreStats();
|
|
48684
|
+
return out;
|
|
48685
|
+
}
|
|
48487
48686
|
getActiveTracks() {
|
|
48488
48687
|
return this.tracks;
|
|
48489
48688
|
}
|
|
@@ -48858,6 +49057,8 @@ var FrameProcessor = class {
|
|
|
48858
49057
|
detectionRules;
|
|
48859
49058
|
/** See {@link setZoneMembershipMinOverlap}. 0 = any positive overlap. */
|
|
48860
49059
|
zoneMembershipMinOverlap;
|
|
49060
|
+
/** See {@link setLowScoreContinuationFloor}. `null` = tier off. */
|
|
49061
|
+
lowScoreFloor = LOW_SCORE_CONTINUATION_FLOOR_DEFAULT;
|
|
48861
49062
|
/** See {@link getLastZoneOverlaps}. */
|
|
48862
49063
|
lastZoneOverlaps;
|
|
48863
49064
|
/** See {@link getLastZoneRejections}. */
|
|
@@ -48970,6 +49171,18 @@ var FrameProcessor = class {
|
|
|
48970
49171
|
* failure mode this whole area keeps producing. {@link lastZoneOverlaps}
|
|
48971
49172
|
* exists so the distribution can be read before a number is picked.
|
|
48972
49173
|
*/
|
|
49174
|
+
/**
|
|
49175
|
+
* The score at or above which a box the operator's class floor dropped is
|
|
49176
|
+
* handed to the tracker as low-score continuation evidence (D665); `null`
|
|
49177
|
+
* turns the tier off for this camera. Pushed per frame, like the zones.
|
|
49178
|
+
*/
|
|
49179
|
+
setLowScoreContinuationFloor(floor) {
|
|
49180
|
+
this.lowScoreFloor = floor !== null && Number.isFinite(floor) && floor >= 0 && floor <= 1 ? floor : null;
|
|
49181
|
+
}
|
|
49182
|
+
/** The tracker's low-score continuation counts since the last call (D665). */
|
|
49183
|
+
drainLowScoreStats() {
|
|
49184
|
+
return this.tracker.drainLowScoreStats();
|
|
49185
|
+
}
|
|
48973
49186
|
setZoneMembershipMinOverlap(minOverlap) {
|
|
48974
49187
|
this.zoneMembershipMinOverlap = Number.isFinite(minOverlap) && minOverlap >= 0 && minOverlap <= 1 ? minOverlap : 0;
|
|
48975
49188
|
}
|
|
@@ -49362,10 +49575,16 @@ var FrameProcessor = class {
|
|
|
49362
49575
|
}
|
|
49363
49576
|
trackerInput.push(det);
|
|
49364
49577
|
}
|
|
49578
|
+
const lowCandidates = selectLowScoreContinuations(frame.discarded, this.lowScoreFloor);
|
|
49579
|
+
const lowScore = lowCandidates.length === 0 ? lowCandidates : this.zoneEngine.filterDetections(lowCandidates.map((d) => ({
|
|
49580
|
+
...d.bbox,
|
|
49581
|
+
detection: d
|
|
49582
|
+
})), this.zones, this.detectionRules, frameWidth, frameHeight, (fd) => fd.detection.class).passed.map((fd) => fd.detection);
|
|
49365
49583
|
const trackedDetections = this.tracker.update(trackerInput, timestamp, {
|
|
49366
49584
|
frameWidth,
|
|
49367
49585
|
frameHeight,
|
|
49368
|
-
continuationOnly
|
|
49586
|
+
continuationOnly,
|
|
49587
|
+
lowScore
|
|
49369
49588
|
});
|
|
49370
49589
|
const stationaryContinued = continuableBoxes.size === 0 ? 0 : trackedDetections.filter((t) => t.matchedThisFrame !== false && continuableBoxes.has(t.bbox)).length;
|
|
49371
49590
|
const riderFolds = foldByVehicleBbox.size === 0 ? EMPTY_RIDER_FOLDS : [...foldByVehicleBbox].map(([bbox, fold]) => {
|
|
@@ -49607,6 +49826,19 @@ var FrameProcessor = class {
|
|
|
49607
49826
|
}
|
|
49608
49827
|
};
|
|
49609
49828
|
//#endregion
|
|
49829
|
+
//#region src/pipeline-analytics/face-identity-retraction.ts
|
|
49830
|
+
/** The recognizer's step id — the attribution a face identity label carries. */
|
|
49831
|
+
var FACE_RECOGNIZER_STEP_ID = "face-recognizer";
|
|
49832
|
+
async function retractFaceIdentityLabel(stores, trackId) {
|
|
49833
|
+
const meta = stores.trackStore.getActiveByTrack(trackId)?.subLabelMeta;
|
|
49834
|
+
if (meta === void 0) return "no-label";
|
|
49835
|
+
if (meta.stepId !== "face-recognizer") return "not-face-recognizer";
|
|
49836
|
+
await stores.eventStore.clearLabelForTrack(trackId, 2);
|
|
49837
|
+
await stores.trackStore.clearLabel(trackId, 2);
|
|
49838
|
+
await stores.recomputeImportance?.(trackId);
|
|
49839
|
+
return "retracted";
|
|
49840
|
+
}
|
|
49841
|
+
//#endregion
|
|
49610
49842
|
//#region src/pipeline-analytics/pipeline/track-provenance.ts
|
|
49611
49843
|
function trackProvenanceOf(frame) {
|
|
49612
49844
|
if (!(frame.width > 0 && frame.height > 0)) return void 0;
|
|
@@ -53746,11 +53978,10 @@ var EventStore = class {
|
|
|
53746
53978
|
let entryId;
|
|
53747
53979
|
for (const row of rows) {
|
|
53748
53980
|
const bbox = row.data["bbox"];
|
|
53749
|
-
if (bbox
|
|
53750
|
-
|
|
53751
|
-
|
|
53752
|
-
|
|
53753
|
-
}
|
|
53981
|
+
if (bbox === null || typeof bbox !== "object") continue;
|
|
53982
|
+
const bw = "w" in bbox && typeof bbox.w === "number" ? bbox.w : 0;
|
|
53983
|
+
const bh = "h" in bbox && typeof bbox.h === "number" ? bbox.h : 0;
|
|
53984
|
+
if (bw <= 0 || bh <= 0) continue;
|
|
53754
53985
|
const conf = typeof row.data["confidence"] === "number" ? row.data["confidence"] : 0;
|
|
53755
53986
|
if (conf > bestConf) {
|
|
53756
53987
|
bestConf = conf;
|
|
@@ -62453,6 +62684,26 @@ var EmbeddingDispatcher = class {
|
|
|
62453
62684
|
return this.pendingCrops.size;
|
|
62454
62685
|
}
|
|
62455
62686
|
};
|
|
62687
|
+
//#endregion
|
|
62688
|
+
//#region src/pipeline-analytics/embedding-rebuild.ts
|
|
62689
|
+
/**
|
|
62690
|
+
* Track sources a rebuild never lists (D664). An audio track and a synthetic
|
|
62691
|
+
* sensor track have no object event and no subject to embed — on the live hub
|
|
62692
|
+
* 6,726 + 180 of 42,254 tracks — and walking them only inflated the skip
|
|
62693
|
+
* counters an operator reads to judge media coverage.
|
|
62694
|
+
*/
|
|
62695
|
+
var REBUILD_EXCLUDED_SOURCES = ["audio", "sensor"];
|
|
62696
|
+
/**
|
|
62697
|
+
* One page of the rebuild listing as a `queryRecentIds` query. Pure, so the
|
|
62698
|
+
* exclusion is assertable: dropping it would look identical in every result
|
|
62699
|
+
* except the skip counters.
|
|
62700
|
+
*/
|
|
62701
|
+
function rebuildTrackPageQuery(window) {
|
|
62702
|
+
return {
|
|
62703
|
+
...window,
|
|
62704
|
+
excludeSources: REBUILD_EXCLUDED_SOURCES
|
|
62705
|
+
};
|
|
62706
|
+
}
|
|
62456
62707
|
/**
|
|
62457
62708
|
* Re-embed every listed track from its key frame.
|
|
62458
62709
|
*
|
|
@@ -62468,6 +62719,14 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
|
|
|
62468
62719
|
let recoveredKeyFrame = 0;
|
|
62469
62720
|
let missingEvent = 0;
|
|
62470
62721
|
let missingBbox = 0;
|
|
62722
|
+
let boxSubject = 0;
|
|
62723
|
+
let boxPositions = 0;
|
|
62724
|
+
let boxEvent = 0;
|
|
62725
|
+
let boxRefused = 0;
|
|
62726
|
+
let forcedInSpace = 0;
|
|
62727
|
+
let skippedOutOfSpace = 0;
|
|
62728
|
+
const refusalsByDevice = /* @__PURE__ */ new Map();
|
|
62729
|
+
const force = options.force === true;
|
|
62471
62730
|
let alreadyInSpace = 0;
|
|
62472
62731
|
let notRunnable = 0;
|
|
62473
62732
|
let noCapableNode = 0;
|
|
@@ -62478,6 +62737,12 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
|
|
|
62478
62737
|
let budgetExhausted = false;
|
|
62479
62738
|
/** Set when the cluster ran out of nodes — the pass stops, `complete` is false. */
|
|
62480
62739
|
let abandoned = null;
|
|
62740
|
+
const boxSourceCounts = () => ({
|
|
62741
|
+
subjectBox: boxSubject,
|
|
62742
|
+
positions: boxPositions,
|
|
62743
|
+
event: boxEvent,
|
|
62744
|
+
refused: boxRefused
|
|
62745
|
+
});
|
|
62481
62746
|
while (scanned < maxTracks && abandoned === null) {
|
|
62482
62747
|
const page = await deps.listTrackIdPage(cursor);
|
|
62483
62748
|
if (page.trackIds.length === 0) {
|
|
@@ -62485,9 +62750,14 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
|
|
|
62485
62750
|
break;
|
|
62486
62751
|
}
|
|
62487
62752
|
let pageIds = page.trackIds;
|
|
62753
|
+
let forcedPage = false;
|
|
62488
62754
|
if (deps.filterOutOfSpace) {
|
|
62489
62755
|
const split = await deps.filterOutOfSpace(page.trackIds);
|
|
62490
|
-
if (split !== null) {
|
|
62756
|
+
if (split !== null && force) {
|
|
62757
|
+
skippedOutOfSpace += split.outOfSpace.length;
|
|
62758
|
+
pageIds = split.inSpace;
|
|
62759
|
+
forcedPage = true;
|
|
62760
|
+
} else if (split !== null) {
|
|
62491
62761
|
alreadyInSpace += split.alreadyInSpace;
|
|
62492
62762
|
pageIds = split.outOfSpace;
|
|
62493
62763
|
}
|
|
@@ -62503,6 +62773,7 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
|
|
|
62503
62773
|
const room = maxTracks - scanned;
|
|
62504
62774
|
const batch = pageIds.slice(0, Number.isFinite(room) ? room : void 0);
|
|
62505
62775
|
if (batch.length < pageIds.length) budgetExhausted = true;
|
|
62776
|
+
if (forcedPage) forcedInSpace += batch.length;
|
|
62506
62777
|
for (const trackId of batch) {
|
|
62507
62778
|
if (!deps.canDispatch()) {
|
|
62508
62779
|
abandoned = "no node left in the rotation can serve the pinned model — stopped before loading any further media";
|
|
@@ -62521,6 +62792,25 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
|
|
|
62521
62792
|
missingEvent += 1;
|
|
62522
62793
|
return;
|
|
62523
62794
|
}
|
|
62795
|
+
if (source.bbox === null || source.bbox.w <= 0 || source.bbox.h <= 0) {
|
|
62796
|
+
missingBbox += 1;
|
|
62797
|
+
boxRefused += 1;
|
|
62798
|
+
const reason = source.boxSource.kind === "refused" ? source.boxSource.reason : "degenerate-box";
|
|
62799
|
+
const n = (refusalsByDevice.get(source.deviceId) ?? 0) + 1;
|
|
62800
|
+
refusalsByDevice.set(source.deviceId, n);
|
|
62801
|
+
if (n === 1 || n % 100 === 0) deps.logger.warn("embedding rebuild: no box measured on the key frame — track refused (D303)", {
|
|
62802
|
+
tags: { deviceId: source.deviceId },
|
|
62803
|
+
meta: {
|
|
62804
|
+
trackId,
|
|
62805
|
+
reason,
|
|
62806
|
+
refusedOnThisCamera: n
|
|
62807
|
+
}
|
|
62808
|
+
});
|
|
62809
|
+
return;
|
|
62810
|
+
}
|
|
62811
|
+
if (source.boxSource.kind === "subjectBox") boxSubject += 1;
|
|
62812
|
+
else if (source.boxSource.kind === "positions") boxPositions += 1;
|
|
62813
|
+
else boxEvent += 1;
|
|
62524
62814
|
let keyFrame = source.keyFrame;
|
|
62525
62815
|
let recovered = false;
|
|
62526
62816
|
if (keyFrame === null && deps.recoverKeyFrame) {
|
|
@@ -62532,10 +62822,6 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
|
|
|
62532
62822
|
return;
|
|
62533
62823
|
}
|
|
62534
62824
|
if (recovered) recoveredKeyFrame += 1;
|
|
62535
|
-
if (source.bbox === null || source.bbox.w <= 0 || source.bbox.h <= 0) {
|
|
62536
|
-
missingBbox += 1;
|
|
62537
|
-
return;
|
|
62538
|
-
}
|
|
62539
62825
|
const outcome = await deps.embed(recovered ? {
|
|
62540
62826
|
...source,
|
|
62541
62827
|
keyFrame
|
|
@@ -62603,6 +62889,9 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
|
|
|
62603
62889
|
recoveredKeyFrame,
|
|
62604
62890
|
missingEvent,
|
|
62605
62891
|
missingBbox,
|
|
62892
|
+
boxSource: boxSourceCounts(),
|
|
62893
|
+
forcedInSpace,
|
|
62894
|
+
skippedOutOfSpace,
|
|
62606
62895
|
alreadyInSpace,
|
|
62607
62896
|
notRunnable,
|
|
62608
62897
|
noCapableNode,
|
|
@@ -62626,6 +62915,10 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
|
|
|
62626
62915
|
recoveredKeyFrame,
|
|
62627
62916
|
missingEvent,
|
|
62628
62917
|
missingBbox,
|
|
62918
|
+
boxSource: boxSourceCounts(),
|
|
62919
|
+
force,
|
|
62920
|
+
forcedInSpace,
|
|
62921
|
+
skippedOutOfSpace,
|
|
62629
62922
|
alreadyInSpace,
|
|
62630
62923
|
notRunnable,
|
|
62631
62924
|
noCapableNode,
|
|
@@ -62640,6 +62933,9 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
|
|
|
62640
62933
|
recoveredKeyFrame,
|
|
62641
62934
|
missingEvent,
|
|
62642
62935
|
missingBbox,
|
|
62936
|
+
boxSource: boxSourceCounts(),
|
|
62937
|
+
forcedInSpace,
|
|
62938
|
+
skippedOutOfSpace,
|
|
62643
62939
|
alreadyInSpace,
|
|
62644
62940
|
notRunnable,
|
|
62645
62941
|
noCapableNode,
|
|
@@ -63198,8 +63494,6 @@ function faceSizeScore(detectionFrameShortSidePx) {
|
|
|
63198
63494
|
}
|
|
63199
63495
|
//#endregion
|
|
63200
63496
|
//#region src/pipeline-analytics/face-recognizer.ts
|
|
63201
|
-
/** Attribution of an identity resolved by the in-process ArcFace matcher. */
|
|
63202
|
-
var FACE_RECOGNIZER_STEP_ID = "face-recognizer";
|
|
63203
63497
|
/** At most one "dropping imageless track" log per this interval, per recognizer. */
|
|
63204
63498
|
var FACE_IMAGELESS_LOG_THROTTLE_MS = 6e4;
|
|
63205
63499
|
/** At most one 'embedding magnitude' sample line per device per this interval. */
|
|
@@ -63225,6 +63519,9 @@ var FaceRecognizer = class {
|
|
|
63225
63519
|
gallery = [];
|
|
63226
63520
|
names = /* @__PURE__ */ new Map();
|
|
63227
63521
|
aggregates = /* @__PURE__ */ new Map();
|
|
63522
|
+
/** The aggregate a veto-lifted quarantine set aside, restored if the first
|
|
63523
|
+
* face proves the same person (D665). */
|
|
63524
|
+
stashedIdentity = /* @__PURE__ */ new Map();
|
|
63228
63525
|
bestFace = /* @__PURE__ */ new Map();
|
|
63229
63526
|
/** Frame instant of each live track's last source park (D644). */
|
|
63230
63527
|
lastSourceParkAt = /* @__PURE__ */ new Map();
|
|
@@ -63416,9 +63713,35 @@ var FaceRecognizer = class {
|
|
|
63416
63713
|
* • the face becomes non-promotable: {@link onTrackEnd} will not write it to
|
|
63417
63714
|
* the gallery while the flag stands.
|
|
63418
63715
|
*
|
|
63419
|
-
* The
|
|
63420
|
-
|
|
63421
|
-
|
|
63716
|
+
* The identity AGGREGATE depends on the scene (D665):
|
|
63717
|
+
*
|
|
63718
|
+
* • `multi-subject` — a bind that needed real overlap (IoU ≥ 0.3) with the
|
|
63719
|
+
* corpse's last box. The aggregate is LEFT ALONE: the next face either
|
|
63720
|
+
* confirms (same person, name intact at no cost — the common case, a
|
|
63721
|
+
* seated person occluded for a few seconds) or is rejected by cosine, and
|
|
63722
|
+
* a rejection is a PROVEN subject change: the aggregate is deleted and the
|
|
63723
|
+
* written identity label retracted then.
|
|
63724
|
+
* • `sole-subject-veto-lifted` — a bind on proximity alone. The aggregate is
|
|
63725
|
+
* STASHED and reset, and the written identity label retracted at once:
|
|
63726
|
+
* `updateTrackAggregate` keeps an assignment through every unmatched frame,
|
|
63727
|
+
* so without the reset a stranger who never matched anybody would carry
|
|
63728
|
+
* the name. The first face-bearing frame decides — `confirmed` restores the
|
|
63729
|
+
* stash and rewrites the label (the same person, back immediately);
|
|
63730
|
+
* `rejected`, or no face before the track ends, discards it.
|
|
63731
|
+
*/
|
|
63732
|
+
async quarantineHeldFace(deviceId, trackId, scene) {
|
|
63733
|
+
if (scene === "sole-subject-veto-lifted") {
|
|
63734
|
+
const aggregate = this.aggregates.get(trackId);
|
|
63735
|
+
if (aggregate?.assignedIdentityId != null) {
|
|
63736
|
+
const peak = this.bestMatchScore.get(trackId);
|
|
63737
|
+
this.stashedIdentity.set(trackId, {
|
|
63738
|
+
aggregate,
|
|
63739
|
+
...peak !== void 0 ? { bestMatchScore: peak } : {}
|
|
63740
|
+
});
|
|
63741
|
+
this.aggregates.delete(trackId);
|
|
63742
|
+
await this.retractIdentity(deviceId, trackId, scene, aggregate.assignedIdentityId);
|
|
63743
|
+
} else this.aggregates.delete(trackId);
|
|
63744
|
+
}
|
|
63422
63745
|
const held = this.bestFace.get(trackId);
|
|
63423
63746
|
if (held === void 0 || held.unverified === true) return;
|
|
63424
63747
|
const quarantined = {
|
|
@@ -63441,6 +63764,7 @@ var FaceRecognizer = class {
|
|
|
63441
63764
|
trackId
|
|
63442
63765
|
},
|
|
63443
63766
|
meta: {
|
|
63767
|
+
scene,
|
|
63444
63768
|
heldScore: held.score,
|
|
63445
63769
|
heldAt: held.timestamp,
|
|
63446
63770
|
strippedIdentityId: held.recognizedIdentityId ?? null,
|
|
@@ -63449,6 +63773,40 @@ var FaceRecognizer = class {
|
|
|
63449
63773
|
});
|
|
63450
63774
|
}
|
|
63451
63775
|
/**
|
|
63776
|
+
* Take the identity label back from the track and its events — only when the
|
|
63777
|
+
* face recognizer wrote it (`retractFaceIdentityLabel`). Stated either way.
|
|
63778
|
+
*/
|
|
63779
|
+
async retractIdentity(deviceId, trackId, reason, identityId) {
|
|
63780
|
+
const retract = this.deps.retractIdentityLabel;
|
|
63781
|
+
if (retract === void 0) return;
|
|
63782
|
+
try {
|
|
63783
|
+
const outcome = await retract(trackId);
|
|
63784
|
+
this.deps.logger.info("face: identity label retracted after an unverified re-attach", {
|
|
63785
|
+
tags: {
|
|
63786
|
+
deviceId,
|
|
63787
|
+
trackId
|
|
63788
|
+
},
|
|
63789
|
+
meta: {
|
|
63790
|
+
reason,
|
|
63791
|
+
identityId,
|
|
63792
|
+
outcome
|
|
63793
|
+
}
|
|
63794
|
+
});
|
|
63795
|
+
} catch (err) {
|
|
63796
|
+
this.deps.logger.warn("face: identity label retraction failed", {
|
|
63797
|
+
tags: {
|
|
63798
|
+
deviceId,
|
|
63799
|
+
trackId
|
|
63800
|
+
},
|
|
63801
|
+
meta: {
|
|
63802
|
+
reason,
|
|
63803
|
+
identityId,
|
|
63804
|
+
error: String(err)
|
|
63805
|
+
}
|
|
63806
|
+
});
|
|
63807
|
+
}
|
|
63808
|
+
}
|
|
63809
|
+
/**
|
|
63452
63810
|
* Ask a FRESH face whether the quarantined held face is still about the same
|
|
63453
63811
|
* person, at the same bar the gallery uses to say two vectors are one person
|
|
63454
63812
|
* (`similarityThreshold` — one knob over one band, D222).
|
|
@@ -63470,7 +63828,7 @@ var FaceRecognizer = class {
|
|
|
63470
63828
|
}
|
|
63471
63829
|
async processFrame(input) {
|
|
63472
63830
|
const { settings } = input;
|
|
63473
|
-
for (const t of input.tracked) if (t.resurrectionScene
|
|
63831
|
+
for (const t of input.tracked) if (t.resurrectionScene !== void 0 && t.resurrectionScene !== "sole-subject") await this.quarantineHeldFace(input.deviceId, t.trackId, t.resurrectionScene);
|
|
63474
63832
|
const candidates = input.tracked.filter((t) => Array.isArray(t.embedding) && t.embedding.length > 0 && typeof t.embeddingModelId === "string" && (t.nativeFaceShortSidePx ?? (t.faceBbox ? Math.min(t.faceBbox.w, t.faceBbox.h) : void 0) ?? Number.POSITIVE_INFINITY) >= settings.minFacePx);
|
|
63475
63833
|
if (candidates.length === 0) return;
|
|
63476
63834
|
this.deps.logger.debug("face: frame candidates", {
|
|
@@ -63537,11 +63895,26 @@ var FaceRecognizer = class {
|
|
|
63537
63895
|
embedding: c.embedding,
|
|
63538
63896
|
embeddingModelId: c.embeddingModelId
|
|
63539
63897
|
}, settings.similarityThreshold);
|
|
63540
|
-
|
|
63541
|
-
|
|
63542
|
-
|
|
63543
|
-
|
|
63544
|
-
|
|
63898
|
+
const stashed = this.stashedIdentity.get(c.trackId);
|
|
63899
|
+
this.stashedIdentity.delete(c.trackId);
|
|
63900
|
+
if (verdict === "confirmed") {
|
|
63901
|
+
this.bestFace.set(c.trackId, {
|
|
63902
|
+
...quarantined,
|
|
63903
|
+
unverified: false
|
|
63904
|
+
});
|
|
63905
|
+
const name = stashed?.aggregate.assignedIdentityId;
|
|
63906
|
+
if (stashed !== void 0 && name != null) {
|
|
63907
|
+
this.aggregates.set(c.trackId, stashed.aggregate);
|
|
63908
|
+
if (stashed.bestMatchScore !== void 0) this.bestMatchScore.set(c.trackId, stashed.bestMatchScore);
|
|
63909
|
+
labelWork.push({
|
|
63910
|
+
trackId: c.trackId,
|
|
63911
|
+
assignedIdentityId: name,
|
|
63912
|
+
matchScore: stashed.bestMatchScore ?? null
|
|
63913
|
+
});
|
|
63914
|
+
}
|
|
63915
|
+
} else if (verdict === "rejected") {
|
|
63916
|
+
const named = this.aggregates.get(c.trackId)?.assignedIdentityId;
|
|
63917
|
+
if (named != null) await this.retractIdentity(input.deviceId, c.trackId, "rejected", named);
|
|
63545
63918
|
this.bestFace.delete(c.trackId);
|
|
63546
63919
|
this.bestTracker.delete(c.trackId);
|
|
63547
63920
|
this.aggregates.delete(c.trackId);
|
|
@@ -63756,6 +64129,7 @@ var FaceRecognizer = class {
|
|
|
63756
64129
|
const bestMatchScore = this.bestMatchScore.get(trackId);
|
|
63757
64130
|
const bestSuggestion = this.bestSuggestion.get(trackId);
|
|
63758
64131
|
this.aggregates.delete(trackId);
|
|
64132
|
+
this.stashedIdentity.delete(trackId);
|
|
63759
64133
|
this.bestFace.delete(trackId);
|
|
63760
64134
|
this.bestTracker.delete(trackId);
|
|
63761
64135
|
this.bestMatchScore.delete(trackId);
|
|
@@ -67917,6 +68291,19 @@ function shouldSkipDropoutFrame(activeTrackCount, detectionCount, consecutiveSki
|
|
|
67917
68291
|
if (activeTrackCount <= 0) return false;
|
|
67918
68292
|
return consecutiveSkips < cfg.maxSkipFrames;
|
|
67919
68293
|
}
|
|
68294
|
+
/**
|
|
68295
|
+
* How much evidence a frame carries for the dropout decision: its detections,
|
|
68296
|
+
* plus the sub-floor boxes the tracker may continue a track on (D665).
|
|
68297
|
+
*
|
|
68298
|
+
* Until D665 the skip counted `frame.detections` only, so a frame whose sole
|
|
68299
|
+
* box was a person at 0.5 read as a detector glitch and never reached the
|
|
68300
|
+
* tracker — on camera 590 more than half the frames of a re-split gap were
|
|
68301
|
+
* dropout-skipped. A frame with a continuable box is not empty; `floor` is the
|
|
68302
|
+
* camera's low-score floor, `null` when the tier is off.
|
|
68303
|
+
*/
|
|
68304
|
+
function dropoutEvidenceCount(frame, floor) {
|
|
68305
|
+
return frame.detections.length + selectLowScoreContinuations(frame.discarded, floor).length;
|
|
68306
|
+
}
|
|
67920
68307
|
//#endregion
|
|
67921
68308
|
//#region src/pipeline-analytics/pipeline/key-event-query.ts
|
|
67922
68309
|
async function rankKeyEvents(candidates, options, peakLookup) {
|
|
@@ -73038,6 +73425,58 @@ function hasKeyFrame(source) {
|
|
|
73038
73425
|
}
|
|
73039
73426
|
//#endregion
|
|
73040
73427
|
//#region src/pipeline-analytics/rebuild-source-loader.ts
|
|
73428
|
+
function refused(reason) {
|
|
73429
|
+
return {
|
|
73430
|
+
bbox: null,
|
|
73431
|
+
boxSource: {
|
|
73432
|
+
kind: "refused",
|
|
73433
|
+
reason
|
|
73434
|
+
}
|
|
73435
|
+
};
|
|
73436
|
+
}
|
|
73437
|
+
function normaliseMeasured(box) {
|
|
73438
|
+
if (box.frameWidth <= 0 || box.frameHeight <= 0 || box.w <= 0 || box.h <= 0) return null;
|
|
73439
|
+
return {
|
|
73440
|
+
x: box.x / box.frameWidth,
|
|
73441
|
+
y: box.y / box.frameHeight,
|
|
73442
|
+
w: box.w / box.frameWidth,
|
|
73443
|
+
h: box.h / box.frameHeight
|
|
73444
|
+
};
|
|
73445
|
+
}
|
|
73446
|
+
/**
|
|
73447
|
+
* The box measured on THIS key frame: its own `subjectBox`, else the track
|
|
73448
|
+
* position at exactly its instant, else a named refusal. Never another
|
|
73449
|
+
* frame's box (D303).
|
|
73450
|
+
*/
|
|
73451
|
+
async function keyFrameBox(deps, trackId, keyFrame) {
|
|
73452
|
+
const own = (await deps.media.listSubjectBoxesByOwner("track", trackId)).find((row) => row.mediaId === keyFrame.mediaId);
|
|
73453
|
+
if (own !== void 0) {
|
|
73454
|
+
const bbox = normaliseMeasured(own.subjectBox);
|
|
73455
|
+
if (bbox !== null) return {
|
|
73456
|
+
bbox,
|
|
73457
|
+
boxSource: { kind: "subjectBox" }
|
|
73458
|
+
};
|
|
73459
|
+
}
|
|
73460
|
+
const track = await deps.tracks.getPersistedByTrackId(trackId);
|
|
73461
|
+
if (track === null) return refused("track-row-missing");
|
|
73462
|
+
const { frameWidth, frameHeight } = track;
|
|
73463
|
+
if (frameWidth === void 0 || frameHeight === void 0) return refused("track-frame-size-unknown");
|
|
73464
|
+
const at = track.positions.find((p) => p.timestamp === keyFrame.timestamp);
|
|
73465
|
+
if (at === void 0) return refused("no-position-at-key-frame");
|
|
73466
|
+
const bbox = normaliseMeasured({
|
|
73467
|
+
...at.bbox,
|
|
73468
|
+
frameWidth,
|
|
73469
|
+
frameHeight
|
|
73470
|
+
});
|
|
73471
|
+
if (bbox === null) return refused("no-position-at-key-frame");
|
|
73472
|
+
return {
|
|
73473
|
+
bbox,
|
|
73474
|
+
boxSource: { kind: "positions" }
|
|
73475
|
+
};
|
|
73476
|
+
}
|
|
73477
|
+
function hasBox(event) {
|
|
73478
|
+
return event.bbox !== void 0 && event.bbox.w > 0 && event.bbox.h > 0;
|
|
73479
|
+
}
|
|
73041
73480
|
/**
|
|
73042
73481
|
* Assemble one track's rebuild inputs, reading at most ONE blob.
|
|
73043
73482
|
*
|
|
@@ -73045,7 +73484,8 @@ function hasKeyFrame(source) {
|
|
|
73045
73484
|
* to describe, and the caller counts it. A track WITH an event but WITHOUT a
|
|
73046
73485
|
* key frame still returns a source (with `keyFrame: null`) so the caller's
|
|
73047
73486
|
* `missingKeyFrame` counter stays the one place that decision is made; no blob
|
|
73048
|
-
* is read in that case either.
|
|
73487
|
+
* is read in that case either. A key frame with no box measured on it returns
|
|
73488
|
+
* `bbox: null` and a named refusal, also without reading the blob.
|
|
73049
73489
|
*/
|
|
73050
73490
|
async function loadRebuildSource(deps, trackId) {
|
|
73051
73491
|
const media = await deps.media.listInfoByOwner("track", trackId);
|
|
@@ -73053,26 +73493,44 @@ async function loadRebuildSource(deps, trackId) {
|
|
|
73053
73493
|
const cropInfo = media.find((m) => m.kind === "thumbnail");
|
|
73054
73494
|
const peak = await deps.events.peakForTrack(trackId);
|
|
73055
73495
|
const events = await deps.events.queryObjectByTrackIds([trackId]);
|
|
73056
|
-
const best = events.find((e) => String(e.id) === peak.bestEventId) ?? events[0];
|
|
73496
|
+
const best = events.find((e) => String(e.id) === peak.bestEventId) ?? events.find(hasBox) ?? events[0];
|
|
73057
73497
|
if (best === void 0) return null;
|
|
73058
73498
|
const common = {
|
|
73059
73499
|
trackId,
|
|
73060
73500
|
deviceId: best.deviceId,
|
|
73061
|
-
timestamp: best.timestamp,
|
|
73062
73501
|
className: best.className,
|
|
73063
73502
|
confidence: best.confidence ?? 0,
|
|
73064
|
-
bbox: normaliseEventBbox(best),
|
|
73065
73503
|
...cropInfo !== void 0 ? { mediaId: cropInfo.mediaId } : {}
|
|
73066
73504
|
};
|
|
73067
|
-
if (keyFrameInfo === void 0)
|
|
73505
|
+
if (keyFrameInfo === void 0) {
|
|
73506
|
+
const bbox = normaliseEventBbox(best);
|
|
73507
|
+
return {
|
|
73508
|
+
...common,
|
|
73509
|
+
timestamp: best.timestamp,
|
|
73510
|
+
bbox,
|
|
73511
|
+
boxSource: bbox !== null ? { kind: "event" } : {
|
|
73512
|
+
kind: "refused",
|
|
73513
|
+
reason: "event-without-box"
|
|
73514
|
+
},
|
|
73515
|
+
keyFrame: null
|
|
73516
|
+
};
|
|
73517
|
+
}
|
|
73518
|
+
const box = await keyFrameBox(deps, trackId, keyFrameInfo);
|
|
73519
|
+
const keyed = {
|
|
73068
73520
|
...common,
|
|
73521
|
+
timestamp: keyFrameInfo.timestamp,
|
|
73522
|
+
bbox: box.bbox,
|
|
73523
|
+
boxSource: box.boxSource,
|
|
73524
|
+
keyFrameMediaId: keyFrameInfo.mediaId
|
|
73525
|
+
};
|
|
73526
|
+
if (box.bbox === null) return {
|
|
73527
|
+
...keyed,
|
|
73069
73528
|
keyFrame: null
|
|
73070
73529
|
};
|
|
73071
73530
|
const keyFrameFile = await deps.media.getById(keyFrameInfo.mediaId);
|
|
73072
73531
|
return {
|
|
73073
|
-
...
|
|
73074
|
-
keyFrame: keyFrameFile ? Buffer.from(keyFrameFile.base64, "base64") : null
|
|
73075
|
-
keyFrameMediaId: keyFrameInfo.mediaId
|
|
73532
|
+
...keyed,
|
|
73533
|
+
keyFrame: keyFrameFile ? Buffer.from(keyFrameFile.base64, "base64") : null
|
|
73076
73534
|
};
|
|
73077
73535
|
}
|
|
73078
73536
|
//#endregion
|
|
@@ -82799,7 +83257,21 @@ var TrackingSettingsSchema = require_clip_model_registry.object({
|
|
|
82799
83257
|
* passes, only for a coarse box below `animalOverPersonMaxScore`, never on
|
|
82800
83258
|
* the rescue / resurrection passes. Reported label stays via the class
|
|
82801
83259
|
* vote. Off = exact per-frame class gating. */
|
|
82802
|
-
classGroupAssoc: require_clip_model_registry.boolean().default(true)
|
|
83260
|
+
classGroupAssoc: require_clip_model_registry.boolean().default(true),
|
|
83261
|
+
/** Hand the boxes the per-class confidence floor dropped to the tracker as
|
|
83262
|
+
* CONTINUATION-ONLY evidence (D665): they may keep a confirmed track alive,
|
|
83263
|
+
* never open one, never produce media, events or notifications. */
|
|
83264
|
+
lowScoreContinuationEnabled: require_clip_model_registry.boolean().default(true),
|
|
83265
|
+
/** Score at/above which a floor-dropped box counts as continuation evidence
|
|
83266
|
+
* (D665 — justified in `low-score-continuation.ts`). */
|
|
83267
|
+
lowScoreContinuationFloor: require_clip_model_registry.number().min(0).max(1).default(.35),
|
|
83268
|
+
/** Longest a track may be held by floor-dropped boxes alone, from its last
|
|
83269
|
+
* confident box (ms). */
|
|
83270
|
+
lowScoreContinuationMaxMs: require_clip_model_registry.number().min(0).default(8e3),
|
|
83271
|
+
/** Graveyard reach scaled by the gap, and the plausible-birth veto lifted for
|
|
83272
|
+
* a sole subject (D665). Off = the flat pre-D665 resurrection gate — the
|
|
83273
|
+
* per-camera rollback that needs no redeploy. */
|
|
83274
|
+
resurrectionTimeScaled: require_clip_model_registry.boolean().default(true)
|
|
82803
83275
|
});
|
|
82804
83276
|
var TRACKING_DEFAULTS = TrackingSettingsSchema.parse({});
|
|
82805
83277
|
/**
|
|
@@ -82854,7 +83326,11 @@ function resolveTrackingSettings(raw) {
|
|
|
82854
83326
|
minSpawnAreaFracPerson: s.minSpawnAreaFracPerson.catch(TRACKING_DEFAULTS.minSpawnAreaFracPerson).parse(raw.minSpawnAreaFracPerson),
|
|
82855
83327
|
minSpawnAreaFracAnimal: s.minSpawnAreaFracAnimal.catch(TRACKING_DEFAULTS.minSpawnAreaFracAnimal).parse(raw.minSpawnAreaFracAnimal),
|
|
82856
83328
|
minSpawnAreaFracVehicle: s.minSpawnAreaFracVehicle.catch(TRACKING_DEFAULTS.minSpawnAreaFracVehicle).parse(raw.minSpawnAreaFracVehicle),
|
|
82857
|
-
classGroupAssoc: s.classGroupAssoc.catch(TRACKING_DEFAULTS.classGroupAssoc).parse(raw.classGroupAssoc)
|
|
83329
|
+
classGroupAssoc: s.classGroupAssoc.catch(TRACKING_DEFAULTS.classGroupAssoc).parse(raw.classGroupAssoc),
|
|
83330
|
+
lowScoreContinuationEnabled: s.lowScoreContinuationEnabled.catch(TRACKING_DEFAULTS.lowScoreContinuationEnabled).parse(raw.lowScoreContinuationEnabled),
|
|
83331
|
+
lowScoreContinuationFloor: s.lowScoreContinuationFloor.catch(TRACKING_DEFAULTS.lowScoreContinuationFloor).parse(raw.lowScoreContinuationFloor),
|
|
83332
|
+
lowScoreContinuationMaxMs: s.lowScoreContinuationMaxMs.catch(TRACKING_DEFAULTS.lowScoreContinuationMaxMs).parse(raw.lowScoreContinuationMaxMs),
|
|
83333
|
+
resurrectionTimeScaled: s.resurrectionTimeScaled.catch(TRACKING_DEFAULTS.resurrectionTimeScaled).parse(raw.resurrectionTimeScaled)
|
|
82858
83334
|
};
|
|
82859
83335
|
}
|
|
82860
83336
|
//#endregion
|
|
@@ -83284,6 +83760,40 @@ function buildDetectionSettingsSections() {
|
|
|
83284
83760
|
step: .05,
|
|
83285
83761
|
default: TRACKING_DEFAULTS.byteTrackLowIouThreshold
|
|
83286
83762
|
},
|
|
83763
|
+
{
|
|
83764
|
+
type: "boolean",
|
|
83765
|
+
key: "lowScoreContinuationEnabled",
|
|
83766
|
+
label: "Keep tracks through low-confidence frames",
|
|
83767
|
+
description: "Boxes below the class confidence floor may keep an EXISTING track alive while the detector is unsure, so a person read at 0.5 for a few seconds keeps one id instead of splitting. They never start a track and never produce events, notifications or snapshots.",
|
|
83768
|
+
default: TRACKING_DEFAULTS.lowScoreContinuationEnabled
|
|
83769
|
+
},
|
|
83770
|
+
{
|
|
83771
|
+
type: "number",
|
|
83772
|
+
key: "lowScoreContinuationFloor",
|
|
83773
|
+
label: "Low-confidence continuation floor",
|
|
83774
|
+
description: "Score at or above which a box the class floor dropped still counts as evidence that a tracked subject is there. Only used with the option above.",
|
|
83775
|
+
min: 0,
|
|
83776
|
+
max: 1,
|
|
83777
|
+
step: .05,
|
|
83778
|
+
default: TRACKING_DEFAULTS.lowScoreContinuationFloor
|
|
83779
|
+
},
|
|
83780
|
+
{
|
|
83781
|
+
type: "number",
|
|
83782
|
+
key: "lowScoreContinuationMaxMs",
|
|
83783
|
+
label: "Low-confidence hold limit (ms)",
|
|
83784
|
+
description: "Longest a track may be kept alive by low-confidence boxes alone, counted from its last confident box.",
|
|
83785
|
+
min: 0,
|
|
83786
|
+
max: 3e4,
|
|
83787
|
+
step: 500,
|
|
83788
|
+
default: TRACKING_DEFAULTS.lowScoreContinuationMaxMs
|
|
83789
|
+
},
|
|
83790
|
+
{
|
|
83791
|
+
type: "boolean",
|
|
83792
|
+
key: "resurrectionTimeScaled",
|
|
83793
|
+
label: "Wider re-attach after a long gap",
|
|
83794
|
+
description: "Let a track that was lost for a few seconds be re-attached to a subject that reappears further away, the longer the gap (up to two box diagonals) — only when nobody else of that class is in view. A recognised face is re-checked after such a re-attach. Off = the fixed short re-attach distance.",
|
|
83795
|
+
default: TRACKING_DEFAULTS.resurrectionTimeScaled
|
|
83796
|
+
},
|
|
83287
83797
|
{
|
|
83288
83798
|
type: "boolean",
|
|
83289
83799
|
key: "minSpawnAreaEnabled",
|
|
@@ -83621,7 +84131,7 @@ function buildGlobalSettingsSchema(cap) {
|
|
|
83621
84131
|
addonId: "pipeline-analytics",
|
|
83622
84132
|
action: "embedding.rebuildStatus",
|
|
83623
84133
|
buttonLabel: "Check progress",
|
|
83624
|
-
successMessage: "{modelId} → {index}: indexed {indexed} · scanned {scanned} · rebuilt {rebuilt} · no key frame {missingKeyFrame} · not runnable {notRunnable} · failed {failed}"
|
|
84134
|
+
successMessage: "{modelId} → {index}: indexed {indexed} · scanned {scanned} · rebuilt {rebuilt} · no key frame {missingKeyFrame} · no box {missingBbox} · no event {missingEvent} · not runnable {notRunnable} · failed {failed}"
|
|
83625
84135
|
},
|
|
83626
84136
|
{
|
|
83627
84137
|
type: "addon-action-button",
|
|
@@ -87784,6 +88294,8 @@ function decodeEmbeddingBase64(base64) {
|
|
|
87784
88294
|
var PHANTOM_CELL_PX = 32;
|
|
87785
88295
|
/** How long a cell remembers its closes. */
|
|
87786
88296
|
var PHANTOM_CELL_WINDOW_MS = 360 * 6e4;
|
|
88297
|
+
/** Cadence of the per-camera low-score continuation line (D665). */
|
|
88298
|
+
var LOW_SCORE_REPORT_INTERVAL_MS = 6e4;
|
|
87787
88299
|
var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_model_registry.BaseAddon {
|
|
87788
88300
|
/** Recent SHORT+MOTIONLESS track closes per `<device>:<class>:<cell>` —
|
|
87789
88301
|
* see {@link noteShortMotionlessTrack}. Measurement only; each entry is
|
|
@@ -88451,6 +88963,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
88451
88963
|
packageBackground = null;
|
|
88452
88964
|
/** Consecutive detector-dropout frames skipped per (deviceId, source) key. */
|
|
88453
88965
|
dropoutSkipsByKey = /* @__PURE__ */ new Map();
|
|
88966
|
+
/** Last time each processor's low-score continuation counts were logged (D665). */
|
|
88967
|
+
lowScoreReportAtByKey = /* @__PURE__ */ new Map();
|
|
88454
88968
|
/** The per-track close-path orchestrator (S2, refactor spec 2026-07-22):
|
|
88455
88969
|
* owns the single-flight sweep latch + the FULL per-track close sequence
|
|
88456
88970
|
* (zero-media policy, lastFrame promotion, recognizer ends with the
|
|
@@ -90009,6 +90523,14 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
90009
90523
|
mediaStore: stores.mediaStore,
|
|
90010
90524
|
trackStore: stores.trackStore,
|
|
90011
90525
|
eventStore: stores.eventStore,
|
|
90526
|
+
retractIdentityLabel: (trackId) => retractFaceIdentityLabel({
|
|
90527
|
+
trackStore: stores.trackStore,
|
|
90528
|
+
eventStore: stores.eventStore,
|
|
90529
|
+
recomputeImportance: (id) => recomputeTrackImportance({
|
|
90530
|
+
trackStore: stores.trackStore,
|
|
90531
|
+
eventStore: stores.eventStore
|
|
90532
|
+
}, id)
|
|
90533
|
+
}, trackId),
|
|
90012
90534
|
captureCrop: (frameHandle, bbox, frameWidth, frameHeight, padding, ownerId, deviceId) => this.captureScheduler.request({
|
|
90013
90535
|
deviceId,
|
|
90014
90536
|
trackId: ownerId,
|
|
@@ -91963,6 +92485,31 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
91963
92485
|
* tracking/zone/event state never crosses between sources. Emits the
|
|
91964
92486
|
* SAME canonical events, distinguished only by `source`.
|
|
91965
92487
|
*/
|
|
92488
|
+
/**
|
|
92489
|
+
* One line per camera per minute with what the low-score continuation tier
|
|
92490
|
+
* did (D665): boxes offered, tracks continued, holds that ended in a
|
|
92491
|
+
* confident box again (`resumed` — an id the tier kept), holds cut at the
|
|
92492
|
+
* cap. Never one line per box. Silent while nothing was offered.
|
|
92493
|
+
*/
|
|
92494
|
+
reportLowScoreContinuation(deviceId, key, processor) {
|
|
92495
|
+
const now = Date.now();
|
|
92496
|
+
const last = this.lowScoreReportAtByKey.get(key);
|
|
92497
|
+
if (last === void 0) {
|
|
92498
|
+
this.lowScoreReportAtByKey.set(key, now);
|
|
92499
|
+
return;
|
|
92500
|
+
}
|
|
92501
|
+
if (now - last < LOW_SCORE_REPORT_INTERVAL_MS) return;
|
|
92502
|
+
this.lowScoreReportAtByKey.set(key, now);
|
|
92503
|
+
const stats = processor.drainLowScoreStats();
|
|
92504
|
+
if (stats.offered === 0 && stats.sustainCapDrops === 0) return;
|
|
92505
|
+
this.ctx.logger.info("tracker: low-score continuation", {
|
|
92506
|
+
tags: { deviceId },
|
|
92507
|
+
meta: {
|
|
92508
|
+
...stats,
|
|
92509
|
+
windowMs: now - last
|
|
92510
|
+
}
|
|
92511
|
+
});
|
|
92512
|
+
}
|
|
91966
92513
|
async processFrame(deviceId, frame, source, rawFrameHandle, detailSteps, occasion = "session") {
|
|
91967
92514
|
if (this.shuttingDown) return;
|
|
91968
92515
|
if (!await this.bindingCache.isActive(deviceId)) {
|
|
@@ -91987,7 +92534,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
91987
92534
|
const trk = await this.resolveDeviceTrackingSettings(deviceId);
|
|
91988
92535
|
const activeCount = this.lastActiveTrackIds.get(key)?.size ?? 0;
|
|
91989
92536
|
const skips = this.dropoutSkipsByKey.get(key) ?? 0;
|
|
91990
|
-
|
|
92537
|
+
const lowScoreFloor = trk.lowScoreContinuationEnabled ? trk.lowScoreContinuationFloor : null;
|
|
92538
|
+
if (shouldSkipDropoutFrame(activeCount, dropoutEvidenceCount(frame, lowScoreFloor), skips, {
|
|
91991
92539
|
enabled: trk.dropoutSkipEnabled,
|
|
91992
92540
|
maxSkipFrames: trk.dropoutMaxSkipFrames
|
|
91993
92541
|
})) {
|
|
@@ -92015,6 +92563,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
92015
92563
|
processor.setZones(liveZones);
|
|
92016
92564
|
processor.setDetectionRules(liveRules);
|
|
92017
92565
|
processor.setZoneMembershipMinOverlap(trk.zoneMembershipMinOverlap);
|
|
92566
|
+
processor.setLowScoreContinuationFloor(lowScoreFloor);
|
|
92018
92567
|
const injection = injectSecondarySubjects(frame, this.secondarySubjects.take(key, Date.now()));
|
|
92019
92568
|
for (const id of injection.covered) this.secondarySubjects.drop(key, id);
|
|
92020
92569
|
const trackerFrame = injection.frame;
|
|
@@ -92030,6 +92579,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
92030
92579
|
timestamp: frame.timestamp,
|
|
92031
92580
|
frame: trackerFrame
|
|
92032
92581
|
});
|
|
92582
|
+
this.reportLowScoreContinuation(deviceId, key, processor);
|
|
92033
92583
|
for (const r of processor.getLastLabelWriteRefusals()) {
|
|
92034
92584
|
const message = r.reason.kind === "macro-class" ? "label tier collapse refused" : "label write refused";
|
|
92035
92585
|
this.ctx.logger.info(message, {
|
|
@@ -95717,7 +96267,10 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
95717
96267
|
animal: trk.minSpawnAreaFracAnimal,
|
|
95718
96268
|
vehicle: trk.minSpawnAreaFracVehicle
|
|
95719
96269
|
},
|
|
95720
|
-
classGroupAssoc: trk.classGroupAssoc
|
|
96270
|
+
classGroupAssoc: trk.classGroupAssoc,
|
|
96271
|
+
lowScoreContinuationEnabled: trk.lowScoreContinuationEnabled,
|
|
96272
|
+
lowScoreContinuationMaxMs: trk.lowScoreContinuationMaxMs,
|
|
96273
|
+
resurrectionTimeScaled: trk.resurrectionTimeScaled
|
|
95721
96274
|
}, {
|
|
95722
96275
|
stationaryThresholdSec,
|
|
95723
96276
|
...stillSpeedFracPerSec !== void 0 ? { stillSpeedFracPerSec } : {}
|
|
@@ -97208,6 +97761,15 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
97208
97761
|
refusal: "stores-unavailable"
|
|
97209
97762
|
};
|
|
97210
97763
|
}
|
|
97764
|
+
const force = input.force === true;
|
|
97765
|
+
if (force && input.maxTracks === void 0) {
|
|
97766
|
+
this.ctx.logger.warn("rebuildObjectEmbeddings: refusing a force pass with no maxTracks", { meta: { refusal: "force-requires-max-tracks" } });
|
|
97767
|
+
return {
|
|
97768
|
+
started: false,
|
|
97769
|
+
alreadyRunning: false,
|
|
97770
|
+
refusal: "force-requires-max-tracks"
|
|
97771
|
+
};
|
|
97772
|
+
}
|
|
97211
97773
|
const confirmation = await this.getClusterPinConfirmer(api, "clip-rebuild").confirm();
|
|
97212
97774
|
if (confirmation.kind === "refused") {
|
|
97213
97775
|
this.ctx.logger.warn("rebuildObjectEmbeddings: the cluster CLIP model is not confirmed — refusing to start a pass that cannot pin one", { meta: { refusal: confirmation.reason } });
|
|
@@ -97251,16 +97813,17 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
97251
97813
|
logger: this.ctx.logger
|
|
97252
97814
|
}));
|
|
97253
97815
|
const deps = {
|
|
97254
|
-
listTrackIdPage: async (cursor) => trackStore.queryRecentIds({
|
|
97816
|
+
listTrackIdPage: async (cursor) => trackStore.queryRecentIds(rebuildTrackPageQuery({
|
|
97255
97817
|
deviceIds,
|
|
97256
97818
|
...input.since !== void 0 ? { since: input.since } : {},
|
|
97257
97819
|
...input.until !== void 0 ? { until: input.until } : {},
|
|
97258
97820
|
...cursor !== null ? { cursor } : {},
|
|
97259
97821
|
limit: REBUILD_PAGE_SIZE
|
|
97260
|
-
}),
|
|
97822
|
+
})),
|
|
97261
97823
|
loadSource: (trackId) => loadRebuildSource({
|
|
97262
97824
|
media: mediaStore,
|
|
97263
|
-
events: eventStore
|
|
97825
|
+
events: eventStore,
|
|
97826
|
+
tracks: trackStore
|
|
97264
97827
|
}, trackId),
|
|
97265
97828
|
recoverKeyFrame: async (source) => {
|
|
97266
97829
|
const recovered = await recordedStills.recover(source.deviceId, source.timestamp, RECOVERED_KEY_FRAME_MAX_WIDTH);
|
|
@@ -97286,7 +97849,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
97286
97849
|
if (split === null) return null;
|
|
97287
97850
|
return {
|
|
97288
97851
|
outOfSpace: split.outOfSpaceIds,
|
|
97289
|
-
alreadyInSpace: split.inSpace
|
|
97852
|
+
alreadyInSpace: split.inSpace,
|
|
97853
|
+
inSpace: split.inSpaceIds
|
|
97290
97854
|
};
|
|
97291
97855
|
},
|
|
97292
97856
|
logger: {
|
|
@@ -97298,6 +97862,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
97298
97862
|
running: true,
|
|
97299
97863
|
modelId,
|
|
97300
97864
|
index,
|
|
97865
|
+
force,
|
|
97301
97866
|
startedAtMs: Date.now(),
|
|
97302
97867
|
progress: {
|
|
97303
97868
|
scanned: 0,
|
|
@@ -97306,6 +97871,14 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
97306
97871
|
recoveredKeyFrame: 0,
|
|
97307
97872
|
missingEvent: 0,
|
|
97308
97873
|
missingBbox: 0,
|
|
97874
|
+
boxSource: {
|
|
97875
|
+
subjectBox: 0,
|
|
97876
|
+
positions: 0,
|
|
97877
|
+
event: 0,
|
|
97878
|
+
refused: 0
|
|
97879
|
+
},
|
|
97880
|
+
forcedInSpace: 0,
|
|
97881
|
+
skippedOutOfSpace: 0,
|
|
97309
97882
|
alreadyInSpace: 0,
|
|
97310
97883
|
notRunnable: 0,
|
|
97311
97884
|
noCapableNode: 0,
|
|
@@ -97313,6 +97886,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
97313
97886
|
}
|
|
97314
97887
|
};
|
|
97315
97888
|
rebuildObjectEmbeddings(deps, {
|
|
97889
|
+
force,
|
|
97316
97890
|
...input.maxTracks !== void 0 ? { maxTracks: input.maxTracks } : {},
|
|
97317
97891
|
...input.pacingMs !== void 0 ? { pacingMs: input.pacingMs } : {},
|
|
97318
97892
|
onProgress: (progress) => {
|
|
@@ -97324,6 +97898,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
97324
97898
|
running: false,
|
|
97325
97899
|
modelId,
|
|
97326
97900
|
index,
|
|
97901
|
+
force,
|
|
97327
97902
|
startedAtMs: this.rebuildState.startedAtMs ?? Date.now(),
|
|
97328
97903
|
finishedAtMs: Date.now(),
|
|
97329
97904
|
progress: outcome,
|
|
@@ -97339,6 +97914,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
97339
97914
|
running: false,
|
|
97340
97915
|
modelId,
|
|
97341
97916
|
index,
|
|
97917
|
+
force,
|
|
97342
97918
|
startedAtMs: this.rebuildState.startedAtMs ?? Date.now(),
|
|
97343
97919
|
finishedAtMs: Date.now(),
|
|
97344
97920
|
error: String(err)
|
|
@@ -97353,7 +97929,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
97353
97929
|
modelId,
|
|
97354
97930
|
index,
|
|
97355
97931
|
deviceId: input.deviceId ?? "all",
|
|
97356
|
-
maxTracks: input.maxTracks ?? "unbounded"
|
|
97932
|
+
maxTracks: input.maxTracks ?? "unbounded",
|
|
97933
|
+
force
|
|
97357
97934
|
} });
|
|
97358
97935
|
return {
|
|
97359
97936
|
started: true,
|
|
@@ -98355,8 +98932,18 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
98355
98932
|
running: state.running,
|
|
98356
98933
|
scanned: p?.scanned ?? 0,
|
|
98357
98934
|
rebuilt: p?.rebuilt ?? 0,
|
|
98358
|
-
missingKeyFrame:
|
|
98935
|
+
missingKeyFrame: p?.missingKeyFrame ?? 0,
|
|
98936
|
+
missingEvent: p?.missingEvent ?? 0,
|
|
98359
98937
|
missingBbox: p?.missingBbox ?? 0,
|
|
98938
|
+
boxSource: p?.boxSource ?? {
|
|
98939
|
+
subjectBox: 0,
|
|
98940
|
+
positions: 0,
|
|
98941
|
+
event: 0,
|
|
98942
|
+
refused: 0
|
|
98943
|
+
},
|
|
98944
|
+
alreadyInSpace: p?.alreadyInSpace ?? 0,
|
|
98945
|
+
force: state.force ?? null,
|
|
98946
|
+
forcedInSpace: p?.forcedInSpace ?? 0,
|
|
98360
98947
|
notRunnable: p?.notRunnable ?? 0,
|
|
98361
98948
|
noCapableNode: p?.noCapableNode ?? 0,
|
|
98362
98949
|
failed: p?.failed ?? 0,
|