@camstack/addon-post-analysis 1.2.241 → 1.2.243
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-CCw4SKRp.mjs → dist-C-1xTyB-.mjs} +39 -24
- package/dist/{dist-DjW7k4lv.js → dist-DxZAf0Hs.js} +38 -23
- 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-CG4H07lP.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-zMqSbyyP.mjs} +2 -2
- package/dist/pipeline-analytics/{hostInit-B0Q8UzBd.mjs → hostInit-CCJzKURF.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +195 -134
- package/dist/pipeline-analytics/index.mjs +195 -134
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
|
@@ -5359,7 +5359,7 @@ var ZodIssueCode = {
|
|
|
5359
5359
|
var ZodFirstPartyTypeKind;
|
|
5360
5360
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5361
5361
|
//#endregion
|
|
5362
|
-
//#region ../types/dist/sleep-
|
|
5362
|
+
//#region ../types/dist/sleep-B9qx0cEV.mjs
|
|
5363
5363
|
/**
|
|
5364
5364
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5365
5365
|
* window to float samples (D455).
|
|
@@ -7303,6 +7303,28 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
|
|
|
7303
7303
|
DeviceRole["GenericNotifier"] = "generic-notifier";
|
|
7304
7304
|
return DeviceRole;
|
|
7305
7305
|
}({});
|
|
7306
|
+
/**
|
|
7307
|
+
* Identity — preserves literal types for downstream inference.
|
|
7308
|
+
*
|
|
7309
|
+
* The constraint is `Record<string, unknown>` (not `CustomActionsSpec`) so
|
|
7310
|
+
* TypeScript does not widen each entry's literal `kind`/`auth` fields to
|
|
7311
|
+
* the broader unions declared on `CustomActionSpec`'s default generics.
|
|
7312
|
+
* Shape validity is enforced separately by the `customAction(...)` helper
|
|
7313
|
+
* whose return type is already a `CustomActionSpec<...>`.
|
|
7314
|
+
*/
|
|
7315
|
+
function defineCustomActions(spec) {
|
|
7316
|
+
return spec;
|
|
7317
|
+
}
|
|
7318
|
+
function customAction(input, output, options) {
|
|
7319
|
+
return {
|
|
7320
|
+
input,
|
|
7321
|
+
output,
|
|
7322
|
+
kind: options?.kind ?? "query",
|
|
7323
|
+
auth: options?.auth ?? "protected",
|
|
7324
|
+
scope: options?.scope ?? { kind: "system" },
|
|
7325
|
+
...options?.caller ? { caller: "required" } : {}
|
|
7326
|
+
};
|
|
7327
|
+
}
|
|
7306
7328
|
var StaticDirOutputSchema = object({ staticDir: string() });
|
|
7307
7329
|
var VersionOutputSchema = object({ version: string() });
|
|
7308
7330
|
method(_void(), StaticDirOutputSchema, { auth: "admin" }), method(_void(), VersionOutputSchema, { auth: "admin" });
|
|
@@ -12146,6 +12168,21 @@ var streamSignalsCapability = {
|
|
|
12146
12168
|
mode: "singleton",
|
|
12147
12169
|
deviceTypes: Object.values(DeviceType),
|
|
12148
12170
|
runtimeState: StreamSignalsStatusSchema,
|
|
12171
|
+
/**
|
|
12172
|
+
* Runtime-state durability: **session** — mirrored in RAM, never written.
|
|
12173
|
+
*
|
|
12174
|
+
* The slice holds what the DEVICE says it can emit. That is a probed fact,
|
|
12175
|
+
* not an operator choice: the provider re-declares it on every registration,
|
|
12176
|
+
* so losing it loses nothing and persisting it would freeze an answer the
|
|
12177
|
+
* camera is entitled to change. Measured the same day on the sibling case —
|
|
12178
|
+
* `native-object-detection.supportedClasses` was persisted, and a firmware
|
|
12179
|
+
* class the camera really detected stayed missing for the life of the row
|
|
12180
|
+
* because the fix could not reach it.
|
|
12181
|
+
*
|
|
12182
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
12183
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
12184
|
+
*/
|
|
12185
|
+
durability: "session",
|
|
12149
12186
|
methods: {
|
|
12150
12187
|
/**
|
|
12151
12188
|
* What this device can emit. Empty is a valid and common answer — most
|
|
@@ -13124,28 +13161,6 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
|
|
|
13124
13161
|
content: string()
|
|
13125
13162
|
})) }), { auth: "admin" });
|
|
13126
13163
|
/**
|
|
13127
|
-
* Identity — preserves literal types for downstream inference.
|
|
13128
|
-
*
|
|
13129
|
-
* The constraint is `Record<string, unknown>` (not `CustomActionsSpec`) so
|
|
13130
|
-
* TypeScript does not widen each entry's literal `kind`/`auth` fields to
|
|
13131
|
-
* the broader unions declared on `CustomActionSpec`'s default generics.
|
|
13132
|
-
* Shape validity is enforced separately by the `customAction(...)` helper
|
|
13133
|
-
* whose return type is already a `CustomActionSpec<...>`.
|
|
13134
|
-
*/
|
|
13135
|
-
function defineCustomActions(spec) {
|
|
13136
|
-
return spec;
|
|
13137
|
-
}
|
|
13138
|
-
function customAction(input, output, options) {
|
|
13139
|
-
return {
|
|
13140
|
-
input,
|
|
13141
|
-
output,
|
|
13142
|
-
kind: options?.kind ?? "query",
|
|
13143
|
-
auth: options?.auth ?? "protected",
|
|
13144
|
-
scope: options?.scope ?? { kind: "system" },
|
|
13145
|
-
...options?.caller ? { caller: "required" } : {}
|
|
13146
|
-
};
|
|
13147
|
-
}
|
|
13148
|
-
/**
|
|
13149
13164
|
* `custom-model-registry` — collection cap exposing operator-registered
|
|
13150
13165
|
* custom detection models. Each provider (today: `addon-model-studio`)
|
|
13151
13166
|
* contributes a list of `CustomModelDescriptor`s; the hub auto-concatenates
|
|
@@ -46398,4 +46413,4 @@ function vectorDimFromBase64(encoded) {
|
|
|
46398
46413
|
return Math.floor(Buffer.from(encoded, "base64").byteLength / 4);
|
|
46399
46414
|
}
|
|
46400
46415
|
//#endregion
|
|
46401
|
-
export { TimelapseRuleSchema as $, boolean as $t, NcConditionDescriptorSchema as A,
|
|
46416
|
+
export { TimelapseRuleSchema as $, boolean as $t, NcConditionDescriptorSchema as A, readTimelapseGeneratedAt as At, NcTaxonomySchema as B, errMsg as Bt, MOTION_CLOSE_AFTER_MS as C, mayWriteToLocation as Ct, NC_DEFAULT_SNOOZE_MINUTES as D, pipelineAnalyticsCapability as Dt, NC_CONDITION_CATALOG as E, pickClusterStepModels as Et, NcScheduleSchema as F, subKindsOf as Ft, SCENE_CONFIRM_DEFAULT_TIMEOUT_MS as G, customAction as Gt, PoolMemoryWatchdog as H, CamProfileSchema as Ht, NcSnoozeInputSchema as I, systemEventFilterApplies as It, SCENE_DIVERGED as J, isDeviceScopedCap as Jt, SCENE_DEFAULT_ANCHOR_THRESHOLD as K, defineCustomActions as Kt, NcSnoozeSchema as L, vectorDimFromBase64 as Lt, NcRulePatchSchema as M, resolvePoolMemoryPolicy as Mt, NcRuleSchema as N, sceneMonitorCapability as Nt, NC_SNOOZE_MAX_MINUTES as O, plateGalleryCapability as Ot, NcRuleTargetSchema as P, storageOccupancyCapability as Pt, TimelapseRulePatchSchema as Q, array as Qt, NcSnoozeSuppressedSchema as R, videoclipsCapability as Rt, MAX_BIRTH_LATENCY_PROXY_MS as S, kebabToCamel as St, NC_ALARM_SYSTEM_EVENT_KINDS as T, parseProcStatus as Tt, RECORDING_EXPORT_MAX_READ_BYTES as U, DeviceType as Ut, OpsLogEntrySchema as V, BaseAddon as Vt, RetrainStatusSchema as W, createEvent as Wt, TIMELAPSE_DENSE_FLOOR_SEC as X, sleep as Xt, SceneMonitorSchema as Y, nodePin as Yt, TimelapseRuleInputSchema as Z, _enum as Zt, FULL_IMAGE_BBOX as _, isDetectionMacroClass as _t, CLUSTER_MODEL_SCOPED_STEPS as a, record as an, audioMetricsCapability as at, MAX_ARCHIVED_DEBUG_NOTES as b, isScheduleActive as bt, DEFAULT_FIRST_SIGHTING_FRESHNESS_MS as c, EventCategory as cn, cosineSimilarity as ct, DETECTION_PIPELINE_CAP_NAME as d, encodeVectorBase64 as dt, discriminatedUnion as en, TrackSourceSchema as et, DeclaredDevices as f, evaluateSensorEdge as ft, FIRST_LEVEL_MACRO_CLASSES as g, hfModelUrl as gt, EVENT_PAD_MS as h, failureContributionCapability as ht, BirthDecisionRecordSchema as i, partialRecord as in, assertTimelapseCadences as it, NcRuleInputSchema as j, resolveLocationMode as jt, NC_TAXONOMY as k, readDeviceStateFrom as kt, DEFAULT_TIMELAPSE_PREVIEW_TEXT as l, deriveRecordingMode as lt, EVENT_OWNER_TYPES as m, faceGalleryCapability as mt, ArchivedDebugNoteSchema as n, number as nn, addonWidgetsSourceCapability as nt, COCO_TO_MACRO as o, string as on, audioModeOf as ot, EVENT_KIND_BY_CAP as p, evictionPolicyOfLocation as pt, SCENE_DEFAULT_UNCOVERED_POLICY as q, hydrateSchema as qt, BaseDevice as r, object as rn, alarmPanelCapability as rt, DEFAULT_EVENT_COLOR as s, unknown as sn, buildEventKindDescriptor as st, AUDIO_MACRO_LABELS as t, literal as tn, VISIT_MERGE_GAP_MS as tt, DETECTION_MACRO_CLASSES as u, embeddingEncoderCapability as ut, FailureCounters as v, isEventOwnerType as vt, MediaFileKindEnum as w, notificationRulesCapability as wt, MAX_BIRTH_DECISION_RECORDS as x, isSourceCap as xt, LabelAttributionSchema as y, isMediaOwnerType as yt, NcSystemEventKindSchema as z, zoneAnalyticsCapability as zt };
|
|
@@ -5390,7 +5390,7 @@ var ZodIssueCode = {
|
|
|
5390
5390
|
var ZodFirstPartyTypeKind;
|
|
5391
5391
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5392
5392
|
//#endregion
|
|
5393
|
-
//#region ../types/dist/sleep-
|
|
5393
|
+
//#region ../types/dist/sleep-B9qx0cEV.mjs
|
|
5394
5394
|
/**
|
|
5395
5395
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5396
5396
|
* window to float samples (D455).
|
|
@@ -7334,6 +7334,28 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
|
|
|
7334
7334
|
DeviceRole["GenericNotifier"] = "generic-notifier";
|
|
7335
7335
|
return DeviceRole;
|
|
7336
7336
|
}({});
|
|
7337
|
+
/**
|
|
7338
|
+
* Identity — preserves literal types for downstream inference.
|
|
7339
|
+
*
|
|
7340
|
+
* The constraint is `Record<string, unknown>` (not `CustomActionsSpec`) so
|
|
7341
|
+
* TypeScript does not widen each entry's literal `kind`/`auth` fields to
|
|
7342
|
+
* the broader unions declared on `CustomActionSpec`'s default generics.
|
|
7343
|
+
* Shape validity is enforced separately by the `customAction(...)` helper
|
|
7344
|
+
* whose return type is already a `CustomActionSpec<...>`.
|
|
7345
|
+
*/
|
|
7346
|
+
function defineCustomActions(spec) {
|
|
7347
|
+
return spec;
|
|
7348
|
+
}
|
|
7349
|
+
function customAction(input, output, options) {
|
|
7350
|
+
return {
|
|
7351
|
+
input,
|
|
7352
|
+
output,
|
|
7353
|
+
kind: options?.kind ?? "query",
|
|
7354
|
+
auth: options?.auth ?? "protected",
|
|
7355
|
+
scope: options?.scope ?? { kind: "system" },
|
|
7356
|
+
...options?.caller ? { caller: "required" } : {}
|
|
7357
|
+
};
|
|
7358
|
+
}
|
|
7337
7359
|
var StaticDirOutputSchema = object({ staticDir: string() });
|
|
7338
7360
|
var VersionOutputSchema = object({ version: string() });
|
|
7339
7361
|
method(_void(), StaticDirOutputSchema, { auth: "admin" }), method(_void(), VersionOutputSchema, { auth: "admin" });
|
|
@@ -12177,6 +12199,21 @@ var streamSignalsCapability = {
|
|
|
12177
12199
|
mode: "singleton",
|
|
12178
12200
|
deviceTypes: Object.values(DeviceType),
|
|
12179
12201
|
runtimeState: StreamSignalsStatusSchema,
|
|
12202
|
+
/**
|
|
12203
|
+
* Runtime-state durability: **session** — mirrored in RAM, never written.
|
|
12204
|
+
*
|
|
12205
|
+
* The slice holds what the DEVICE says it can emit. That is a probed fact,
|
|
12206
|
+
* not an operator choice: the provider re-declares it on every registration,
|
|
12207
|
+
* so losing it loses nothing and persisting it would freeze an answer the
|
|
12208
|
+
* camera is entitled to change. Measured the same day on the sibling case —
|
|
12209
|
+
* `native-object-detection.supportedClasses` was persisted, and a firmware
|
|
12210
|
+
* class the camera really detected stayed missing for the life of the row
|
|
12211
|
+
* because the fix could not reach it.
|
|
12212
|
+
*
|
|
12213
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
12214
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
12215
|
+
*/
|
|
12216
|
+
durability: "session",
|
|
12180
12217
|
methods: {
|
|
12181
12218
|
/**
|
|
12182
12219
|
* What this device can emit. Empty is a valid and common answer — most
|
|
@@ -13155,28 +13192,6 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
|
|
|
13155
13192
|
content: string()
|
|
13156
13193
|
})) }), { auth: "admin" });
|
|
13157
13194
|
/**
|
|
13158
|
-
* Identity — preserves literal types for downstream inference.
|
|
13159
|
-
*
|
|
13160
|
-
* The constraint is `Record<string, unknown>` (not `CustomActionsSpec`) so
|
|
13161
|
-
* TypeScript does not widen each entry's literal `kind`/`auth` fields to
|
|
13162
|
-
* the broader unions declared on `CustomActionSpec`'s default generics.
|
|
13163
|
-
* Shape validity is enforced separately by the `customAction(...)` helper
|
|
13164
|
-
* whose return type is already a `CustomActionSpec<...>`.
|
|
13165
|
-
*/
|
|
13166
|
-
function defineCustomActions(spec) {
|
|
13167
|
-
return spec;
|
|
13168
|
-
}
|
|
13169
|
-
function customAction(input, output, options) {
|
|
13170
|
-
return {
|
|
13171
|
-
input,
|
|
13172
|
-
output,
|
|
13173
|
-
kind: options?.kind ?? "query",
|
|
13174
|
-
auth: options?.auth ?? "protected",
|
|
13175
|
-
scope: options?.scope ?? { kind: "system" },
|
|
13176
|
-
...options?.caller ? { caller: "required" } : {}
|
|
13177
|
-
};
|
|
13178
|
-
}
|
|
13179
|
-
/**
|
|
13180
13195
|
* `custom-model-registry` — collection cap exposing operator-registered
|
|
13181
13196
|
* custom detection models. Each provider (today: `addon-model-studio`)
|
|
13182
13197
|
* contributes a list of `CustomModelDescriptor`s; the hub auto-concatenates
|
|
@@ -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-DxZAf0Hs.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 {
|
|
1
|
+
import { H as PoolMemoryWatchdog, Mt as resolvePoolMemoryPolicy, Tt as parseProcStatus, Vt as BaseAddon, gt as hfModelUrl, ut as embeddingEncoderCapability } from "../dist-C-1xTyB-.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";
|
|
@@ -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.195",
|
|
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.160",
|
|
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.195",
|
|
40
40
|
scope: "default",
|
|
41
41
|
shareConfig: {
|
|
42
42
|
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.160",
|
|
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-DxZAf0Hs.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");
|
|
@@ -55625,7 +55625,8 @@ function buildOverlayDetections(input) {
|
|
|
55625
55625
|
...det,
|
|
55626
55626
|
track: {
|
|
55627
55627
|
trackId: t.trackId,
|
|
55628
|
-
zones: t.zones
|
|
55628
|
+
zones: t.zones,
|
|
55629
|
+
...t.state !== void 0 ? { movementState: t.state } : {}
|
|
55629
55630
|
},
|
|
55630
55631
|
labels: hasName && !alreadyLabeled ? [...det.labels, {
|
|
55631
55632
|
label: name,
|
|
@@ -57583,6 +57584,10 @@ function centroidInside(b, p) {
|
|
|
57583
57584
|
}
|
|
57584
57585
|
/** Parked-box coverage (intersection / entry area) that counts as swallowed. */
|
|
57585
57586
|
var SWALLOW_MIN_COVERAGE = .5;
|
|
57587
|
+
/** Fraction of a detection's OWN area that must lie inside a parked entry for
|
|
57588
|
+
* the entry to swallow it. Deliberately near-total: a fragment of a parked
|
|
57589
|
+
* object does not stick out of it. */
|
|
57590
|
+
var FRAGMENT_MIN_CONTAINMENT = .9;
|
|
57586
57591
|
function isSceneSizedBox(bbox, frameWidth, frameHeight) {
|
|
57587
57592
|
const frame = frameWidth * frameHeight;
|
|
57588
57593
|
return frame > 0 && bbox.w * bbox.h / frame >= .55;
|
|
@@ -57602,6 +57607,29 @@ function detectionSwallowsEntry(det, parked) {
|
|
|
57602
57607
|
if (!centroidInside(det, centroid(parked))) return false;
|
|
57603
57608
|
return intersectionArea$2(parked, det) / entryArea >= SWALLOW_MIN_COVERAGE;
|
|
57604
57609
|
}
|
|
57610
|
+
/**
|
|
57611
|
+
* True when `det` is a FRAGMENT of the parked object — much smaller than the
|
|
57612
|
+
* entry and sitting (almost) wholly inside it.
|
|
57613
|
+
*
|
|
57614
|
+
* The mirror of {@link detectionSwallowsEntry}, and the case neither IoU
|
|
57615
|
+
* threshold can see. Measured on 617: a 34x137 box inside a 452x420 parked
|
|
57616
|
+
* motorcycle scores IoU 0.024 — BELOW `wakeAssocIou` (0.1), so the filter read
|
|
57617
|
+
* it as "no detection overlaps this entry" and let it open a track, accumulate
|
|
57618
|
+
* its own evidence and promote into a registry entry of its own. The camera
|
|
57619
|
+
* then reported two parked vehicles where there was one motorcycle.
|
|
57620
|
+
*
|
|
57621
|
+
* The size bound is the reciprocal of {@link SWALLOW_AREA_RATIO}, so the two
|
|
57622
|
+
* swallow rules meet without overlapping: a box between 1/1.35 and 1.35 of the
|
|
57623
|
+
* entry's area, contained, already scores IoU >= 0.74 and is answered by
|
|
57624
|
+
* `suppressIou`. Nothing is a fragment and a near-identical box at once.
|
|
57625
|
+
*/
|
|
57626
|
+
function entrySwallowsDetection(parked, det) {
|
|
57627
|
+
const entryArea = parked.w * parked.h;
|
|
57628
|
+
const detArea = det.w * det.h;
|
|
57629
|
+
if (entryArea <= 0 || detArea <= 0) return false;
|
|
57630
|
+
if (detArea > entryArea / 1.35) return false;
|
|
57631
|
+
return intersectionArea$2(parked, det) / detArea >= FRAGMENT_MIN_CONTAINMENT;
|
|
57632
|
+
}
|
|
57605
57633
|
function parkedEntryHoldsDetection(entry, detBbox, config) {
|
|
57606
57634
|
const overlap = iou(entry.bbox, detBbox);
|
|
57607
57635
|
if (overlap >= config.suppressIou) return {
|
|
@@ -57616,6 +57644,10 @@ function parkedEntryHoldsDetection(entry, detBbox, config) {
|
|
|
57616
57644
|
strong: false,
|
|
57617
57645
|
holds: false
|
|
57618
57646
|
};
|
|
57647
|
+
if (entrySwallowsDetection(entry.bbox, detBbox)) return {
|
|
57648
|
+
strong: false,
|
|
57649
|
+
holds: true
|
|
57650
|
+
};
|
|
57619
57651
|
return {
|
|
57620
57652
|
strong: false,
|
|
57621
57653
|
holds: overlap >= config.wakeAssocIou && centroidInside(entry.bbox, centroid(detBbox))
|
|
@@ -57634,6 +57666,34 @@ function bboxMatchesParkedEntry(bbox, className, entries, suppressIou = SUPPRESS
|
|
|
57634
57666
|
}
|
|
57635
57667
|
return false;
|
|
57636
57668
|
}
|
|
57669
|
+
/**
|
|
57670
|
+
* The live entry already censusing this spot, if any — the ONE answer to "is
|
|
57671
|
+
* this place taken?", asked at BIRTH.
|
|
57672
|
+
*
|
|
57673
|
+
* It is deliberately the same predicate the per-frame filter uses
|
|
57674
|
+
* ({@link parkedEntryHoldsDetection}): a box the filter would suppress as a
|
|
57675
|
+
* parked object's own must not be able to become a second parked object. Device
|
|
57676
|
+
* 617 held four rows for one car and a fifth for a fragment of a motorcycle
|
|
57677
|
+
* because the birth path asked nothing at all.
|
|
57678
|
+
*/
|
|
57679
|
+
function findParkedOccupant(entries, className, bbox, suppressIou = SUPPRESS_IOU, wakeAssocIou = WAKE_ASSOC_IOU) {
|
|
57680
|
+
const cls = normaliseStationaryClass(className);
|
|
57681
|
+
const config = {
|
|
57682
|
+
suppressIou,
|
|
57683
|
+
wakeAssocIou
|
|
57684
|
+
};
|
|
57685
|
+
let best;
|
|
57686
|
+
for (const e of entries) {
|
|
57687
|
+
if (normaliseStationaryClass(e.className) !== cls) continue;
|
|
57688
|
+
if (!parkedEntryHoldsDetection(e, bbox, config).holds) continue;
|
|
57689
|
+
const overlap = iou(e.bbox, bbox);
|
|
57690
|
+
if (best === void 0 || overlap > best.overlap) best = {
|
|
57691
|
+
entry: e,
|
|
57692
|
+
overlap
|
|
57693
|
+
};
|
|
57694
|
+
}
|
|
57695
|
+
return best?.entry;
|
|
57696
|
+
}
|
|
57637
57697
|
/** Best-shot veto: current crop is a parked object the trail did not start on. */
|
|
57638
57698
|
function thumbnailWouldShowParkedNotSubject(input) {
|
|
57639
57699
|
if (!bboxMatchesParkedEntry(input.currentBbox, input.className, input.entries)) return false;
|
|
@@ -58619,85 +58679,6 @@ var StartedBirthRegistry = class {
|
|
|
58619
58679
|
//#endregion
|
|
58620
58680
|
//#region src/pipeline-analytics/pipeline/stationary/stationary-evidence.ts
|
|
58621
58681
|
/**
|
|
58622
|
-
* stationary-evidence — how a parked object EARNS a registry entry.
|
|
58623
|
-
*
|
|
58624
|
-
* ## The criterion this replaces, and why it was measuring the wrong thing
|
|
58625
|
-
*
|
|
58626
|
-
* Until 2026-08-28 an object became stationary by TRACK CONTINUITY: one track
|
|
58627
|
-
* had to stay alive and barely move for `PROMOTION_WINDOW_MS` (30 s). That
|
|
58628
|
-
* rewards an object that is *seen well and often* — precisely the opposite of
|
|
58629
|
-
* what the operator needs. Measured on device 617 (the parking lot), 24 h:
|
|
58630
|
-
*
|
|
58631
|
-
* - three cars are parked in view; the registry held exactly ONE entry;
|
|
58632
|
-
* - the two it missed were re-discovered as **140 and 112 separate tracks**,
|
|
58633
|
-
* median life **6.8 s**, p90 29 s. Not one of them could ever reach 30 s.
|
|
58634
|
-
* - those two objects alone produced **13 494 of 18 402** vehicle
|
|
58635
|
-
* observations (73%) — the whole re-spawn flood the registry exists to stop.
|
|
58636
|
-
*
|
|
58637
|
-
* The track dies; the OBJECT does not. So the evidence must outlive the track.
|
|
58638
|
-
*
|
|
58639
|
-
* ## The new criterion: a RATIO, not a count
|
|
58640
|
-
*
|
|
58641
|
-
* "The same thing, in the same place, most of the times it was seen."
|
|
58642
|
-
*
|
|
58643
|
-
* N — observations that AGREED with the candidate's anchor box (IoU ≥
|
|
58644
|
-
* {@link StationaryEvidencePolicy.agreeIou}).
|
|
58645
|
-
* M — observations OFFERED by a track once it bound to this candidate,
|
|
58646
|
-
* agreeing or not.
|
|
58647
|
-
*
|
|
58648
|
-
* A count would rescue nothing: a badly-lit car at night is seen rarely, and
|
|
58649
|
-
* any floor high enough to exclude a passer-by would exclude it too. A RATIO
|
|
58650
|
-
* asks a question a rarely-seen object can still answer well — 8 sightings out
|
|
58651
|
-
* of 10 in the same spot is stationary even though 10 is a small number.
|
|
58652
|
-
*
|
|
58653
|
-
* The dilution is the whole point, and it is why M counts a bound track's
|
|
58654
|
-
* observations even after it walks away: a car that drives THROUGH a spot binds
|
|
58655
|
-
* for a few frames and then contributes dozens of disagreeing ones. Measured on
|
|
58656
|
-
* 617 the two populations do not overlap:
|
|
58657
|
-
*
|
|
58658
|
-
* | candidate | N / M | ratio |
|
|
58659
|
-
* | ---------------------------- | ----------- | ----- |
|
|
58660
|
-
* | parked car A (missed today) | 6405 / 6467 | 0.99 |
|
|
58661
|
-
* | parked car B (missed today) | 343 / 343 | 1.00 |
|
|
58662
|
-
* | car crossing the lot | 5 / 70 | 0.07 |
|
|
58663
|
-
* | car turning through the lot | 14 / 91 | 0.15 |
|
|
58664
|
-
* | car manoeuvring | 12 / 41 | 0.29 |
|
|
58665
|
-
*
|
|
58666
|
-
* The threshold sits in the empty band between 0.29 and 0.99.
|
|
58667
|
-
*
|
|
58668
|
-
* ## The span is what a ratio cannot say
|
|
58669
|
-
*
|
|
58670
|
-
* A ratio alone promotes a car that paused for four seconds — its handful of
|
|
58671
|
-
* observations all agree. So evidence must also STRETCH: the agreeing
|
|
58672
|
-
* observations must span {@link StationaryEvidencePolicy.minSpanMs}. Measured
|
|
58673
|
-
* on 617, real parked cars span 2 min to 24 h; the two momentary stops in the
|
|
58674
|
-
* same day spanned 4.2 s and 8.0 s. Setting the span to
|
|
58675
|
-
* `DEFAULT_MAX_SESSION_HOLD_MS` also buys a structural guarantee: no single
|
|
58676
|
-
* motion session, however long it is held open, can promote on its own.
|
|
58677
|
-
*
|
|
58678
|
-
* ## ONE ledger, two producers
|
|
58679
|
-
*
|
|
58680
|
-
* Evidence is anchored to a BOX, not to a track and not to a session, so an
|
|
58681
|
-
* object confirmed half by an active motion session and half by a periodic
|
|
58682
|
-
* burst still promotes. Occasion A (the session) is implemented and is
|
|
58683
|
-
* sufficient on its own — it is free, because the pipeline is already decoding
|
|
58684
|
-
* and already inferring. Occasion B (the burst) is a precaution for the cases
|
|
58685
|
-
* where A could not have fired at all (hub restarted, camera offline, motion
|
|
58686
|
-
* detection failed, object already parked at boot); it is NOT implemented yet,
|
|
58687
|
-
* and {@link StationaryEvidenceSource} is the seam it will arrive through.
|
|
58688
|
-
*
|
|
58689
|
-
* ## Identity is IoU. There is no grid.
|
|
58690
|
-
*
|
|
58691
|
-
* The predecessor mechanism (`phantom-cells.ts`) quantises a point to a 32 px
|
|
58692
|
-
* cell, and its write path passes a CENTROID while its read path passes a
|
|
58693
|
-
* top-left CORNER — at 32 px every box bigger than 32 px lands in a different
|
|
58694
|
-
* cell on read, and its unit test never crosses that boundary because it calls
|
|
58695
|
-
* both halves with the same literal coordinates. This module has no such
|
|
58696
|
-
* boundary to get wrong: a candidate is found by overlap against the boxes a
|
|
58697
|
-
* device already holds, bounded by {@link EVIDENCE_MAX_CANDIDATES_PER_DEVICE}.
|
|
58698
|
-
* Device 617 — the busiest lot in the fleet — holds 8.
|
|
58699
|
-
*/
|
|
58700
|
-
/**
|
|
58701
58682
|
* `vehicle` and its kin. Every number below is read off device 617 over 24 h
|
|
58702
58683
|
* (18 402 vehicle observations, 290 tracks) with the three parked cars the
|
|
58703
58684
|
* operator can see out of the window as the bench.
|
|
@@ -58719,11 +58700,11 @@ var VEHICLE_EVIDENCE_POLICY = {
|
|
|
58719
58700
|
agreeIou: .5,
|
|
58720
58701
|
minAgreed: 8,
|
|
58721
58702
|
minAgreementRatio: .6,
|
|
58722
|
-
minSpanMs: 12e4
|
|
58723
|
-
promotesByDefault: true
|
|
58703
|
+
minSpanMs: 12e4
|
|
58724
58704
|
};
|
|
58725
58705
|
/**
|
|
58726
|
-
* `person`, and
|
|
58706
|
+
* `person`, promoted like every other class since 2026-09-14 — and still the
|
|
58707
|
+
* strictest numbers in the file, because these are MEASURED, not cautious.
|
|
58727
58708
|
*
|
|
58728
58709
|
* This is not caution, it is a measurement. Replaying 24 h of person
|
|
58729
58710
|
* observations from devices 617, 1443, 590 and 592 through this exact ledger:
|
|
@@ -58742,17 +58723,24 @@ var VEHICLE_EVIDENCE_POLICY = {
|
|
|
58742
58723
|
*
|
|
58743
58724
|
* Promoting a person is a BLINDING decision (D49 read in the direction that
|
|
58744
58725
|
* destroys work: for a car the destructive read is the RETIREMENT — the flood
|
|
58745
|
-
* comes back; for a person it is the BIRTH — we stop seeing them).
|
|
58746
|
-
*
|
|
58747
|
-
*
|
|
58748
|
-
*
|
|
58726
|
+
* comes back; for a person it is the BIRTH — we stop seeing them). It used to
|
|
58727
|
+
* ship OFF behind a per-camera switch for that reason; the switch is gone —
|
|
58728
|
+
* an opt-in nobody turns on produces nothing, and it was producing nothing.
|
|
58729
|
+
*
|
|
58730
|
+
* What makes that affordable now is that agreement is no longer overlap alone:
|
|
58731
|
+
* `observationAgrees` requires the TRACKER to call the subject still, so a
|
|
58732
|
+
* person who merely pauses inside the anchor no longer feeds the candidate.
|
|
58733
|
+
* The statue and the poster still promote — which is what you want, they ARE
|
|
58734
|
+
* furniture — while the real person who stops for a moment no longer does.
|
|
58735
|
+
*
|
|
58736
|
+
* The thresholds stay where the measurement put them. They are the difference
|
|
58737
|
+
* between 22 promotions a day and 10.
|
|
58749
58738
|
*/
|
|
58750
58739
|
var PERSON_EVIDENCE_POLICY = {
|
|
58751
58740
|
agreeIou: .5,
|
|
58752
58741
|
minAgreed: 40,
|
|
58753
58742
|
minAgreementRatio: .95,
|
|
58754
|
-
minSpanMs: 18e5
|
|
58755
|
-
promotesByDefault: false
|
|
58743
|
+
minSpanMs: 18e5
|
|
58756
58744
|
};
|
|
58757
58745
|
/** Classes with no policy of their own fall back to the vehicle numbers —
|
|
58758
58746
|
* `animal`, `package` and friends are all "an object that ended up somewhere",
|
|
@@ -58771,7 +58759,7 @@ function stationaryEvidenceKey(deviceId, className, anchorId) {
|
|
|
58771
58759
|
* Pure, and ordered cheapest-question-first so the reason a candidate was
|
|
58772
58760
|
* refused is the FIRST thing it failed, not the last.
|
|
58773
58761
|
*/
|
|
58774
|
-
function evaluateStationaryEvidence(row, policy
|
|
58762
|
+
function evaluateStationaryEvidence(row, policy) {
|
|
58775
58763
|
const windowSeen = row.windowSeen + row.prevWindowSeen;
|
|
58776
58764
|
const windowAgreed = row.windowAgreed + row.prevWindowAgreed;
|
|
58777
58765
|
const ratio = windowSeen > 0 ? windowAgreed / windowSeen : 0;
|
|
@@ -58782,11 +58770,6 @@ function evaluateStationaryEvidence(row, policy, classEnabled) {
|
|
|
58782
58770
|
ratio,
|
|
58783
58771
|
spanMs
|
|
58784
58772
|
};
|
|
58785
|
-
if (!classEnabled) return {
|
|
58786
|
-
kind: "accumulating",
|
|
58787
|
-
shortfall: "class-not-promoted",
|
|
58788
|
-
...base
|
|
58789
|
-
};
|
|
58790
58773
|
if (row.agreed < policy.minAgreed) return {
|
|
58791
58774
|
kind: "accumulating",
|
|
58792
58775
|
shortfall: "too-few-agreements",
|
|
@@ -58861,6 +58844,32 @@ function matchEvidenceCandidate(rows, className, bbox, agreeIou) {
|
|
|
58861
58844
|
};
|
|
58862
58845
|
}
|
|
58863
58846
|
/**
|
|
58847
|
+
* Does this observation count as EVIDENCE that the object is parked?
|
|
58848
|
+
*
|
|
58849
|
+
* Two questions, deliberately kept apart:
|
|
58850
|
+
*
|
|
58851
|
+
* - IDENTITY — is this the same object in the same place? That is the IoU,
|
|
58852
|
+
* and it is the only notion of sameness in this module.
|
|
58853
|
+
* - STILLNESS — is it going anywhere? That is the TRACKER's verdict, and
|
|
58854
|
+
* nothing here re-derives it.
|
|
58855
|
+
*
|
|
58856
|
+
* Before this, `agrees` was the IoU alone, so a long run of overlaps WAS the
|
|
58857
|
+
* stillness argument — a second derivation of a fact `StateAnalyzer` already
|
|
58858
|
+
* computes per track, slower and able to disagree with it. Now the ledger adds
|
|
58859
|
+
* only what the analyzer cannot know: that the same still object has been here
|
|
58860
|
+
* across a succession of tracks.
|
|
58861
|
+
*
|
|
58862
|
+
* `entering` is the tracker's undecided window — five frames that begin EVERY
|
|
58863
|
+
* track, including a parked car's re-detection. It is not disagreement, and
|
|
58864
|
+
* counting it as such would punish a candidate for the tracker's own warm-up.
|
|
58865
|
+
* An ABSENT verdict likewise falls back to overlap alone: a path that never had
|
|
58866
|
+
* an opinion must not be silently reclassified.
|
|
58867
|
+
*/
|
|
58868
|
+
function observationAgrees(overlap, agreeIou, movementState) {
|
|
58869
|
+
if (overlap < agreeIou) return false;
|
|
58870
|
+
return movementState !== "moving" && movementState !== "left";
|
|
58871
|
+
}
|
|
58872
|
+
/**
|
|
58864
58873
|
* Fold one observation into a candidate.
|
|
58865
58874
|
*
|
|
58866
58875
|
* `agrees` is decided by the CALLER, because M is what makes the ratio mean
|
|
@@ -59163,7 +59172,7 @@ var StationaryEvidenceLedger = class {
|
|
|
59163
59172
|
* slow or failing store can never change what this frame decides. The
|
|
59164
59173
|
* durable half is fired behind the verdict.
|
|
59165
59174
|
*/
|
|
59166
|
-
observe(observation
|
|
59175
|
+
observe(observation) {
|
|
59167
59176
|
const policy = evidencePolicyFor(observation.className);
|
|
59168
59177
|
this.expire(observation.deviceId, observation.timestamp);
|
|
59169
59178
|
const bindKey = observation.trackId === void 0 ? void 0 : `${observation.deviceId}:${observation.trackId}`;
|
|
@@ -59211,18 +59220,18 @@ var StationaryEvidenceLedger = class {
|
|
|
59211
59220
|
this.capDevice(observation.deviceId);
|
|
59212
59221
|
return {
|
|
59213
59222
|
row,
|
|
59214
|
-
verdict: evaluateStationaryEvidence(row, policy
|
|
59223
|
+
verdict: evaluateStationaryEvidence(row, policy),
|
|
59215
59224
|
promoted: false
|
|
59216
59225
|
};
|
|
59217
59226
|
}
|
|
59218
59227
|
if (bindKey !== void 0) this.binding.set(bindKey, row.key);
|
|
59219
59228
|
}
|
|
59220
|
-
const agrees = overlap
|
|
59229
|
+
const agrees = observationAgrees(overlap, policy.agreeIou, observation.movementState);
|
|
59221
59230
|
const next = foldObservation(row, observation, agrees, overlap);
|
|
59222
59231
|
this.ledger.stage(next);
|
|
59223
59232
|
this.dirty.add(next.key);
|
|
59224
59233
|
if (created) this.capDevice(observation.deviceId);
|
|
59225
|
-
const verdict = evaluateStationaryEvidence(next, policy
|
|
59234
|
+
const verdict = evaluateStationaryEvidence(next, policy);
|
|
59226
59235
|
if (verdict.kind !== "promote") return {
|
|
59227
59236
|
row: next,
|
|
59228
59237
|
verdict,
|
|
@@ -59670,8 +59679,32 @@ var StationaryObjectRegistry = class {
|
|
|
59670
59679
|
});
|
|
59671
59680
|
}
|
|
59672
59681
|
}
|
|
59673
|
-
/**
|
|
59674
|
-
|
|
59682
|
+
/**
|
|
59683
|
+
* Promote a parked track into a persisted stationary entry — or, when the
|
|
59684
|
+
* spot is already censused, CONFIRM the entry sitting on it.
|
|
59685
|
+
*
|
|
59686
|
+
* The occupancy question is asked here and nowhere else, with the same
|
|
59687
|
+
* predicate the per-frame filter uses (`findParkedOccupant`): a box the
|
|
59688
|
+
* filter would suppress as a parked object's own can never become a second
|
|
59689
|
+
* parked object. Until 2026-09-14 this method minted whatever it was handed,
|
|
59690
|
+
* and device 617 reported EIGHT parked vehicles — four of them one car, the
|
|
59691
|
+
* same box to the decimal, promoted again roughly once an hour because the
|
|
59692
|
+
* weak-continuation path (D410) keeps feeding the ledger from a track riding
|
|
59693
|
+
* a parked object. The re-promotion is not the anomaly and is not worth
|
|
59694
|
+
* suppressing: it is a fresh, independent confirmation that the thing is
|
|
59695
|
+
* still there. It just must not produce a neighbour.
|
|
59696
|
+
*
|
|
59697
|
+
* A merge never DOWNGRADES the tenant: it advances the clock and adopts an
|
|
59698
|
+
* identity the tenant lacks (the 617 duplicates arrived unlabelled while the
|
|
59699
|
+
* row they belonged to was already `Car`), and it never overwrites one it
|
|
59700
|
+
* has.
|
|
59701
|
+
*/
|
|
59702
|
+
async promote(entry, config) {
|
|
59703
|
+
const occupant = findParkedOccupant(this.list(entry.deviceId), entry.className, entry.bbox, (config ?? this.matchConfig).suppressIou, (config ?? this.matchConfig).wakeAssocIou);
|
|
59704
|
+
if (occupant !== void 0) return {
|
|
59705
|
+
outcome: "merged",
|
|
59706
|
+
entry: this.absorbInto(occupant, entry)
|
|
59707
|
+
};
|
|
59675
59708
|
this.indexEntry(entry.deviceId, entry.id);
|
|
59676
59709
|
this.dirty.delete(entry.id);
|
|
59677
59710
|
await this.ledger.put(entry);
|
|
@@ -59680,6 +59713,33 @@ var StationaryObjectRegistry = class {
|
|
|
59680
59713
|
entry,
|
|
59681
59714
|
timestamp: entry.becameStationaryAt
|
|
59682
59715
|
});
|
|
59716
|
+
return {
|
|
59717
|
+
outcome: "born",
|
|
59718
|
+
entry
|
|
59719
|
+
};
|
|
59720
|
+
}
|
|
59721
|
+
/** Fold a refused promotion into the entry that already holds its spot. */
|
|
59722
|
+
absorbInto(occupant, refused) {
|
|
59723
|
+
const merged = {
|
|
59724
|
+
...occupant,
|
|
59725
|
+
lastConfirmedAt: Math.max(occupant.lastConfirmedAt, refused.becameStationaryAt),
|
|
59726
|
+
...occupant.label === void 0 && refused.label !== void 0 ? { label: refused.label } : {},
|
|
59727
|
+
...occupant.keyFrameMediaId === void 0 && refused.keyFrameMediaId !== void 0 ? { keyFrameMediaId: refused.keyFrameMediaId } : {}
|
|
59728
|
+
};
|
|
59729
|
+
this.ledger.stage(merged);
|
|
59730
|
+
this.dirty.add(merged.id);
|
|
59731
|
+
this.logger.info("stationary promotion absorbed — the spot is already censused", {
|
|
59732
|
+
tags: { deviceId: occupant.deviceId },
|
|
59733
|
+
meta: {
|
|
59734
|
+
deviceId: occupant.deviceId,
|
|
59735
|
+
entryId: occupant.id,
|
|
59736
|
+
className: occupant.className,
|
|
59737
|
+
refusedTrackId: refused.sourceTrackId,
|
|
59738
|
+
parkedForMs: refused.becameStationaryAt - occupant.becameStationaryAt,
|
|
59739
|
+
gainedLabel: occupant.label === void 0 && refused.label !== void 0
|
|
59740
|
+
}
|
|
59741
|
+
});
|
|
59742
|
+
return merged;
|
|
59683
59743
|
}
|
|
59684
59744
|
/**
|
|
59685
59745
|
* Retire entries unconfirmed for longer than the TTL of OBSERVED time, and
|
|
@@ -69950,18 +70010,6 @@ var StationarySettingsSchema = require_dist.object({
|
|
|
69950
70010
|
* session open (also the minimum track age). Creates nothing — birth is
|
|
69951
70011
|
* decided by repeated evidence. `SESSION_STILLNESS_WINDOW_MS`. */
|
|
69952
70012
|
sessionStillnessWindowMs: require_dist.number().int().min(5e3).max(12e4).default(SESSION_STILLNESS_WINDOW_MS),
|
|
69953
|
-
/**
|
|
69954
|
-
* Let a motionless PERSON become a stationary entry on this camera.
|
|
69955
|
-
*
|
|
69956
|
-
* Off, and it is a measurement rather than caution. Replaying 24 h of person
|
|
69957
|
-
* observations from devices 617/1443/590/592 through the evidence ledger
|
|
69958
|
-
* produces 6-22 entries a day depending on how strictly it is tuned, and none
|
|
69959
|
-
* of them can be tuned to zero: device 592 alone yielded eleven candidates at
|
|
69960
|
-
* a perfect agreement ratio between 02:00 and 05:00. A stationary entry
|
|
69961
|
-
* SUPPRESSES its object, so a wrong one makes the camera blind to a person —
|
|
69962
|
-
* turn this on only for a camera where you know what is standing there.
|
|
69963
|
-
*/
|
|
69964
|
-
promotePersons: require_dist.boolean().default(false),
|
|
69965
70013
|
/** Observed-time TTL: retire an entry unconfirmed for this long (measured on
|
|
69966
70014
|
* frames-flowing time, not wall-clock). `ENTRY_TTL_MS`. */
|
|
69967
70015
|
entryTtlMs: require_dist.number().int().min(6e4).max(18e5).default(ENTRY_TTL_MS)
|
|
@@ -69981,7 +70029,6 @@ function resolveStationarySettings(raw) {
|
|
|
69981
70029
|
suppressIou: pick("suppressIou"),
|
|
69982
70030
|
wakeAssocIou: pick("wakeAssocIou"),
|
|
69983
70031
|
sessionStillnessWindowMs: pick("sessionStillnessWindowMs"),
|
|
69984
|
-
promotePersons: pick("promotePersons"),
|
|
69985
70032
|
entryTtlMs: pick("entryTtlMs")
|
|
69986
70033
|
};
|
|
69987
70034
|
}
|
|
@@ -70517,13 +70564,6 @@ function buildDetectionSettingsSections() {
|
|
|
70517
70564
|
unit: "s",
|
|
70518
70565
|
displayScale: 1e3
|
|
70519
70566
|
},
|
|
70520
|
-
{
|
|
70521
|
-
type: "boolean",
|
|
70522
|
-
key: "promotePersons",
|
|
70523
|
-
label: "Also park motionless people",
|
|
70524
|
-
description: "Off by default. A stationary entry SUPPRESSES its object, so a wrong one makes this camera blind to a person standing there. Replaying a day of real person detections produced entries on every camera tried, at every threshold tried — turn this on only where you know what is standing still.",
|
|
70525
|
-
default: STATIONARY_DEFAULTS.promotePersons
|
|
70526
|
-
},
|
|
70527
70567
|
{
|
|
70528
70568
|
type: "slider",
|
|
70529
70569
|
key: "entryTtlMs",
|
|
@@ -73867,11 +73907,7 @@ var ZoneAnalyticsProvider = class {
|
|
|
73867
73907
|
});
|
|
73868
73908
|
}
|
|
73869
73909
|
this.snapshots.set(input.deviceId, snapshot);
|
|
73870
|
-
|
|
73871
|
-
if (noteOccupancy !== void 0) {
|
|
73872
|
-
noteOccupancy(input.deviceId, total, input.timestamp);
|
|
73873
|
-
for (const zone of snapshot.zones) noteOccupancy(input.deviceId, zone.totalObjects, input.timestamp, zone.zoneId);
|
|
73874
|
-
}
|
|
73910
|
+
this.noteLtsSamples(input.deviceId, snapshot, input.timestamp);
|
|
73875
73911
|
this.appendHistory(input.deviceId, snapshot);
|
|
73876
73912
|
this.emitSnapshot(input.deviceId, snapshot);
|
|
73877
73913
|
this.sliceThrottle.push(input.deviceId, snapshot);
|
|
@@ -73938,11 +73974,30 @@ var ZoneAnalyticsProvider = class {
|
|
|
73938
73974
|
});
|
|
73939
73975
|
if (snapshot) {
|
|
73940
73976
|
this.appendHistory(deviceId, snapshot);
|
|
73977
|
+
this.noteLtsSamples(deviceId, snapshot, now);
|
|
73941
73978
|
this.emitSnapshot(deviceId, snapshot);
|
|
73942
73979
|
this.sliceThrottle.push(deviceId, snapshot);
|
|
73943
73980
|
}
|
|
73944
73981
|
}
|
|
73945
73982
|
}
|
|
73983
|
+
/**
|
|
73984
|
+
* Append this snapshot to the long-term occupancy series — frame-wide first
|
|
73985
|
+
* (`scope: ''`), then one sample per zone.
|
|
73986
|
+
*
|
|
73987
|
+
* Shared by BOTH producers on purpose. It used to live inline in
|
|
73988
|
+
* `recordFrame`, so the baseline sampler — the only producer a camera with no
|
|
73989
|
+
* frames flowing has — wrote the RAM ring, the NC watcher and the state slice
|
|
73990
|
+
* but never LTS. The long-term series therefore had a hole exactly where the
|
|
73991
|
+
* scene was nothing but parked objects: the one span an occupancy history is
|
|
73992
|
+
* asked about. The RAM ring showed a flat line over the same minutes, which
|
|
73993
|
+
* is what made the gap invisible.
|
|
73994
|
+
*/
|
|
73995
|
+
noteLtsSamples(deviceId, snapshot, timestamp) {
|
|
73996
|
+
const note = this.ctx.onOccupancySample;
|
|
73997
|
+
if (note === void 0) return;
|
|
73998
|
+
note(deviceId, snapshot.frame.totalObjects, timestamp);
|
|
73999
|
+
for (const zone of snapshot.zones) note(deviceId, zone.totalObjects, timestamp, zone.zoneId);
|
|
74000
|
+
}
|
|
73946
74001
|
/** Push one snapshot to the occupancy tap, isolating any consumer throw from
|
|
73947
74002
|
* the frame path (occupancy notifications must never break analytics). */
|
|
73948
74003
|
emitSnapshot(deviceId, snapshot) {
|
|
@@ -78277,7 +78332,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
78277
78332
|
}
|
|
78278
78333
|
});
|
|
78279
78334
|
}
|
|
78280
|
-
await this.processFrame(deviceId, frame, data.source ?? "pipeline", data.frameHandle, data.detailSteps);
|
|
78335
|
+
await this.processFrame(deviceId, frame, data.source ?? "pipeline", data.frameHandle, data.detailSteps, data.occasion ?? "session");
|
|
78281
78336
|
}
|
|
78282
78337
|
/**
|
|
78283
78338
|
* Run one detection frame through the analysis layers for a given
|
|
@@ -78287,7 +78342,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
78287
78342
|
* tracking/zone/event state never crosses between sources. Emits the
|
|
78288
78343
|
* SAME canonical events, distinguished only by `source`.
|
|
78289
78344
|
*/
|
|
78290
|
-
async processFrame(deviceId, frame, source, rawFrameHandle, detailSteps) {
|
|
78345
|
+
async processFrame(deviceId, frame, source, rawFrameHandle, detailSteps, occasion = "session") {
|
|
78291
78346
|
if (this.shuttingDown) return;
|
|
78292
78347
|
if (!await this.bindingCache.isActive(deviceId)) {
|
|
78293
78348
|
const now = Date.now();
|
|
@@ -78695,7 +78750,6 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
78695
78750
|
const evidence = this.stationaryEvidence;
|
|
78696
78751
|
for (const t of result.tracked) {
|
|
78697
78752
|
if (packageClasses.has(t.className)) continue;
|
|
78698
|
-
const classEnabled = t.className === "person" ? stationarySettings.promotePersons : evidencePolicyFor(t.className).promotesByDefault;
|
|
78699
78753
|
const active = this.trackStore?.peekActive(t.trackId) ?? null;
|
|
78700
78754
|
const observedLabel = active === null ? void 0 : displayLabel(active);
|
|
78701
78755
|
const observedKeyFrame = this.residents.keyFrameMediaId(t.trackId);
|
|
@@ -78704,12 +78758,13 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
78704
78758
|
className: t.className,
|
|
78705
78759
|
bbox: { ...t.bbox },
|
|
78706
78760
|
timestamp: result.timestamp,
|
|
78707
|
-
source:
|
|
78761
|
+
source: occasion,
|
|
78708
78762
|
trackId: t.trackId,
|
|
78709
78763
|
...observedKeyFrame !== void 0 ? { keyFrameMediaId: observedKeyFrame } : {},
|
|
78710
|
-
...observedLabel !== void 0 ? { label: observedLabel } : {}
|
|
78764
|
+
...observedLabel !== void 0 ? { label: observedLabel } : {},
|
|
78765
|
+
movementState: t.state
|
|
78711
78766
|
};
|
|
78712
|
-
const outcome = evidence.observe(observation
|
|
78767
|
+
const outcome = evidence.observe(observation);
|
|
78713
78768
|
if (outcome === void 0 || !outcome.promoted) continue;
|
|
78714
78769
|
this.birthStationaryFromEvidence({
|
|
78715
78770
|
deviceId,
|
|
@@ -81812,7 +81867,13 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
81812
81867
|
...label !== void 0 ? { label } : {},
|
|
81813
81868
|
...keyFrameMediaId !== void 0 ? { keyFrameMediaId } : {}
|
|
81814
81869
|
};
|
|
81815
|
-
if (plausible)
|
|
81870
|
+
if (plausible) {
|
|
81871
|
+
const gate = this.stationarySettingsFromCache(deviceId);
|
|
81872
|
+
this.stationaryRegistry?.promote(entry, {
|
|
81873
|
+
suppressIou: gate.suppressIou,
|
|
81874
|
+
wakeAssocIou: gate.wakeAssocIou
|
|
81875
|
+
});
|
|
81876
|
+
}
|
|
81816
81877
|
processor.dropTrack(track.trackId);
|
|
81817
81878
|
this.trackStore?.closeActive(track.trackId, "stationary-promotion").catch((err) => {
|
|
81818
81879
|
this.ctx.logger.warn("stationary promotion: closing the source track failed — its media, events and vector are now unowned", {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as TimelapseRuleSchema, $t as boolean, A as NcConditionDescriptorSchema, At as
|
|
1
|
+
import { $ as TimelapseRuleSchema, $t as boolean, A as NcConditionDescriptorSchema, At as readTimelapseGeneratedAt, B as NcTaxonomySchema, Bt as errMsg$1, C as MOTION_CLOSE_AFTER_MS, Ct as mayWriteToLocation, D as NC_DEFAULT_SNOOZE_MINUTES, Dt as pipelineAnalyticsCapability, E as NC_CONDITION_CATALOG, Et as pickClusterStepModels, F as NcScheduleSchema, Ft as subKindsOf, Gt as customAction, Ht as CamProfileSchema, I as NcSnoozeInputSchema, It as systemEventFilterApplies, J as SCENE_DIVERGED, Jt as isDeviceScopedCap, K as SCENE_DEFAULT_ANCHOR_THRESHOLD, Kt as defineCustomActions, L as NcSnoozeSchema, Lt as vectorDimFromBase64, M as NcRulePatchSchema, N as NcRuleSchema, Nt as sceneMonitorCapability, Ot as plateGalleryCapability, P as NcRuleTargetSchema, Pt as storageOccupancyCapability, Q as TimelapseRulePatchSchema, Qt as array, R as NcSnoozeSuppressedSchema, Rt as videoclipsCapability, St as kebabToCamel, T as NC_ALARM_SYSTEM_EVENT_KINDS, U as RECORDING_EXPORT_MAX_READ_BYTES, Ut as DeviceType, V as OpsLogEntrySchema, Vt as BaseAddon, W as RetrainStatusSchema, Wt as createEvent, X as TIMELAPSE_DENSE_FLOOR_SEC, Xt as sleep$1, Y as SceneMonitorSchema, Yt as nodePin, Z as TimelapseRuleInputSchema, Zt as _enum, _ as FULL_IMAGE_BBOX, _t as isDetectionMacroClass, a as CLUSTER_MODEL_SCOPED_STEPS, an as record, at as audioMetricsCapability, b as MAX_ARCHIVED_DEBUG_NOTES, bt as isScheduleActive, cn as EventCategory, ct as cosineSimilarity$1, dt as encodeVectorBase64, en as discriminatedUnion, et as TrackSourceSchema, f as DeclaredDevices, ft as evaluateSensorEdge, g as FIRST_LEVEL_MACRO_CLASSES, h as EVENT_PAD_MS, ht as failureContributionCapability, i as BirthDecisionRecordSchema, in as partialRecord, it as assertTimelapseCadences, j as NcRuleInputSchema, jt as resolveLocationMode, k as NC_TAXONOMY, kt as readDeviceStateFrom, lt as deriveRecordingMode, m as EVENT_OWNER_TYPES, mt as faceGalleryCapability, n as ArchivedDebugNoteSchema, nn as number, nt as addonWidgetsSourceCapability, o as COCO_TO_MACRO, on as string, ot as audioModeOf, p as EVENT_KIND_BY_CAP, pt as evictionPolicyOfLocation, q as SCENE_DEFAULT_UNCOVERED_POLICY, qt as hydrateSchema, r as BaseDevice, rn as object, rt as alarmPanelCapability, s as DEFAULT_EVENT_COLOR, sn as unknown, st as buildEventKindDescriptor, t as AUDIO_MACRO_LABELS, tn as literal, tt as VISIT_MERGE_GAP_MS, u as DETECTION_MACRO_CLASSES, v as FailureCounters, vt as isEventOwnerType, w as MediaFileKindEnum, wt as notificationRulesCapability, x as MAX_BIRTH_DECISION_RECORDS, xt as isSourceCap, y as LabelAttributionSchema, yt as isMediaOwnerType, z as NcSystemEventKindSchema, zt as zoneAnalyticsCapability } from "../dist-C-1xTyB-.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";
|
|
@@ -55551,7 +55551,8 @@ function buildOverlayDetections(input) {
|
|
|
55551
55551
|
...det,
|
|
55552
55552
|
track: {
|
|
55553
55553
|
trackId: t.trackId,
|
|
55554
|
-
zones: t.zones
|
|
55554
|
+
zones: t.zones,
|
|
55555
|
+
...t.state !== void 0 ? { movementState: t.state } : {}
|
|
55555
55556
|
},
|
|
55556
55557
|
labels: hasName && !alreadyLabeled ? [...det.labels, {
|
|
55557
55558
|
label: name,
|
|
@@ -57509,6 +57510,10 @@ function centroidInside(b, p) {
|
|
|
57509
57510
|
}
|
|
57510
57511
|
/** Parked-box coverage (intersection / entry area) that counts as swallowed. */
|
|
57511
57512
|
var SWALLOW_MIN_COVERAGE = .5;
|
|
57513
|
+
/** Fraction of a detection's OWN area that must lie inside a parked entry for
|
|
57514
|
+
* the entry to swallow it. Deliberately near-total: a fragment of a parked
|
|
57515
|
+
* object does not stick out of it. */
|
|
57516
|
+
var FRAGMENT_MIN_CONTAINMENT = .9;
|
|
57512
57517
|
function isSceneSizedBox(bbox, frameWidth, frameHeight) {
|
|
57513
57518
|
const frame = frameWidth * frameHeight;
|
|
57514
57519
|
return frame > 0 && bbox.w * bbox.h / frame >= .55;
|
|
@@ -57528,6 +57533,29 @@ function detectionSwallowsEntry(det, parked) {
|
|
|
57528
57533
|
if (!centroidInside(det, centroid(parked))) return false;
|
|
57529
57534
|
return intersectionArea$2(parked, det) / entryArea >= SWALLOW_MIN_COVERAGE;
|
|
57530
57535
|
}
|
|
57536
|
+
/**
|
|
57537
|
+
* True when `det` is a FRAGMENT of the parked object — much smaller than the
|
|
57538
|
+
* entry and sitting (almost) wholly inside it.
|
|
57539
|
+
*
|
|
57540
|
+
* The mirror of {@link detectionSwallowsEntry}, and the case neither IoU
|
|
57541
|
+
* threshold can see. Measured on 617: a 34x137 box inside a 452x420 parked
|
|
57542
|
+
* motorcycle scores IoU 0.024 — BELOW `wakeAssocIou` (0.1), so the filter read
|
|
57543
|
+
* it as "no detection overlaps this entry" and let it open a track, accumulate
|
|
57544
|
+
* its own evidence and promote into a registry entry of its own. The camera
|
|
57545
|
+
* then reported two parked vehicles where there was one motorcycle.
|
|
57546
|
+
*
|
|
57547
|
+
* The size bound is the reciprocal of {@link SWALLOW_AREA_RATIO}, so the two
|
|
57548
|
+
* swallow rules meet without overlapping: a box between 1/1.35 and 1.35 of the
|
|
57549
|
+
* entry's area, contained, already scores IoU >= 0.74 and is answered by
|
|
57550
|
+
* `suppressIou`. Nothing is a fragment and a near-identical box at once.
|
|
57551
|
+
*/
|
|
57552
|
+
function entrySwallowsDetection(parked, det) {
|
|
57553
|
+
const entryArea = parked.w * parked.h;
|
|
57554
|
+
const detArea = det.w * det.h;
|
|
57555
|
+
if (entryArea <= 0 || detArea <= 0) return false;
|
|
57556
|
+
if (detArea > entryArea / 1.35) return false;
|
|
57557
|
+
return intersectionArea$2(parked, det) / detArea >= FRAGMENT_MIN_CONTAINMENT;
|
|
57558
|
+
}
|
|
57531
57559
|
function parkedEntryHoldsDetection(entry, detBbox, config) {
|
|
57532
57560
|
const overlap = iou(entry.bbox, detBbox);
|
|
57533
57561
|
if (overlap >= config.suppressIou) return {
|
|
@@ -57542,6 +57570,10 @@ function parkedEntryHoldsDetection(entry, detBbox, config) {
|
|
|
57542
57570
|
strong: false,
|
|
57543
57571
|
holds: false
|
|
57544
57572
|
};
|
|
57573
|
+
if (entrySwallowsDetection(entry.bbox, detBbox)) return {
|
|
57574
|
+
strong: false,
|
|
57575
|
+
holds: true
|
|
57576
|
+
};
|
|
57545
57577
|
return {
|
|
57546
57578
|
strong: false,
|
|
57547
57579
|
holds: overlap >= config.wakeAssocIou && centroidInside(entry.bbox, centroid(detBbox))
|
|
@@ -57560,6 +57592,34 @@ function bboxMatchesParkedEntry(bbox, className, entries, suppressIou = SUPPRESS
|
|
|
57560
57592
|
}
|
|
57561
57593
|
return false;
|
|
57562
57594
|
}
|
|
57595
|
+
/**
|
|
57596
|
+
* The live entry already censusing this spot, if any — the ONE answer to "is
|
|
57597
|
+
* this place taken?", asked at BIRTH.
|
|
57598
|
+
*
|
|
57599
|
+
* It is deliberately the same predicate the per-frame filter uses
|
|
57600
|
+
* ({@link parkedEntryHoldsDetection}): a box the filter would suppress as a
|
|
57601
|
+
* parked object's own must not be able to become a second parked object. Device
|
|
57602
|
+
* 617 held four rows for one car and a fifth for a fragment of a motorcycle
|
|
57603
|
+
* because the birth path asked nothing at all.
|
|
57604
|
+
*/
|
|
57605
|
+
function findParkedOccupant(entries, className, bbox, suppressIou = SUPPRESS_IOU, wakeAssocIou = WAKE_ASSOC_IOU) {
|
|
57606
|
+
const cls = normaliseStationaryClass(className);
|
|
57607
|
+
const config = {
|
|
57608
|
+
suppressIou,
|
|
57609
|
+
wakeAssocIou
|
|
57610
|
+
};
|
|
57611
|
+
let best;
|
|
57612
|
+
for (const e of entries) {
|
|
57613
|
+
if (normaliseStationaryClass(e.className) !== cls) continue;
|
|
57614
|
+
if (!parkedEntryHoldsDetection(e, bbox, config).holds) continue;
|
|
57615
|
+
const overlap = iou(e.bbox, bbox);
|
|
57616
|
+
if (best === void 0 || overlap > best.overlap) best = {
|
|
57617
|
+
entry: e,
|
|
57618
|
+
overlap
|
|
57619
|
+
};
|
|
57620
|
+
}
|
|
57621
|
+
return best?.entry;
|
|
57622
|
+
}
|
|
57563
57623
|
/** Best-shot veto: current crop is a parked object the trail did not start on. */
|
|
57564
57624
|
function thumbnailWouldShowParkedNotSubject(input) {
|
|
57565
57625
|
if (!bboxMatchesParkedEntry(input.currentBbox, input.className, input.entries)) return false;
|
|
@@ -58545,85 +58605,6 @@ var StartedBirthRegistry = class {
|
|
|
58545
58605
|
//#endregion
|
|
58546
58606
|
//#region src/pipeline-analytics/pipeline/stationary/stationary-evidence.ts
|
|
58547
58607
|
/**
|
|
58548
|
-
* stationary-evidence — how a parked object EARNS a registry entry.
|
|
58549
|
-
*
|
|
58550
|
-
* ## The criterion this replaces, and why it was measuring the wrong thing
|
|
58551
|
-
*
|
|
58552
|
-
* Until 2026-08-28 an object became stationary by TRACK CONTINUITY: one track
|
|
58553
|
-
* had to stay alive and barely move for `PROMOTION_WINDOW_MS` (30 s). That
|
|
58554
|
-
* rewards an object that is *seen well and often* — precisely the opposite of
|
|
58555
|
-
* what the operator needs. Measured on device 617 (the parking lot), 24 h:
|
|
58556
|
-
*
|
|
58557
|
-
* - three cars are parked in view; the registry held exactly ONE entry;
|
|
58558
|
-
* - the two it missed were re-discovered as **140 and 112 separate tracks**,
|
|
58559
|
-
* median life **6.8 s**, p90 29 s. Not one of them could ever reach 30 s.
|
|
58560
|
-
* - those two objects alone produced **13 494 of 18 402** vehicle
|
|
58561
|
-
* observations (73%) — the whole re-spawn flood the registry exists to stop.
|
|
58562
|
-
*
|
|
58563
|
-
* The track dies; the OBJECT does not. So the evidence must outlive the track.
|
|
58564
|
-
*
|
|
58565
|
-
* ## The new criterion: a RATIO, not a count
|
|
58566
|
-
*
|
|
58567
|
-
* "The same thing, in the same place, most of the times it was seen."
|
|
58568
|
-
*
|
|
58569
|
-
* N — observations that AGREED with the candidate's anchor box (IoU ≥
|
|
58570
|
-
* {@link StationaryEvidencePolicy.agreeIou}).
|
|
58571
|
-
* M — observations OFFERED by a track once it bound to this candidate,
|
|
58572
|
-
* agreeing or not.
|
|
58573
|
-
*
|
|
58574
|
-
* A count would rescue nothing: a badly-lit car at night is seen rarely, and
|
|
58575
|
-
* any floor high enough to exclude a passer-by would exclude it too. A RATIO
|
|
58576
|
-
* asks a question a rarely-seen object can still answer well — 8 sightings out
|
|
58577
|
-
* of 10 in the same spot is stationary even though 10 is a small number.
|
|
58578
|
-
*
|
|
58579
|
-
* The dilution is the whole point, and it is why M counts a bound track's
|
|
58580
|
-
* observations even after it walks away: a car that drives THROUGH a spot binds
|
|
58581
|
-
* for a few frames and then contributes dozens of disagreeing ones. Measured on
|
|
58582
|
-
* 617 the two populations do not overlap:
|
|
58583
|
-
*
|
|
58584
|
-
* | candidate | N / M | ratio |
|
|
58585
|
-
* | ---------------------------- | ----------- | ----- |
|
|
58586
|
-
* | parked car A (missed today) | 6405 / 6467 | 0.99 |
|
|
58587
|
-
* | parked car B (missed today) | 343 / 343 | 1.00 |
|
|
58588
|
-
* | car crossing the lot | 5 / 70 | 0.07 |
|
|
58589
|
-
* | car turning through the lot | 14 / 91 | 0.15 |
|
|
58590
|
-
* | car manoeuvring | 12 / 41 | 0.29 |
|
|
58591
|
-
*
|
|
58592
|
-
* The threshold sits in the empty band between 0.29 and 0.99.
|
|
58593
|
-
*
|
|
58594
|
-
* ## The span is what a ratio cannot say
|
|
58595
|
-
*
|
|
58596
|
-
* A ratio alone promotes a car that paused for four seconds — its handful of
|
|
58597
|
-
* observations all agree. So evidence must also STRETCH: the agreeing
|
|
58598
|
-
* observations must span {@link StationaryEvidencePolicy.minSpanMs}. Measured
|
|
58599
|
-
* on 617, real parked cars span 2 min to 24 h; the two momentary stops in the
|
|
58600
|
-
* same day spanned 4.2 s and 8.0 s. Setting the span to
|
|
58601
|
-
* `DEFAULT_MAX_SESSION_HOLD_MS` also buys a structural guarantee: no single
|
|
58602
|
-
* motion session, however long it is held open, can promote on its own.
|
|
58603
|
-
*
|
|
58604
|
-
* ## ONE ledger, two producers
|
|
58605
|
-
*
|
|
58606
|
-
* Evidence is anchored to a BOX, not to a track and not to a session, so an
|
|
58607
|
-
* object confirmed half by an active motion session and half by a periodic
|
|
58608
|
-
* burst still promotes. Occasion A (the session) is implemented and is
|
|
58609
|
-
* sufficient on its own — it is free, because the pipeline is already decoding
|
|
58610
|
-
* and already inferring. Occasion B (the burst) is a precaution for the cases
|
|
58611
|
-
* where A could not have fired at all (hub restarted, camera offline, motion
|
|
58612
|
-
* detection failed, object already parked at boot); it is NOT implemented yet,
|
|
58613
|
-
* and {@link StationaryEvidenceSource} is the seam it will arrive through.
|
|
58614
|
-
*
|
|
58615
|
-
* ## Identity is IoU. There is no grid.
|
|
58616
|
-
*
|
|
58617
|
-
* The predecessor mechanism (`phantom-cells.ts`) quantises a point to a 32 px
|
|
58618
|
-
* cell, and its write path passes a CENTROID while its read path passes a
|
|
58619
|
-
* top-left CORNER — at 32 px every box bigger than 32 px lands in a different
|
|
58620
|
-
* cell on read, and its unit test never crosses that boundary because it calls
|
|
58621
|
-
* both halves with the same literal coordinates. This module has no such
|
|
58622
|
-
* boundary to get wrong: a candidate is found by overlap against the boxes a
|
|
58623
|
-
* device already holds, bounded by {@link EVIDENCE_MAX_CANDIDATES_PER_DEVICE}.
|
|
58624
|
-
* Device 617 — the busiest lot in the fleet — holds 8.
|
|
58625
|
-
*/
|
|
58626
|
-
/**
|
|
58627
58608
|
* `vehicle` and its kin. Every number below is read off device 617 over 24 h
|
|
58628
58609
|
* (18 402 vehicle observations, 290 tracks) with the three parked cars the
|
|
58629
58610
|
* operator can see out of the window as the bench.
|
|
@@ -58645,11 +58626,11 @@ var VEHICLE_EVIDENCE_POLICY = {
|
|
|
58645
58626
|
agreeIou: .5,
|
|
58646
58627
|
minAgreed: 8,
|
|
58647
58628
|
minAgreementRatio: .6,
|
|
58648
|
-
minSpanMs: 12e4
|
|
58649
|
-
promotesByDefault: true
|
|
58629
|
+
minSpanMs: 12e4
|
|
58650
58630
|
};
|
|
58651
58631
|
/**
|
|
58652
|
-
* `person`, and
|
|
58632
|
+
* `person`, promoted like every other class since 2026-09-14 — and still the
|
|
58633
|
+
* strictest numbers in the file, because these are MEASURED, not cautious.
|
|
58653
58634
|
*
|
|
58654
58635
|
* This is not caution, it is a measurement. Replaying 24 h of person
|
|
58655
58636
|
* observations from devices 617, 1443, 590 and 592 through this exact ledger:
|
|
@@ -58668,17 +58649,24 @@ var VEHICLE_EVIDENCE_POLICY = {
|
|
|
58668
58649
|
*
|
|
58669
58650
|
* Promoting a person is a BLINDING decision (D49 read in the direction that
|
|
58670
58651
|
* destroys work: for a car the destructive read is the RETIREMENT — the flood
|
|
58671
|
-
* comes back; for a person it is the BIRTH — we stop seeing them).
|
|
58672
|
-
*
|
|
58673
|
-
*
|
|
58674
|
-
*
|
|
58652
|
+
* comes back; for a person it is the BIRTH — we stop seeing them). It used to
|
|
58653
|
+
* ship OFF behind a per-camera switch for that reason; the switch is gone —
|
|
58654
|
+
* an opt-in nobody turns on produces nothing, and it was producing nothing.
|
|
58655
|
+
*
|
|
58656
|
+
* What makes that affordable now is that agreement is no longer overlap alone:
|
|
58657
|
+
* `observationAgrees` requires the TRACKER to call the subject still, so a
|
|
58658
|
+
* person who merely pauses inside the anchor no longer feeds the candidate.
|
|
58659
|
+
* The statue and the poster still promote — which is what you want, they ARE
|
|
58660
|
+
* furniture — while the real person who stops for a moment no longer does.
|
|
58661
|
+
*
|
|
58662
|
+
* The thresholds stay where the measurement put them. They are the difference
|
|
58663
|
+
* between 22 promotions a day and 10.
|
|
58675
58664
|
*/
|
|
58676
58665
|
var PERSON_EVIDENCE_POLICY = {
|
|
58677
58666
|
agreeIou: .5,
|
|
58678
58667
|
minAgreed: 40,
|
|
58679
58668
|
minAgreementRatio: .95,
|
|
58680
|
-
minSpanMs: 18e5
|
|
58681
|
-
promotesByDefault: false
|
|
58669
|
+
minSpanMs: 18e5
|
|
58682
58670
|
};
|
|
58683
58671
|
/** Classes with no policy of their own fall back to the vehicle numbers —
|
|
58684
58672
|
* `animal`, `package` and friends are all "an object that ended up somewhere",
|
|
@@ -58697,7 +58685,7 @@ function stationaryEvidenceKey(deviceId, className, anchorId) {
|
|
|
58697
58685
|
* Pure, and ordered cheapest-question-first so the reason a candidate was
|
|
58698
58686
|
* refused is the FIRST thing it failed, not the last.
|
|
58699
58687
|
*/
|
|
58700
|
-
function evaluateStationaryEvidence(row, policy
|
|
58688
|
+
function evaluateStationaryEvidence(row, policy) {
|
|
58701
58689
|
const windowSeen = row.windowSeen + row.prevWindowSeen;
|
|
58702
58690
|
const windowAgreed = row.windowAgreed + row.prevWindowAgreed;
|
|
58703
58691
|
const ratio = windowSeen > 0 ? windowAgreed / windowSeen : 0;
|
|
@@ -58708,11 +58696,6 @@ function evaluateStationaryEvidence(row, policy, classEnabled) {
|
|
|
58708
58696
|
ratio,
|
|
58709
58697
|
spanMs
|
|
58710
58698
|
};
|
|
58711
|
-
if (!classEnabled) return {
|
|
58712
|
-
kind: "accumulating",
|
|
58713
|
-
shortfall: "class-not-promoted",
|
|
58714
|
-
...base
|
|
58715
|
-
};
|
|
58716
58699
|
if (row.agreed < policy.minAgreed) return {
|
|
58717
58700
|
kind: "accumulating",
|
|
58718
58701
|
shortfall: "too-few-agreements",
|
|
@@ -58787,6 +58770,32 @@ function matchEvidenceCandidate(rows, className, bbox, agreeIou) {
|
|
|
58787
58770
|
};
|
|
58788
58771
|
}
|
|
58789
58772
|
/**
|
|
58773
|
+
* Does this observation count as EVIDENCE that the object is parked?
|
|
58774
|
+
*
|
|
58775
|
+
* Two questions, deliberately kept apart:
|
|
58776
|
+
*
|
|
58777
|
+
* - IDENTITY — is this the same object in the same place? That is the IoU,
|
|
58778
|
+
* and it is the only notion of sameness in this module.
|
|
58779
|
+
* - STILLNESS — is it going anywhere? That is the TRACKER's verdict, and
|
|
58780
|
+
* nothing here re-derives it.
|
|
58781
|
+
*
|
|
58782
|
+
* Before this, `agrees` was the IoU alone, so a long run of overlaps WAS the
|
|
58783
|
+
* stillness argument — a second derivation of a fact `StateAnalyzer` already
|
|
58784
|
+
* computes per track, slower and able to disagree with it. Now the ledger adds
|
|
58785
|
+
* only what the analyzer cannot know: that the same still object has been here
|
|
58786
|
+
* across a succession of tracks.
|
|
58787
|
+
*
|
|
58788
|
+
* `entering` is the tracker's undecided window — five frames that begin EVERY
|
|
58789
|
+
* track, including a parked car's re-detection. It is not disagreement, and
|
|
58790
|
+
* counting it as such would punish a candidate for the tracker's own warm-up.
|
|
58791
|
+
* An ABSENT verdict likewise falls back to overlap alone: a path that never had
|
|
58792
|
+
* an opinion must not be silently reclassified.
|
|
58793
|
+
*/
|
|
58794
|
+
function observationAgrees(overlap, agreeIou, movementState) {
|
|
58795
|
+
if (overlap < agreeIou) return false;
|
|
58796
|
+
return movementState !== "moving" && movementState !== "left";
|
|
58797
|
+
}
|
|
58798
|
+
/**
|
|
58790
58799
|
* Fold one observation into a candidate.
|
|
58791
58800
|
*
|
|
58792
58801
|
* `agrees` is decided by the CALLER, because M is what makes the ratio mean
|
|
@@ -59089,7 +59098,7 @@ var StationaryEvidenceLedger = class {
|
|
|
59089
59098
|
* slow or failing store can never change what this frame decides. The
|
|
59090
59099
|
* durable half is fired behind the verdict.
|
|
59091
59100
|
*/
|
|
59092
|
-
observe(observation
|
|
59101
|
+
observe(observation) {
|
|
59093
59102
|
const policy = evidencePolicyFor(observation.className);
|
|
59094
59103
|
this.expire(observation.deviceId, observation.timestamp);
|
|
59095
59104
|
const bindKey = observation.trackId === void 0 ? void 0 : `${observation.deviceId}:${observation.trackId}`;
|
|
@@ -59137,18 +59146,18 @@ var StationaryEvidenceLedger = class {
|
|
|
59137
59146
|
this.capDevice(observation.deviceId);
|
|
59138
59147
|
return {
|
|
59139
59148
|
row,
|
|
59140
|
-
verdict: evaluateStationaryEvidence(row, policy
|
|
59149
|
+
verdict: evaluateStationaryEvidence(row, policy),
|
|
59141
59150
|
promoted: false
|
|
59142
59151
|
};
|
|
59143
59152
|
}
|
|
59144
59153
|
if (bindKey !== void 0) this.binding.set(bindKey, row.key);
|
|
59145
59154
|
}
|
|
59146
|
-
const agrees = overlap
|
|
59155
|
+
const agrees = observationAgrees(overlap, policy.agreeIou, observation.movementState);
|
|
59147
59156
|
const next = foldObservation(row, observation, agrees, overlap);
|
|
59148
59157
|
this.ledger.stage(next);
|
|
59149
59158
|
this.dirty.add(next.key);
|
|
59150
59159
|
if (created) this.capDevice(observation.deviceId);
|
|
59151
|
-
const verdict = evaluateStationaryEvidence(next, policy
|
|
59160
|
+
const verdict = evaluateStationaryEvidence(next, policy);
|
|
59152
59161
|
if (verdict.kind !== "promote") return {
|
|
59153
59162
|
row: next,
|
|
59154
59163
|
verdict,
|
|
@@ -59596,8 +59605,32 @@ var StationaryObjectRegistry = class {
|
|
|
59596
59605
|
});
|
|
59597
59606
|
}
|
|
59598
59607
|
}
|
|
59599
|
-
/**
|
|
59600
|
-
|
|
59608
|
+
/**
|
|
59609
|
+
* Promote a parked track into a persisted stationary entry — or, when the
|
|
59610
|
+
* spot is already censused, CONFIRM the entry sitting on it.
|
|
59611
|
+
*
|
|
59612
|
+
* The occupancy question is asked here and nowhere else, with the same
|
|
59613
|
+
* predicate the per-frame filter uses (`findParkedOccupant`): a box the
|
|
59614
|
+
* filter would suppress as a parked object's own can never become a second
|
|
59615
|
+
* parked object. Until 2026-09-14 this method minted whatever it was handed,
|
|
59616
|
+
* and device 617 reported EIGHT parked vehicles — four of them one car, the
|
|
59617
|
+
* same box to the decimal, promoted again roughly once an hour because the
|
|
59618
|
+
* weak-continuation path (D410) keeps feeding the ledger from a track riding
|
|
59619
|
+
* a parked object. The re-promotion is not the anomaly and is not worth
|
|
59620
|
+
* suppressing: it is a fresh, independent confirmation that the thing is
|
|
59621
|
+
* still there. It just must not produce a neighbour.
|
|
59622
|
+
*
|
|
59623
|
+
* A merge never DOWNGRADES the tenant: it advances the clock and adopts an
|
|
59624
|
+
* identity the tenant lacks (the 617 duplicates arrived unlabelled while the
|
|
59625
|
+
* row they belonged to was already `Car`), and it never overwrites one it
|
|
59626
|
+
* has.
|
|
59627
|
+
*/
|
|
59628
|
+
async promote(entry, config) {
|
|
59629
|
+
const occupant = findParkedOccupant(this.list(entry.deviceId), entry.className, entry.bbox, (config ?? this.matchConfig).suppressIou, (config ?? this.matchConfig).wakeAssocIou);
|
|
59630
|
+
if (occupant !== void 0) return {
|
|
59631
|
+
outcome: "merged",
|
|
59632
|
+
entry: this.absorbInto(occupant, entry)
|
|
59633
|
+
};
|
|
59601
59634
|
this.indexEntry(entry.deviceId, entry.id);
|
|
59602
59635
|
this.dirty.delete(entry.id);
|
|
59603
59636
|
await this.ledger.put(entry);
|
|
@@ -59606,6 +59639,33 @@ var StationaryObjectRegistry = class {
|
|
|
59606
59639
|
entry,
|
|
59607
59640
|
timestamp: entry.becameStationaryAt
|
|
59608
59641
|
});
|
|
59642
|
+
return {
|
|
59643
|
+
outcome: "born",
|
|
59644
|
+
entry
|
|
59645
|
+
};
|
|
59646
|
+
}
|
|
59647
|
+
/** Fold a refused promotion into the entry that already holds its spot. */
|
|
59648
|
+
absorbInto(occupant, refused) {
|
|
59649
|
+
const merged = {
|
|
59650
|
+
...occupant,
|
|
59651
|
+
lastConfirmedAt: Math.max(occupant.lastConfirmedAt, refused.becameStationaryAt),
|
|
59652
|
+
...occupant.label === void 0 && refused.label !== void 0 ? { label: refused.label } : {},
|
|
59653
|
+
...occupant.keyFrameMediaId === void 0 && refused.keyFrameMediaId !== void 0 ? { keyFrameMediaId: refused.keyFrameMediaId } : {}
|
|
59654
|
+
};
|
|
59655
|
+
this.ledger.stage(merged);
|
|
59656
|
+
this.dirty.add(merged.id);
|
|
59657
|
+
this.logger.info("stationary promotion absorbed — the spot is already censused", {
|
|
59658
|
+
tags: { deviceId: occupant.deviceId },
|
|
59659
|
+
meta: {
|
|
59660
|
+
deviceId: occupant.deviceId,
|
|
59661
|
+
entryId: occupant.id,
|
|
59662
|
+
className: occupant.className,
|
|
59663
|
+
refusedTrackId: refused.sourceTrackId,
|
|
59664
|
+
parkedForMs: refused.becameStationaryAt - occupant.becameStationaryAt,
|
|
59665
|
+
gainedLabel: occupant.label === void 0 && refused.label !== void 0
|
|
59666
|
+
}
|
|
59667
|
+
});
|
|
59668
|
+
return merged;
|
|
59609
59669
|
}
|
|
59610
59670
|
/**
|
|
59611
59671
|
* Retire entries unconfirmed for longer than the TTL of OBSERVED time, and
|
|
@@ -69876,18 +69936,6 @@ var StationarySettingsSchema = object({
|
|
|
69876
69936
|
* session open (also the minimum track age). Creates nothing — birth is
|
|
69877
69937
|
* decided by repeated evidence. `SESSION_STILLNESS_WINDOW_MS`. */
|
|
69878
69938
|
sessionStillnessWindowMs: number().int().min(5e3).max(12e4).default(SESSION_STILLNESS_WINDOW_MS),
|
|
69879
|
-
/**
|
|
69880
|
-
* Let a motionless PERSON become a stationary entry on this camera.
|
|
69881
|
-
*
|
|
69882
|
-
* Off, and it is a measurement rather than caution. Replaying 24 h of person
|
|
69883
|
-
* observations from devices 617/1443/590/592 through the evidence ledger
|
|
69884
|
-
* produces 6-22 entries a day depending on how strictly it is tuned, and none
|
|
69885
|
-
* of them can be tuned to zero: device 592 alone yielded eleven candidates at
|
|
69886
|
-
* a perfect agreement ratio between 02:00 and 05:00. A stationary entry
|
|
69887
|
-
* SUPPRESSES its object, so a wrong one makes the camera blind to a person —
|
|
69888
|
-
* turn this on only for a camera where you know what is standing there.
|
|
69889
|
-
*/
|
|
69890
|
-
promotePersons: boolean().default(false),
|
|
69891
69939
|
/** Observed-time TTL: retire an entry unconfirmed for this long (measured on
|
|
69892
69940
|
* frames-flowing time, not wall-clock). `ENTRY_TTL_MS`. */
|
|
69893
69941
|
entryTtlMs: number().int().min(6e4).max(18e5).default(ENTRY_TTL_MS)
|
|
@@ -69907,7 +69955,6 @@ function resolveStationarySettings(raw) {
|
|
|
69907
69955
|
suppressIou: pick("suppressIou"),
|
|
69908
69956
|
wakeAssocIou: pick("wakeAssocIou"),
|
|
69909
69957
|
sessionStillnessWindowMs: pick("sessionStillnessWindowMs"),
|
|
69910
|
-
promotePersons: pick("promotePersons"),
|
|
69911
69958
|
entryTtlMs: pick("entryTtlMs")
|
|
69912
69959
|
};
|
|
69913
69960
|
}
|
|
@@ -70443,13 +70490,6 @@ function buildDetectionSettingsSections() {
|
|
|
70443
70490
|
unit: "s",
|
|
70444
70491
|
displayScale: 1e3
|
|
70445
70492
|
},
|
|
70446
|
-
{
|
|
70447
|
-
type: "boolean",
|
|
70448
|
-
key: "promotePersons",
|
|
70449
|
-
label: "Also park motionless people",
|
|
70450
|
-
description: "Off by default. A stationary entry SUPPRESSES its object, so a wrong one makes this camera blind to a person standing there. Replaying a day of real person detections produced entries on every camera tried, at every threshold tried — turn this on only where you know what is standing still.",
|
|
70451
|
-
default: STATIONARY_DEFAULTS.promotePersons
|
|
70452
|
-
},
|
|
70453
70493
|
{
|
|
70454
70494
|
type: "slider",
|
|
70455
70495
|
key: "entryTtlMs",
|
|
@@ -73793,11 +73833,7 @@ var ZoneAnalyticsProvider = class {
|
|
|
73793
73833
|
});
|
|
73794
73834
|
}
|
|
73795
73835
|
this.snapshots.set(input.deviceId, snapshot);
|
|
73796
|
-
|
|
73797
|
-
if (noteOccupancy !== void 0) {
|
|
73798
|
-
noteOccupancy(input.deviceId, total, input.timestamp);
|
|
73799
|
-
for (const zone of snapshot.zones) noteOccupancy(input.deviceId, zone.totalObjects, input.timestamp, zone.zoneId);
|
|
73800
|
-
}
|
|
73836
|
+
this.noteLtsSamples(input.deviceId, snapshot, input.timestamp);
|
|
73801
73837
|
this.appendHistory(input.deviceId, snapshot);
|
|
73802
73838
|
this.emitSnapshot(input.deviceId, snapshot);
|
|
73803
73839
|
this.sliceThrottle.push(input.deviceId, snapshot);
|
|
@@ -73864,11 +73900,30 @@ var ZoneAnalyticsProvider = class {
|
|
|
73864
73900
|
});
|
|
73865
73901
|
if (snapshot) {
|
|
73866
73902
|
this.appendHistory(deviceId, snapshot);
|
|
73903
|
+
this.noteLtsSamples(deviceId, snapshot, now);
|
|
73867
73904
|
this.emitSnapshot(deviceId, snapshot);
|
|
73868
73905
|
this.sliceThrottle.push(deviceId, snapshot);
|
|
73869
73906
|
}
|
|
73870
73907
|
}
|
|
73871
73908
|
}
|
|
73909
|
+
/**
|
|
73910
|
+
* Append this snapshot to the long-term occupancy series — frame-wide first
|
|
73911
|
+
* (`scope: ''`), then one sample per zone.
|
|
73912
|
+
*
|
|
73913
|
+
* Shared by BOTH producers on purpose. It used to live inline in
|
|
73914
|
+
* `recordFrame`, so the baseline sampler — the only producer a camera with no
|
|
73915
|
+
* frames flowing has — wrote the RAM ring, the NC watcher and the state slice
|
|
73916
|
+
* but never LTS. The long-term series therefore had a hole exactly where the
|
|
73917
|
+
* scene was nothing but parked objects: the one span an occupancy history is
|
|
73918
|
+
* asked about. The RAM ring showed a flat line over the same minutes, which
|
|
73919
|
+
* is what made the gap invisible.
|
|
73920
|
+
*/
|
|
73921
|
+
noteLtsSamples(deviceId, snapshot, timestamp) {
|
|
73922
|
+
const note = this.ctx.onOccupancySample;
|
|
73923
|
+
if (note === void 0) return;
|
|
73924
|
+
note(deviceId, snapshot.frame.totalObjects, timestamp);
|
|
73925
|
+
for (const zone of snapshot.zones) note(deviceId, zone.totalObjects, timestamp, zone.zoneId);
|
|
73926
|
+
}
|
|
73872
73927
|
/** Push one snapshot to the occupancy tap, isolating any consumer throw from
|
|
73873
73928
|
* the frame path (occupancy notifications must never break analytics). */
|
|
73874
73929
|
emitSnapshot(deviceId, snapshot) {
|
|
@@ -78203,7 +78258,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
78203
78258
|
}
|
|
78204
78259
|
});
|
|
78205
78260
|
}
|
|
78206
|
-
await this.processFrame(deviceId, frame, data.source ?? "pipeline", data.frameHandle, data.detailSteps);
|
|
78261
|
+
await this.processFrame(deviceId, frame, data.source ?? "pipeline", data.frameHandle, data.detailSteps, data.occasion ?? "session");
|
|
78207
78262
|
}
|
|
78208
78263
|
/**
|
|
78209
78264
|
* Run one detection frame through the analysis layers for a given
|
|
@@ -78213,7 +78268,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
78213
78268
|
* tracking/zone/event state never crosses between sources. Emits the
|
|
78214
78269
|
* SAME canonical events, distinguished only by `source`.
|
|
78215
78270
|
*/
|
|
78216
|
-
async processFrame(deviceId, frame, source, rawFrameHandle, detailSteps) {
|
|
78271
|
+
async processFrame(deviceId, frame, source, rawFrameHandle, detailSteps, occasion = "session") {
|
|
78217
78272
|
if (this.shuttingDown) return;
|
|
78218
78273
|
if (!await this.bindingCache.isActive(deviceId)) {
|
|
78219
78274
|
const now = Date.now();
|
|
@@ -78621,7 +78676,6 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
78621
78676
|
const evidence = this.stationaryEvidence;
|
|
78622
78677
|
for (const t of result.tracked) {
|
|
78623
78678
|
if (packageClasses.has(t.className)) continue;
|
|
78624
|
-
const classEnabled = t.className === "person" ? stationarySettings.promotePersons : evidencePolicyFor(t.className).promotesByDefault;
|
|
78625
78679
|
const active = this.trackStore?.peekActive(t.trackId) ?? null;
|
|
78626
78680
|
const observedLabel = active === null ? void 0 : displayLabel(active);
|
|
78627
78681
|
const observedKeyFrame = this.residents.keyFrameMediaId(t.trackId);
|
|
@@ -78630,12 +78684,13 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
78630
78684
|
className: t.className,
|
|
78631
78685
|
bbox: { ...t.bbox },
|
|
78632
78686
|
timestamp: result.timestamp,
|
|
78633
|
-
source:
|
|
78687
|
+
source: occasion,
|
|
78634
78688
|
trackId: t.trackId,
|
|
78635
78689
|
...observedKeyFrame !== void 0 ? { keyFrameMediaId: observedKeyFrame } : {},
|
|
78636
|
-
...observedLabel !== void 0 ? { label: observedLabel } : {}
|
|
78690
|
+
...observedLabel !== void 0 ? { label: observedLabel } : {},
|
|
78691
|
+
movementState: t.state
|
|
78637
78692
|
};
|
|
78638
|
-
const outcome = evidence.observe(observation
|
|
78693
|
+
const outcome = evidence.observe(observation);
|
|
78639
78694
|
if (outcome === void 0 || !outcome.promoted) continue;
|
|
78640
78695
|
this.birthStationaryFromEvidence({
|
|
78641
78696
|
deviceId,
|
|
@@ -81738,7 +81793,13 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
81738
81793
|
...label !== void 0 ? { label } : {},
|
|
81739
81794
|
...keyFrameMediaId !== void 0 ? { keyFrameMediaId } : {}
|
|
81740
81795
|
};
|
|
81741
|
-
if (plausible)
|
|
81796
|
+
if (plausible) {
|
|
81797
|
+
const gate = this.stationarySettingsFromCache(deviceId);
|
|
81798
|
+
this.stationaryRegistry?.promote(entry, {
|
|
81799
|
+
suppressIou: gate.suppressIou,
|
|
81800
|
+
wakeAssocIou: gate.wakeAssocIou
|
|
81801
|
+
});
|
|
81802
|
+
}
|
|
81742
81803
|
processor.dropTrack(track.trackId);
|
|
81743
81804
|
this.trackStore?.closeActive(track.trackId, "stationary-promotion").catch((err) => {
|
|
81744
81805
|
this.ctx.logger.warn("stationary promotion: closing the source track failed — its media, events and vector are now unowned", {
|
|
@@ -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-zMqSbyyP.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.243",
|
|
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",
|