@camstack/addon-post-analysis 1.2.269 → 1.2.271
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-B78I61U1.js → dist-DTeAk1Ce.js} +101 -7
- package/dist/{dist-ABnG0S3f.mjs → dist-K6zDULiD.mjs} +101 -7
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/pipeline-analytics/_stub.js +1 -1
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-D42gEB0t.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-Bs8m-J-O.mjs} +2 -2
- package/dist/pipeline-analytics/{_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-C8ZDqFq3.mjs → _virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-B0sS9pCe.mjs} +1 -1
- package/dist/pipeline-analytics/{hostInit-fxXc6Mk3.mjs → hostInit-jMBJBSqj.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +395 -77
- package/dist/pipeline-analytics/index.mjs +395 -77
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
- package/python/raw_tensor_inference.py +16 -1
- package/python/session_threads.py +33 -0
- package/python/test_session_threads.py +65 -0
- package/python/text_encoder_inference.py +15 -1
|
@@ -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-DTeAk1Ce.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");
|
|
@@ -30055,10 +30055,31 @@ var MediaStore = class {
|
|
|
30055
30055
|
* was simply never returned, which is why every retention ops-log row this
|
|
30056
30056
|
* addon has ever written says `bytesReclaimed: 0` (D161).
|
|
30057
30057
|
*/
|
|
30058
|
-
async deleteForOwnerCounted(ownerKind, ownerIds) {
|
|
30059
|
-
return this.gated(() => this.deleteForOwnerUnchecked(ownerKind, ownerIds));
|
|
30058
|
+
async deleteForOwnerCounted(ownerKind, ownerIds, spare) {
|
|
30059
|
+
return this.gated(() => this.deleteForOwnerUnchecked(ownerKind, ownerIds, spare));
|
|
30060
30060
|
}
|
|
30061
|
-
|
|
30061
|
+
/**
|
|
30062
|
+
* Who owns a media row — kind and id — or `null` for an unknown id. The
|
|
30063
|
+
* stationary registry asks this before releasing an entry's key frame
|
|
30064
|
+
* (D547): the row is still `track`-owned by a track whose row is gone, and
|
|
30065
|
+
* only when no track row owns it is the entry the last thing vouching for it.
|
|
30066
|
+
*/
|
|
30067
|
+
async ownerOf(mediaId) {
|
|
30068
|
+
const row = await this.store.get.query({
|
|
30069
|
+
collection: MEDIA_COLLECTION,
|
|
30070
|
+
key: String(mediaId)
|
|
30071
|
+
});
|
|
30072
|
+
if (!row) return null;
|
|
30073
|
+
const data = row;
|
|
30074
|
+
const ownerKind = String(data["ownerKind"] ?? "");
|
|
30075
|
+
const ownerId = String(data["ownerId"] ?? "");
|
|
30076
|
+
if (!isMediaOwnerKind(ownerKind) || ownerId === "") return null;
|
|
30077
|
+
return {
|
|
30078
|
+
ownerKind,
|
|
30079
|
+
ownerId
|
|
30080
|
+
};
|
|
30081
|
+
}
|
|
30082
|
+
async deleteForOwnerUnchecked(ownerKind, ownerIds, spare) {
|
|
30062
30083
|
let rows = 0;
|
|
30063
30084
|
let bytes = 0;
|
|
30064
30085
|
for (const ownerId of ownerIds) {
|
|
@@ -30070,6 +30091,7 @@ var MediaStore = class {
|
|
|
30070
30091
|
} }
|
|
30071
30092
|
});
|
|
30072
30093
|
for (const row of found) {
|
|
30094
|
+
if (spare !== void 0 && spare.has(Number(row.id))) continue;
|
|
30073
30095
|
const rowData = row.data;
|
|
30074
30096
|
const path = String(rowData["path"] ?? "");
|
|
30075
30097
|
const sizeBytes = Number(rowData["sizeBytes"]);
|
|
@@ -30388,16 +30410,17 @@ var MediaStore = class {
|
|
|
30388
30410
|
* `deleteForOwner` is best-effort per row, so one bad row can't abort the
|
|
30389
30411
|
* batch.
|
|
30390
30412
|
*/
|
|
30391
|
-
async deleteByTracks(trackIds) {
|
|
30413
|
+
async deleteByTracks(trackIds, opts) {
|
|
30414
|
+
const spare = opts?.spare;
|
|
30392
30415
|
const own = await this.gated(async () => {
|
|
30393
|
-
const tracks = await this.deleteForOwnerUnchecked("track", trackIds);
|
|
30394
|
-
const faces = await this.deleteForOwnerUnchecked("face", trackIds.map((id) => FACE_MEDIA_OWNER_PREFIX + id));
|
|
30416
|
+
const tracks = await this.deleteForOwnerUnchecked("track", trackIds, spare);
|
|
30417
|
+
const faces = await this.deleteForOwnerUnchecked("face", trackIds.map((id) => FACE_MEDIA_OWNER_PREFIX + id), spare);
|
|
30395
30418
|
return {
|
|
30396
30419
|
rows: tracks.rows + faces.rows,
|
|
30397
30420
|
bytes: tracks.bytes + faces.bytes
|
|
30398
30421
|
};
|
|
30399
30422
|
});
|
|
30400
|
-
const plates = await this.deleteForOwnerCounted("plate", trackIds.map((id) => PLATE_MEDIA_OWNER_PREFIX + id));
|
|
30423
|
+
const plates = await this.deleteForOwnerCounted("plate", trackIds.map((id) => PLATE_MEDIA_OWNER_PREFIX + id), spare);
|
|
30401
30424
|
return [{
|
|
30402
30425
|
family: "media",
|
|
30403
30426
|
rows: own.rows + plates.rows,
|
|
@@ -37179,6 +37202,14 @@ var SpawnRefusalSampler = class {
|
|
|
37179
37202
|
return count % 10 === 0 ? count : void 0;
|
|
37180
37203
|
}
|
|
37181
37204
|
};
|
|
37205
|
+
function shouldFreezePrediction(input) {
|
|
37206
|
+
const { speedPxPerMs, emitIntervalMs, boxDiagonalPx, stationarySpeedPx } = input;
|
|
37207
|
+
const calibratedBar = input.stillSpeedFracPerSec;
|
|
37208
|
+
const optedIn = stationarySpeedPx === 0;
|
|
37209
|
+
if (optedIn && calibratedBar !== void 0 && calibratedBar > 0 && boxDiagonalPx > 0) return speedPxPerMs * 1e3 / boxDiagonalPx < calibratedBar;
|
|
37210
|
+
const bar = optedIn ? 2 : stationarySpeedPx;
|
|
37211
|
+
return speedPxPerMs * emitIntervalMs < bar;
|
|
37212
|
+
}
|
|
37182
37213
|
//#endregion
|
|
37183
37214
|
//#region src/pipeline-analytics/pipeline/tracker/sort-tracker.ts
|
|
37184
37215
|
var DEFAULT_TRACKER_CONFIG = {
|
|
@@ -37774,9 +37805,10 @@ var SortTracker = class SortTracker {
|
|
|
37774
37805
|
* Where a track is expected at `nowMs` — extrapolated by velocity while
|
|
37775
37806
|
* coasting (`predictiveCoasting`), else its last known bbox.
|
|
37776
37807
|
*
|
|
37777
|
-
* A stationary track
|
|
37778
|
-
*
|
|
37779
|
-
*
|
|
37808
|
+
* A stationary track is frozen so bbox jitter cannot walk the box off a
|
|
37809
|
+
* sitting object. WHICH bar decides — the legacy px-per-update one or this
|
|
37810
|
+
* camera's calibrated diagonals-per-second — is `shouldFreezePrediction`'s
|
|
37811
|
+
* question, not this method's. Otherwise the drift is the
|
|
37780
37812
|
* velocity carried over the ELAPSED interval, capped by
|
|
37781
37813
|
* {@link driftAllowancePx} — which is itself a function of that interval, so
|
|
37782
37814
|
* a stale velocity cannot fling the box across the frame and a long, honest
|
|
@@ -37785,7 +37817,13 @@ var SortTracker = class SortTracker {
|
|
|
37785
37817
|
*/
|
|
37786
37818
|
predicted(t, nowMs) {
|
|
37787
37819
|
if (!this.config.predictiveCoasting) return t.bbox;
|
|
37788
|
-
if (
|
|
37820
|
+
if (shouldFreezePrediction({
|
|
37821
|
+
speedPxPerMs: Math.hypot(t.velocity.dx, t.velocity.dy),
|
|
37822
|
+
emitIntervalMs: t.emitIntervalMs,
|
|
37823
|
+
boxDiagonalPx: Math.hypot(t.bbox.w, t.bbox.h),
|
|
37824
|
+
stationarySpeedPx: this.config.stationarySpeedPx,
|
|
37825
|
+
...this.config.stillSpeedFracPerSec !== void 0 ? { stillSpeedFracPerSec: this.config.stillSpeedFracPerSec } : {}
|
|
37826
|
+
})) return t.bbox;
|
|
37789
37827
|
if (!this.config.reacquireTimeScaled) {
|
|
37790
37828
|
const f = t.age + 1;
|
|
37791
37829
|
const perFrameX = t.velocity.dx * t.emitIntervalMs;
|
|
@@ -39371,7 +39409,9 @@ var FrameProcessor = class {
|
|
|
39371
39409
|
const refusals = [];
|
|
39372
39410
|
const labelPatchByTrack = /* @__PURE__ */ new Map();
|
|
39373
39411
|
const tracked = trackedDetections.map((td) => {
|
|
39374
|
-
const
|
|
39412
|
+
const objectState = objectStates.find((o) => o.trackId === td.trackId);
|
|
39413
|
+
const state = mapObjectStateToTrackState(objectState?.state);
|
|
39414
|
+
const settling = objectState?.stationarySince !== void 0 && state !== "idle";
|
|
39375
39415
|
const candidate = resolveDetectionLabelCandidate({
|
|
39376
39416
|
className: td.class,
|
|
39377
39417
|
originalClass: td.originalClass,
|
|
@@ -39409,6 +39449,7 @@ var FrameProcessor = class {
|
|
|
39409
39449
|
zones: zonesByTrack.get(td.trackId) ?? [],
|
|
39410
39450
|
state,
|
|
39411
39451
|
matchedThisFrame: td.matchedThisFrame !== false,
|
|
39452
|
+
settling,
|
|
39412
39453
|
...td.resurrectionScene !== void 0 ? { resurrectionScene: td.resurrectionScene } : {},
|
|
39413
39454
|
...labelPatch !== void 0 ? { labelPatch } : {},
|
|
39414
39455
|
...emb !== void 0 ? {
|
|
@@ -40404,8 +40445,16 @@ var TrackCloser = class {
|
|
|
40404
40445
|
try {
|
|
40405
40446
|
const trackStore = this.deps.trackStore();
|
|
40406
40447
|
if (!trackStore) return;
|
|
40407
|
-
const
|
|
40448
|
+
const discarded = [];
|
|
40449
|
+
const discardTracks = this.deps.discardTracks;
|
|
40450
|
+
const expired = await trackStore.expireStale(this.now(), {
|
|
40451
|
+
...this.deps.isKnownParked !== void 0 ? { isKnownParked: this.deps.isKnownParked } : {},
|
|
40452
|
+
...discardTracks !== void 0 ? { onDiscard: (t) => {
|
|
40453
|
+
discarded.push(t);
|
|
40454
|
+
} } : {}
|
|
40455
|
+
});
|
|
40408
40456
|
for (const t of expired) await this.closeExpired(t);
|
|
40457
|
+
if (discardTracks !== void 0 && discarded.length > 0) await discardTracks(discarded);
|
|
40409
40458
|
} catch (err) {
|
|
40410
40459
|
if (this.deps.isShuttingDown()) return;
|
|
40411
40460
|
this.deps.logger.debug("sweepExpiredTracks failed", { meta: { error: String(err) } });
|
|
@@ -44103,6 +44152,19 @@ var VEHICLE_CLASSES$1 = new Set([
|
|
|
44103
44152
|
"truck",
|
|
44104
44153
|
"bus"
|
|
44105
44154
|
]);
|
|
44155
|
+
/**
|
|
44156
|
+
* A short motionless PERSON is stamped and persisted, never discarded (D547).
|
|
44157
|
+
*
|
|
44158
|
+
* The discard exists for re-sightings of an OBJECT an entity represents — a
|
|
44159
|
+
* parked car the registry censuses in two sessions, a plate flash on it. No
|
|
44160
|
+
* entity ever represents a person who stood still for ten seconds (the person
|
|
44161
|
+
* policy needs thirty minutes of agreement, on purpose), and the face read
|
|
44162
|
+
* such a track holds is the only record of who was there. Deleting the track
|
|
44163
|
+
* would delete that; hiding the row from the feed was always the whole rule.
|
|
44164
|
+
*/
|
|
44165
|
+
function isDiscardExemptClass(className) {
|
|
44166
|
+
return className.toLowerCase() === "person";
|
|
44167
|
+
}
|
|
44106
44168
|
function motionlessMaxPxForClass(className) {
|
|
44107
44169
|
return VEHICLE_CLASSES$1.has(className.toLowerCase()) ? 8 : 40;
|
|
44108
44170
|
}
|
|
@@ -44860,7 +44922,30 @@ var TrackStore = class {
|
|
|
44860
44922
|
for (const [trackId, t] of this.active) {
|
|
44861
44923
|
if (nowMs - t.lastSeen < this.config.ttlMs) continue;
|
|
44862
44924
|
t.active = false;
|
|
44863
|
-
|
|
44925
|
+
const knownParked = t.positions.length > 0 && opts?.isKnownParked?.(t) === true;
|
|
44926
|
+
const shortMotionless = t.positions.length > 0 && isShortMotionlessTrack(t.lastSeen - t.firstSeen, netDisplacementPx(t.positions), t.className);
|
|
44927
|
+
const stillObject = knownParked || shortMotionless;
|
|
44928
|
+
const discardable = knownParked || shortMotionless && !isDiscardExemptClass(t.className);
|
|
44929
|
+
const onDiscard = opts?.onDiscard;
|
|
44930
|
+
if (discardable && onDiscard !== void 0) {
|
|
44931
|
+
const record = cloneTrack(t);
|
|
44932
|
+
this.active.delete(trackId);
|
|
44933
|
+
this.noteRowClosed(trackId, nowMs);
|
|
44934
|
+
this.logger.debug("track discarded on expiry — a still object is an entity, not a track", {
|
|
44935
|
+
tags: { deviceId: t.deviceId },
|
|
44936
|
+
meta: {
|
|
44937
|
+
deviceId: t.deviceId,
|
|
44938
|
+
trackId,
|
|
44939
|
+
className: t.className,
|
|
44940
|
+
lifeMs: t.lastSeen - t.firstSeen,
|
|
44941
|
+
netDisplacementPx: Math.round(netDisplacementPx(t.positions)),
|
|
44942
|
+
knownParked
|
|
44943
|
+
}
|
|
44944
|
+
});
|
|
44945
|
+
onDiscard(record);
|
|
44946
|
+
continue;
|
|
44947
|
+
}
|
|
44948
|
+
if (stillObject) t.closedReason = "stationary-promotion";
|
|
44864
44949
|
const record = cloneTrack(t);
|
|
44865
44950
|
try {
|
|
44866
44951
|
await this.persistCompleted(record);
|
|
@@ -53472,6 +53557,7 @@ var FaceRecognizer = class {
|
|
|
53472
53557
|
zones: [],
|
|
53473
53558
|
state: "moving",
|
|
53474
53559
|
matchedThisFrame: true,
|
|
53560
|
+
settling: false,
|
|
53475
53561
|
embedding: input.embedding,
|
|
53476
53562
|
embeddingModelId: modelId,
|
|
53477
53563
|
...input.faceBbox !== void 0 ? { faceBbox: input.faceBbox } : {},
|
|
@@ -60866,64 +60952,53 @@ function stillnessVerdictFor(source, trackState) {
|
|
|
60866
60952
|
return source === "burst" ? void 0 : trackState;
|
|
60867
60953
|
}
|
|
60868
60954
|
/**
|
|
60869
|
-
*
|
|
60870
|
-
*
|
|
60871
|
-
* Two questions, deliberately kept apart:
|
|
60955
|
+
* What one observation is worth to a candidate: `agree` (N and M), `disagree`
|
|
60956
|
+
* (M only) or `undecided` (neither).
|
|
60872
60957
|
*
|
|
60873
|
-
*
|
|
60874
|
-
* and it is the only notion of sameness in this module.
|
|
60875
|
-
* - STILLNESS — is it going anywhere? On a session frame that is the
|
|
60876
|
-
* TRACKER's verdict, and nothing here re-derives it. On a burst frame it is
|
|
60877
|
-
* the OCCASION: the runner rechecks a still scene and nothing else (see
|
|
60878
|
-
* {@link stillnessVerdictFor}).
|
|
60958
|
+
* ## Why a third answer exists (D547)
|
|
60879
60959
|
*
|
|
60880
|
-
*
|
|
60960
|
+
* The ratio N/M exists to dilute a car that merely drove through the spot.
|
|
60961
|
+
* It was also diluting a car that had not yet had the CHANCE to be called
|
|
60962
|
+
* still: the five `entered` frames every track begins with, and every frame
|
|
60963
|
+
* on which the analyzer's settled test already passed but
|
|
60964
|
+
* `stationaryThresholdSec` had not elapsed — `moving` in name only. On a
|
|
60965
|
+
* session-dispatched camera those frames are a third of every 36 s track, and
|
|
60966
|
+
* a session cut short by a passer-by is made of nothing else. Measured on
|
|
60967
|
+
* device 617 (DNRG309, 2026-09-19 20:32 → 09-20 07:53): 13 of the 21 tracks
|
|
60968
|
+
* bound to the candidate never reached `idle`, the ledger's ratio was 0.493
|
|
60969
|
+
* against 0.6, and a parked car took 11.3 h and 19 tracks to be censused.
|
|
60881
60970
|
*
|
|
60882
|
-
*
|
|
60883
|
-
*
|
|
60884
|
-
* the
|
|
60885
|
-
*
|
|
60886
|
-
* living 1 to 6 seconds, identical boxes under different ids, which tipped the
|
|
60887
|
-
* ledger into an entry on 10 agreements (the real cars carried 773 and 1565).
|
|
60888
|
-
* The first answer was a second clock in this module (`MIN_STILL_FOR_EVIDENCE_MS`,
|
|
60889
|
-
* 10 s) — a copy of the tracker's own dwell, kept in step by hand. The
|
|
60890
|
-
* shortcut is gone instead: `idle` now means the per-device
|
|
60891
|
-
* `stationaryThresholdSec` has elapsed, on every camera, and this module has
|
|
60892
|
-
* no number of its own.
|
|
60893
|
-
*
|
|
60894
|
-
* ## A verdict, not the absence of one
|
|
60895
|
-
*
|
|
60896
|
-
* `entered` — the five-frame warm-up every track begins with — does not agree
|
|
60897
|
-
* on a session frame. Measured on 617 over 12 h (2026-09-15): a car merely
|
|
60898
|
-
* passing the edge is gone in ~8 frames, five of them `entered`, and 5/8 = 0.62
|
|
60899
|
-
* cleared the 0.6 ratio unaided — an entry born on an empty spot, retired 24
|
|
60900
|
-
* absent readings later, one "parked" and one "departed" notification for a
|
|
60901
|
-
* passage. A parked car's re-detection loses five observations of ~280.
|
|
60902
|
-
*
|
|
60903
|
-
* A session observation with NO verdict at all does not agree either. There
|
|
60904
|
-
* is no trackless producer any more (the hydrated baseline sampler is gone),
|
|
60905
|
-
* so an absent verdict on a session frame is a producer that forgot the
|
|
60906
|
-
* field — the one way D494 said this could rot — and it must read as a miss,
|
|
60907
|
-
* never as the old geometry-only rule.
|
|
60971
|
+
* "Not yet decided" is not evidence either way, so it counts as neither. A
|
|
60972
|
+
* subject that is TRAVELLING — `moving` with the settled test failing — still
|
|
60973
|
+
* dilutes, so the drive-through case D508 measured (5 `entered` of 8 frames)
|
|
60974
|
+
* now scores 0/3 instead of the 5/8 that once minted an entry on an empty spot.
|
|
60908
60975
|
*/
|
|
60909
|
-
function
|
|
60910
|
-
if (overlap < agreeIou) return
|
|
60911
|
-
if (source === "burst") return
|
|
60912
|
-
|
|
60976
|
+
function observationVote(overlap, agreeIou, source, movementState, settling) {
|
|
60977
|
+
if (overlap < agreeIou) return "disagree";
|
|
60978
|
+
if (source === "burst") return "agree";
|
|
60979
|
+
if (movementState === "idle") return "agree";
|
|
60980
|
+
if (movementState === "entered") return "undecided";
|
|
60981
|
+
if (movementState === "moving" && settling) return "undecided";
|
|
60982
|
+
return "disagree";
|
|
60913
60983
|
}
|
|
60914
60984
|
/**
|
|
60915
60985
|
* Fold one observation into a candidate.
|
|
60916
60986
|
*
|
|
60917
|
-
* `
|
|
60987
|
+
* The `vote` is decided by the CALLER, because M is what makes the ratio mean
|
|
60918
60988
|
* anything: once a track has bound to a candidate, every later observation of
|
|
60919
60989
|
* that track counts toward M whether it agrees or not. That is what dilutes a
|
|
60920
60990
|
* car that merely drove through the spot, and it is why this function cannot
|
|
60921
60991
|
* re-derive agreement from the box alone.
|
|
60922
60992
|
*/
|
|
60923
|
-
function foldObservation(row, observation,
|
|
60924
|
-
const seen = row.seen + 1;
|
|
60993
|
+
function foldObservation(row, observation, vote, overlap) {
|
|
60925
60994
|
const rotated = rotateWindow(row, observation.timestamp);
|
|
60926
|
-
if (
|
|
60995
|
+
if (vote === "undecided") return {
|
|
60996
|
+
...rotated,
|
|
60997
|
+
undecided: row.undecided + 1,
|
|
60998
|
+
updatedAt: observation.timestamp
|
|
60999
|
+
};
|
|
61000
|
+
const seen = row.seen + 1;
|
|
61001
|
+
if (vote === "disagree") return {
|
|
60927
61002
|
...rotated,
|
|
60928
61003
|
seen,
|
|
60929
61004
|
windowSeen: rotated.windowSeen + 1,
|
|
@@ -60971,6 +61046,7 @@ function newEvidenceRow(observation, anchorId) {
|
|
|
60971
61046
|
...observation.label !== void 0 ? { label: observation.label } : {},
|
|
60972
61047
|
sessionAgreed: observation.source === "session" ? 1 : 0,
|
|
60973
61048
|
burstAgreed: observation.source === "burst" ? 1 : 0,
|
|
61049
|
+
undecided: 0,
|
|
60974
61050
|
updatedAt: observation.timestamp
|
|
60975
61051
|
};
|
|
60976
61052
|
}
|
|
@@ -61043,6 +61119,10 @@ var STATIONARY_EVIDENCE_COLUMNS = [
|
|
|
61043
61119
|
name: "burstAgreed",
|
|
61044
61120
|
type: "INTEGER"
|
|
61045
61121
|
},
|
|
61122
|
+
{
|
|
61123
|
+
name: "undecided",
|
|
61124
|
+
type: "INTEGER"
|
|
61125
|
+
},
|
|
61046
61126
|
{
|
|
61047
61127
|
name: "updatedAt",
|
|
61048
61128
|
type: "INTEGER"
|
|
@@ -61090,6 +61170,9 @@ var StationaryEvidenceLedger = class {
|
|
|
61090
61170
|
* every observation of a box that never changes (3836's jacket is seen five
|
|
61091
61171
|
* times a second in a session), so the line is said once per candidate. */
|
|
61092
61172
|
implausibleReported = /* @__PURE__ */ new Set();
|
|
61173
|
+
/** `<deviceId>:<trackId>` of tracks whose first undecided fold was logged
|
|
61174
|
+
* (D547). Pruned with the bindings in {@link retainTracks}. */
|
|
61175
|
+
undecidedReported = /* @__PURE__ */ new Set();
|
|
61093
61176
|
anchorSeq = 0;
|
|
61094
61177
|
constructor(deps) {
|
|
61095
61178
|
this.logger = deps.logger;
|
|
@@ -61118,6 +61201,7 @@ var StationaryEvidenceLedger = class {
|
|
|
61118
61201
|
...row.label !== void 0 ? { label: row.label } : {},
|
|
61119
61202
|
sessionAgreed: row.sessionAgreed,
|
|
61120
61203
|
burstAgreed: row.burstAgreed,
|
|
61204
|
+
undecided: row.undecided,
|
|
61121
61205
|
updatedAt: row.updatedAt
|
|
61122
61206
|
}),
|
|
61123
61207
|
fromRecord: (key, data) => {
|
|
@@ -61151,6 +61235,7 @@ var StationaryEvidenceLedger = class {
|
|
|
61151
61235
|
...label !== void 0 ? { label } : {},
|
|
61152
61236
|
sessionAgreed: numberOr(data.sessionAgreed, 0),
|
|
61153
61237
|
burstAgreed: numberOr(data.burstAgreed, 0),
|
|
61238
|
+
undecided: numberOr(data.undecided, 0),
|
|
61154
61239
|
updatedAt
|
|
61155
61240
|
};
|
|
61156
61241
|
},
|
|
@@ -61285,8 +61370,10 @@ var StationaryEvidenceLedger = class {
|
|
|
61285
61370
|
}
|
|
61286
61371
|
if (bindKey !== void 0) this.binding.set(bindKey, row.key);
|
|
61287
61372
|
}
|
|
61288
|
-
const
|
|
61289
|
-
const
|
|
61373
|
+
const vote = observationVote(overlap, policy.agreeIou, observation.source, observation.movementState, observation.settling === true);
|
|
61374
|
+
const agrees = vote === "agree";
|
|
61375
|
+
if (vote === "undecided") this.noteUndecided(row, observation);
|
|
61376
|
+
const next = foldObservation(row, observation, vote, overlap);
|
|
61290
61377
|
this.ledger.stage(next);
|
|
61291
61378
|
this.dirty.add(next.key);
|
|
61292
61379
|
if (created) this.capDevice(observation.deviceId);
|
|
@@ -61336,6 +61423,10 @@ var StationaryEvidenceLedger = class {
|
|
|
61336
61423
|
if (!bindKey.startsWith(prefix)) continue;
|
|
61337
61424
|
if (!liveTrackIds.has(bindKey.slice(prefix.length))) this.binding.delete(bindKey);
|
|
61338
61425
|
}
|
|
61426
|
+
for (const key of [...this.undecidedReported]) {
|
|
61427
|
+
if (!key.startsWith(prefix)) continue;
|
|
61428
|
+
if (!liveTrackIds.has(key.slice(prefix.length))) this.undecidedReported.delete(key);
|
|
61429
|
+
}
|
|
61339
61430
|
}
|
|
61340
61431
|
forgetKey(key) {
|
|
61341
61432
|
this.forgetKeyDurable(key);
|
|
@@ -61348,6 +61439,32 @@ var StationaryEvidenceLedger = class {
|
|
|
61348
61439
|
* work is never silent (D391), and one that refuses it five times a second
|
|
61349
61440
|
* is never loud.
|
|
61350
61441
|
*/
|
|
61442
|
+
/**
|
|
61443
|
+
* An undecided observation is dropped from the tally on purpose (D547), and
|
|
61444
|
+
* a branch that drops work says so — once per track, at debug: the same
|
|
61445
|
+
* track offers one every frame until it decides, and a line per frame would
|
|
61446
|
+
* bury what it reports. The lifetime count rides the row (`undecided`).
|
|
61447
|
+
*/
|
|
61448
|
+
noteUndecided(row, observation) {
|
|
61449
|
+
const trackId = observation.trackId;
|
|
61450
|
+
if (trackId === void 0) return;
|
|
61451
|
+
const key = `${observation.deviceId}:${trackId}`;
|
|
61452
|
+
if (this.undecidedReported.has(key)) return;
|
|
61453
|
+
this.undecidedReported.add(key);
|
|
61454
|
+
this.logger.debug("stationary evidence: undecided observation — counted as neither N nor M", {
|
|
61455
|
+
tags: { deviceId: observation.deviceId },
|
|
61456
|
+
meta: {
|
|
61457
|
+
deviceId: observation.deviceId,
|
|
61458
|
+
trackId,
|
|
61459
|
+
candidateKey: row.key,
|
|
61460
|
+
movementState: observation.movementState ?? null,
|
|
61461
|
+
settling: observation.settling === true,
|
|
61462
|
+
agreed: row.agreed,
|
|
61463
|
+
seen: row.seen,
|
|
61464
|
+
undecided: row.undecided
|
|
61465
|
+
}
|
|
61466
|
+
});
|
|
61467
|
+
}
|
|
61351
61468
|
noteImplausible(row, frame) {
|
|
61352
61469
|
if (this.implausibleReported.has(row.key)) return;
|
|
61353
61470
|
this.implausibleReported.add(row.key);
|
|
@@ -61420,6 +61537,65 @@ var StationaryEvidenceLedger = class {
|
|
|
61420
61537
|
for (const row of excess) this.forgetKey(row.key);
|
|
61421
61538
|
}
|
|
61422
61539
|
};
|
|
61540
|
+
//#endregion
|
|
61541
|
+
//#region src/pipeline-analytics/pipeline/stationary/stationary-track-disposal.ts
|
|
61542
|
+
function planStationaryTrackDisposal(input) {
|
|
61543
|
+
const trackIds = [input.liveTrackId];
|
|
61544
|
+
if (input.sourceTrackId !== void 0 && input.sourceTrackId !== input.liveTrackId) trackIds.push(input.sourceTrackId);
|
|
61545
|
+
const keepMediaIds = /* @__PURE__ */ new Set();
|
|
61546
|
+
if (input.entryKeyFrameMediaId !== void 0) keepMediaIds.add(input.entryKeyFrameMediaId);
|
|
61547
|
+
return {
|
|
61548
|
+
trackIds,
|
|
61549
|
+
keepMediaIds
|
|
61550
|
+
};
|
|
61551
|
+
}
|
|
61552
|
+
/**
|
|
61553
|
+
* Run the plan. The live track is dropped from RAM FIRST so it can never be
|
|
61554
|
+
* persisted by a sweep racing this call; the cascade then removes what every
|
|
61555
|
+
* track owned, sparing the entry's key frame, and deletes the row (a no-op for
|
|
61556
|
+
* the never-persisted live track).
|
|
61557
|
+
*
|
|
61558
|
+
* Never silent: the line names the entity, what went and what was kept — a
|
|
61559
|
+
* deleted track is dropped work, and the operator asked for it by name.
|
|
61560
|
+
*/
|
|
61561
|
+
async function disposeStationaryTracks(deps, plan) {
|
|
61562
|
+
const [liveTrackId] = plan.trackIds;
|
|
61563
|
+
if (liveTrackId !== void 0) deps.trackStore.dropActive(liveTrackId);
|
|
61564
|
+
const sparingMedia = { deleteByTracks: (trackIds) => deps.media.deleteByTracks(trackIds, { spare: plan.keepMediaIds }) };
|
|
61565
|
+
const result = await runTrackCascadeBatch({
|
|
61566
|
+
registry: [...deps.leaves, sparingMedia],
|
|
61567
|
+
trackStore: deps.trackStore,
|
|
61568
|
+
deviceId: deps.deviceId,
|
|
61569
|
+
onTrackCleanup: deps.onTrackCleanup,
|
|
61570
|
+
onFailure: (trackId, err) => {
|
|
61571
|
+
deps.logger.warn("stationary promotion: a source track could not be disposed of", {
|
|
61572
|
+
tags: { deviceId: deps.deviceId },
|
|
61573
|
+
meta: {
|
|
61574
|
+
deviceId: deps.deviceId,
|
|
61575
|
+
entryId: deps.entryId,
|
|
61576
|
+
trackId,
|
|
61577
|
+
error: String(err)
|
|
61578
|
+
}
|
|
61579
|
+
});
|
|
61580
|
+
}
|
|
61581
|
+
}, plan.trackIds);
|
|
61582
|
+
deps.logger.info("stationary promotion: source tracks discarded — the entity keeps its key frame", {
|
|
61583
|
+
tags: { deviceId: deps.deviceId },
|
|
61584
|
+
meta: {
|
|
61585
|
+
deviceId: deps.deviceId,
|
|
61586
|
+
entryId: deps.entryId,
|
|
61587
|
+
trackIds: plan.trackIds,
|
|
61588
|
+
keptMediaIds: [...plan.keepMediaIds],
|
|
61589
|
+
deleted: result.deleted,
|
|
61590
|
+
failed: result.failed,
|
|
61591
|
+
mediaRows: result.tally.media
|
|
61592
|
+
}
|
|
61593
|
+
});
|
|
61594
|
+
return {
|
|
61595
|
+
deleted: result.deleted,
|
|
61596
|
+
failed: result.failed
|
|
61597
|
+
};
|
|
61598
|
+
}
|
|
61423
61599
|
/**
|
|
61424
61600
|
* How long the same weak box must keep being seen before it may re-anchor.
|
|
61425
61601
|
*
|
|
@@ -61599,6 +61775,7 @@ var StationaryObjectRegistry = class {
|
|
|
61599
61775
|
sessionGapMs;
|
|
61600
61776
|
warmUpMs;
|
|
61601
61777
|
ttlForDevice;
|
|
61778
|
+
onEntryDropped;
|
|
61602
61779
|
onChange;
|
|
61603
61780
|
requestConfirmationLook;
|
|
61604
61781
|
/**
|
|
@@ -61692,6 +61869,7 @@ var StationaryObjectRegistry = class {
|
|
|
61692
61869
|
this.sessionGapMs = deps.sessionGapMs ?? 5e3;
|
|
61693
61870
|
this.warmUpMs = deps.warmUpMs ?? 2e3;
|
|
61694
61871
|
this.ttlForDevice = deps.ttlForDevice;
|
|
61872
|
+
this.onEntryDropped = deps.onEntryDropped;
|
|
61695
61873
|
this.onChange = deps.onChange;
|
|
61696
61874
|
this.requestConfirmationLook = deps.requestConfirmationLook;
|
|
61697
61875
|
}
|
|
@@ -62301,6 +62479,7 @@ var StationaryObjectRegistry = class {
|
|
|
62301
62479
|
return at !== void 0 && at > entry.lastConfirmedAt;
|
|
62302
62480
|
}
|
|
62303
62481
|
dropEntry(deviceId, id) {
|
|
62482
|
+
const dropped = this.ledger.get(id);
|
|
62304
62483
|
this.idsByDevice.get(deviceId)?.delete(id);
|
|
62305
62484
|
this.dirty.delete(id);
|
|
62306
62485
|
this.weakRuns.delete(id);
|
|
@@ -62310,6 +62489,7 @@ var StationaryObjectRegistry = class {
|
|
|
62310
62489
|
if (armed.size === 0) this.departureStrikes.delete(deviceId);
|
|
62311
62490
|
}
|
|
62312
62491
|
this.ledger.forget(id);
|
|
62492
|
+
if (dropped !== void 0) this.onEntryDropped?.(dropped);
|
|
62313
62493
|
}
|
|
62314
62494
|
};
|
|
62315
62495
|
/** The persisted column map for an entry (the `id` PK is passed separately). */
|
|
@@ -68242,8 +68422,10 @@ async function auditScope(scope, deps, limits, cursor) {
|
|
|
68242
68422
|
let liveTracks;
|
|
68243
68423
|
const liveEvents = /* @__PURE__ */ new Map();
|
|
68244
68424
|
let liveSummaries;
|
|
68425
|
+
let keptMedia = /* @__PURE__ */ new Set();
|
|
68245
68426
|
try {
|
|
68246
68427
|
liveTracks = trackIds.length > 0 ? await deps.liveTrackIds(trackIds) : /* @__PURE__ */ new Set();
|
|
68428
|
+
if (deps.keptMediaKeys !== void 0 && scope.collection === "pipeline-analytics:media") keptMedia = await deps.keptMediaKeys(rows.map((row) => row.key));
|
|
68247
68429
|
for (const [table, ids] of eventIdsByTable) {
|
|
68248
68430
|
if (ids.length === 0) continue;
|
|
68249
68431
|
liveEvents.set(table, await deps.liveEventIds(table, ids));
|
|
@@ -68276,7 +68458,7 @@ async function auditScope(scope, deps, limits, cursor) {
|
|
|
68276
68458
|
tally.unresolvable += 1;
|
|
68277
68459
|
continue;
|
|
68278
68460
|
}
|
|
68279
|
-
if (ref.kind === "track" ? liveTracks.has(ref.trackId) : ref.kind === "event" ? liveEvents.get(ref.table)?.has(ref.eventId) ?? false : ref.kind === "summary" ? liveSummaries.has(ref.summaryId) : false) continue;
|
|
68461
|
+
if (ref.kind === "track" ? liveTracks.has(ref.trackId) || keptMedia.has(row.key) : ref.kind === "event" ? liveEvents.get(ref.table)?.has(ref.eventId) ?? false : ref.kind === "summary" ? liveSummaries.has(ref.summaryId) : false) continue;
|
|
68280
68462
|
const ts = num(row.data, scope.timestampField);
|
|
68281
68463
|
if (ts !== null && ts > graceFloor) {
|
|
68282
68464
|
tally.withinGrace += 1;
|
|
@@ -72602,7 +72784,7 @@ function buildDetectionSettingsSections() {
|
|
|
72602
72784
|
type: "number",
|
|
72603
72785
|
key: "stationarySpeedPx",
|
|
72604
72786
|
label: "Stationary speed",
|
|
72605
|
-
description: "Speed
|
|
72787
|
+
description: "Speed below which a track is treated as stationary and its prediction is frozen, so bbox jitter cannot walk the box off a sitting object. The unit is pixels per REPORT, and this camera decides how often it reports — the same number means 35.7 px/s on a fast camera and 0.94 px/s on an armed-on-motion one. Set it to 0 to judge this camera on its calibrated stillness bar instead (Stationary → Calibrate), which is measured per second and relative to the subject’s own size. With no calibration, 0 keeps the shipped default.",
|
|
72606
72788
|
min: 0,
|
|
72607
72789
|
step: .5,
|
|
72608
72790
|
default: TRACKING_DEFAULTS.stationarySpeedPx,
|
|
@@ -78010,10 +78192,149 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
78010
78192
|
const last = track.positions.at(-1)?.bbox;
|
|
78011
78193
|
if (!last) return false;
|
|
78012
78194
|
return bboxMatchesParkedEntry(last, track.className, this.stationaryRegistry?.listViews(track.deviceId) ?? []);
|
|
78013
|
-
}
|
|
78195
|
+
},
|
|
78196
|
+
discardTracks: (tracks) => this.cascadeDiscardedTracks(tracks)
|
|
78014
78197
|
});
|
|
78015
78198
|
return this._trackCloser;
|
|
78016
78199
|
}
|
|
78200
|
+
/**
|
|
78201
|
+
* Cascade what `expireStale` refused to persist (D547): scenery re-fires and
|
|
78202
|
+
* re-sightings of a censused object. Per device, per track, through the
|
|
78203
|
+
* shipped registry — never a raw delete — so the vector, the crops and the
|
|
78204
|
+
* events go with the media. A track whose cascade fails is named.
|
|
78205
|
+
*/
|
|
78206
|
+
async cascadeDiscardedTracks(tracks) {
|
|
78207
|
+
const cascade = this.buildTrackCascadeRegistry();
|
|
78208
|
+
if (!cascade) {
|
|
78209
|
+
this.ctx.logger.warn("discarded tracks could not be cascaded — stores not ready", { meta: { trackIds: tracks.map((t) => t.trackId) } });
|
|
78210
|
+
return;
|
|
78211
|
+
}
|
|
78212
|
+
const byDevice = /* @__PURE__ */ new Map();
|
|
78213
|
+
for (const t of tracks) {
|
|
78214
|
+
const ids = byDevice.get(t.deviceId) ?? [];
|
|
78215
|
+
ids.push(t.trackId);
|
|
78216
|
+
byDevice.set(t.deviceId, ids);
|
|
78217
|
+
}
|
|
78218
|
+
for (const [deviceId, trackIds] of byDevice) {
|
|
78219
|
+
const result = await runTrackCascadeBatch({
|
|
78220
|
+
registry: cascade.registry,
|
|
78221
|
+
trackStore: cascade.trackStore,
|
|
78222
|
+
deviceId,
|
|
78223
|
+
onTrackCleanup: (id, trackId) => this.clearLiveTrackState(id, trackId),
|
|
78224
|
+
onFailure: (trackId, err) => {
|
|
78225
|
+
this.ctx.logger.warn("discarded track could not be cascaded", {
|
|
78226
|
+
tags: { deviceId },
|
|
78227
|
+
meta: {
|
|
78228
|
+
deviceId,
|
|
78229
|
+
trackId,
|
|
78230
|
+
error: String(err)
|
|
78231
|
+
}
|
|
78232
|
+
});
|
|
78233
|
+
}
|
|
78234
|
+
}, trackIds);
|
|
78235
|
+
this.ctx.logger.info("tracks discarded on expiry — still objects are entities, not tracks", {
|
|
78236
|
+
tags: { deviceId },
|
|
78237
|
+
meta: {
|
|
78238
|
+
deviceId,
|
|
78239
|
+
trackIds,
|
|
78240
|
+
deleted: result.deleted,
|
|
78241
|
+
failed: result.failed,
|
|
78242
|
+
mediaRows: result.tally.media
|
|
78243
|
+
}
|
|
78244
|
+
});
|
|
78245
|
+
}
|
|
78246
|
+
}
|
|
78247
|
+
/**
|
|
78248
|
+
* Dispose of the tracks that carried a promoted candidate's evidence (D547):
|
|
78249
|
+
* the one alive on the tipping frame and the persisted source of the entry's
|
|
78250
|
+
* best box. The entry's key frame is spared — it is the one row the entity
|
|
78251
|
+
* vouches for, and `keptStationaryMediaKeys` keeps the orphan audit off it.
|
|
78252
|
+
*/
|
|
78253
|
+
async disposePromotedTracks(deviceId, entry, liveTrackId, sourceTrackId) {
|
|
78254
|
+
const cascade = this.buildTrackCascadeRegistry();
|
|
78255
|
+
const mediaStore = this.mediaStore;
|
|
78256
|
+
if (!cascade || !mediaStore) {
|
|
78257
|
+
this.ctx.logger.warn("stationary promotion: source tracks could not be disposed of — stores not ready", {
|
|
78258
|
+
tags: { deviceId },
|
|
78259
|
+
meta: {
|
|
78260
|
+
deviceId,
|
|
78261
|
+
entryId: entry.id,
|
|
78262
|
+
liveTrackId,
|
|
78263
|
+
sourceTrackId: sourceTrackId ?? null
|
|
78264
|
+
}
|
|
78265
|
+
});
|
|
78266
|
+
return;
|
|
78267
|
+
}
|
|
78268
|
+
const plan = planStationaryTrackDisposal({
|
|
78269
|
+
liveTrackId,
|
|
78270
|
+
...sourceTrackId !== void 0 ? { sourceTrackId } : {},
|
|
78271
|
+
...entry.keyFrameMediaId !== void 0 ? { entryKeyFrameMediaId: entry.keyFrameMediaId } : {}
|
|
78272
|
+
});
|
|
78273
|
+
await disposeStationaryTracks({
|
|
78274
|
+
deviceId,
|
|
78275
|
+
entryId: entry.id,
|
|
78276
|
+
leaves: cascade.registry.filter((leaf) => leaf !== mediaStore),
|
|
78277
|
+
media: mediaStore,
|
|
78278
|
+
trackStore: cascade.trackStore,
|
|
78279
|
+
onTrackCleanup: (id, trackId) => this.clearLiveTrackState(id, trackId),
|
|
78280
|
+
logger: this.ctx.logger
|
|
78281
|
+
}, plan);
|
|
78282
|
+
}
|
|
78283
|
+
/**
|
|
78284
|
+
* Media row keys a live stationary entry vouches for (D547) — the orphan
|
|
78285
|
+
* audit's probe. Answered from the registry's RAM index, which is the
|
|
78286
|
+
* authority on which entries are alive.
|
|
78287
|
+
*/
|
|
78288
|
+
keptStationaryMediaKeys(keys) {
|
|
78289
|
+
const registry = this.stationaryRegistry;
|
|
78290
|
+
if (!registry) return /* @__PURE__ */ new Set();
|
|
78291
|
+
const held = /* @__PURE__ */ new Set();
|
|
78292
|
+
for (const deviceId of registry.deviceIds()) for (const entry of registry.list(deviceId)) if (entry.keyFrameMediaId !== void 0) held.add(String(entry.keyFrameMediaId));
|
|
78293
|
+
return new Set(keys.filter((key) => held.has(key)));
|
|
78294
|
+
}
|
|
78295
|
+
/**
|
|
78296
|
+
* An entry left the registry: free the key frame it vouched for (D547),
|
|
78297
|
+
* unless another live entry still holds it or a track row still owns it
|
|
78298
|
+
* (then retention's cascade will). Never silent either way.
|
|
78299
|
+
*/
|
|
78300
|
+
async releaseStationaryKeyFrame(entry) {
|
|
78301
|
+
const mediaId = entry.keyFrameMediaId;
|
|
78302
|
+
if (mediaId === void 0) return;
|
|
78303
|
+
const deviceId = entry.deviceId;
|
|
78304
|
+
const registry = this.stationaryRegistry;
|
|
78305
|
+
const mediaStore = this.mediaStore;
|
|
78306
|
+
const trackStore = this.trackStore;
|
|
78307
|
+
if (!registry || !mediaStore || !trackStore) return;
|
|
78308
|
+
if (registry.list(deviceId).some((e) => e.keyFrameMediaId === mediaId)) return;
|
|
78309
|
+
try {
|
|
78310
|
+
const owner = await mediaStore.ownerOf(mediaId);
|
|
78311
|
+
if (owner === null) return;
|
|
78312
|
+
if (owner.ownerKind === "track") {
|
|
78313
|
+
if ((await trackStore.existingTrackIds([owner.ownerId])).has(owner.ownerId)) return;
|
|
78314
|
+
}
|
|
78315
|
+
await mediaStore.deleteById(mediaId);
|
|
78316
|
+
this.ctx.logger.info("stationary entry released its key frame", {
|
|
78317
|
+
tags: { deviceId },
|
|
78318
|
+
meta: {
|
|
78319
|
+
deviceId,
|
|
78320
|
+
entryId: entry.id,
|
|
78321
|
+
mediaId,
|
|
78322
|
+
ownerKind: owner.ownerKind,
|
|
78323
|
+
ownerId: owner.ownerId
|
|
78324
|
+
}
|
|
78325
|
+
});
|
|
78326
|
+
} catch (err) {
|
|
78327
|
+
this.ctx.logger.warn("stationary entry key frame could not be released", {
|
|
78328
|
+
tags: { deviceId },
|
|
78329
|
+
meta: {
|
|
78330
|
+
deviceId,
|
|
78331
|
+
entryId: entry.id,
|
|
78332
|
+
mediaId,
|
|
78333
|
+
error: String(err)
|
|
78334
|
+
}
|
|
78335
|
+
});
|
|
78336
|
+
}
|
|
78337
|
+
}
|
|
78017
78338
|
/** Master toggle for the migrated object/face EmbeddingDispatcher. Read at
|
|
78018
78339
|
* init; a change takes effect on addon restart (same convention as
|
|
78019
78340
|
* `postProcessingNodeId`).
|
|
@@ -78739,6 +79060,9 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
78739
79060
|
store: api.settingsStore,
|
|
78740
79061
|
logger: logger.child("StationaryRegistry"),
|
|
78741
79062
|
ttlForDevice: (deviceId) => this.stationarySettingsFromCache(deviceId).entryTtlMs,
|
|
79063
|
+
onEntryDropped: (entry) => {
|
|
79064
|
+
this.releaseStationaryKeyFrame(entry);
|
|
79065
|
+
},
|
|
78742
79066
|
requestConfirmationLook: (ask) => this.requestConfirmationLook(ask),
|
|
78743
79067
|
onChange: ({ phase, entry, timestamp, observedAt }) => {
|
|
78744
79068
|
this.ctx.eventBus.emit({
|
|
@@ -80127,6 +80451,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
80127
80451
|
anchor: { ...row.anchor },
|
|
80128
80452
|
agreed: row.agreed,
|
|
80129
80453
|
seen: row.seen,
|
|
80454
|
+
undecided: row.undecided,
|
|
80130
80455
|
ratio: row.seen > 0 ? row.agreed / row.seen : 0,
|
|
80131
80456
|
spanMs: row.lastAgreedAt - row.firstAgreedAt,
|
|
80132
80457
|
firstAgreedAt: row.firstAgreedAt,
|
|
@@ -81527,7 +81852,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
81527
81852
|
frameHeight: result.frameHeight,
|
|
81528
81853
|
...observedKeyFrame !== void 0 ? { keyFrameMediaId: observedKeyFrame } : {},
|
|
81529
81854
|
...observedLabel !== void 0 ? { label: observedLabel } : {},
|
|
81530
|
-
...movementState !== void 0 ? { movementState } : {}
|
|
81855
|
+
...movementState !== void 0 ? { movementState } : {},
|
|
81856
|
+
settling: t.settling
|
|
81531
81857
|
};
|
|
81532
81858
|
const outcome = evidence.observe(observation);
|
|
81533
81859
|
if (outcome === void 0 || !outcome.promoted) continue;
|
|
@@ -84677,17 +85003,6 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
84677
85003
|
wakeAssocIou: gate.wakeAssocIou
|
|
84678
85004
|
});
|
|
84679
85005
|
processor.dropTrack(track.trackId);
|
|
84680
|
-
this.trackStore?.closeActive(track.trackId, "stationary-promotion").catch((err) => {
|
|
84681
|
-
this.ctx.logger.warn("stationary promotion: closing the source track failed — its media, events and vector are now unowned", {
|
|
84682
|
-
tags: { deviceId },
|
|
84683
|
-
meta: {
|
|
84684
|
-
deviceId,
|
|
84685
|
-
trackId: track.trackId,
|
|
84686
|
-
entryId: entry.id,
|
|
84687
|
-
error: String(err)
|
|
84688
|
-
}
|
|
84689
|
-
});
|
|
84690
|
-
});
|
|
84691
85006
|
const faceEnd = this.faceRecognizer?.onTrackEnd(deviceId, track.trackId);
|
|
84692
85007
|
const dropKeyFrame = () => {
|
|
84693
85008
|
this.residents.dropKeyFrameMediaId(track.trackId);
|
|
@@ -84695,7 +85010,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
84695
85010
|
if (faceEnd) faceEnd.finally(dropKeyFrame);
|
|
84696
85011
|
else dropKeyFrame();
|
|
84697
85012
|
const promotedPath = this.trackStore?.getActiveByTrack(track.trackId)?.positions;
|
|
84698
|
-
this.plateRecognizer?.onTrackEnd(deviceId, track.trackId, promotedPath);
|
|
85013
|
+
const plateEnd = this.plateRecognizer?.onTrackEnd(deviceId, track.trackId, promotedPath);
|
|
85014
|
+
Promise.allSettled([faceEnd, plateEnd]).then(() => this.disposePromotedTracks(deviceId, entry, track.trackId, row.sourceTrackId));
|
|
84699
85015
|
this.residents.close(track.trackId, "stationary");
|
|
84700
85016
|
this.detailDispatcher?.onTrackEnded(deviceId, track.trackId);
|
|
84701
85017
|
this.overlayState.onTrackEnded(deviceId, track.trackId);
|
|
@@ -84738,6 +85054,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
84738
85054
|
rescueCentroidFactor: trk.rescueCentroidFactor,
|
|
84739
85055
|
resurrectionWindowMs: trk.resurrectionWindowMs,
|
|
84740
85056
|
stationarySpeedPx: trk.stationarySpeedPx,
|
|
85057
|
+
...stillSpeedFracPerSec !== void 0 ? { stillSpeedFracPerSec } : {},
|
|
84741
85058
|
maxTrackLifetimeMs: trk.maxTrackLifetimeSec * 1e3,
|
|
84742
85059
|
dedupEnabled: trk.dedupEnabled,
|
|
84743
85060
|
dedupSpawnIou: trk.dedupSpawnIou,
|
|
@@ -85637,6 +85954,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
85637
85954
|
if (!summaries) throw new Error("summary store not built — probe unavailable");
|
|
85638
85955
|
return summaries.existingIds(ids);
|
|
85639
85956
|
},
|
|
85957
|
+
keptMediaKeys: async (keys) => this.keptStationaryMediaKeys(keys),
|
|
85640
85958
|
reclaim: buildOrphanReclaimer({
|
|
85641
85959
|
deleteFace: async (faceId) => {
|
|
85642
85960
|
await this.faceStore?.delete(faceId);
|