@camstack/addon-post-analysis 1.2.192 → 1.2.194
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-CoI1xhsw.mjs → dist-DLVNGF8I.mjs} +52 -36
- package/dist/{dist-CJKdyCag.js → dist-DMnyp081.js} +52 -36
- 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-Gc-d7G7c.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-DAc8SUDf.mjs} +3 -3
- package/dist/pipeline-analytics/{_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BK6J3Mhb.mjs → _virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CJb6-kIP.mjs} +1 -1
- package/dist/pipeline-analytics/{hostInit-Bprfn5kJ.mjs → hostInit-B4lsosaS.mjs} +3 -3
- package/dist/pipeline-analytics/index.js +410 -22
- package/dist/pipeline-analytics/index.mjs +410 -22
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as audioModeOf, A as NcSnoozeSchema, At as BaseAddon, B as SCENE_DEFAULT_UNCOVERED_POLICY, Bt as boolean, C as NcConditionDescriptorSchema, Ct as sceneMonitorCapability, D as NcRuleTargetSchema, Dt as videoclipsCapability, E as NcRuleSchema, Et as vectorDimFromBase64, Ft as isDeviceScopedCap, G as TimelapseRulePatchSchema, Gt as partialRecord, H as SceneMonitorSchema, Ht as literal, I as RECORDING_EXPORT_MAX_READ_BYTES, It as nodePin, J as VISIT_MERGE_GAP_MS, Jt as unknown, K as TimelapseRuleSchema, Kt as record, L as RetrainStatusSchema, Lt as sleep$1, M as NcSystemEventKindSchema, Mt as DeviceType, N as NcTaxonomySchema, Nt as createEvent, O as NcScheduleSchema, Ot as zoneAnalyticsCapability, P as OpsLogEntrySchema, Pt as hydrateSchema, Q as audioMetricsCapability, Rt as _enum, S as NC_TAXONOMY, T as NcRulePatchSchema, Tt as systemEventFilterApplies, U as TIMELAPSE_DENSE_FLOOR_SEC, Ut as number, V as SCENE_DIVERGED, Vt as discriminatedUnion, W as TimelapseRuleInputSchema, Wt as object, X as alarmPanelCapability, Y as addonWidgetsSourceCapability, Yt as EventCategory, Z as assertTimelapseCadences, _ as MediaFileKindEnum, _t as pickClusterStepModels, a as DEFAULT_EVENT_COLOR, b as NC_DEFAULT_SNOOZE_MINUTES, bt as readDeviceStateFrom, c as DETECTION_MACRO_CLASSES, ct as faceGalleryCapability, d as EVENT_KIND_BY_CAP, dt as isDetectionMacroClass, et as buildEventKindDescriptor, f as EVENT_PAD_MS, ft as isScheduleActive, g as MACRO_LABELS, h as LabelAttributionSchema, ht as notificationRulesCapability, i as COCO_TO_MACRO, it as deriveRecordingMode, j as NcSnoozeSuppressedSchema, jt as CamProfileSchema, k as NcSnoozeInputSchema, kt as errMsg$1, lt as failureContributionCapability, m as FailureCounters, mt as kebabToCamel, n as BaseDevice, nt as customAction, ot as encodeVectorBase64, p as FULL_IMAGE_BBOX, pt as isSourceCap, q as TrackSourceSchema, qt as string, r as CLUSTER_MODEL_SCOPED_STEPS, rt as defineCustomActions, st as evaluateSensorEdge, t as AUDIO_MACRO_LABELS, tt as cosineSimilarity$1, u as DeclaredDevices, v as NC_ALARM_SYSTEM_EVENT_KINDS, vt as pipelineAnalyticsCapability, w as NcRuleInputSchema, wt as subKindsOf, xt as readTimelapseGeneratedAt, y as NC_CONDITION_CATALOG, yt as plateGalleryCapability, z as SCENE_DEFAULT_ANCHOR_THRESHOLD, zt as array } from "../dist-
|
|
1
|
+
import { $ as audioModeOf, A as NcSnoozeSchema, At as BaseAddon, B as SCENE_DEFAULT_UNCOVERED_POLICY, Bt as boolean, C as NcConditionDescriptorSchema, Ct as sceneMonitorCapability, D as NcRuleTargetSchema, Dt as videoclipsCapability, E as NcRuleSchema, Et as vectorDimFromBase64, Ft as isDeviceScopedCap, G as TimelapseRulePatchSchema, Gt as partialRecord, H as SceneMonitorSchema, Ht as literal, I as RECORDING_EXPORT_MAX_READ_BYTES, It as nodePin, J as VISIT_MERGE_GAP_MS, Jt as unknown, K as TimelapseRuleSchema, Kt as record, L as RetrainStatusSchema, Lt as sleep$1, M as NcSystemEventKindSchema, Mt as DeviceType, N as NcTaxonomySchema, Nt as createEvent, O as NcScheduleSchema, Ot as zoneAnalyticsCapability, P as OpsLogEntrySchema, Pt as hydrateSchema, Q as audioMetricsCapability, Rt as _enum, S as NC_TAXONOMY, T as NcRulePatchSchema, Tt as systemEventFilterApplies, U as TIMELAPSE_DENSE_FLOOR_SEC, Ut as number, V as SCENE_DIVERGED, Vt as discriminatedUnion, W as TimelapseRuleInputSchema, Wt as object, X as alarmPanelCapability, Y as addonWidgetsSourceCapability, Yt as EventCategory, Z as assertTimelapseCadences, _ as MediaFileKindEnum, _t as pickClusterStepModels, a as DEFAULT_EVENT_COLOR, b as NC_DEFAULT_SNOOZE_MINUTES, bt as readDeviceStateFrom, c as DETECTION_MACRO_CLASSES, ct as faceGalleryCapability, d as EVENT_KIND_BY_CAP, dt as isDetectionMacroClass, et as buildEventKindDescriptor, f as EVENT_PAD_MS, ft as isScheduleActive, g as MACRO_LABELS, h as LabelAttributionSchema, ht as notificationRulesCapability, i as COCO_TO_MACRO, it as deriveRecordingMode, j as NcSnoozeSuppressedSchema, jt as CamProfileSchema, k as NcSnoozeInputSchema, kt as errMsg$1, lt as failureContributionCapability, m as FailureCounters, mt as kebabToCamel, n as BaseDevice, nt as customAction, ot as encodeVectorBase64, p as FULL_IMAGE_BBOX, pt as isSourceCap, q as TrackSourceSchema, qt as string, r as CLUSTER_MODEL_SCOPED_STEPS, rt as defineCustomActions, st as evaluateSensorEdge, t as AUDIO_MACRO_LABELS, tt as cosineSimilarity$1, u as DeclaredDevices, v as NC_ALARM_SYSTEM_EVENT_KINDS, vt as pipelineAnalyticsCapability, w as NcRuleInputSchema, wt as subKindsOf, xt as readTimelapseGeneratedAt, y as NC_CONDITION_CATALOG, yt as plateGalleryCapability, z as SCENE_DEFAULT_ANCHOR_THRESHOLD, zt as array } from "../dist-DLVNGF8I.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";
|
|
@@ -33992,6 +33992,18 @@ var LooseBindSampler = class {
|
|
|
33992
33992
|
this.counts.clear();
|
|
33993
33993
|
}
|
|
33994
33994
|
};
|
|
33995
|
+
/** Per-reason counter + sampling decision. One instance per tracker, so the
|
|
33996
|
+
* counts are per camera and per detection source. */
|
|
33997
|
+
var SpawnRefusalSampler = class {
|
|
33998
|
+
counts = /* @__PURE__ */ new Map();
|
|
33999
|
+
/** The running count when this refusal should be reported, else `undefined`. */
|
|
34000
|
+
next(reason) {
|
|
34001
|
+
const count = (this.counts.get(reason) ?? 0) + 1;
|
|
34002
|
+
this.counts.set(reason, count);
|
|
34003
|
+
if (count <= 10) return count;
|
|
34004
|
+
return count % 10 === 0 ? count : void 0;
|
|
34005
|
+
}
|
|
34006
|
+
};
|
|
33995
34007
|
//#endregion
|
|
33996
34008
|
//#region src/pipeline-analytics/pipeline/tracker/sort-tracker.ts
|
|
33997
34009
|
var DEFAULT_TRACKER_CONFIG = {
|
|
@@ -34076,6 +34088,10 @@ var SortTracker = class SortTracker {
|
|
|
34076
34088
|
/** Sink for salvage-bind diagnostics. Null until an owner that knows the
|
|
34077
34089
|
* `deviceId` wires one — see {@link setBindObserver}. */
|
|
34078
34090
|
bindObserver = null;
|
|
34091
|
+
/** Per-reason counters behind the sampled dropped-detection diagnostics. */
|
|
34092
|
+
refusalSampler = new SpawnRefusalSampler();
|
|
34093
|
+
/** Sink for dropped-detection diagnostics — see {@link setSpawnRefusalObserver}. */
|
|
34094
|
+
spawnRefusalObserver = null;
|
|
34079
34095
|
constructor(config = {}) {
|
|
34080
34096
|
this.config = {
|
|
34081
34097
|
...DEFAULT_TRACKER_CONFIG,
|
|
@@ -34091,6 +34107,37 @@ var SortTracker = class SortTracker {
|
|
|
34091
34107
|
setBindObserver(observer) {
|
|
34092
34108
|
this.bindObserver = observer;
|
|
34093
34109
|
}
|
|
34110
|
+
/**
|
|
34111
|
+
* Wire the diagnostics sink for detections the tracker DROPS — a spawn a
|
|
34112
|
+
* gate refused, or a box a track of another class absorbed. Same contract as
|
|
34113
|
+
* {@link setBindObserver}: the tracker has no `deviceId`, the caller stamps
|
|
34114
|
+
* it. Nothing is computed when no observer is wired.
|
|
34115
|
+
*/
|
|
34116
|
+
setSpawnRefusalObserver(observer) {
|
|
34117
|
+
this.spawnRefusalObserver = observer;
|
|
34118
|
+
}
|
|
34119
|
+
/** Emit one dropped-detection record, subject to the refusal sampler. */
|
|
34120
|
+
reportRefusal(reason, det, extra = {}) {
|
|
34121
|
+
const observer = this.spawnRefusalObserver;
|
|
34122
|
+
if (!observer) return;
|
|
34123
|
+
const refusalCount = this.refusalSampler.next(reason);
|
|
34124
|
+
if (refusalCount === void 0) return;
|
|
34125
|
+
observer({
|
|
34126
|
+
reason,
|
|
34127
|
+
detClass: det.class,
|
|
34128
|
+
detScore: det.score,
|
|
34129
|
+
bbox: det.bbox,
|
|
34130
|
+
...det.nativeRecovery !== void 0 ? { nativeRecoveryScore: det.nativeRecovery.score } : {},
|
|
34131
|
+
...extra.threshold !== void 0 ? { threshold: extra.threshold } : {},
|
|
34132
|
+
...extra.measured !== void 0 ? { measured: extra.measured } : {},
|
|
34133
|
+
...extra.against !== void 0 ? {
|
|
34134
|
+
againstTrackId: extra.against.id,
|
|
34135
|
+
againstTrackClass: extra.against.class
|
|
34136
|
+
} : {},
|
|
34137
|
+
refusalCount,
|
|
34138
|
+
sampledEvery: 10
|
|
34139
|
+
});
|
|
34140
|
+
}
|
|
34094
34141
|
/** Emit one salvage decision, subject to {@link LOOSE_BIND_LOG_SAMPLE_EVERY}.
|
|
34095
34142
|
* Nothing is computed when no observer is wired. */
|
|
34096
34143
|
reportBind(stage, gate, track, det, timestamp) {
|
|
@@ -34150,9 +34197,49 @@ var SortTracker = class SortTracker {
|
|
|
34150
34197
|
* exact class when `classGating` is off (no gate at all) — matching the legacy
|
|
34151
34198
|
* contract — otherwise same association group (group == exact class unless
|
|
34152
34199
|
* `classGroupAssoc` widened it to {person,animal}). */
|
|
34153
|
-
associable(trackClass,
|
|
34200
|
+
associable(trackClass, det) {
|
|
34201
|
+
if (!this.sameGroupAsMeasured(trackClass, det)) return false;
|
|
34154
34202
|
if (!this.config.classGating) return true;
|
|
34155
|
-
return this.assocGroup(trackClass) === this.assocGroup(
|
|
34203
|
+
return this.assocGroup(trackClass) === this.assocGroup(det.class);
|
|
34204
|
+
}
|
|
34205
|
+
/**
|
|
34206
|
+
* A NATIVE-MEASURED BOX IS ITS OWN SUBJECT ACROSS CLASSES (D378).
|
|
34207
|
+
*
|
|
34208
|
+
* `classGroupAssoc` collapses {person, animal} into one association group for
|
|
34209
|
+
* one failure of the COARSE full-frame pass: a bending person re-labelled
|
|
34210
|
+
* `animal` for a frame, over the person's own box. Letting that box re-match
|
|
34211
|
+
* the person track is strictly better than minting a concurrent id.
|
|
34212
|
+
*
|
|
34213
|
+
* The runner's second pass makes the opposite measurement — the same detector
|
|
34214
|
+
* on a 640-800 px native square around a motion region the full-frame boxes
|
|
34215
|
+
* do NOT account for. Such a box is, by construction, not the subject the
|
|
34216
|
+
* full pass already has; absorbing it into a person track means the animal is
|
|
34217
|
+
* never seen at all, which on 2026-09-07 07:51:33 is exactly what happened to
|
|
34218
|
+
* an `animal` at `nativeRecoveryScore=0.833`.
|
|
34219
|
+
*
|
|
34220
|
+
* So the widening does not apply to a detection carrying `nativeRecovery`:
|
|
34221
|
+
* it associates to its OWN class or it spawns. Every other guard on that
|
|
34222
|
+
* spawn still runs — the per-class score floor, the area floor, the
|
|
34223
|
+
* `dedupSpawnIou` overlap, and `resolveDuplicates` afterwards, where a person
|
|
34224
|
+
* still beats a cross-class animal.
|
|
34225
|
+
*/
|
|
34226
|
+
sameGroupAsMeasured(trackClass, det) {
|
|
34227
|
+
if (det.nativeRecovery === void 0) return true;
|
|
34228
|
+
return trackClass === det.class;
|
|
34229
|
+
}
|
|
34230
|
+
/**
|
|
34231
|
+
* Whether a matched track's box refuses `det` a birth. The gate exists for a
|
|
34232
|
+
* detector double-firing INSIDE a subject it already has, so it is a
|
|
34233
|
+
* same-subject claim: for a box the native pass measured (D378), a
|
|
34234
|
+
* cross-class occluder is not that claim — a dog at its owner's knee is 100%
|
|
34235
|
+
* contained in the person's box and is not the person. 2026-09-07 07:56:37,
|
|
34236
|
+
* an `animal` at `nativeRecoveryScore=0.5143` refused on `threshold=0.6`.
|
|
34237
|
+
* A SAME-class occluder still refuses it: that is the torso the crop cut out
|
|
34238
|
+
* of a person the full pass already has.
|
|
34239
|
+
*/
|
|
34240
|
+
occludesSpawn(det, occluder) {
|
|
34241
|
+
if (!this.sameGroupAsMeasured(occluder.class, det)) return false;
|
|
34242
|
+
return containment(det.bbox, occluder.bbox) >= this.config.occlusionContainment;
|
|
34156
34243
|
}
|
|
34157
34244
|
/** Whether a spawn candidate clears its per-class minimum bbox-area floor
|
|
34158
34245
|
* (fraction of frame area). Ungated when disabled, when the class has no floor,
|
|
@@ -34174,14 +34261,30 @@ var SortTracker = class SortTracker {
|
|
|
34174
34261
|
isDuplicateSpawn(det, live, timestamp) {
|
|
34175
34262
|
for (const t of live) {
|
|
34176
34263
|
if (!this.dedupCompatible(t.class, det.class)) continue;
|
|
34177
|
-
|
|
34264
|
+
const overlap = iou$3(det.bbox, t.bbox);
|
|
34265
|
+
if (overlap >= this.config.dedupSpawnIou) {
|
|
34266
|
+
this.reportRefusal("dedup-iou", det, {
|
|
34267
|
+
threshold: this.config.dedupSpawnIou,
|
|
34268
|
+
measured: overlap,
|
|
34269
|
+
against: t
|
|
34270
|
+
});
|
|
34271
|
+
return true;
|
|
34272
|
+
}
|
|
34178
34273
|
if (t.class === det.class && growthAdjacentMatch(t.bbox, det.bbox, SortTracker.growthContext(t, timestamp))) {
|
|
34179
34274
|
this.reportBind("spawn-dedup", "growth", t, det, timestamp);
|
|
34180
34275
|
return true;
|
|
34181
34276
|
}
|
|
34182
34277
|
}
|
|
34183
|
-
if (this.config.personAnimalDedup && det.class === ANIMAL_CLASS && det.score < this.config.animalOverPersonMaxScore) {
|
|
34184
|
-
|
|
34278
|
+
if (this.config.personAnimalDedup && det.class === ANIMAL_CLASS && det.nativeRecovery === void 0 && det.score < this.config.animalOverPersonMaxScore) for (const t of live) {
|
|
34279
|
+
const overlap = iou$3(det.bbox, t.bbox);
|
|
34280
|
+
if (t.class === PERSON_CLASS && overlap >= this.config.animalOverPersonIou) {
|
|
34281
|
+
this.reportRefusal("animal-over-person", det, {
|
|
34282
|
+
threshold: this.config.animalOverPersonMaxScore,
|
|
34283
|
+
measured: overlap,
|
|
34284
|
+
against: t
|
|
34285
|
+
});
|
|
34286
|
+
return true;
|
|
34287
|
+
}
|
|
34185
34288
|
}
|
|
34186
34289
|
return false;
|
|
34187
34290
|
}
|
|
@@ -34296,6 +34399,7 @@ var SortTracker = class SortTracker {
|
|
|
34296
34399
|
* class.
|
|
34297
34400
|
*/
|
|
34298
34401
|
looseMatch(track, det, timestamp) {
|
|
34402
|
+
if (!this.sameGroupAsMeasured(track.class, det)) return null;
|
|
34299
34403
|
if (this.assocGroup(track.class) !== this.assocGroup(det.class)) return null;
|
|
34300
34404
|
if (iou$3(track.bbox, det.bbox) >= this.config.rescueIouThreshold) return "iou";
|
|
34301
34405
|
const tc = bboxCentroid(track.bbox);
|
|
@@ -34382,7 +34486,7 @@ var SortTracker = class SortTracker {
|
|
|
34382
34486
|
for (const di of candidateIdxs) {
|
|
34383
34487
|
if (used.has(di)) continue;
|
|
34384
34488
|
const det = detections[di];
|
|
34385
|
-
if (!this.associable(track.class, det
|
|
34489
|
+
if (!this.associable(track.class, det)) continue;
|
|
34386
34490
|
const score = iou$3(pbox, det.bbox);
|
|
34387
34491
|
if (score >= iouThreshold) pairs.push({
|
|
34388
34492
|
track,
|
|
@@ -34461,6 +34565,7 @@ var SortTracker = class SortTracker {
|
|
|
34461
34565
|
}
|
|
34462
34566
|
if (this.config.byteTrackEnabled && lowIdxs.length > 0) this.greedyAssociate(detections, lowIdxs, this.config.byteTrackLowIouThreshold, used, matchedTracks, matched);
|
|
34463
34567
|
for (const [track, det] of matched) {
|
|
34568
|
+
if (track.class !== det.class) this.reportRefusal("cross-class-absorbed", det, { against: track });
|
|
34464
34569
|
const prevCenter = bboxCentroid({
|
|
34465
34570
|
x: track.bbox.x,
|
|
34466
34571
|
y: track.bbox.y,
|
|
@@ -34489,15 +34594,18 @@ var SortTracker = class SortTracker {
|
|
|
34489
34594
|
if (track.path.length > MAX_PATH_LENGTH) track.path.shift();
|
|
34490
34595
|
this.addClassVote(track, det);
|
|
34491
34596
|
}
|
|
34492
|
-
const
|
|
34493
|
-
for (const track of matchedTracks)
|
|
34597
|
+
const occluders = [];
|
|
34598
|
+
for (const track of matchedTracks) occluders.push({
|
|
34599
|
+
bbox: track.bbox,
|
|
34600
|
+
class: track.class
|
|
34601
|
+
});
|
|
34494
34602
|
const surviving = [];
|
|
34495
34603
|
for (const track of this.tracks) {
|
|
34496
34604
|
if (matchedTracks.has(track)) {
|
|
34497
34605
|
surviving.push(track);
|
|
34498
34606
|
continue;
|
|
34499
34607
|
}
|
|
34500
|
-
const occluded = this.config.occlusionEnabled &&
|
|
34608
|
+
const occluded = this.config.occlusionEnabled && occluders.some((ob) => containment(this.predicted(track), ob.bbox) >= this.config.occlusionContainment);
|
|
34501
34609
|
track.age++;
|
|
34502
34610
|
if (timestamp - track.lastSeen > (occluded ? this.config.occlusionMaxMissedMs : this.config.maxMissedMs)) {
|
|
34503
34611
|
track.lost = true;
|
|
@@ -34576,14 +34684,25 @@ var SortTracker = class SortTracker {
|
|
|
34576
34684
|
for (const di of unmatchedIdx) {
|
|
34577
34685
|
const det = detections[di];
|
|
34578
34686
|
if (!this.meetsClassScoreFloor(det)) {
|
|
34687
|
+
this.reportRefusal("score-floor", det, {
|
|
34688
|
+
threshold: this.config.classMinScores[det.class] ?? 0,
|
|
34689
|
+
measured: det.score
|
|
34690
|
+
});
|
|
34579
34691
|
used.add(di);
|
|
34580
34692
|
continue;
|
|
34581
34693
|
}
|
|
34582
34694
|
if (!this.meetsSpawnAreaFloor(det, frameArea)) {
|
|
34695
|
+
this.reportRefusal("area-floor", det, {
|
|
34696
|
+
threshold: this.config.classSpawnAreaFracs[det.class] ?? 0,
|
|
34697
|
+
measured: frameArea > 0 ? det.bbox.w * det.bbox.h / frameArea : 0
|
|
34698
|
+
});
|
|
34583
34699
|
used.add(di);
|
|
34584
34700
|
continue;
|
|
34585
34701
|
}
|
|
34586
|
-
if (this.config.occlusionEnabled &&
|
|
34702
|
+
if (this.config.occlusionEnabled && occluders.some((ob) => this.occludesSpawn(det, ob))) {
|
|
34703
|
+
this.reportRefusal("occlusion", det, { threshold: this.config.occlusionContainment });
|
|
34704
|
+
continue;
|
|
34705
|
+
}
|
|
34587
34706
|
if (this.config.dedupEnabled && this.isDuplicateSpawn(det, surviving, timestamp)) {
|
|
34588
34707
|
used.add(di);
|
|
34589
34708
|
continue;
|
|
@@ -35071,6 +35190,9 @@ var FrameProcessor = class {
|
|
|
35071
35190
|
* Only the rescue / resurrection / growth branches report, and they report
|
|
35072
35191
|
* sampled: stage 1 runs per track per frame at frame-rate.
|
|
35073
35192
|
*/
|
|
35193
|
+
setSpawnRefusalObserver(observer) {
|
|
35194
|
+
this.tracker.setSpawnRefusalObserver(observer);
|
|
35195
|
+
}
|
|
35074
35196
|
setBindObserver(observer) {
|
|
35075
35197
|
this.tracker.setBindObserver(observer);
|
|
35076
35198
|
}
|
|
@@ -35093,6 +35215,8 @@ var FrameProcessor = class {
|
|
|
35093
35215
|
const faceAlignedCropByBbox = /* @__PURE__ */ new Map();
|
|
35094
35216
|
const plateByBbox = /* @__PURE__ */ new Map();
|
|
35095
35217
|
const maskByBbox = /* @__PURE__ */ new Map();
|
|
35218
|
+
const nativeRecoveryByBbox = /* @__PURE__ */ new Map();
|
|
35219
|
+
const birthEvidenceByBbox = /* @__PURE__ */ new Map();
|
|
35096
35220
|
const flatDetections = frame.detections.filter((d) => d.kind === "first-level").map((det) => {
|
|
35097
35221
|
const bbox = {
|
|
35098
35222
|
x: det.bbox.x,
|
|
@@ -35104,8 +35228,11 @@ var FrameProcessor = class {
|
|
|
35104
35228
|
class: det.macroClass,
|
|
35105
35229
|
originalClass: det.debug?.originalClass ?? det.macroClass,
|
|
35106
35230
|
score: det.score,
|
|
35107
|
-
bbox
|
|
35231
|
+
bbox,
|
|
35232
|
+
...det.nativeRecovery !== void 0 ? { nativeRecovery: det.nativeRecovery } : {}
|
|
35108
35233
|
};
|
|
35234
|
+
if (det.nativeRecovery !== void 0) nativeRecoveryByBbox.set(bbox, det.nativeRecovery);
|
|
35235
|
+
if (det.birthEvidence !== void 0) birthEvidenceByBbox.set(bbox, det.birthEvidence);
|
|
35109
35236
|
if (det.labels.length > 0) labelsByBbox.set(bbox, det.labels);
|
|
35110
35237
|
if (det.debug?.originalClassTier !== void 0) originalClassTierByBbox.set(bbox, det.debug.originalClassTier);
|
|
35111
35238
|
if (det.embedding !== void 0) embeddingByBbox.set(bbox, {
|
|
@@ -35330,6 +35457,8 @@ var FrameProcessor = class {
|
|
|
35330
35457
|
const faceAlignedCrop = faceAlignedCropByBbox.get(td.bbox);
|
|
35331
35458
|
const plate = plateByBbox.get(td.bbox);
|
|
35332
35459
|
const sourceDetectionId = sourceIdByBbox.get(td.bbox);
|
|
35460
|
+
const nativeRecovery = td.matchedThisFrame !== false ? nativeRecoveryByBbox.get(td.bbox) : void 0;
|
|
35461
|
+
const birthEvidence = td.matchedThisFrame !== false ? birthEvidenceByBbox.get(td.bbox) : void 0;
|
|
35333
35462
|
return {
|
|
35334
35463
|
trackId: td.trackId,
|
|
35335
35464
|
...sourceDetectionId !== void 0 ? { sourceDetectionId } : {},
|
|
@@ -35352,7 +35481,9 @@ var FrameProcessor = class {
|
|
|
35352
35481
|
plateText: plate.text,
|
|
35353
35482
|
plateScore: plate.score,
|
|
35354
35483
|
plateBbox: plate.bbox
|
|
35355
|
-
} : {}
|
|
35484
|
+
} : {},
|
|
35485
|
+
...nativeRecovery !== void 0 ? { nativeRecovery } : {},
|
|
35486
|
+
...birthEvidence !== void 0 ? { birthEvidence } : {}
|
|
35356
35487
|
};
|
|
35357
35488
|
});
|
|
35358
35489
|
this.lastLabelWriteRefusals = refusals;
|
|
@@ -45154,9 +45285,28 @@ function withTimeout$2(promise, timeoutMs) {
|
|
|
45154
45285
|
});
|
|
45155
45286
|
}
|
|
45156
45287
|
async function runConfirmation(candidate, config, deps, exhausted) {
|
|
45288
|
+
const carried = candidate.evidenceCrop ?? null;
|
|
45289
|
+
if (carried) {
|
|
45290
|
+
const verdict = await measure(candidate, carried, config, deps, "carried");
|
|
45291
|
+
if (verdict.verdict === "confirmed" || verdict.reason === "redetect-error") return verdict;
|
|
45292
|
+
}
|
|
45157
45293
|
let crop = await deps.fetchCrop(candidate);
|
|
45158
|
-
|
|
45294
|
+
let cropSource = "native";
|
|
45295
|
+
if (!crop && exhausted && deps.fetchFallbackCrop) {
|
|
45296
|
+
crop = await deps.fetchFallbackCrop(candidate);
|
|
45297
|
+
cropSource = "fallback";
|
|
45298
|
+
}
|
|
45159
45299
|
if (!crop) return undecided(candidate, "no-crop");
|
|
45300
|
+
return measure(candidate, crop, config, deps, cropSource);
|
|
45301
|
+
}
|
|
45302
|
+
/**
|
|
45303
|
+
* Re-detect on ONE crop and turn the result into a verdict.
|
|
45304
|
+
*
|
|
45305
|
+
* Shared by all three sources so a crop can never be judged by two slightly
|
|
45306
|
+
* different rules depending on where it came from — the `cropSource` argument
|
|
45307
|
+
* is the ONLY thing that varies, and it varies in exactly one place below.
|
|
45308
|
+
*/
|
|
45309
|
+
async function measure(candidate, crop, config, deps, cropSource) {
|
|
45160
45310
|
const detections = await deps.redetect(crop);
|
|
45161
45311
|
if (detections === null) return undecided(candidate, "redetect-error");
|
|
45162
45312
|
let best;
|
|
@@ -45164,8 +45314,9 @@ async function runConfirmation(candidate, config, deps, exhausted) {
|
|
|
45164
45314
|
for (const d of detections) if (isConfirmationCompatible(candidate.className, d.macroClass)) {
|
|
45165
45315
|
if (!best || d.score > best.score) best = d;
|
|
45166
45316
|
} else if (!bestIncompatible || d.score > bestIncompatible.score) bestIncompatible = d;
|
|
45167
|
-
const bar =
|
|
45317
|
+
const bar = barFor(candidate, config, deps);
|
|
45168
45318
|
const confirmed = best !== void 0 && best.score >= bar;
|
|
45319
|
+
if (!confirmed && cropSource !== "native") return undecided(candidate, cropSource === "fallback" ? "fallback-inconclusive" : "carried-inconclusive");
|
|
45169
45320
|
const secondary = bestIncompatible ? promoteSecondary(candidate, bestIncompatible, bar, deps) : null;
|
|
45170
45321
|
return {
|
|
45171
45322
|
trackId: candidate.trackId,
|
|
@@ -45173,6 +45324,7 @@ async function runConfirmation(candidate, config, deps, exhausted) {
|
|
|
45173
45324
|
reason: confirmed ? "confirmed" : "suppressed",
|
|
45174
45325
|
className: candidate.className,
|
|
45175
45326
|
appliedMinConfidence: bar,
|
|
45327
|
+
cropSource,
|
|
45176
45328
|
...best ? { bestScore: best.score } : {},
|
|
45177
45329
|
...bestIncompatible ? {
|
|
45178
45330
|
bestIncompatibleClass: bestIncompatible.macroClass,
|
|
@@ -45203,7 +45355,27 @@ function promoteSecondary(candidate, detection, bar, deps) {
|
|
|
45203
45355
|
}
|
|
45204
45356
|
};
|
|
45205
45357
|
}
|
|
45358
|
+
/**
|
|
45359
|
+
* The bar this candidate faces. Never below the configured one: the
|
|
45360
|
+
* `minConfidenceFor` hook is monotonic by construction, and clamping here says
|
|
45361
|
+
* so at the point of use rather than trusting the caller to have been careful.
|
|
45362
|
+
*/
|
|
45363
|
+
function barFor(candidate, config, deps) {
|
|
45364
|
+
return Math.max(config.minConfidence, deps.minConfidenceFor?.(candidate) ?? 0);
|
|
45365
|
+
}
|
|
45206
45366
|
async function confirmOne(candidate, config, deps, exhausted) {
|
|
45367
|
+
const native = candidate.nativeRecovery;
|
|
45368
|
+
if (native !== void 0) {
|
|
45369
|
+
const bar = barFor(candidate, config, deps);
|
|
45370
|
+
if (native.score >= bar) return {
|
|
45371
|
+
trackId: candidate.trackId,
|
|
45372
|
+
verdict: "confirmed",
|
|
45373
|
+
reason: "native-pass",
|
|
45374
|
+
className: candidate.className,
|
|
45375
|
+
bestScore: native.score,
|
|
45376
|
+
appliedMinConfidence: bar
|
|
45377
|
+
};
|
|
45378
|
+
}
|
|
45207
45379
|
const cropPx = Math.max(candidate.bbox.w, candidate.bbox.h);
|
|
45208
45380
|
if (config.minCropPx > 0 && cropPx < config.minCropPx) return undecided(candidate, "below-min-crop");
|
|
45209
45381
|
try {
|
|
@@ -51665,6 +51837,97 @@ async function normalizePhotoOrientation(photo) {
|
|
|
51665
51837
|
if (orientation === void 0 || orientation === EXIF_UPRIGHT) return photo;
|
|
51666
51838
|
return sharp(photo).rotate().jpeg({ quality: 95 }).toBuffer();
|
|
51667
51839
|
}
|
|
51840
|
+
/**
|
|
51841
|
+
* How long one birth's evidence is kept. Twice `confirmationGateMaxDeferralMs`
|
|
51842
|
+
* (2 000 ms default) — the longest a deferred birth can still be re-offered.
|
|
51843
|
+
*/
|
|
51844
|
+
var BIRTH_EVIDENCE_TTL_MS = 4e3;
|
|
51845
|
+
var BirthEvidenceStore = class {
|
|
51846
|
+
slotsPerCamera;
|
|
51847
|
+
ttlMs;
|
|
51848
|
+
byDevice = /* @__PURE__ */ new Map();
|
|
51849
|
+
constructor(slotsPerCamera = 8, ttlMs = BIRTH_EVIDENCE_TTL_MS) {
|
|
51850
|
+
this.slotsPerCamera = slotsPerCamera;
|
|
51851
|
+
this.ttlMs = ttlMs;
|
|
51852
|
+
}
|
|
51853
|
+
/**
|
|
51854
|
+
* Keep this birth's crop.
|
|
51855
|
+
*
|
|
51856
|
+
* The FIRST crop for an id wins. A later frame's crop of the same track is a
|
|
51857
|
+
* different instant, and this store exists precisely to preserve the instant
|
|
51858
|
+
* the birth was measured on — overwriting it would quietly reintroduce the
|
|
51859
|
+
* defect the fallback asymmetry exists to distrust.
|
|
51860
|
+
*/
|
|
51861
|
+
remember(deviceKey, trackId, crop, nowMs) {
|
|
51862
|
+
let slots = this.byDevice.get(deviceKey);
|
|
51863
|
+
if (!slots) {
|
|
51864
|
+
slots = /* @__PURE__ */ new Map();
|
|
51865
|
+
this.byDevice.set(deviceKey, slots);
|
|
51866
|
+
}
|
|
51867
|
+
if (slots.has(trackId)) return;
|
|
51868
|
+
slots.set(trackId, {
|
|
51869
|
+
crop,
|
|
51870
|
+
atMs: nowMs
|
|
51871
|
+
});
|
|
51872
|
+
this.evict(deviceKey, slots, nowMs);
|
|
51873
|
+
}
|
|
51874
|
+
/** This birth's crop, or `null` when it was never kept or has aged out. */
|
|
51875
|
+
get(deviceKey, trackId, nowMs) {
|
|
51876
|
+
const entry = this.byDevice.get(deviceKey)?.get(trackId);
|
|
51877
|
+
if (!entry) return null;
|
|
51878
|
+
if (nowMs - entry.atMs > this.ttlMs) {
|
|
51879
|
+
this.resolve(deviceKey, trackId);
|
|
51880
|
+
return null;
|
|
51881
|
+
}
|
|
51882
|
+
return entry.crop;
|
|
51883
|
+
}
|
|
51884
|
+
/** A verdict finally arrived — the crop is dead weight from here. */
|
|
51885
|
+
resolve(deviceKey, trackId) {
|
|
51886
|
+
const slots = this.byDevice.get(deviceKey);
|
|
51887
|
+
if (!slots) return;
|
|
51888
|
+
slots.delete(trackId);
|
|
51889
|
+
if (slots.size === 0) this.byDevice.delete(deviceKey);
|
|
51890
|
+
}
|
|
51891
|
+
/**
|
|
51892
|
+
* Forget every id the tracker is no longer carrying — the same per-frame
|
|
51893
|
+
* contract as `DeferredBirthRegistry.retain`. An id the tracker dropped can
|
|
51894
|
+
* never be decided, so holding its pixels only leaks.
|
|
51895
|
+
*/
|
|
51896
|
+
retain(deviceKey, currentTrackIds) {
|
|
51897
|
+
const slots = this.byDevice.get(deviceKey);
|
|
51898
|
+
if (!slots) return;
|
|
51899
|
+
for (const id of [...slots.keys()]) if (!currentTrackIds.has(id)) slots.delete(id);
|
|
51900
|
+
if (slots.size === 0) this.byDevice.delete(deviceKey);
|
|
51901
|
+
}
|
|
51902
|
+
/** Crops held for one camera (diagnostics and tests). */
|
|
51903
|
+
size(deviceKey) {
|
|
51904
|
+
return this.byDevice.get(deviceKey)?.size ?? 0;
|
|
51905
|
+
}
|
|
51906
|
+
/** Bytes held across every camera — the declared budget, checkable. */
|
|
51907
|
+
totalBytes() {
|
|
51908
|
+
let total = 0;
|
|
51909
|
+
for (const slots of this.byDevice.values()) for (const entry of slots.values()) total += entry.crop.byteLength;
|
|
51910
|
+
return total;
|
|
51911
|
+
}
|
|
51912
|
+
/** Drop a camera's reserve wholesale (device removed / pipeline reset). */
|
|
51913
|
+
clearDevice(deviceKey) {
|
|
51914
|
+
this.byDevice.delete(deviceKey);
|
|
51915
|
+
}
|
|
51916
|
+
/**
|
|
51917
|
+
* Age first, then the quota — and only ever inside THIS camera's slots.
|
|
51918
|
+
* `Map` keeps insertion order and a crop is inserted once, so the first key
|
|
51919
|
+
* is always the oldest.
|
|
51920
|
+
*/
|
|
51921
|
+
evict(deviceKey, slots, nowMs) {
|
|
51922
|
+
for (const [id, entry] of [...slots]) if (nowMs - entry.atMs > this.ttlMs) slots.delete(id);
|
|
51923
|
+
while (slots.size > this.slotsPerCamera) {
|
|
51924
|
+
const oldest = slots.keys().next().value;
|
|
51925
|
+
if (oldest === void 0) break;
|
|
51926
|
+
slots.delete(oldest);
|
|
51927
|
+
}
|
|
51928
|
+
if (slots.size === 0) this.byDevice.delete(deviceKey);
|
|
51929
|
+
}
|
|
51930
|
+
};
|
|
51668
51931
|
//#endregion
|
|
51669
51932
|
//#region src/pipeline-analytics/pipeline/deferred-births.ts
|
|
51670
51933
|
var DeferredBirthRegistry = class {
|
|
@@ -53821,6 +54084,77 @@ function personOverVehicle(person, neighbours, threshold = CONTAINMENT_THRESHOLD
|
|
|
53821
54084
|
return best >= threshold ? best : null;
|
|
53822
54085
|
}
|
|
53823
54086
|
//#endregion
|
|
54087
|
+
//#region src/pipeline-analytics/pipeline/started-births.ts
|
|
54088
|
+
/**
|
|
54089
|
+
* Memory of births whose ritual has already RUN, per device+source.
|
|
54090
|
+
*
|
|
54091
|
+
* A birth is a decision, and a decision is taken once. Until 2026-09-07 it
|
|
54092
|
+
* could be taken twice, on camera 615 it routinely was, and the second vote
|
|
54093
|
+
* won:
|
|
54094
|
+
*
|
|
54095
|
+
* 04:59:38.335Z confirmation gate: birth confirmed {trackId=f585b2ee…
|
|
54096
|
+
* className=animal bestScore=0.7068}
|
|
54097
|
+
* 04:59:38.335Z track started {trackId=f585b2ee…}
|
|
54098
|
+
* 04:59:38.821Z confirmation gate: birth suppressed (false positive)
|
|
54099
|
+
* {trackId=f585b2ee… className=animal} ← no bestScore:
|
|
54100
|
+
* the second crop held no compatible detection AT ALL
|
|
54101
|
+
* 04:59:38.821Z birth suppressed — track record retracted
|
|
54102
|
+
*
|
|
54103
|
+
* TWO conditions have to meet, and both are structural:
|
|
54104
|
+
*
|
|
54105
|
+
* 1. `processFrame` reads `lastActiveTrackIds` at the top and writes it at the
|
|
54106
|
+
* bottom, and AWAITS the confirmation gate in between (a crop round trip to
|
|
54107
|
+
* the frame's node plus a re-detection: 130–490 ms measured on this camera,
|
|
54108
|
+
* against 100 ms frame spacing). Several frames are therefore in flight at
|
|
54109
|
+
* once and each of them sees the same fresh id as unjudged.
|
|
54110
|
+
* 2. The other birth discriminator, `positionsCount > 1`, does not close the
|
|
54111
|
+
* gap either: a COASTED frame deliberately appends no position
|
|
54112
|
+
* (`TrackStore.upsert`, `matched: false`), so a track the full-frame pass
|
|
54113
|
+
* cannot see — which is the entire premise of the small-subject recovery —
|
|
54114
|
+
* keeps exactly one position for as long as it coasts, and
|
|
54115
|
+
* `classifyTrackAppearance` keeps answering `birth`.
|
|
54116
|
+
*
|
|
54117
|
+
* So the id that has already been born is remembered HERE, and both halves of
|
|
54118
|
+
* the birth path consult it: it is not offered to the gate again, and a verdict
|
|
54119
|
+
* that arrives for it anyway cannot retract it. Same lifetime discipline as
|
|
54120
|
+
* {@link SuppressedBirthRegistry} — held while the tracker still carries the
|
|
54121
|
+
* id, forgotten the frame it stops.
|
|
54122
|
+
*/
|
|
54123
|
+
var StartedBirthRegistry = class {
|
|
54124
|
+
byDevice = /* @__PURE__ */ new Map();
|
|
54125
|
+
/** Record that this id's birth ritual ran — `start`, firstFrame, the lot. */
|
|
54126
|
+
markStarted(deviceKey, trackId) {
|
|
54127
|
+
let ids = this.byDevice.get(deviceKey);
|
|
54128
|
+
if (!ids) {
|
|
54129
|
+
ids = /* @__PURE__ */ new Set();
|
|
54130
|
+
this.byDevice.set(deviceKey, ids);
|
|
54131
|
+
}
|
|
54132
|
+
ids.add(trackId);
|
|
54133
|
+
}
|
|
54134
|
+
hasStarted(deviceKey, trackId) {
|
|
54135
|
+
return this.byDevice.get(deviceKey)?.has(trackId) === true;
|
|
54136
|
+
}
|
|
54137
|
+
/**
|
|
54138
|
+
* Forget every started id the tracker is no longer carrying. Called once per
|
|
54139
|
+
* frame with the ids present THIS frame — an id the tracker has dropped can
|
|
54140
|
+
* never be re-offered as a birth, so holding it serves nothing.
|
|
54141
|
+
*/
|
|
54142
|
+
retain(deviceKey, currentTrackIds) {
|
|
54143
|
+
const ids = this.byDevice.get(deviceKey);
|
|
54144
|
+
if (!ids) return;
|
|
54145
|
+
for (const id of ids) if (!currentTrackIds.has(id)) ids.delete(id);
|
|
54146
|
+
if (ids.size === 0) this.byDevice.delete(deviceKey);
|
|
54147
|
+
}
|
|
54148
|
+
/** Started ids currently held for a device — diagnostics and tests. */
|
|
54149
|
+
size(deviceKey) {
|
|
54150
|
+
return this.byDevice.get(deviceKey)?.size ?? 0;
|
|
54151
|
+
}
|
|
54152
|
+
/** Drop a device's memory wholesale (device removed / pipeline reset). */
|
|
54153
|
+
clearDevice(deviceKey) {
|
|
54154
|
+
this.byDevice.delete(deviceKey);
|
|
54155
|
+
}
|
|
54156
|
+
};
|
|
54157
|
+
//#endregion
|
|
53824
54158
|
//#region src/pipeline-analytics/pipeline/stationary/stationary-evidence.ts
|
|
53825
54159
|
/**
|
|
53826
54160
|
* stationary-evidence — how a parked object EARNS a registry entry.
|
|
@@ -67453,9 +67787,22 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
67453
67787
|
/** See `pipeline/suppressed-births.ts` — rejected births must not be
|
|
67454
67788
|
* re-upserted, and must be forgotten when the tracker drops the id. */
|
|
67455
67789
|
suppressedBirths = new SuppressedBirthRegistry();
|
|
67790
|
+
/** Ids whose birth ritual has already run — a birth is decided ONCE (D377). */
|
|
67791
|
+
startedBirths = new StartedBirthRegistry();
|
|
67456
67792
|
/** Births the gate could not MEASURE, awaiting another look on a later frame. */
|
|
67457
67793
|
deferredBirths = new DeferredBirthRegistry();
|
|
67458
67794
|
/**
|
|
67795
|
+
* The pixels each deferred birth arrived with — a RESERVE per camera, evicted
|
|
67796
|
+
* by age inside that camera's own quota (D379).
|
|
67797
|
+
*
|
|
67798
|
+
* Not a shared cap. The path this replaces was one node-wide 4 096-entry LRU
|
|
67799
|
+
* measured pinned full in 6 814 of 6 849 samples over three hours, in which a
|
|
67800
|
+
* busy camera evicted a quiet one's proof and every camera added shortened
|
|
67801
|
+
* every other camera's window. See `pipeline/birth-evidence-store.ts` for the
|
|
67802
|
+
* two measurements the slot count and the TTL are taken from.
|
|
67803
|
+
*/
|
|
67804
|
+
birthEvidence = new BirthEvidenceStore();
|
|
67805
|
+
/**
|
|
67459
67806
|
* Subjects the confirmation gate found in a birth's crop BESIDES the birth
|
|
67460
67807
|
* itself (a dog in a person's crop), waiting to ride the next frame of the
|
|
67461
67808
|
* same (device, source) as detections — `pipeline/secondary-subjects.ts`.
|
|
@@ -70671,10 +71018,12 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
70671
71018
|
if (prevIds.has(id)) continue;
|
|
70672
71019
|
const t = result.tracked.find((x) => x.trackId === id);
|
|
70673
71020
|
if (!t) continue;
|
|
70674
|
-
|
|
71021
|
+
const appearance = classifyTrackAppearance({
|
|
70675
71022
|
inPrevActive: false,
|
|
70676
71023
|
positionsCount: positionsCountById.get(id) ?? 1
|
|
70677
|
-
})
|
|
71024
|
+
});
|
|
71025
|
+
if (this.startedBirths.hasStarted(key, id)) continue;
|
|
71026
|
+
if (appearance === "resurrection") {
|
|
70678
71027
|
log.info("track resumed", {
|
|
70679
71028
|
tags: { deviceId },
|
|
70680
71029
|
meta: {
|
|
@@ -70707,11 +71056,20 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
70707
71056
|
const exhaustionNowMs = Date.now();
|
|
70708
71057
|
const exhaustedIds = /* @__PURE__ */ new Set();
|
|
70709
71058
|
for (const { id } of bornCandidates) if (this.deferredBirths.exhausted(key, id, exhaustionNowMs, gateSettings.maxAttempts, gateSettings.maxDeferralMs)) exhaustedIds.add(id);
|
|
70710
|
-
const
|
|
70711
|
-
|
|
70712
|
-
|
|
70713
|
-
|
|
70714
|
-
})
|
|
71059
|
+
for (const { id, t } of bornCandidates) {
|
|
71060
|
+
if (t.birthEvidence === void 0) continue;
|
|
71061
|
+
this.birthEvidence.remember(key, id, Buffer.from(t.birthEvidence.jpegBase64, "base64"), exhaustionNowMs);
|
|
71062
|
+
}
|
|
71063
|
+
const outcome = await this.confirmTrackBirths(bornCandidates.map(({ id, t }) => {
|
|
71064
|
+
const evidenceCrop = this.birthEvidence.get(key, id, exhaustionNowMs);
|
|
71065
|
+
return {
|
|
71066
|
+
trackId: id,
|
|
71067
|
+
className: t.className,
|
|
71068
|
+
bbox: t.bbox,
|
|
71069
|
+
...t.nativeRecovery !== void 0 ? { nativeRecovery: t.nativeRecovery } : {},
|
|
71070
|
+
...evidenceCrop !== null ? { evidenceCrop } : {}
|
|
71071
|
+
};
|
|
71072
|
+
}), {
|
|
70715
71073
|
deviceId,
|
|
70716
71074
|
source,
|
|
70717
71075
|
frameHandle,
|
|
@@ -70735,6 +71093,17 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
70735
71093
|
const deferredForMs = this.deferredBirths.elapsedMs(key, id, gateNowMs);
|
|
70736
71094
|
const deferredAttempts = this.deferredBirths.attemptsFor(key, id);
|
|
70737
71095
|
this.deferredBirths.resolve(key, id);
|
|
71096
|
+
this.birthEvidence.resolve(key, id);
|
|
71097
|
+
if (this.startedBirths.hasStarted(key, id)) {
|
|
71098
|
+
this.deferredBirths.resolve(key, id);
|
|
71099
|
+
log.info("late birth verdict ignored — the track was already born", { meta: {
|
|
71100
|
+
trackId: id,
|
|
71101
|
+
className: t.className,
|
|
71102
|
+
source,
|
|
71103
|
+
verdict: outcome.confirmed.has(id) ? "confirmed" : "suppressed"
|
|
71104
|
+
} });
|
|
71105
|
+
continue;
|
|
71106
|
+
}
|
|
70738
71107
|
if (!outcome.confirmed.has(id)) {
|
|
70739
71108
|
this.suppressedBirths.reject(key, id);
|
|
70740
71109
|
this.trackStore?.dropActive(id);
|
|
@@ -70772,6 +71141,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
70772
71141
|
})));
|
|
70773
71142
|
this.failureReport.notePersonBirth(deviceId, overlap === null ? void 0 : REASON_PERSON_OVER_VEHICLE);
|
|
70774
71143
|
}
|
|
71144
|
+
this.startedBirths.markStarted(key, id);
|
|
70775
71145
|
log.info("track started", { meta: {
|
|
70776
71146
|
trackId: id,
|
|
70777
71147
|
className: t.className,
|
|
@@ -70839,7 +71209,9 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
70839
71209
|
});
|
|
70840
71210
|
}
|
|
70841
71211
|
this.suppressedBirths.retain(key, currentTrackIds);
|
|
71212
|
+
this.startedBirths.retain(key, currentTrackIds);
|
|
70842
71213
|
this.deferredBirths.retain(key, currentTrackIds);
|
|
71214
|
+
this.birthEvidence.retain(key, currentTrackIds);
|
|
70843
71215
|
this.lastActiveTrackIds.set(key, currentTrackIds);
|
|
70844
71216
|
if (currentTrackIds.size > 0) this.lastTrackActivityMs.set(deviceId, Date.now());
|
|
70845
71217
|
const stationarySettings = this.stationarySettingsFromCache(deviceId);
|
|
@@ -71418,6 +71790,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
71418
71790
|
return allConfirmed();
|
|
71419
71791
|
}
|
|
71420
71792
|
const nodeId = frameHandle.nodeId;
|
|
71793
|
+
const candidateNativeRecovery = /* @__PURE__ */ new Map();
|
|
71794
|
+
for (const c of candidates) if (c.nativeRecovery !== void 0) candidateNativeRecovery.set(c.trackId, c.nativeRecovery);
|
|
71421
71795
|
return confirmBirths(candidates, config, {
|
|
71422
71796
|
fetchCrop: (candidate) => this.captureScheduler.request({
|
|
71423
71797
|
deviceId,
|
|
@@ -71467,6 +71841,11 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
71467
71841
|
bestIncompatibleScore: decision.bestIncompatibleScore
|
|
71468
71842
|
} : {},
|
|
71469
71843
|
minConfidence: config.minConfidence,
|
|
71844
|
+
...decision.cropSource !== void 0 ? { cropSource: decision.cropSource } : {},
|
|
71845
|
+
...decision.reason === "native-pass" ? {
|
|
71846
|
+
nativeRecoveryCropSidePx: candidateNativeRecovery.get(decision.trackId)?.cropSidePx,
|
|
71847
|
+
nativeRecoveryViewPx: candidateNativeRecovery.get(decision.trackId)?.viewPx
|
|
71848
|
+
} : {},
|
|
71470
71849
|
...decision.appliedMinConfidence !== void 0 && decision.appliedMinConfidence > config.minConfidence ? {
|
|
71471
71850
|
appliedMinConfidence: decision.appliedMinConfidence,
|
|
71472
71851
|
barRaisedByPhantomCell: true
|
|
@@ -73728,6 +74107,15 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
73728
74107
|
}
|
|
73729
74108
|
});
|
|
73730
74109
|
});
|
|
74110
|
+
p.setSpawnRefusalObserver((record) => {
|
|
74111
|
+
this.ctx.logger.info("tracker: detection dropped — no track was born from it", {
|
|
74112
|
+
tags: { deviceId },
|
|
74113
|
+
meta: {
|
|
74114
|
+
source,
|
|
74115
|
+
...record
|
|
74116
|
+
}
|
|
74117
|
+
});
|
|
74118
|
+
});
|
|
73731
74119
|
p.setTrackLabelState((trackId) => {
|
|
73732
74120
|
const active = this.trackStore?.getActiveByTrack(trackId);
|
|
73733
74121
|
return active !== null && active !== void 0 ? {
|
|
@@ -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-DAc8SUDf.mjs")).catch((e) => {
|
|
34
34
|
throw l = void 0, e;
|
|
35
35
|
}), l;
|
|
36
36
|
}
|