@camstack/addon-post-analysis 1.2.199 → 1.2.201
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/{dist-BrszC4uJ.mjs → dist-1xB8qj2h.mjs} +19 -2
- package/dist/{dist-fmXXxa9D.js → dist-DbgfbjZC.js} +19 -2
- 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-CTKbm1Jm.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-DwFi68p5.mjs} +3 -3
- package/dist/pipeline-analytics/{hostInit-DXFji_c2.mjs → hostInit-POMJnj4w.mjs} +3 -3
- package/dist/pipeline-analytics/index.js +106 -23
- package/dist/pipeline-analytics/index.mjs +106 -23
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
|
@@ -22106,8 +22106,25 @@ var occupancyRecheckFramesField = {
|
|
|
22106
22106
|
* (analyzer attaches detected `regions[]`; onboard does not — the
|
|
22107
22107
|
* camera typically only reports a binary signal plus an optional
|
|
22108
22108
|
* channel/AI class which lives in dedicated event channels).
|
|
22109
|
-
|
|
22110
|
-
|
|
22109
|
+
*
|
|
22110
|
+
* - `onboard` — the camera's firmware said something moved.
|
|
22111
|
+
* - `analyzer` — this runner's frame-diff said so, and attaches `regions[]`.
|
|
22112
|
+
* - `device-activity` — the DEVICE said it is doing its job: the
|
|
22113
|
+
* `recording-signal` LEVEL the same device raises for the recorder
|
|
22114
|
+
* ([D380](../../../../docs/decisions/adr-0380-a-device-decided-recording-is-a-mode-with-no-schedule-seeded-once.md)),
|
|
22115
|
+
* republished as a motion source. It attaches **nothing** — no regions, no
|
|
22116
|
+
* class: the only fact it carries is that the device is active, and a robot
|
|
22117
|
+
* vacuum that is itself the moving object has no region worth sending. It is
|
|
22118
|
+
* a LEVEL, so unlike `onboard` it has a real falling edge, and unlike
|
|
22119
|
+
* `analyzer` it must not open the frame-diff side-channel — the runner's
|
|
22120
|
+
* `handleOnboardMotionAnalyzer` gate is `source === 'onboard'` and stays that
|
|
22121
|
+
* way ([D392](../../../../docs/decisions/adr-0392-a-device-that-says-it-is-working-is-a-motion-source-of-its-own.md)).
|
|
22122
|
+
*/
|
|
22123
|
+
var MotionSourceEnum = _enum([
|
|
22124
|
+
"onboard",
|
|
22125
|
+
"analyzer",
|
|
22126
|
+
"device-activity"
|
|
22127
|
+
]);
|
|
22111
22128
|
/**
|
|
22112
22129
|
* List of motion sources active on a camera. Empty array is valid:
|
|
22113
22130
|
* "no source" — happens for battery cams without firmware motion when
|
|
@@ -22137,8 +22137,25 @@ var occupancyRecheckFramesField = {
|
|
|
22137
22137
|
* (analyzer attaches detected `regions[]`; onboard does not — the
|
|
22138
22138
|
* camera typically only reports a binary signal plus an optional
|
|
22139
22139
|
* channel/AI class which lives in dedicated event channels).
|
|
22140
|
-
|
|
22141
|
-
|
|
22140
|
+
*
|
|
22141
|
+
* - `onboard` — the camera's firmware said something moved.
|
|
22142
|
+
* - `analyzer` — this runner's frame-diff said so, and attaches `regions[]`.
|
|
22143
|
+
* - `device-activity` — the DEVICE said it is doing its job: the
|
|
22144
|
+
* `recording-signal` LEVEL the same device raises for the recorder
|
|
22145
|
+
* ([D380](../../../../docs/decisions/adr-0380-a-device-decided-recording-is-a-mode-with-no-schedule-seeded-once.md)),
|
|
22146
|
+
* republished as a motion source. It attaches **nothing** — no regions, no
|
|
22147
|
+
* class: the only fact it carries is that the device is active, and a robot
|
|
22148
|
+
* vacuum that is itself the moving object has no region worth sending. It is
|
|
22149
|
+
* a LEVEL, so unlike `onboard` it has a real falling edge, and unlike
|
|
22150
|
+
* `analyzer` it must not open the frame-diff side-channel — the runner's
|
|
22151
|
+
* `handleOnboardMotionAnalyzer` gate is `source === 'onboard'` and stays that
|
|
22152
|
+
* way ([D392](../../../../docs/decisions/adr-0392-a-device-that-says-it-is-working-is-a-motion-source-of-its-own.md)).
|
|
22153
|
+
*/
|
|
22154
|
+
var MotionSourceEnum = _enum([
|
|
22155
|
+
"onboard",
|
|
22156
|
+
"analyzer",
|
|
22157
|
+
"device-activity"
|
|
22158
|
+
]);
|
|
22142
22159
|
/**
|
|
22143
22160
|
* List of motion sources active on a camera. Empty array is valid:
|
|
22144
22161
|
* "no source" — happens for battery cams without firmware motion when
|
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
5
|
+
const require_dist = require("../dist-DbgfbjZC.js");
|
|
6
6
|
let node_fs = require("node:fs");
|
|
7
7
|
node_fs = require_dist.__toESM(node_fs);
|
|
8
8
|
let node_path = require("node:path");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { F as PoolMemoryWatchdog, Pt as BaseAddon, Tt as resolvePoolMemoryPolicy, at as embeddingEncoderCapability, dt as hfModelUrl, vt as parseProcStatus } from "../dist-
|
|
1
|
+
import { F as PoolMemoryWatchdog, Pt as BaseAddon, Tt as resolvePoolMemoryPolicy, at as embeddingEncoderCapability, dt as hfModelUrl, vt as parseProcStatus } from "../dist-1xB8qj2h.mjs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
@@ -3,7 +3,7 @@ import "./dist-CYZr2fwk.mjs";
|
|
|
3
3
|
var e = {
|
|
4
4
|
"@camstack/sdk": {
|
|
5
5
|
name: "@camstack/sdk",
|
|
6
|
-
version: "1.2.
|
|
6
|
+
version: "1.2.84",
|
|
7
7
|
scope: ["default"],
|
|
8
8
|
loaded: !1,
|
|
9
9
|
from: "addon_pipeline_analytics_widgets",
|
|
@@ -18,7 +18,7 @@ var e = {
|
|
|
18
18
|
},
|
|
19
19
|
"@camstack/types": {
|
|
20
20
|
name: "@camstack/types",
|
|
21
|
-
version: "1.2.
|
|
21
|
+
version: "1.2.164",
|
|
22
22
|
scope: ["default"],
|
|
23
23
|
loaded: !1,
|
|
24
24
|
from: "addon_pipeline_analytics_widgets",
|
|
@@ -33,7 +33,7 @@ var e = {
|
|
|
33
33
|
},
|
|
34
34
|
"@camstack/ui-library": {
|
|
35
35
|
name: "@camstack/ui-library",
|
|
36
|
-
version: "1.2.
|
|
36
|
+
version: "1.2.132",
|
|
37
37
|
scope: ["default"],
|
|
38
38
|
loaded: !1,
|
|
39
39
|
from: "addon_pipeline_analytics_widgets",
|
|
@@ -36,7 +36,7 @@ async function r() {
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"@camstack/types": {
|
|
39
|
-
version: "1.2.
|
|
39
|
+
version: "1.2.164",
|
|
40
40
|
scope: "default",
|
|
41
41
|
shareConfig: {
|
|
42
42
|
singleton: !0,
|
|
@@ -45,7 +45,7 @@ async function r() {
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"@camstack/sdk": {
|
|
48
|
-
version: "1.2.
|
|
48
|
+
version: "1.2.84",
|
|
49
49
|
scope: "default",
|
|
50
50
|
shareConfig: {
|
|
51
51
|
singleton: !0,
|
|
@@ -81,7 +81,7 @@ async function r() {
|
|
|
81
81
|
}
|
|
82
82
|
},
|
|
83
83
|
"@camstack/ui-library": {
|
|
84
|
-
version: "1.2.
|
|
84
|
+
version: "1.2.132",
|
|
85
85
|
scope: "default",
|
|
86
86
|
shareConfig: {
|
|
87
87
|
singleton: !0,
|
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
5
|
+
const require_dist = require("../dist-DbgfbjZC.js");
|
|
6
6
|
let node_fs = require("node:fs");
|
|
7
7
|
node_fs = require_dist.__toESM(node_fs, 1);
|
|
8
8
|
let node_path = require("node:path");
|
|
@@ -34061,6 +34061,22 @@ function pairRiders(detections) {
|
|
|
34061
34061
|
}
|
|
34062
34062
|
return pairs;
|
|
34063
34063
|
}
|
|
34064
|
+
/** Per-(stage, gate) counter + sampling decision. One instance per tracker, so
|
|
34065
|
+
* the counts are per camera and per detection source. */
|
|
34066
|
+
var LooseBindSampler = class {
|
|
34067
|
+
counts = /* @__PURE__ */ new Map();
|
|
34068
|
+
/** Increment the (stage, gate) counter and answer with the running total when
|
|
34069
|
+
* this decision should be reported, or `undefined` when it is sampled out. */
|
|
34070
|
+
next(stage, gate) {
|
|
34071
|
+
const key = `${stage}|${gate}`;
|
|
34072
|
+
const count = (this.counts.get(key) ?? 0) + 1;
|
|
34073
|
+
this.counts.set(key, count);
|
|
34074
|
+
if (count === 1 || count % 50 === 0) return count;
|
|
34075
|
+
}
|
|
34076
|
+
reset() {
|
|
34077
|
+
this.counts.clear();
|
|
34078
|
+
}
|
|
34079
|
+
};
|
|
34064
34080
|
//#endregion
|
|
34065
34081
|
//#region src/pipeline-analytics/pipeline/tracker/rescue-growth.ts
|
|
34066
34082
|
/**
|
|
@@ -34118,22 +34134,6 @@ function growthAdjacentMatch(track, det, ctx) {
|
|
|
34118
34134
|
const dc = bboxCentroid(det);
|
|
34119
34135
|
return Math.hypot(tc.x - dc.x, tc.y - dc.y) <= GROWTH_CENTROID_FACTOR * Math.hypot(track.w, track.h);
|
|
34120
34136
|
}
|
|
34121
|
-
/** Per-(stage, gate) counter + sampling decision. One instance per tracker, so
|
|
34122
|
-
* the counts are per camera and per detection source. */
|
|
34123
|
-
var LooseBindSampler = class {
|
|
34124
|
-
counts = /* @__PURE__ */ new Map();
|
|
34125
|
-
/** Increment the (stage, gate) counter and answer with the running total when
|
|
34126
|
-
* this decision should be reported, or `undefined` when it is sampled out. */
|
|
34127
|
-
next(stage, gate) {
|
|
34128
|
-
const key = `${stage}|${gate}`;
|
|
34129
|
-
const count = (this.counts.get(key) ?? 0) + 1;
|
|
34130
|
-
this.counts.set(key, count);
|
|
34131
|
-
if (count === 1 || count % 50 === 0) return count;
|
|
34132
|
-
}
|
|
34133
|
-
reset() {
|
|
34134
|
-
this.counts.clear();
|
|
34135
|
-
}
|
|
34136
|
-
};
|
|
34137
34137
|
/** Per-reason counter + sampling decision. One instance per tracker, so the
|
|
34138
34138
|
* counts are per camera and per detection source. */
|
|
34139
34139
|
var SpawnRefusalSampler = class {
|
|
@@ -34277,7 +34277,35 @@ var SortTracker = class SortTracker {
|
|
|
34277
34277
|
againstTrackClass: extra.against.class
|
|
34278
34278
|
} : {},
|
|
34279
34279
|
refusalCount,
|
|
34280
|
-
sampledEvery: 10
|
|
34280
|
+
sampledEvery: 10,
|
|
34281
|
+
...extra.droppedTrackId !== void 0 ? { droppedTrackId: extra.droppedTrackId } : {},
|
|
34282
|
+
...extra.trackLifeMs !== void 0 ? { trackLifeMs: extra.trackLifeMs } : {}
|
|
34283
|
+
});
|
|
34284
|
+
}
|
|
34285
|
+
/**
|
|
34286
|
+
* Report a TRACK that ends having emitted nothing — the last silent branch
|
|
34287
|
+
* on the detection→position path (D391).
|
|
34288
|
+
*
|
|
34289
|
+
* The subject is the track, not one box, so the record carries the track's
|
|
34290
|
+
* own last class/score/bbox: those ARE the detection that fed it last, and
|
|
34291
|
+
* they are what an operator matches against the recovery line that produced
|
|
34292
|
+
* it. Reported once (`lossReported`) and only where the track can no longer
|
|
34293
|
+
* come back — a graveyarded track may still resurrect, and a line about work
|
|
34294
|
+
* that was not lost is worse than no line.
|
|
34295
|
+
*/
|
|
34296
|
+
reportTrackLoss(track) {
|
|
34297
|
+
if (track.emitted || track.lossReported) return;
|
|
34298
|
+
track.lossReported = true;
|
|
34299
|
+
this.reportRefusal("unconfirmed-spawn-lost", {
|
|
34300
|
+
class: track.class,
|
|
34301
|
+
originalClass: track.originalClass,
|
|
34302
|
+
score: track.score,
|
|
34303
|
+
bbox: track.bbox
|
|
34304
|
+
}, {
|
|
34305
|
+
threshold: this.config.minHits,
|
|
34306
|
+
measured: track.hits,
|
|
34307
|
+
droppedTrackId: track.id,
|
|
34308
|
+
trackLifeMs: track.lastSeen - track.firstSeen
|
|
34281
34309
|
});
|
|
34282
34310
|
}
|
|
34283
34311
|
/** Emit one salvage decision, subject to {@link LOOSE_BIND_LOG_SAMPLE_EVERY}.
|
|
@@ -34472,7 +34500,22 @@ var SortTracker = class SortTracker {
|
|
|
34472
34500
|
}
|
|
34473
34501
|
const streak = (this.dupStreaks.get(key) ?? 0) + 1;
|
|
34474
34502
|
if (streak >= this.config.dedupMergeFrames) {
|
|
34475
|
-
|
|
34503
|
+
const loser = SortTracker.duplicateLoser(a, b);
|
|
34504
|
+
const winner = loser === a ? b : a;
|
|
34505
|
+
dropIds.add(loser.id);
|
|
34506
|
+
this.reportRefusal("merged-duplicate", {
|
|
34507
|
+
class: loser.class,
|
|
34508
|
+
originalClass: loser.originalClass,
|
|
34509
|
+
score: loser.score,
|
|
34510
|
+
bbox: loser.bbox
|
|
34511
|
+
}, {
|
|
34512
|
+
threshold: this.config.dedupMergeIou,
|
|
34513
|
+
measured: iou$3(a.bbox, b.bbox),
|
|
34514
|
+
against: winner,
|
|
34515
|
+
droppedTrackId: loser.id,
|
|
34516
|
+
trackLifeMs: loser.lastSeen - loser.firstSeen
|
|
34517
|
+
});
|
|
34518
|
+
loser.lossReported = true;
|
|
34476
34519
|
this.dupStreaks.delete(key);
|
|
34477
34520
|
} else this.dupStreaks.set(key, streak);
|
|
34478
34521
|
}
|
|
@@ -34654,6 +34697,7 @@ var SortTracker = class SortTracker {
|
|
|
34654
34697
|
track.lost = true;
|
|
34655
34698
|
track.lostAt = timestamp;
|
|
34656
34699
|
track.resurrectable = false;
|
|
34700
|
+
this.reportTrackLoss(track);
|
|
34657
34701
|
this.lostTracks.push(track);
|
|
34658
34702
|
} else alive.push(track);
|
|
34659
34703
|
this.tracks = alive;
|
|
@@ -34665,11 +34709,15 @@ var SortTracker = class SortTracker {
|
|
|
34665
34709
|
track.lost = true;
|
|
34666
34710
|
track.lostAt = track.lastSeen;
|
|
34667
34711
|
track.resurrectable = false;
|
|
34712
|
+
this.reportTrackLoss(track);
|
|
34668
34713
|
this.lostTracks.push(track);
|
|
34669
34714
|
} else alive.push(track);
|
|
34670
34715
|
this.tracks = alive;
|
|
34671
34716
|
}
|
|
34672
|
-
|
|
34717
|
+
const stillResurrectable = [];
|
|
34718
|
+
for (const t of this.lostTracks) if (timestamp - t.lostAt <= this.config.resurrectionWindowMs) stillResurrectable.push(t);
|
|
34719
|
+
else this.reportTrackLoss(t);
|
|
34720
|
+
this.lostTracks = stillResurrectable;
|
|
34673
34721
|
const used = /* @__PURE__ */ new Set();
|
|
34674
34722
|
const matchedTracks = /* @__PURE__ */ new Set();
|
|
34675
34723
|
const matched = /* @__PURE__ */ new Map();
|
|
@@ -34867,12 +34915,15 @@ var SortTracker = class SortTracker {
|
|
|
34867
34915
|
lost: false,
|
|
34868
34916
|
lostAt: 0,
|
|
34869
34917
|
resurrectable: true,
|
|
34870
|
-
classVotes: new Map([[det.class, det.score > 0 ? det.score : Number.EPSILON]])
|
|
34918
|
+
classVotes: new Map([[det.class, det.score > 0 ? det.score : Number.EPSILON]]),
|
|
34919
|
+
emitted: false,
|
|
34920
|
+
lossReported: false
|
|
34871
34921
|
});
|
|
34872
34922
|
}
|
|
34873
34923
|
this.tracks = surviving;
|
|
34874
34924
|
this.resolveDuplicates();
|
|
34875
34925
|
return this.tracks.filter((t) => this.isConfirmedForEmit(t)).map((t) => {
|
|
34926
|
+
t.emitted = true;
|
|
34876
34927
|
const scene = resurrectedScene.get(t);
|
|
34877
34928
|
return {
|
|
34878
34929
|
class: this.resolveReportedClass(t),
|
|
@@ -35240,6 +35291,10 @@ var FrameProcessor = class {
|
|
|
35240
35291
|
*/
|
|
35241
35292
|
reportedRefusalByTrack = /* @__PURE__ */ new Map();
|
|
35242
35293
|
zoneEngine = new ZoneEngine();
|
|
35294
|
+
/** Sink + sampler for the drops this stage owns (the detection-stage zone
|
|
35295
|
+
* filter). Shares the tracker's vocabulary — see {@link SpawnRefusalReason}. */
|
|
35296
|
+
spawnRefusalObserver = null;
|
|
35297
|
+
zoneRefusalSampler = new SpawnRefusalSampler();
|
|
35243
35298
|
/** Optional stationary-object gate (parked-object suppression). Null until
|
|
35244
35299
|
* the addon wires it via {@link setStationaryGate}. */
|
|
35245
35300
|
stationaryGate = null;
|
|
@@ -35334,6 +35389,7 @@ var FrameProcessor = class {
|
|
|
35334
35389
|
*/
|
|
35335
35390
|
setSpawnRefusalObserver(observer) {
|
|
35336
35391
|
this.tracker.setSpawnRefusalObserver(observer);
|
|
35392
|
+
this.spawnRefusalObserver = observer;
|
|
35337
35393
|
}
|
|
35338
35394
|
setBindObserver(observer) {
|
|
35339
35395
|
this.tracker.setBindObserver(observer);
|
|
@@ -35343,6 +35399,32 @@ var FrameProcessor = class {
|
|
|
35343
35399
|
dropTrack(trackId) {
|
|
35344
35400
|
this.tracker.dropTrack(trackId);
|
|
35345
35401
|
}
|
|
35402
|
+
/**
|
|
35403
|
+
* Say which detections a detection-stage zone rule excluded (D391).
|
|
35404
|
+
*
|
|
35405
|
+
* `filterDetections` has always ANSWERED this — it returns `excluded` — and
|
|
35406
|
+
* this stage dropped the answer on the floor, so a camera whose subject the
|
|
35407
|
+
* rules exclude was indistinguishable in the log from a camera whose tracker
|
|
35408
|
+
* ate it. Sampled like every other refusal: an excluded box is excluded on
|
|
35409
|
+
* every frame of a passage.
|
|
35410
|
+
*/
|
|
35411
|
+
reportZoneFiltered(excluded) {
|
|
35412
|
+
const observer = this.spawnRefusalObserver;
|
|
35413
|
+
if (!observer || excluded.length === 0) return;
|
|
35414
|
+
for (const fd of excluded) {
|
|
35415
|
+
const refusalCount = this.zoneRefusalSampler.next("zone-filtered");
|
|
35416
|
+
if (refusalCount === void 0) continue;
|
|
35417
|
+
observer({
|
|
35418
|
+
reason: "zone-filtered",
|
|
35419
|
+
detClass: fd.detection.class,
|
|
35420
|
+
detScore: fd.detection.score,
|
|
35421
|
+
bbox: fd.detection.bbox,
|
|
35422
|
+
...fd.detection.nativeRecovery !== void 0 ? { nativeRecoveryScore: fd.detection.nativeRecovery.score } : {},
|
|
35423
|
+
refusalCount,
|
|
35424
|
+
sampledEvery: 10
|
|
35425
|
+
});
|
|
35426
|
+
}
|
|
35427
|
+
}
|
|
35346
35428
|
process(input) {
|
|
35347
35429
|
const { timestamp, frame } = input;
|
|
35348
35430
|
const frameWidth = frame.width;
|
|
@@ -35428,7 +35510,8 @@ var FrameProcessor = class {
|
|
|
35428
35510
|
}
|
|
35429
35511
|
});
|
|
35430
35512
|
}
|
|
35431
|
-
const { passed } = this.zoneEngine.filterDetections(flatDetections, this.zones, this.detectionRules, frameWidth, frameHeight, (fd) => fd.detection.class);
|
|
35513
|
+
const { passed, excluded } = this.zoneEngine.filterDetections(flatDetections, this.zones, this.detectionRules, frameWidth, frameHeight, (fd) => fd.detection.class);
|
|
35514
|
+
this.reportZoneFiltered(excluded);
|
|
35432
35515
|
let filteredDetections = passed.map((fd) => fd.detection);
|
|
35433
35516
|
const riderPairs = pairRiders(filteredDetections.map((d, i) => ({
|
|
35434
35517
|
id: String(i),
|
|
@@ -74338,7 +74421,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
74338
74421
|
});
|
|
74339
74422
|
});
|
|
74340
74423
|
p.setSpawnRefusalObserver((record) => {
|
|
74341
|
-
this.ctx.logger.info("tracker: detection dropped —
|
|
74424
|
+
this.ctx.logger.info("tracker: detection dropped — nothing downstream received it", {
|
|
74342
74425
|
tags: { deviceId },
|
|
74343
74426
|
meta: {
|
|
74344
74427
|
source,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as audioModeOf, $t as EventCategory, A as NcSnoozeSchema, At as vectorDimFromBase64, B as SCENE_DEFAULT_UNCOVERED_POLICY, Bt as nodePin, C as NcConditionDescriptorSchema, Ct as readTimelapseGeneratedAt, D as NcRuleTargetSchema, Dt as storageOccupancyCapability, E as NcRuleSchema, Et as sceneMonitorCapability, Ft as CamProfileSchema, G as TimelapseRulePatchSchema, Gt as discriminatedUnion, H as SceneMonitorSchema, Ht as _enum, I as RECORDING_EXPORT_MAX_READ_BYTES, It as DeviceType, J as VISIT_MERGE_GAP_MS, Jt as object, K as TimelapseRuleSchema, Kt as literal, L as RetrainStatusSchema, Lt as createEvent, M as NcSystemEventKindSchema, Mt as zoneAnalyticsCapability, N as NcTaxonomySchema, Nt as errMsg$1, O as NcScheduleSchema, Ot as subKindsOf, P as OpsLogEntrySchema, Pt as BaseAddon, Q as audioMetricsCapability, Qt as unknown, Rt as hydrateSchema, S as NC_TAXONOMY, St as readDeviceStateFrom, T as NcRulePatchSchema, U as TIMELAPSE_DENSE_FLOOR_SEC, Ut as array, V as SCENE_DIVERGED, Vt as sleep$1, W as TimelapseRuleInputSchema, Wt as boolean, X as alarmPanelCapability, Xt as record, Y as addonWidgetsSourceCapability, Yt as partialRecord, Z as assertTimelapseCadences, Zt as string, _ as MediaFileKindEnum, _t as notificationRulesCapability, a as DEFAULT_EVENT_COLOR, b as NC_DEFAULT_SNOOZE_MINUTES, bt as pipelineAnalyticsCapability, c as DETECTION_MACRO_CLASSES, ct as evictionPolicyOfLocation, d as EVENT_KIND_BY_CAP, et as buildEventKindDescriptor, f as EVENT_PAD_MS, ft as isDetectionMacroClass, g as MACRO_LABELS, gt as mayWriteToLocation, h as LabelAttributionSchema, ht as kebabToCamel, i as COCO_TO_MACRO, it as deriveRecordingMode, j as NcSnoozeSuppressedSchema, jt as videoclipsCapability, k as NcSnoozeInputSchema, kt as systemEventFilterApplies, lt as faceGalleryCapability, m as FailureCounters, mt as isSourceCap, n as BaseDevice, nt as customAction, ot as encodeVectorBase64, p as FULL_IMAGE_BBOX, pt as isScheduleActive, q as TrackSourceSchema, qt as number, r as CLUSTER_MODEL_SCOPED_STEPS, rt as defineCustomActions, st as evaluateSensorEdge, t as AUDIO_MACRO_LABELS, tt as cosineSimilarity$1, u as DeclaredDevices, ut as failureContributionCapability, v as NC_ALARM_SYSTEM_EVENT_KINDS, w as NcRuleInputSchema, wt as resolveLocationMode, xt as plateGalleryCapability, y as NC_CONDITION_CATALOG, yt as pickClusterStepModels, z as SCENE_DEFAULT_ANCHOR_THRESHOLD, zt as isDeviceScopedCap } from "../dist-
|
|
1
|
+
import { $ as audioModeOf, $t as EventCategory, A as NcSnoozeSchema, At as vectorDimFromBase64, B as SCENE_DEFAULT_UNCOVERED_POLICY, Bt as nodePin, C as NcConditionDescriptorSchema, Ct as readTimelapseGeneratedAt, D as NcRuleTargetSchema, Dt as storageOccupancyCapability, E as NcRuleSchema, Et as sceneMonitorCapability, Ft as CamProfileSchema, G as TimelapseRulePatchSchema, Gt as discriminatedUnion, H as SceneMonitorSchema, Ht as _enum, I as RECORDING_EXPORT_MAX_READ_BYTES, It as DeviceType, J as VISIT_MERGE_GAP_MS, Jt as object, K as TimelapseRuleSchema, Kt as literal, L as RetrainStatusSchema, Lt as createEvent, M as NcSystemEventKindSchema, Mt as zoneAnalyticsCapability, N as NcTaxonomySchema, Nt as errMsg$1, O as NcScheduleSchema, Ot as subKindsOf, P as OpsLogEntrySchema, Pt as BaseAddon, Q as audioMetricsCapability, Qt as unknown, Rt as hydrateSchema, S as NC_TAXONOMY, St as readDeviceStateFrom, T as NcRulePatchSchema, U as TIMELAPSE_DENSE_FLOOR_SEC, Ut as array, V as SCENE_DIVERGED, Vt as sleep$1, W as TimelapseRuleInputSchema, Wt as boolean, X as alarmPanelCapability, Xt as record, Y as addonWidgetsSourceCapability, Yt as partialRecord, Z as assertTimelapseCadences, Zt as string, _ as MediaFileKindEnum, _t as notificationRulesCapability, a as DEFAULT_EVENT_COLOR, b as NC_DEFAULT_SNOOZE_MINUTES, bt as pipelineAnalyticsCapability, c as DETECTION_MACRO_CLASSES, ct as evictionPolicyOfLocation, d as EVENT_KIND_BY_CAP, et as buildEventKindDescriptor, f as EVENT_PAD_MS, ft as isDetectionMacroClass, g as MACRO_LABELS, gt as mayWriteToLocation, h as LabelAttributionSchema, ht as kebabToCamel, i as COCO_TO_MACRO, it as deriveRecordingMode, j as NcSnoozeSuppressedSchema, jt as videoclipsCapability, k as NcSnoozeInputSchema, kt as systemEventFilterApplies, lt as faceGalleryCapability, m as FailureCounters, mt as isSourceCap, n as BaseDevice, nt as customAction, ot as encodeVectorBase64, p as FULL_IMAGE_BBOX, pt as isScheduleActive, q as TrackSourceSchema, qt as number, r as CLUSTER_MODEL_SCOPED_STEPS, rt as defineCustomActions, st as evaluateSensorEdge, t as AUDIO_MACRO_LABELS, tt as cosineSimilarity$1, u as DeclaredDevices, ut as failureContributionCapability, v as NC_ALARM_SYSTEM_EVENT_KINDS, w as NcRuleInputSchema, wt as resolveLocationMode, xt as plateGalleryCapability, y as NC_CONDITION_CATALOG, yt as pickClusterStepModels, z as SCENE_DEFAULT_ANCHOR_THRESHOLD, zt as isDeviceScopedCap } from "../dist-1xB8qj2h.mjs";
|
|
2
2
|
import { t as __exportAll } from "../embedding-encoder/index.mjs";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import { promises } from "node:fs";
|
|
@@ -34036,6 +34036,22 @@ function pairRiders(detections) {
|
|
|
34036
34036
|
}
|
|
34037
34037
|
return pairs;
|
|
34038
34038
|
}
|
|
34039
|
+
/** Per-(stage, gate) counter + sampling decision. One instance per tracker, so
|
|
34040
|
+
* the counts are per camera and per detection source. */
|
|
34041
|
+
var LooseBindSampler = class {
|
|
34042
|
+
counts = /* @__PURE__ */ new Map();
|
|
34043
|
+
/** Increment the (stage, gate) counter and answer with the running total when
|
|
34044
|
+
* this decision should be reported, or `undefined` when it is sampled out. */
|
|
34045
|
+
next(stage, gate) {
|
|
34046
|
+
const key = `${stage}|${gate}`;
|
|
34047
|
+
const count = (this.counts.get(key) ?? 0) + 1;
|
|
34048
|
+
this.counts.set(key, count);
|
|
34049
|
+
if (count === 1 || count % 50 === 0) return count;
|
|
34050
|
+
}
|
|
34051
|
+
reset() {
|
|
34052
|
+
this.counts.clear();
|
|
34053
|
+
}
|
|
34054
|
+
};
|
|
34039
34055
|
//#endregion
|
|
34040
34056
|
//#region src/pipeline-analytics/pipeline/tracker/rescue-growth.ts
|
|
34041
34057
|
/**
|
|
@@ -34093,22 +34109,6 @@ function growthAdjacentMatch(track, det, ctx) {
|
|
|
34093
34109
|
const dc = bboxCentroid(det);
|
|
34094
34110
|
return Math.hypot(tc.x - dc.x, tc.y - dc.y) <= GROWTH_CENTROID_FACTOR * Math.hypot(track.w, track.h);
|
|
34095
34111
|
}
|
|
34096
|
-
/** Per-(stage, gate) counter + sampling decision. One instance per tracker, so
|
|
34097
|
-
* the counts are per camera and per detection source. */
|
|
34098
|
-
var LooseBindSampler = class {
|
|
34099
|
-
counts = /* @__PURE__ */ new Map();
|
|
34100
|
-
/** Increment the (stage, gate) counter and answer with the running total when
|
|
34101
|
-
* this decision should be reported, or `undefined` when it is sampled out. */
|
|
34102
|
-
next(stage, gate) {
|
|
34103
|
-
const key = `${stage}|${gate}`;
|
|
34104
|
-
const count = (this.counts.get(key) ?? 0) + 1;
|
|
34105
|
-
this.counts.set(key, count);
|
|
34106
|
-
if (count === 1 || count % 50 === 0) return count;
|
|
34107
|
-
}
|
|
34108
|
-
reset() {
|
|
34109
|
-
this.counts.clear();
|
|
34110
|
-
}
|
|
34111
|
-
};
|
|
34112
34112
|
/** Per-reason counter + sampling decision. One instance per tracker, so the
|
|
34113
34113
|
* counts are per camera and per detection source. */
|
|
34114
34114
|
var SpawnRefusalSampler = class {
|
|
@@ -34252,7 +34252,35 @@ var SortTracker = class SortTracker {
|
|
|
34252
34252
|
againstTrackClass: extra.against.class
|
|
34253
34253
|
} : {},
|
|
34254
34254
|
refusalCount,
|
|
34255
|
-
sampledEvery: 10
|
|
34255
|
+
sampledEvery: 10,
|
|
34256
|
+
...extra.droppedTrackId !== void 0 ? { droppedTrackId: extra.droppedTrackId } : {},
|
|
34257
|
+
...extra.trackLifeMs !== void 0 ? { trackLifeMs: extra.trackLifeMs } : {}
|
|
34258
|
+
});
|
|
34259
|
+
}
|
|
34260
|
+
/**
|
|
34261
|
+
* Report a TRACK that ends having emitted nothing — the last silent branch
|
|
34262
|
+
* on the detection→position path (D391).
|
|
34263
|
+
*
|
|
34264
|
+
* The subject is the track, not one box, so the record carries the track's
|
|
34265
|
+
* own last class/score/bbox: those ARE the detection that fed it last, and
|
|
34266
|
+
* they are what an operator matches against the recovery line that produced
|
|
34267
|
+
* it. Reported once (`lossReported`) and only where the track can no longer
|
|
34268
|
+
* come back — a graveyarded track may still resurrect, and a line about work
|
|
34269
|
+
* that was not lost is worse than no line.
|
|
34270
|
+
*/
|
|
34271
|
+
reportTrackLoss(track) {
|
|
34272
|
+
if (track.emitted || track.lossReported) return;
|
|
34273
|
+
track.lossReported = true;
|
|
34274
|
+
this.reportRefusal("unconfirmed-spawn-lost", {
|
|
34275
|
+
class: track.class,
|
|
34276
|
+
originalClass: track.originalClass,
|
|
34277
|
+
score: track.score,
|
|
34278
|
+
bbox: track.bbox
|
|
34279
|
+
}, {
|
|
34280
|
+
threshold: this.config.minHits,
|
|
34281
|
+
measured: track.hits,
|
|
34282
|
+
droppedTrackId: track.id,
|
|
34283
|
+
trackLifeMs: track.lastSeen - track.firstSeen
|
|
34256
34284
|
});
|
|
34257
34285
|
}
|
|
34258
34286
|
/** Emit one salvage decision, subject to {@link LOOSE_BIND_LOG_SAMPLE_EVERY}.
|
|
@@ -34447,7 +34475,22 @@ var SortTracker = class SortTracker {
|
|
|
34447
34475
|
}
|
|
34448
34476
|
const streak = (this.dupStreaks.get(key) ?? 0) + 1;
|
|
34449
34477
|
if (streak >= this.config.dedupMergeFrames) {
|
|
34450
|
-
|
|
34478
|
+
const loser = SortTracker.duplicateLoser(a, b);
|
|
34479
|
+
const winner = loser === a ? b : a;
|
|
34480
|
+
dropIds.add(loser.id);
|
|
34481
|
+
this.reportRefusal("merged-duplicate", {
|
|
34482
|
+
class: loser.class,
|
|
34483
|
+
originalClass: loser.originalClass,
|
|
34484
|
+
score: loser.score,
|
|
34485
|
+
bbox: loser.bbox
|
|
34486
|
+
}, {
|
|
34487
|
+
threshold: this.config.dedupMergeIou,
|
|
34488
|
+
measured: iou$3(a.bbox, b.bbox),
|
|
34489
|
+
against: winner,
|
|
34490
|
+
droppedTrackId: loser.id,
|
|
34491
|
+
trackLifeMs: loser.lastSeen - loser.firstSeen
|
|
34492
|
+
});
|
|
34493
|
+
loser.lossReported = true;
|
|
34451
34494
|
this.dupStreaks.delete(key);
|
|
34452
34495
|
} else this.dupStreaks.set(key, streak);
|
|
34453
34496
|
}
|
|
@@ -34629,6 +34672,7 @@ var SortTracker = class SortTracker {
|
|
|
34629
34672
|
track.lost = true;
|
|
34630
34673
|
track.lostAt = timestamp;
|
|
34631
34674
|
track.resurrectable = false;
|
|
34675
|
+
this.reportTrackLoss(track);
|
|
34632
34676
|
this.lostTracks.push(track);
|
|
34633
34677
|
} else alive.push(track);
|
|
34634
34678
|
this.tracks = alive;
|
|
@@ -34640,11 +34684,15 @@ var SortTracker = class SortTracker {
|
|
|
34640
34684
|
track.lost = true;
|
|
34641
34685
|
track.lostAt = track.lastSeen;
|
|
34642
34686
|
track.resurrectable = false;
|
|
34687
|
+
this.reportTrackLoss(track);
|
|
34643
34688
|
this.lostTracks.push(track);
|
|
34644
34689
|
} else alive.push(track);
|
|
34645
34690
|
this.tracks = alive;
|
|
34646
34691
|
}
|
|
34647
|
-
|
|
34692
|
+
const stillResurrectable = [];
|
|
34693
|
+
for (const t of this.lostTracks) if (timestamp - t.lostAt <= this.config.resurrectionWindowMs) stillResurrectable.push(t);
|
|
34694
|
+
else this.reportTrackLoss(t);
|
|
34695
|
+
this.lostTracks = stillResurrectable;
|
|
34648
34696
|
const used = /* @__PURE__ */ new Set();
|
|
34649
34697
|
const matchedTracks = /* @__PURE__ */ new Set();
|
|
34650
34698
|
const matched = /* @__PURE__ */ new Map();
|
|
@@ -34842,12 +34890,15 @@ var SortTracker = class SortTracker {
|
|
|
34842
34890
|
lost: false,
|
|
34843
34891
|
lostAt: 0,
|
|
34844
34892
|
resurrectable: true,
|
|
34845
|
-
classVotes: new Map([[det.class, det.score > 0 ? det.score : Number.EPSILON]])
|
|
34893
|
+
classVotes: new Map([[det.class, det.score > 0 ? det.score : Number.EPSILON]]),
|
|
34894
|
+
emitted: false,
|
|
34895
|
+
lossReported: false
|
|
34846
34896
|
});
|
|
34847
34897
|
}
|
|
34848
34898
|
this.tracks = surviving;
|
|
34849
34899
|
this.resolveDuplicates();
|
|
34850
34900
|
return this.tracks.filter((t) => this.isConfirmedForEmit(t)).map((t) => {
|
|
34901
|
+
t.emitted = true;
|
|
34851
34902
|
const scene = resurrectedScene.get(t);
|
|
34852
34903
|
return {
|
|
34853
34904
|
class: this.resolveReportedClass(t),
|
|
@@ -35215,6 +35266,10 @@ var FrameProcessor = class {
|
|
|
35215
35266
|
*/
|
|
35216
35267
|
reportedRefusalByTrack = /* @__PURE__ */ new Map();
|
|
35217
35268
|
zoneEngine = new ZoneEngine();
|
|
35269
|
+
/** Sink + sampler for the drops this stage owns (the detection-stage zone
|
|
35270
|
+
* filter). Shares the tracker's vocabulary — see {@link SpawnRefusalReason}. */
|
|
35271
|
+
spawnRefusalObserver = null;
|
|
35272
|
+
zoneRefusalSampler = new SpawnRefusalSampler();
|
|
35218
35273
|
/** Optional stationary-object gate (parked-object suppression). Null until
|
|
35219
35274
|
* the addon wires it via {@link setStationaryGate}. */
|
|
35220
35275
|
stationaryGate = null;
|
|
@@ -35309,6 +35364,7 @@ var FrameProcessor = class {
|
|
|
35309
35364
|
*/
|
|
35310
35365
|
setSpawnRefusalObserver(observer) {
|
|
35311
35366
|
this.tracker.setSpawnRefusalObserver(observer);
|
|
35367
|
+
this.spawnRefusalObserver = observer;
|
|
35312
35368
|
}
|
|
35313
35369
|
setBindObserver(observer) {
|
|
35314
35370
|
this.tracker.setBindObserver(observer);
|
|
@@ -35318,6 +35374,32 @@ var FrameProcessor = class {
|
|
|
35318
35374
|
dropTrack(trackId) {
|
|
35319
35375
|
this.tracker.dropTrack(trackId);
|
|
35320
35376
|
}
|
|
35377
|
+
/**
|
|
35378
|
+
* Say which detections a detection-stage zone rule excluded (D391).
|
|
35379
|
+
*
|
|
35380
|
+
* `filterDetections` has always ANSWERED this — it returns `excluded` — and
|
|
35381
|
+
* this stage dropped the answer on the floor, so a camera whose subject the
|
|
35382
|
+
* rules exclude was indistinguishable in the log from a camera whose tracker
|
|
35383
|
+
* ate it. Sampled like every other refusal: an excluded box is excluded on
|
|
35384
|
+
* every frame of a passage.
|
|
35385
|
+
*/
|
|
35386
|
+
reportZoneFiltered(excluded) {
|
|
35387
|
+
const observer = this.spawnRefusalObserver;
|
|
35388
|
+
if (!observer || excluded.length === 0) return;
|
|
35389
|
+
for (const fd of excluded) {
|
|
35390
|
+
const refusalCount = this.zoneRefusalSampler.next("zone-filtered");
|
|
35391
|
+
if (refusalCount === void 0) continue;
|
|
35392
|
+
observer({
|
|
35393
|
+
reason: "zone-filtered",
|
|
35394
|
+
detClass: fd.detection.class,
|
|
35395
|
+
detScore: fd.detection.score,
|
|
35396
|
+
bbox: fd.detection.bbox,
|
|
35397
|
+
...fd.detection.nativeRecovery !== void 0 ? { nativeRecoveryScore: fd.detection.nativeRecovery.score } : {},
|
|
35398
|
+
refusalCount,
|
|
35399
|
+
sampledEvery: 10
|
|
35400
|
+
});
|
|
35401
|
+
}
|
|
35402
|
+
}
|
|
35321
35403
|
process(input) {
|
|
35322
35404
|
const { timestamp, frame } = input;
|
|
35323
35405
|
const frameWidth = frame.width;
|
|
@@ -35403,7 +35485,8 @@ var FrameProcessor = class {
|
|
|
35403
35485
|
}
|
|
35404
35486
|
});
|
|
35405
35487
|
}
|
|
35406
|
-
const { passed } = this.zoneEngine.filterDetections(flatDetections, this.zones, this.detectionRules, frameWidth, frameHeight, (fd) => fd.detection.class);
|
|
35488
|
+
const { passed, excluded } = this.zoneEngine.filterDetections(flatDetections, this.zones, this.detectionRules, frameWidth, frameHeight, (fd) => fd.detection.class);
|
|
35489
|
+
this.reportZoneFiltered(excluded);
|
|
35407
35490
|
let filteredDetections = passed.map((fd) => fd.detection);
|
|
35408
35491
|
const riderPairs = pairRiders(filteredDetections.map((d, i) => ({
|
|
35409
35492
|
id: String(i),
|
|
@@ -74264,7 +74347,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
74264
74347
|
});
|
|
74265
74348
|
});
|
|
74266
74349
|
p.setSpawnRefusalObserver((record) => {
|
|
74267
|
-
this.ctx.logger.info("tracker: detection dropped —
|
|
74350
|
+
this.ctx.logger.info("tracker: detection dropped — nothing downstream received it", {
|
|
74268
74351
|
tags: { deviceId },
|
|
74269
74352
|
meta: {
|
|
74270
74353
|
source,
|
|
@@ -30,7 +30,7 @@ async function d(e) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
async function f() {
|
|
33
|
-
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-
|
|
33
|
+
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-DwFi68p5.mjs")).catch((e) => {
|
|
34
34
|
throw l = void 0, e;
|
|
35
35
|
}), l;
|
|
36
36
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-post-analysis",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.201",
|
|
4
4
|
"description": "Post-Analysis bundle — enrichment, embedding-encoder, pipeline-analytics. Multi-entry npm package shipping addons that consume pipeline output.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|