@camstack/addon-post-analysis 1.1.22 → 1.1.24
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-DbgDsnRE.js → dist-B0VgBdBN.js} +573 -251
- package/dist/{dist-DiIUcFdN.mjs → dist-DRK78NMq.mjs} +573 -251
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/enrichment-engine/index.js +28 -16
- package/dist/enrichment-engine/index.mjs +27 -15
- package/dist/{node-CdzrYoKs.js → node-Cx0LM0Sq.js} +1 -1
- package/dist/pipeline-analytics/_stub.js +1 -1
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-BaXPYx2C.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-BsRL9Bh-.mjs} +3 -3
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DtOrjE2U.mjs +26 -0
- package/dist/pipeline-analytics/{hostInit-CZ8URdDr.mjs → hostInit-sO1Drmtf.mjs} +3 -3
- package/dist/pipeline-analytics/index.js +424 -166
- package/dist/pipeline-analytics/index.mjs +422 -164
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/dist/{resolve-frame-nQMd6Z8B.js → resolve-frame-DBXwF5fk.js} +1 -1
- package/package.json +1 -1
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BEL-L4wR.mjs +0 -26
|
@@ -2,8 +2,8 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
6
|
-
const require_resolve_frame = require("../resolve-frame-
|
|
5
|
+
const require_dist = require("../dist-B0VgBdBN.js");
|
|
6
|
+
const require_resolve_frame = require("../resolve-frame-DBXwF5fk.js");
|
|
7
7
|
let _camstack_shm_ring = require("@camstack/shm-ring");
|
|
8
8
|
let sharp = require("sharp");
|
|
9
9
|
sharp = require_dist.__toESM(sharp);
|
|
@@ -699,17 +699,24 @@ function normalizeToPixel(p, width, height) {
|
|
|
699
699
|
//#region src/pipeline-analytics/pipeline/tracker/sort-tracker.ts
|
|
700
700
|
var DEFAULT_TRACKER_CONFIG = {
|
|
701
701
|
iouThreshold: .3,
|
|
702
|
-
|
|
702
|
+
maxMissedMs: 4e3,
|
|
703
703
|
minHits: 3,
|
|
704
704
|
predictiveCoasting: true,
|
|
705
705
|
classGating: true,
|
|
706
706
|
occlusionEnabled: true,
|
|
707
707
|
occlusionContainment: .6,
|
|
708
|
-
|
|
709
|
-
maxTrackLifetimeMs: 3e5
|
|
708
|
+
occlusionMaxMissedMs: 8e3,
|
|
709
|
+
maxTrackLifetimeMs: 3e5,
|
|
710
|
+
rescueIouThreshold: .1,
|
|
711
|
+
rescueCentroidFactor: .75,
|
|
712
|
+
resurrectionWindowMs: 8e3,
|
|
713
|
+
stationarySpeedPx: 2
|
|
710
714
|
};
|
|
711
715
|
var MAX_PATH_LENGTH = 300;
|
|
712
716
|
var nextTrackId = 1;
|
|
717
|
+
function clamp(value, min, max) {
|
|
718
|
+
return Math.max(min, Math.min(max, value));
|
|
719
|
+
}
|
|
713
720
|
function iou$1(a, b) {
|
|
714
721
|
const ax1 = a.x, ay1 = a.y, ax2 = a.x + a.w, ay2 = a.y + a.h;
|
|
715
722
|
const bx1 = b.x, by1 = b.y, bx2 = b.x + b.w, by2 = b.y + b.h;
|
|
@@ -739,26 +746,51 @@ var SortTracker = class {
|
|
|
739
746
|
};
|
|
740
747
|
}
|
|
741
748
|
/** Where a track is expected this frame — extrapolated by velocity while
|
|
742
|
-
* coasting (predictiveCoasting), else its last known bbox.
|
|
749
|
+
* coasting (predictiveCoasting), else its last known bbox. A stationary
|
|
750
|
+
* track (speed < stationarySpeedPx) is frozen so bbox jitter can't walk the
|
|
751
|
+
* box off a sitting object; otherwise the drift is capped at one bbox size
|
|
752
|
+
* so a stale velocity can't fling the box across the frame. Public so the
|
|
753
|
+
* clamp is directly unit-testable. */
|
|
743
754
|
predicted(t) {
|
|
744
755
|
if (!this.config.predictiveCoasting) return t.bbox;
|
|
756
|
+
if (Math.hypot(t.velocity.dx, t.velocity.dy) < this.config.stationarySpeedPx) return t.bbox;
|
|
745
757
|
const f = t.age + 1;
|
|
758
|
+
const driftX = clamp(t.velocity.dx * f, -t.bbox.w, t.bbox.w);
|
|
759
|
+
const driftY = clamp(t.velocity.dy * f, -t.bbox.h, t.bbox.h);
|
|
746
760
|
return {
|
|
747
|
-
x: t.bbox.x +
|
|
748
|
-
y: t.bbox.y +
|
|
761
|
+
x: t.bbox.x + driftX,
|
|
762
|
+
y: t.bbox.y + driftY,
|
|
749
763
|
w: t.bbox.w,
|
|
750
764
|
h: t.bbox.h
|
|
751
765
|
};
|
|
752
766
|
}
|
|
767
|
+
/**
|
|
768
|
+
* Loose same-class gate used by the rescue and resurrection passes: accept
|
|
769
|
+
* when the detection overlaps the track's LAST-KNOWN bbox by `rescueIou`, or
|
|
770
|
+
* its centroid is within `rescueCentroidFactor × bbox-diagonal`. Always class-
|
|
771
|
+
* gated (a rescue must never cross classes), independent of `classGating`.
|
|
772
|
+
*/
|
|
773
|
+
looseMatch(track, det) {
|
|
774
|
+
if (track.class !== det.class) return false;
|
|
775
|
+
if (iou$1(track.bbox, det.bbox) >= this.config.rescueIouThreshold) return true;
|
|
776
|
+
const tc = bboxCentroid(track.bbox);
|
|
777
|
+
const dc = bboxCentroid(det.bbox);
|
|
778
|
+
const dist = Math.hypot(tc.x - dc.x, tc.y - dc.y);
|
|
779
|
+
const diag = Math.hypot(track.bbox.w, track.bbox.h);
|
|
780
|
+
return dist <= this.config.rescueCentroidFactor * diag;
|
|
781
|
+
}
|
|
753
782
|
update(detections, timestamp) {
|
|
754
783
|
if (this.config.maxTrackLifetimeMs > 0) {
|
|
755
784
|
const alive = [];
|
|
756
785
|
for (const track of this.tracks) if (timestamp - track.firstSeen > this.config.maxTrackLifetimeMs) {
|
|
757
786
|
track.lost = true;
|
|
787
|
+
track.lostAt = timestamp;
|
|
788
|
+
track.resurrectable = false;
|
|
758
789
|
this.lostTracks.push(track);
|
|
759
790
|
} else alive.push(track);
|
|
760
791
|
this.tracks = alive;
|
|
761
792
|
}
|
|
793
|
+
this.lostTracks = this.lostTracks.filter((t) => timestamp - t.lostAt <= this.config.resurrectionWindowMs);
|
|
762
794
|
const used = /* @__PURE__ */ new Set();
|
|
763
795
|
const matchedTracks = /* @__PURE__ */ new Set();
|
|
764
796
|
const matched = /* @__PURE__ */ new Map();
|
|
@@ -783,6 +815,27 @@ var SortTracker = class {
|
|
|
783
815
|
matchedTracks.add(pair.track);
|
|
784
816
|
used.add(pair.detIdx);
|
|
785
817
|
}
|
|
818
|
+
const rescuePairs = [];
|
|
819
|
+
for (const track of this.tracks) {
|
|
820
|
+
if (matchedTracks.has(track)) continue;
|
|
821
|
+
for (let di = 0; di < detections.length; di++) {
|
|
822
|
+
if (used.has(di)) continue;
|
|
823
|
+
const det = detections[di];
|
|
824
|
+
if (!this.looseMatch(track, det)) continue;
|
|
825
|
+
rescuePairs.push({
|
|
826
|
+
track,
|
|
827
|
+
detIdx: di,
|
|
828
|
+
score: iou$1(track.bbox, det.bbox)
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
rescuePairs.sort((a, b) => b.score - a.score);
|
|
833
|
+
for (const pair of rescuePairs) {
|
|
834
|
+
if (matchedTracks.has(pair.track) || used.has(pair.detIdx)) continue;
|
|
835
|
+
matched.set(pair.track, detections[pair.detIdx]);
|
|
836
|
+
matchedTracks.add(pair.track);
|
|
837
|
+
used.add(pair.detIdx);
|
|
838
|
+
}
|
|
786
839
|
for (const [track, det] of matched) {
|
|
787
840
|
const prevCenter = bboxCentroid({
|
|
788
841
|
x: track.bbox.x,
|
|
@@ -821,12 +874,47 @@ var SortTracker = class {
|
|
|
821
874
|
}
|
|
822
875
|
const occluded = this.config.occlusionEnabled && occluderBoxes.some((ob) => containment(this.predicted(track), ob) >= this.config.occlusionContainment);
|
|
823
876
|
track.age++;
|
|
824
|
-
|
|
825
|
-
if (track.age > limit) {
|
|
877
|
+
if (timestamp - track.lastSeen > (occluded ? this.config.occlusionMaxMissedMs : this.config.maxMissedMs)) {
|
|
826
878
|
track.lost = true;
|
|
879
|
+
track.lostAt = timestamp;
|
|
880
|
+
track.resurrectable = true;
|
|
827
881
|
this.lostTracks.push(track);
|
|
828
882
|
} else surviving.push(track);
|
|
829
883
|
}
|
|
884
|
+
for (let di = 0; di < detections.length; di++) {
|
|
885
|
+
if (used.has(di)) continue;
|
|
886
|
+
const det = detections[di];
|
|
887
|
+
let best;
|
|
888
|
+
let bestScore = -1;
|
|
889
|
+
for (const lost of this.lostTracks) {
|
|
890
|
+
if (!lost.resurrectable) continue;
|
|
891
|
+
if (timestamp - lost.lostAt > this.config.resurrectionWindowMs) continue;
|
|
892
|
+
if (!this.looseMatch(lost, det)) continue;
|
|
893
|
+
const score = iou$1(lost.bbox, det.bbox);
|
|
894
|
+
if (score > bestScore) {
|
|
895
|
+
best = lost;
|
|
896
|
+
bestScore = score;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
if (!best) continue;
|
|
900
|
+
this.lostTracks = this.lostTracks.filter((t) => t !== best);
|
|
901
|
+
best.bbox = det.bbox;
|
|
902
|
+
best.class = det.class;
|
|
903
|
+
best.originalClass = det.originalClass;
|
|
904
|
+
best.score = det.score;
|
|
905
|
+
best.age = 0;
|
|
906
|
+
best.hits++;
|
|
907
|
+
best.lost = false;
|
|
908
|
+
best.lastSeen = timestamp;
|
|
909
|
+
best.velocity = {
|
|
910
|
+
dx: 0,
|
|
911
|
+
dy: 0
|
|
912
|
+
};
|
|
913
|
+
best.path.push(det.bbox);
|
|
914
|
+
if (best.path.length > MAX_PATH_LENGTH) best.path.shift();
|
|
915
|
+
surviving.push(best);
|
|
916
|
+
used.add(di);
|
|
917
|
+
}
|
|
830
918
|
for (let di = 0; di < detections.length; di++) {
|
|
831
919
|
if (used.has(di)) continue;
|
|
832
920
|
const det = detections[di];
|
|
@@ -846,7 +934,9 @@ var SortTracker = class {
|
|
|
846
934
|
dx: 0,
|
|
847
935
|
dy: 0
|
|
848
936
|
},
|
|
849
|
-
lost: false
|
|
937
|
+
lost: false,
|
|
938
|
+
lostAt: 0,
|
|
939
|
+
resurrectable: true
|
|
850
940
|
});
|
|
851
941
|
}
|
|
852
942
|
this.tracks = surviving;
|
|
@@ -961,6 +1051,7 @@ var StateAnalyzer = class {
|
|
|
961
1051
|
//#region src/pipeline-analytics/pipeline/events/event-filter.ts
|
|
962
1052
|
var DEFAULT_EVENT_EMITTER_CONFIG = {
|
|
963
1053
|
minTrackAge: 3,
|
|
1054
|
+
minTrackAgeMs: 0,
|
|
964
1055
|
cooldownSec: 5,
|
|
965
1056
|
enabledTypes: [
|
|
966
1057
|
"object.entering",
|
|
@@ -991,6 +1082,13 @@ var DetectionEventEmitter = class {
|
|
|
991
1082
|
config;
|
|
992
1083
|
previousStates = /* @__PURE__ */ new Map();
|
|
993
1084
|
lastEmitted = /* @__PURE__ */ new Map();
|
|
1085
|
+
/**
|
|
1086
|
+
* Last-known track per id. A `leaving` state fires for a track that is ABSENT
|
|
1087
|
+
* from the current frame, so it can't be resolved from `trackMap` — it is
|
|
1088
|
+
* resolved from this snapshot instead (without it, leaving events never
|
|
1089
|
+
* fired: the latent "0 `left` events in 24h" bug).
|
|
1090
|
+
*/
|
|
1091
|
+
lastKnownTracks = /* @__PURE__ */ new Map();
|
|
994
1092
|
constructor(config = {}) {
|
|
995
1093
|
this.config = {
|
|
996
1094
|
...DEFAULT_EVENT_EMITTER_CONFIG,
|
|
@@ -1002,10 +1100,12 @@ var DetectionEventEmitter = class {
|
|
|
1002
1100
|
const now = Date.now();
|
|
1003
1101
|
const stateMap = new Map(states.map((s) => [s.trackId, s]));
|
|
1004
1102
|
const trackMap = new Map(tracks.map((t) => [t.trackId, t]));
|
|
1103
|
+
for (const t of tracks) this.lastKnownTracks.set(t.trackId, t);
|
|
1005
1104
|
for (const state of states) {
|
|
1006
|
-
const track = trackMap.get(state.trackId);
|
|
1105
|
+
const track = trackMap.get(state.trackId) ?? this.lastKnownTracks.get(state.trackId);
|
|
1007
1106
|
if (!track) continue;
|
|
1008
1107
|
if (track.trackAge < this.config.minTrackAge) continue;
|
|
1108
|
+
if (state.dwellTimeMs < this.config.minTrackAgeMs) continue;
|
|
1009
1109
|
const eventType = STATE_TO_EVENT[state.state];
|
|
1010
1110
|
if (!eventType) continue;
|
|
1011
1111
|
if (!this.config.enabledTypes.includes(eventType)) continue;
|
|
@@ -1051,12 +1151,16 @@ var DetectionEventEmitter = class {
|
|
|
1051
1151
|
trackPath: [...track.path]
|
|
1052
1152
|
});
|
|
1053
1153
|
}
|
|
1054
|
-
for (const state of states) if (state.state === "leaving")
|
|
1154
|
+
for (const state of states) if (state.state === "leaving") {
|
|
1155
|
+
this.previousStates.delete(state.trackId);
|
|
1156
|
+
this.lastKnownTracks.delete(state.trackId);
|
|
1157
|
+
}
|
|
1055
1158
|
return events;
|
|
1056
1159
|
}
|
|
1057
1160
|
reset() {
|
|
1058
1161
|
this.previousStates.clear();
|
|
1059
1162
|
this.lastEmitted.clear();
|
|
1163
|
+
this.lastKnownTracks.clear();
|
|
1060
1164
|
}
|
|
1061
1165
|
};
|
|
1062
1166
|
//#endregion
|
|
@@ -1717,10 +1821,13 @@ var TrackStore = class {
|
|
|
1717
1821
|
try {
|
|
1718
1822
|
await this.persistCompleted(record);
|
|
1719
1823
|
} catch (err) {
|
|
1720
|
-
this.logger.warn("persist completed track failed", {
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1824
|
+
this.logger.warn("persist completed track failed", {
|
|
1825
|
+
tags: { deviceId: t.deviceId },
|
|
1826
|
+
meta: {
|
|
1827
|
+
trackId,
|
|
1828
|
+
error: String(err)
|
|
1829
|
+
}
|
|
1830
|
+
});
|
|
1724
1831
|
}
|
|
1725
1832
|
this.active.delete(trackId);
|
|
1726
1833
|
expired.push(record);
|
|
@@ -1947,10 +2054,13 @@ var MediaStore = class {
|
|
|
1947
2054
|
});
|
|
1948
2055
|
return key;
|
|
1949
2056
|
} catch (err) {
|
|
1950
|
-
this.logger.warn("media put failed", {
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
2057
|
+
this.logger.warn("media put failed", {
|
|
2058
|
+
tags: { deviceId: params.deviceId },
|
|
2059
|
+
meta: {
|
|
2060
|
+
key,
|
|
2061
|
+
error: String(err)
|
|
2062
|
+
}
|
|
2063
|
+
});
|
|
1954
2064
|
throw err;
|
|
1955
2065
|
}
|
|
1956
2066
|
}
|
|
@@ -2139,11 +2249,14 @@ var MediaStore = class {
|
|
|
2139
2249
|
relativePath: oldPath
|
|
2140
2250
|
});
|
|
2141
2251
|
} catch (err) {
|
|
2142
|
-
this.logger.warn("MediaStore.reown: old blob delete failed (best-effort)", {
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2252
|
+
this.logger.warn("MediaStore.reown: old blob delete failed (best-effort)", {
|
|
2253
|
+
tags: { deviceId },
|
|
2254
|
+
meta: {
|
|
2255
|
+
key,
|
|
2256
|
+
oldPath,
|
|
2257
|
+
error: String(err)
|
|
2258
|
+
}
|
|
2259
|
+
});
|
|
2147
2260
|
}
|
|
2148
2261
|
try {
|
|
2149
2262
|
await this.store.delete.mutate({
|
|
@@ -2151,10 +2264,13 @@ var MediaStore = class {
|
|
|
2151
2264
|
key
|
|
2152
2265
|
});
|
|
2153
2266
|
} catch (err) {
|
|
2154
|
-
this.logger.warn("MediaStore.reown: old index row delete failed (best-effort)", {
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2267
|
+
this.logger.warn("MediaStore.reown: old index row delete failed (best-effort)", {
|
|
2268
|
+
tags: { deviceId },
|
|
2269
|
+
meta: {
|
|
2270
|
+
key,
|
|
2271
|
+
error: String(err)
|
|
2272
|
+
}
|
|
2273
|
+
});
|
|
2158
2274
|
}
|
|
2159
2275
|
return newKey;
|
|
2160
2276
|
}
|
|
@@ -2355,10 +2471,13 @@ var EventStore = class {
|
|
|
2355
2471
|
}
|
|
2356
2472
|
});
|
|
2357
2473
|
} catch (err) {
|
|
2358
|
-
this.logger.warn("insertMotion failed", {
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2474
|
+
this.logger.warn("insertMotion failed", {
|
|
2475
|
+
tags: { deviceId: ev.deviceId },
|
|
2476
|
+
meta: {
|
|
2477
|
+
eventId: ev.id,
|
|
2478
|
+
error: String(err)
|
|
2479
|
+
}
|
|
2480
|
+
});
|
|
2362
2481
|
}
|
|
2363
2482
|
}
|
|
2364
2483
|
async insertObject(ev) {
|
|
@@ -2372,10 +2491,13 @@ var EventStore = class {
|
|
|
2372
2491
|
}
|
|
2373
2492
|
});
|
|
2374
2493
|
} catch (err) {
|
|
2375
|
-
this.logger.warn("insertObject failed", {
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2494
|
+
this.logger.warn("insertObject failed", {
|
|
2495
|
+
tags: { deviceId: ev.deviceId },
|
|
2496
|
+
meta: {
|
|
2497
|
+
eventId: ev.id,
|
|
2498
|
+
error: String(err)
|
|
2499
|
+
}
|
|
2500
|
+
});
|
|
2379
2501
|
}
|
|
2380
2502
|
}
|
|
2381
2503
|
async insertAudio(ev) {
|
|
@@ -2389,10 +2511,13 @@ var EventStore = class {
|
|
|
2389
2511
|
}
|
|
2390
2512
|
});
|
|
2391
2513
|
} catch (err) {
|
|
2392
|
-
this.logger.warn("insertAudio failed", {
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2514
|
+
this.logger.warn("insertAudio failed", {
|
|
2515
|
+
tags: { deviceId: ev.deviceId },
|
|
2516
|
+
meta: {
|
|
2517
|
+
eventId: ev.id,
|
|
2518
|
+
error: String(err)
|
|
2519
|
+
}
|
|
2520
|
+
});
|
|
2396
2521
|
}
|
|
2397
2522
|
}
|
|
2398
2523
|
buildFilter(q) {
|
|
@@ -2861,25 +2986,34 @@ var EventMediaDispatcher = class {
|
|
|
2861
2986
|
getRemoteFrame: this.deps.getRemoteFrame
|
|
2862
2987
|
});
|
|
2863
2988
|
} catch (err) {
|
|
2864
|
-
this.deps.logger.debug("event media: resolveFrame threw", {
|
|
2865
|
-
deviceId,
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2989
|
+
this.deps.logger.debug("event media: resolveFrame threw", {
|
|
2990
|
+
tags: { deviceId },
|
|
2991
|
+
meta: {
|
|
2992
|
+
deviceId,
|
|
2993
|
+
shmId: frameHandle.shmId,
|
|
2994
|
+
error: String(err)
|
|
2995
|
+
}
|
|
2996
|
+
});
|
|
2869
2997
|
return;
|
|
2870
2998
|
}
|
|
2871
2999
|
if (!decoded) {
|
|
2872
|
-
this.deps.logger.debug("event media: frame recycled before resolve", {
|
|
2873
|
-
deviceId,
|
|
2874
|
-
|
|
2875
|
-
|
|
3000
|
+
this.deps.logger.debug("event media: frame recycled before resolve", {
|
|
3001
|
+
tags: { deviceId },
|
|
3002
|
+
meta: {
|
|
3003
|
+
deviceId,
|
|
3004
|
+
shmId: frameHandle.shmId
|
|
3005
|
+
}
|
|
3006
|
+
});
|
|
2876
3007
|
return;
|
|
2877
3008
|
}
|
|
2878
3009
|
if (decoded.format !== "rgb") {
|
|
2879
|
-
this.deps.logger.debug("event media: resolved frame is not RGB", {
|
|
2880
|
-
deviceId,
|
|
2881
|
-
|
|
2882
|
-
|
|
3010
|
+
this.deps.logger.debug("event media: resolved frame is not RGB", {
|
|
3011
|
+
tags: { deviceId },
|
|
3012
|
+
meta: {
|
|
3013
|
+
deviceId,
|
|
3014
|
+
format: decoded.format
|
|
3015
|
+
}
|
|
3016
|
+
});
|
|
2883
3017
|
return;
|
|
2884
3018
|
}
|
|
2885
3019
|
const frameData = Buffer.isBuffer(decoded.data) ? decoded.data : Buffer.from(decoded.data);
|
|
@@ -2921,11 +3055,14 @@ var EventMediaDispatcher = class {
|
|
|
2921
3055
|
data: crop
|
|
2922
3056
|
});
|
|
2923
3057
|
} catch (err) {
|
|
2924
|
-
this.deps.logger.warn("event media: crop failed", {
|
|
2925
|
-
deviceId,
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
3058
|
+
this.deps.logger.warn("event media: crop failed", {
|
|
3059
|
+
tags: { deviceId },
|
|
3060
|
+
meta: {
|
|
3061
|
+
deviceId,
|
|
3062
|
+
eventId: ev.eventId,
|
|
3063
|
+
error: err instanceof Error ? err.message : String(err)
|
|
3064
|
+
}
|
|
3065
|
+
});
|
|
2929
3066
|
}
|
|
2930
3067
|
try {
|
|
2931
3068
|
const fullFrame = await drawBoxedFrame(frameData, fw, fh, [], { quality: MEDIA_QUALITY });
|
|
@@ -2938,11 +3075,14 @@ var EventMediaDispatcher = class {
|
|
|
2938
3075
|
data: fullFrame
|
|
2939
3076
|
});
|
|
2940
3077
|
} catch (err) {
|
|
2941
|
-
this.deps.logger.warn("event media: clear full frame failed", {
|
|
2942
|
-
deviceId,
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
3078
|
+
this.deps.logger.warn("event media: clear full frame failed", {
|
|
3079
|
+
tags: { deviceId },
|
|
3080
|
+
meta: {
|
|
3081
|
+
deviceId,
|
|
3082
|
+
eventId: ev.eventId,
|
|
3083
|
+
error: err instanceof Error ? err.message : String(err)
|
|
3084
|
+
}
|
|
3085
|
+
});
|
|
2946
3086
|
}
|
|
2947
3087
|
try {
|
|
2948
3088
|
const fullFrameBoxed = await drawBoxedFrame(frameData, fw, fh, [box], { quality: MEDIA_QUALITY });
|
|
@@ -2955,11 +3095,14 @@ var EventMediaDispatcher = class {
|
|
|
2955
3095
|
data: fullFrameBoxed
|
|
2956
3096
|
});
|
|
2957
3097
|
} catch (err) {
|
|
2958
|
-
this.deps.logger.warn("event media: boxed full frame failed", {
|
|
2959
|
-
deviceId,
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
3098
|
+
this.deps.logger.warn("event media: boxed full frame failed", {
|
|
3099
|
+
tags: { deviceId },
|
|
3100
|
+
meta: {
|
|
3101
|
+
deviceId,
|
|
3102
|
+
eventId: ev.eventId,
|
|
3103
|
+
error: err instanceof Error ? err.message : String(err)
|
|
3104
|
+
}
|
|
3105
|
+
});
|
|
2963
3106
|
}
|
|
2964
3107
|
if (ev.childCrops) for (const child of ev.childCrops) try {
|
|
2965
3108
|
const childRegion = squareSafeCropRegion(child.bbox, {
|
|
@@ -2989,11 +3132,14 @@ var EventMediaDispatcher = class {
|
|
|
2989
3132
|
data: childCropData
|
|
2990
3133
|
});
|
|
2991
3134
|
} catch (err) {
|
|
2992
|
-
this.deps.logger.warn(`event media: ${child.kind} failed`, {
|
|
2993
|
-
deviceId,
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
3135
|
+
this.deps.logger.warn(`event media: ${child.kind} failed`, {
|
|
3136
|
+
tags: { deviceId },
|
|
3137
|
+
meta: {
|
|
3138
|
+
deviceId,
|
|
3139
|
+
eventId: ev.eventId,
|
|
3140
|
+
error: err instanceof Error ? err.message : String(err)
|
|
3141
|
+
}
|
|
3142
|
+
});
|
|
2997
3143
|
}
|
|
2998
3144
|
}
|
|
2999
3145
|
async writeTrackFrame(deviceId, frameData, fw, fh, tf) {
|
|
@@ -3012,11 +3158,14 @@ var EventMediaDispatcher = class {
|
|
|
3012
3158
|
data: boxed
|
|
3013
3159
|
});
|
|
3014
3160
|
} catch (err) {
|
|
3015
|
-
this.deps.logger.warn("event media: track frame failed", {
|
|
3016
|
-
deviceId,
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3161
|
+
this.deps.logger.warn("event media: track frame failed", {
|
|
3162
|
+
tags: { deviceId },
|
|
3163
|
+
meta: {
|
|
3164
|
+
deviceId,
|
|
3165
|
+
trackId: tf.trackId,
|
|
3166
|
+
error: err instanceof Error ? err.message : String(err)
|
|
3167
|
+
}
|
|
3168
|
+
});
|
|
3020
3169
|
}
|
|
3021
3170
|
}
|
|
3022
3171
|
};
|
|
@@ -3732,22 +3881,22 @@ function resolveDetectionSensitivitySettings(raw) {
|
|
|
3732
3881
|
stationaryThresholdSec: pick("stationaryThresholdSec")
|
|
3733
3882
|
};
|
|
3734
3883
|
}
|
|
3735
|
-
//#endregion
|
|
3736
|
-
//#region src/pipeline-analytics/tracking-settings.ts
|
|
3737
|
-
/**
|
|
3738
|
-
* Per-device tracker tuning. These drive the SORT tracker (association,
|
|
3739
|
-
* coasting, occlusion, lifetime) and the detector-dropout frame skip. Every
|
|
3740
|
-
* field is independently overridable per camera from the admin UI; unknown or
|
|
3741
|
-
* invalid values fall back to the field default (never throws on a bad blob).
|
|
3742
|
-
*
|
|
3743
|
-
* Note: `minHits` lives in detection-sensitivity-settings (it predates this
|
|
3744
|
-
* file and already flows to the tracker) — it is NOT duplicated here.
|
|
3745
|
-
*/
|
|
3746
3884
|
var TrackingSettingsSchema = require_dist.object({
|
|
3747
3885
|
/** IoU required to match a (predicted) track to a detection. */
|
|
3748
3886
|
iouThreshold: require_dist.number().min(0).max(1).default(.3),
|
|
3749
|
-
/**
|
|
3887
|
+
/** Wall-clock coasting budget (ms) before a missed track is dropped. Time-
|
|
3888
|
+
* based so behaviour is identical across camera frame rates. */
|
|
3889
|
+
maxMissedMs: require_dist.number().min(0).default(4e3),
|
|
3890
|
+
/** Minimum wall-clock track age (ms) before it may produce events. */
|
|
3891
|
+
minTrackAgeMs: require_dist.number().min(0).default(1e3),
|
|
3892
|
+
/** Extended coasting budget (ms) while a track is occluded. */
|
|
3893
|
+
occlusionMaxMissedMs: require_dist.number().min(0).default(8e3),
|
|
3894
|
+
/** @deprecated frame-based alias for {@link maxMissedMs}. Retained so
|
|
3895
|
+
* existing per-device blobs keep working — resolved into `maxMissedMs`
|
|
3896
|
+
* (× {@link NOMINAL_FRAME_MS}) only when `maxMissedMs` is unset. */
|
|
3750
3897
|
maxMissedFrames: require_dist.number().int().min(0).default(30),
|
|
3898
|
+
/** @deprecated frame-based alias for {@link occlusionMaxMissedMs}. */
|
|
3899
|
+
occlusionMaxMissedFrames: require_dist.number().int().min(0).default(60),
|
|
3751
3900
|
/** Hard cap on total track lifetime — retire+restart beyond this to bound
|
|
3752
3901
|
* drift / long-lived false positives. 0 = unlimited. */
|
|
3753
3902
|
maxTrackLifetimeSec: require_dist.number().min(0).default(300),
|
|
@@ -3761,8 +3910,17 @@ var TrackingSettingsSchema = require_dist.object({
|
|
|
3761
3910
|
/** Containment ratio (intersection / occluded-area) above which a missed
|
|
3762
3911
|
* track is considered hidden behind another track. */
|
|
3763
3912
|
occlusionContainment: require_dist.number().min(0).max(1).default(.6),
|
|
3764
|
-
/**
|
|
3765
|
-
|
|
3913
|
+
/** Loose IoU gate (vs. last-known bbox) for the second (rescue) association
|
|
3914
|
+
* pass + graveyard resurrection. */
|
|
3915
|
+
rescueIouThreshold: require_dist.number().min(0).max(1).default(.1),
|
|
3916
|
+
/** Centroid gate for rescue/resurrection: fraction of the bbox diagonal. */
|
|
3917
|
+
rescueCentroidFactor: require_dist.number().min(0).default(.75),
|
|
3918
|
+
/** Wall-clock graveyard retention (ms): a dropped track re-attaches to a new
|
|
3919
|
+
* same-class detection (keeping its id) for this long. */
|
|
3920
|
+
resurrectionWindowMs: require_dist.number().min(0).default(8e3),
|
|
3921
|
+
/** Speed (px/frame) below which a track's prediction is frozen (stationary
|
|
3922
|
+
* jitter can't drift the box off a sitting object). */
|
|
3923
|
+
stationarySpeedPx: require_dist.number().min(0).default(2),
|
|
3766
3924
|
/** Skip frames where detections suddenly drop to zero (detector glitch). */
|
|
3767
3925
|
dropoutSkipEnabled: require_dist.boolean().default(true),
|
|
3768
3926
|
/** Max consecutive all-zero frames absorbed as a glitch before the scene is
|
|
@@ -3777,15 +3935,26 @@ var TRACKING_DEFAULTS = TrackingSettingsSchema.parse({});
|
|
|
3777
3935
|
*/
|
|
3778
3936
|
function resolveTrackingSettings(raw) {
|
|
3779
3937
|
const s = TrackingSettingsSchema.shape;
|
|
3938
|
+
const maxMissedFrames = s.maxMissedFrames.catch(TRACKING_DEFAULTS.maxMissedFrames).parse(raw.maxMissedFrames);
|
|
3939
|
+
const occlusionMaxMissedFrames = s.occlusionMaxMissedFrames.catch(TRACKING_DEFAULTS.occlusionMaxMissedFrames).parse(raw.occlusionMaxMissedFrames);
|
|
3940
|
+
const maxMissedMs = raw.maxMissedMs !== void 0 ? s.maxMissedMs.catch(TRACKING_DEFAULTS.maxMissedMs).parse(raw.maxMissedMs) : raw.maxMissedFrames !== void 0 ? Math.round(maxMissedFrames * 133) : TRACKING_DEFAULTS.maxMissedMs;
|
|
3941
|
+
const occlusionMaxMissedMs = raw.occlusionMaxMissedMs !== void 0 ? s.occlusionMaxMissedMs.catch(TRACKING_DEFAULTS.occlusionMaxMissedMs).parse(raw.occlusionMaxMissedMs) : raw.occlusionMaxMissedFrames !== void 0 ? Math.round(occlusionMaxMissedFrames * 133) : TRACKING_DEFAULTS.occlusionMaxMissedMs;
|
|
3780
3942
|
return {
|
|
3781
3943
|
iouThreshold: s.iouThreshold.catch(TRACKING_DEFAULTS.iouThreshold).parse(raw.iouThreshold),
|
|
3782
|
-
|
|
3944
|
+
maxMissedMs,
|
|
3945
|
+
minTrackAgeMs: s.minTrackAgeMs.catch(TRACKING_DEFAULTS.minTrackAgeMs).parse(raw.minTrackAgeMs),
|
|
3946
|
+
occlusionMaxMissedMs,
|
|
3947
|
+
maxMissedFrames,
|
|
3948
|
+
occlusionMaxMissedFrames,
|
|
3783
3949
|
maxTrackLifetimeSec: s.maxTrackLifetimeSec.catch(TRACKING_DEFAULTS.maxTrackLifetimeSec).parse(raw.maxTrackLifetimeSec),
|
|
3784
3950
|
predictiveCoasting: s.predictiveCoasting.catch(TRACKING_DEFAULTS.predictiveCoasting).parse(raw.predictiveCoasting),
|
|
3785
3951
|
classGating: s.classGating.catch(TRACKING_DEFAULTS.classGating).parse(raw.classGating),
|
|
3786
3952
|
occlusionEnabled: s.occlusionEnabled.catch(TRACKING_DEFAULTS.occlusionEnabled).parse(raw.occlusionEnabled),
|
|
3787
3953
|
occlusionContainment: s.occlusionContainment.catch(TRACKING_DEFAULTS.occlusionContainment).parse(raw.occlusionContainment),
|
|
3788
|
-
|
|
3954
|
+
rescueIouThreshold: s.rescueIouThreshold.catch(TRACKING_DEFAULTS.rescueIouThreshold).parse(raw.rescueIouThreshold),
|
|
3955
|
+
rescueCentroidFactor: s.rescueCentroidFactor.catch(TRACKING_DEFAULTS.rescueCentroidFactor).parse(raw.rescueCentroidFactor),
|
|
3956
|
+
resurrectionWindowMs: s.resurrectionWindowMs.catch(TRACKING_DEFAULTS.resurrectionWindowMs).parse(raw.resurrectionWindowMs),
|
|
3957
|
+
stationarySpeedPx: s.stationarySpeedPx.catch(TRACKING_DEFAULTS.stationarySpeedPx).parse(raw.stationarySpeedPx),
|
|
3789
3958
|
dropoutSkipEnabled: s.dropoutSkipEnabled.catch(TRACKING_DEFAULTS.dropoutSkipEnabled).parse(raw.dropoutSkipEnabled),
|
|
3790
3959
|
dropoutMaxSkipFrames: s.dropoutMaxSkipFrames.catch(TRACKING_DEFAULTS.dropoutMaxSkipFrames).parse(raw.dropoutMaxSkipFrames)
|
|
3791
3960
|
};
|
|
@@ -4228,10 +4397,13 @@ var FaceStore = class {
|
|
|
4228
4397
|
value: rest
|
|
4229
4398
|
});
|
|
4230
4399
|
} catch (err) {
|
|
4231
|
-
this.logger.warn("FaceStore.insert failed", {
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4400
|
+
this.logger.warn("FaceStore.insert failed", {
|
|
4401
|
+
tags: { deviceId: face.deviceId },
|
|
4402
|
+
meta: {
|
|
4403
|
+
faceId: id,
|
|
4404
|
+
error: String(err)
|
|
4405
|
+
}
|
|
4406
|
+
});
|
|
4235
4407
|
}
|
|
4236
4408
|
}
|
|
4237
4409
|
/**
|
|
@@ -4298,10 +4470,13 @@ var FaceStore = class {
|
|
|
4298
4470
|
});
|
|
4299
4471
|
deleted.push(id);
|
|
4300
4472
|
} catch (err) {
|
|
4301
|
-
this.logger.warn("FaceStore.prune delete failed", {
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4473
|
+
this.logger.warn("FaceStore.prune delete failed", {
|
|
4474
|
+
tags: { deviceId: input.deviceId },
|
|
4475
|
+
meta: {
|
|
4476
|
+
faceId: id,
|
|
4477
|
+
error: String(err)
|
|
4478
|
+
}
|
|
4479
|
+
});
|
|
4305
4480
|
}
|
|
4306
4481
|
return deleted;
|
|
4307
4482
|
}
|
|
@@ -4587,10 +4762,13 @@ var ObjectEmbeddingStore = class {
|
|
|
4587
4762
|
value: record
|
|
4588
4763
|
});
|
|
4589
4764
|
} catch (err) {
|
|
4590
|
-
this.logger.warn("ObjectEmbeddingStore.upsertIfBetter failed", {
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4765
|
+
this.logger.warn("ObjectEmbeddingStore.upsertIfBetter failed", {
|
|
4766
|
+
tags: { deviceId: input.deviceId },
|
|
4767
|
+
meta: {
|
|
4768
|
+
trackId: input.trackId,
|
|
4769
|
+
error: String(err)
|
|
4770
|
+
}
|
|
4771
|
+
});
|
|
4594
4772
|
}
|
|
4595
4773
|
}
|
|
4596
4774
|
/**
|
|
@@ -4802,18 +4980,24 @@ var FaceRecognizer = class {
|
|
|
4802
4980
|
try {
|
|
4803
4981
|
await this.deps.trackStore.setLabel(c.trackId, name);
|
|
4804
4982
|
} catch (err) {
|
|
4805
|
-
this.deps.logger.warn("setLabel failed", {
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4983
|
+
this.deps.logger.warn("setLabel failed", {
|
|
4984
|
+
tags: { deviceId: input.deviceId },
|
|
4985
|
+
meta: {
|
|
4986
|
+
trackId: c.trackId,
|
|
4987
|
+
error: String(err)
|
|
4988
|
+
}
|
|
4989
|
+
});
|
|
4809
4990
|
}
|
|
4810
4991
|
try {
|
|
4811
4992
|
await this.deps.eventStore.setLabelForTrack(c.trackId, name);
|
|
4812
4993
|
} catch (err) {
|
|
4813
|
-
this.deps.logger.warn("setLabelForTrack failed", {
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4994
|
+
this.deps.logger.warn("setLabelForTrack failed", {
|
|
4995
|
+
tags: { deviceId: input.deviceId },
|
|
4996
|
+
meta: {
|
|
4997
|
+
trackId: c.trackId,
|
|
4998
|
+
error: String(err)
|
|
4999
|
+
}
|
|
5000
|
+
});
|
|
4817
5001
|
}
|
|
4818
5002
|
}
|
|
4819
5003
|
}
|
|
@@ -4825,10 +5009,13 @@ var FaceRecognizer = class {
|
|
|
4825
5009
|
if (input.frameHandle !== void 0) try {
|
|
4826
5010
|
crop = await this.deps.captureCrop(input.frameHandle, cropBbox, input.frameWidth, input.frameHeight, input.cropPadding) ?? void 0;
|
|
4827
5011
|
} catch (err) {
|
|
4828
|
-
this.deps.logger.debug("FaceRecognizer crop capture failed", {
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
5012
|
+
this.deps.logger.debug("FaceRecognizer crop capture failed", {
|
|
5013
|
+
tags: { deviceId: input.deviceId },
|
|
5014
|
+
meta: {
|
|
5015
|
+
trackId: c.trackId,
|
|
5016
|
+
error: String(err)
|
|
5017
|
+
}
|
|
5018
|
+
});
|
|
4832
5019
|
}
|
|
4833
5020
|
this.bestFace.set(c.trackId, {
|
|
4834
5021
|
score: c.confidence,
|
|
@@ -4884,10 +5071,13 @@ var FaceRecognizer = class {
|
|
|
4884
5071
|
data: held.crop
|
|
4885
5072
|
});
|
|
4886
5073
|
} catch (err) {
|
|
4887
|
-
this.deps.logger.warn("FaceRecognizer face crop put failed", {
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
5074
|
+
this.deps.logger.warn("FaceRecognizer face crop put failed", {
|
|
5075
|
+
tags: { deviceId },
|
|
5076
|
+
meta: {
|
|
5077
|
+
faceId,
|
|
5078
|
+
error: String(err)
|
|
5079
|
+
}
|
|
5080
|
+
});
|
|
4891
5081
|
}
|
|
4892
5082
|
try {
|
|
4893
5083
|
await this.deps.faceStore.insert({
|
|
@@ -4913,10 +5103,13 @@ var FaceRecognizer = class {
|
|
|
4913
5103
|
}
|
|
4914
5104
|
});
|
|
4915
5105
|
} catch (err) {
|
|
4916
|
-
this.deps.logger.warn("FaceRecognizer faceStore insert failed", {
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
5106
|
+
this.deps.logger.warn("FaceRecognizer faceStore insert failed", {
|
|
5107
|
+
tags: { deviceId },
|
|
5108
|
+
meta: {
|
|
5109
|
+
faceId,
|
|
5110
|
+
error: String(err)
|
|
5111
|
+
}
|
|
5112
|
+
});
|
|
4920
5113
|
}
|
|
4921
5114
|
}
|
|
4922
5115
|
};
|
|
@@ -4996,10 +5189,13 @@ var PlateStore = class {
|
|
|
4996
5189
|
value: rest
|
|
4997
5190
|
});
|
|
4998
5191
|
} catch (err) {
|
|
4999
|
-
this.logger.warn("PlateStore.insert failed", {
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5192
|
+
this.logger.warn("PlateStore.insert failed", {
|
|
5193
|
+
tags: { deviceId: plate.deviceId },
|
|
5194
|
+
meta: {
|
|
5195
|
+
plateId: id,
|
|
5196
|
+
error: String(err)
|
|
5197
|
+
}
|
|
5198
|
+
});
|
|
5003
5199
|
}
|
|
5004
5200
|
}
|
|
5005
5201
|
normalizeRow(r) {
|
|
@@ -5155,10 +5351,13 @@ var PlateRecognizer = class {
|
|
|
5155
5351
|
if (input.frameHandle !== void 0) try {
|
|
5156
5352
|
crop = await this.deps.captureCrop(input.frameHandle, c.plateBbox, input.frameWidth, input.frameHeight, input.cropPadding) ?? void 0;
|
|
5157
5353
|
} catch (err) {
|
|
5158
|
-
this.deps.logger.debug("PlateRecognizer crop capture failed", {
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5354
|
+
this.deps.logger.debug("PlateRecognizer crop capture failed", {
|
|
5355
|
+
tags: { deviceId: input.deviceId },
|
|
5356
|
+
meta: {
|
|
5357
|
+
trackId: c.trackId,
|
|
5358
|
+
error: String(err)
|
|
5359
|
+
}
|
|
5360
|
+
});
|
|
5162
5361
|
}
|
|
5163
5362
|
this.bestPlate.set(c.trackId, {
|
|
5164
5363
|
text: c.plateText,
|
|
@@ -5187,10 +5386,13 @@ var PlateRecognizer = class {
|
|
|
5187
5386
|
data: held.crop
|
|
5188
5387
|
});
|
|
5189
5388
|
} catch (err) {
|
|
5190
|
-
this.deps.logger.warn("PlateRecognizer plate crop put failed", {
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5389
|
+
this.deps.logger.warn("PlateRecognizer plate crop put failed", {
|
|
5390
|
+
tags: { deviceId },
|
|
5391
|
+
meta: {
|
|
5392
|
+
plateId,
|
|
5393
|
+
error: String(err)
|
|
5394
|
+
}
|
|
5395
|
+
});
|
|
5194
5396
|
}
|
|
5195
5397
|
try {
|
|
5196
5398
|
await this.deps.plateStore.insert({
|
|
@@ -5216,10 +5418,13 @@ var PlateRecognizer = class {
|
|
|
5216
5418
|
}
|
|
5217
5419
|
});
|
|
5218
5420
|
} catch (err) {
|
|
5219
|
-
this.deps.logger.warn("PlateRecognizer plateStore insert failed", {
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5421
|
+
this.deps.logger.warn("PlateRecognizer plateStore insert failed", {
|
|
5422
|
+
tags: { deviceId },
|
|
5423
|
+
meta: {
|
|
5424
|
+
plateId,
|
|
5425
|
+
error: String(err)
|
|
5426
|
+
}
|
|
5427
|
+
});
|
|
5223
5428
|
}
|
|
5224
5429
|
}
|
|
5225
5430
|
};
|
|
@@ -5666,7 +5871,7 @@ var PipelineAnalyticsAddon = class extends require_dist.BaseAddon {
|
|
|
5666
5871
|
let storage = this.ctx.kernel.storage;
|
|
5667
5872
|
const mediaRoot = process.env.CAMSTACK_MEDIA_ROOT?.trim();
|
|
5668
5873
|
if (mediaRoot) {
|
|
5669
|
-
const { FilesystemStorageProvider } = await Promise.resolve().then(() => require("../node-
|
|
5874
|
+
const { FilesystemStorageProvider } = await Promise.resolve().then(() => require("../node-Cx0LM0Sq.js"));
|
|
5670
5875
|
storage = new FilesystemStorageProvider(mediaRoot);
|
|
5671
5876
|
logger.info("pipeline-analytics: event media rooted at CAMSTACK_MEDIA_ROOT", { meta: { mediaRoot } });
|
|
5672
5877
|
}
|
|
@@ -6530,7 +6735,10 @@ var PipelineAnalyticsAddon = class extends require_dist.BaseAddon {
|
|
|
6530
6735
|
} : void 0, { classificationMinScore: settings.classificationMinScore });
|
|
6531
6736
|
if (route.kind === "classification") {
|
|
6532
6737
|
if (!topClassification) {
|
|
6533
|
-
this.ctx.logger.warn("classifyAudioFrame returned classification but topClassification is null", {
|
|
6738
|
+
this.ctx.logger.warn("classifyAudioFrame returned classification but topClassification is null", {
|
|
6739
|
+
tags: { deviceId },
|
|
6740
|
+
meta: { deviceId }
|
|
6741
|
+
});
|
|
6534
6742
|
return;
|
|
6535
6743
|
}
|
|
6536
6744
|
const last = this.lastAudioInsertByDevice.get(deviceId);
|
|
@@ -6891,16 +7099,21 @@ var PipelineAnalyticsAddon = class extends require_dist.BaseAddon {
|
|
|
6891
7099
|
p = new FrameProcessor(deviceId, {
|
|
6892
7100
|
minHits,
|
|
6893
7101
|
iouThreshold: trk.iouThreshold,
|
|
6894
|
-
|
|
7102
|
+
maxMissedMs: trk.maxMissedMs,
|
|
6895
7103
|
predictiveCoasting: trk.predictiveCoasting,
|
|
6896
7104
|
classGating: trk.classGating,
|
|
6897
7105
|
occlusionEnabled: trk.occlusionEnabled,
|
|
6898
7106
|
occlusionContainment: trk.occlusionContainment,
|
|
6899
|
-
|
|
7107
|
+
occlusionMaxMissedMs: trk.occlusionMaxMissedMs,
|
|
7108
|
+
rescueIouThreshold: trk.rescueIouThreshold,
|
|
7109
|
+
rescueCentroidFactor: trk.rescueCentroidFactor,
|
|
7110
|
+
resurrectionWindowMs: trk.resurrectionWindowMs,
|
|
7111
|
+
stationarySpeedPx: trk.stationarySpeedPx,
|
|
6900
7112
|
maxTrackLifetimeMs: trk.maxTrackLifetimeSec * 1e3
|
|
6901
7113
|
}, { stationaryThresholdSec }, {
|
|
6902
7114
|
minTrackAge,
|
|
6903
|
-
cooldownSec
|
|
7115
|
+
cooldownSec,
|
|
7116
|
+
minTrackAgeMs: trk.minTrackAgeMs
|
|
6904
7117
|
}, source);
|
|
6905
7118
|
this.processors.set(key, p);
|
|
6906
7119
|
}
|
|
@@ -7080,13 +7293,16 @@ var PipelineAnalyticsAddon = class extends require_dist.BaseAddon {
|
|
|
7080
7293
|
const { counts, ids } = await this.eventStore.pruneBefore(input);
|
|
7081
7294
|
if (ids.length > 0 && this.mediaStore) await this.mediaStore.deleteForEvents([...ids]);
|
|
7082
7295
|
const { motion, object, audio } = counts;
|
|
7083
|
-
if (motion + object + audio > 0) this.ctx.logger.info("analytics event eviction (floor)", {
|
|
7084
|
-
deviceId: input.deviceId,
|
|
7085
|
-
|
|
7086
|
-
|
|
7087
|
-
|
|
7088
|
-
|
|
7089
|
-
|
|
7296
|
+
if (motion + object + audio > 0) this.ctx.logger.info("analytics event eviction (floor)", {
|
|
7297
|
+
tags: { deviceId: input.deviceId },
|
|
7298
|
+
meta: {
|
|
7299
|
+
deviceId: input.deviceId,
|
|
7300
|
+
cutoffMs: input.cutoffMs,
|
|
7301
|
+
motion,
|
|
7302
|
+
object,
|
|
7303
|
+
audio
|
|
7304
|
+
}
|
|
7305
|
+
});
|
|
7090
7306
|
return counts;
|
|
7091
7307
|
}
|
|
7092
7308
|
/**
|
|
@@ -7458,12 +7674,53 @@ var PipelineAnalyticsAddon = class extends require_dist.BaseAddon {
|
|
|
7458
7674
|
},
|
|
7459
7675
|
{
|
|
7460
7676
|
type: "number",
|
|
7461
|
-
key: "
|
|
7462
|
-
label: "Max
|
|
7463
|
-
description: "
|
|
7677
|
+
key: "maxMissedMs",
|
|
7678
|
+
label: "Max coasting time",
|
|
7679
|
+
description: "How long (ms) a track may miss detections before it is dropped. Time-based so it behaves the same on a 5fps and a 23fps camera.",
|
|
7464
7680
|
min: 0,
|
|
7465
|
-
step:
|
|
7466
|
-
default: TRACKING_DEFAULTS.
|
|
7681
|
+
step: 100,
|
|
7682
|
+
default: TRACKING_DEFAULTS.maxMissedMs,
|
|
7683
|
+
unit: "ms"
|
|
7684
|
+
},
|
|
7685
|
+
{
|
|
7686
|
+
type: "number",
|
|
7687
|
+
key: "minTrackAgeMs",
|
|
7688
|
+
label: "Min track age",
|
|
7689
|
+
description: "A track must exist for at least this long (ms) before it can produce events — stops sub-second flicker fragments from becoming events.",
|
|
7690
|
+
min: 0,
|
|
7691
|
+
step: 100,
|
|
7692
|
+
default: TRACKING_DEFAULTS.minTrackAgeMs,
|
|
7693
|
+
unit: "ms"
|
|
7694
|
+
},
|
|
7695
|
+
{
|
|
7696
|
+
type: "number",
|
|
7697
|
+
key: "resurrectionWindowMs",
|
|
7698
|
+
label: "Resurrection window",
|
|
7699
|
+
description: "How long (ms) a dropped track can be re-attached to a re-appearing same-class detection (keeping its id) — the main fix for a stationary object fragmenting into many tracks.",
|
|
7700
|
+
min: 0,
|
|
7701
|
+
step: 500,
|
|
7702
|
+
default: TRACKING_DEFAULTS.resurrectionWindowMs,
|
|
7703
|
+
unit: "ms"
|
|
7704
|
+
},
|
|
7705
|
+
{
|
|
7706
|
+
type: "number",
|
|
7707
|
+
key: "rescueIouThreshold",
|
|
7708
|
+
label: "Rescue IoU threshold",
|
|
7709
|
+
description: "Looser overlap (vs. a track’s last-known box) for the second association + resurrection passes. Lower = more aggressive re-attachment.",
|
|
7710
|
+
min: 0,
|
|
7711
|
+
max: 1,
|
|
7712
|
+
step: .05,
|
|
7713
|
+
default: TRACKING_DEFAULTS.rescueIouThreshold
|
|
7714
|
+
},
|
|
7715
|
+
{
|
|
7716
|
+
type: "number",
|
|
7717
|
+
key: "stationarySpeedPx",
|
|
7718
|
+
label: "Stationary speed",
|
|
7719
|
+
description: "Speed (px/frame) below which a track is treated as stationary and its prediction is frozen, so bbox jitter cannot walk the box off a sitting object.",
|
|
7720
|
+
min: 0,
|
|
7721
|
+
step: .5,
|
|
7722
|
+
default: TRACKING_DEFAULTS.stationarySpeedPx,
|
|
7723
|
+
unit: "px"
|
|
7467
7724
|
},
|
|
7468
7725
|
{
|
|
7469
7726
|
type: "number",
|
|
@@ -7508,12 +7765,13 @@ var PipelineAnalyticsAddon = class extends require_dist.BaseAddon {
|
|
|
7508
7765
|
},
|
|
7509
7766
|
{
|
|
7510
7767
|
type: "number",
|
|
7511
|
-
key: "
|
|
7512
|
-
label: "Occlusion max
|
|
7513
|
-
description: "Extended coasting
|
|
7768
|
+
key: "occlusionMaxMissedMs",
|
|
7769
|
+
label: "Occlusion max coasting time",
|
|
7770
|
+
description: "Extended coasting time (ms) while a track is hidden behind another.",
|
|
7514
7771
|
min: 0,
|
|
7515
|
-
step:
|
|
7516
|
-
default: TRACKING_DEFAULTS.
|
|
7772
|
+
step: 100,
|
|
7773
|
+
default: TRACKING_DEFAULTS.occlusionMaxMissedMs,
|
|
7774
|
+
unit: "ms"
|
|
7517
7775
|
},
|
|
7518
7776
|
{
|
|
7519
7777
|
type: "boolean",
|