@camstack/addon-post-analysis 1.2.216 → 1.2.217
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.
|
@@ -36091,6 +36091,17 @@ function mapObjectStateToTrackState(s) {
|
|
|
36091
36091
|
default: return "new";
|
|
36092
36092
|
}
|
|
36093
36093
|
}
|
|
36094
|
+
/**
|
|
36095
|
+
* How long an identical refusal stays collapsed. A stationary subject is
|
|
36096
|
+
* refused on every analysed frame; at ~2 fps of analysis, 60 s turns a parked
|
|
36097
|
+
* car's 120 lines into one and still reports a subject that comes back later.
|
|
36098
|
+
*/
|
|
36099
|
+
var ZONE_REFUSAL_THROTTLE_MS = 6e4;
|
|
36100
|
+
/** Box quantisation for the collapse key: a subject that moves less than this
|
|
36101
|
+
* is the same sighting for diagnostic purposes. */
|
|
36102
|
+
var ZONE_REFUSAL_BOX_CELL_PX = 64;
|
|
36103
|
+
/** Bound on the collapse map; stale keys are swept when it is exceeded. */
|
|
36104
|
+
var ZONE_REFUSAL_KEY_LIMIT = 512;
|
|
36094
36105
|
var FrameProcessor = class {
|
|
36095
36106
|
deviceId;
|
|
36096
36107
|
/** Detection source this processor handles (`pipeline` | `onboard`).
|
|
@@ -36162,8 +36173,11 @@ var FrameProcessor = class {
|
|
|
36162
36173
|
* as before?" answered first. See `zone-detection-emits-without-a-track`.
|
|
36163
36174
|
*/
|
|
36164
36175
|
refusalsThisFrame = [];
|
|
36165
|
-
/** Running count of
|
|
36176
|
+
/** Running count of refusals-in-zone SEEN — never the throttled number. */
|
|
36166
36177
|
zoneRefusalEventCount = 0;
|
|
36178
|
+
zoneRefusalObserver = null;
|
|
36179
|
+
/** Last report time per collapsed key, for {@link ZONE_REFUSAL_THROTTLE_MS}. */
|
|
36180
|
+
zoneRefusalLastReport = /* @__PURE__ */ new Map();
|
|
36167
36181
|
zoneRefusalSampler = new SpawnRefusalSampler();
|
|
36168
36182
|
/** Optional stationary-object gate (parked-object suppression). Null until
|
|
36169
36183
|
* the addon wires it via {@link setStationaryGate}. */
|
|
@@ -36264,6 +36278,14 @@ var FrameProcessor = class {
|
|
|
36264
36278
|
this.tracker.setSpawnRefusalObserver(observer);
|
|
36265
36279
|
this.spawnRefusalObserver = observer;
|
|
36266
36280
|
}
|
|
36281
|
+
/**
|
|
36282
|
+
* Where a refused-inside-a-zone detection goes. Diagnostics only: this is
|
|
36283
|
+
* deliberately NOT the event stream, so nothing downstream can alert on a
|
|
36284
|
+
* box the confirmation gate threw away (D426).
|
|
36285
|
+
*/
|
|
36286
|
+
setZoneRefusalObserver(observer) {
|
|
36287
|
+
this.zoneRefusalObserver = observer;
|
|
36288
|
+
}
|
|
36267
36289
|
setBindObserver(observer) {
|
|
36268
36290
|
this.tracker.setBindObserver(observer);
|
|
36269
36291
|
}
|
|
@@ -36299,40 +36321,73 @@ var FrameProcessor = class {
|
|
|
36299
36321
|
* operator's own detection rules already said the box does not count on this
|
|
36300
36322
|
* camera, and re-admitting it here would overrule a decision they made.
|
|
36301
36323
|
*/
|
|
36302
|
-
|
|
36324
|
+
/**
|
|
36325
|
+
* Report every detection the tracker refused that fell INSIDE a watched
|
|
36326
|
+
* zone. Diagnostics only — see the spec
|
|
36327
|
+
* `zone-refusal-is-observed-never-notified` for the ten hours of production
|
|
36328
|
+
* that decided this: 1473 of 1511 such refusals were one stationary parcel
|
|
36329
|
+
* re-offered every frame, and both notifications this path ever produced
|
|
36330
|
+
* were phantoms the confirmation gate had correctly rejected.
|
|
36331
|
+
*
|
|
36332
|
+
* Drains {@link refusalsThisFrame}. `zone-filtered` is skipped: a box the
|
|
36333
|
+
* zone filter already removed was never in a zone.
|
|
36334
|
+
*/
|
|
36335
|
+
reportRefusedDetectionsInZones(timestamp, frameWidth, frameHeight) {
|
|
36303
36336
|
const refusals = this.refusalsThisFrame;
|
|
36304
36337
|
this.refusalsThisFrame = [];
|
|
36305
|
-
if (refusals.length === 0 || this.zones.length === 0) return
|
|
36306
|
-
const
|
|
36338
|
+
if (refusals.length === 0 || this.zones.length === 0) return;
|
|
36339
|
+
const observer = this.zoneRefusalObserver;
|
|
36340
|
+
if (observer === null) return;
|
|
36307
36341
|
for (const r of refusals) {
|
|
36308
36342
|
if (r.reason === "zone-filtered") continue;
|
|
36309
36343
|
const { memberships } = this.zoneEngine.splitDetectionZones(r.bbox, this.zones, frameWidth, frameHeight, void 0, void 0, void 0, this.zoneMembershipMinOverlap);
|
|
36310
36344
|
if (memberships.length === 0) continue;
|
|
36311
36345
|
this.zoneRefusalEventCount += 1;
|
|
36312
|
-
|
|
36313
|
-
|
|
36314
|
-
kind: "object",
|
|
36346
|
+
if (!this.shouldReportZoneRefusal(r, memberships, timestamp)) continue;
|
|
36347
|
+
observer({
|
|
36315
36348
|
deviceId: this.deviceId,
|
|
36316
36349
|
timestamp,
|
|
36317
|
-
frameId: frame.frameId,
|
|
36318
36350
|
className: r.detClass,
|
|
36319
36351
|
confidence: r.detScore,
|
|
36320
36352
|
bbox: r.bbox,
|
|
36321
|
-
|
|
36322
|
-
|
|
36323
|
-
zones: memberships.map((m) => m.zoneId),
|
|
36324
|
-
zoneOverlaps: memberships.map((m) => ({
|
|
36353
|
+
reason: r.reason,
|
|
36354
|
+
zones: memberships.map((m) => ({
|
|
36325
36355
|
zoneId: m.zoneId,
|
|
36326
36356
|
...m.zoneName === void 0 ? {} : { zoneName: m.zoneName },
|
|
36327
36357
|
overlapPct: Math.round(m.overlap * 1e3) / 10
|
|
36328
36358
|
})),
|
|
36329
|
-
|
|
36330
|
-
});
|
|
36359
|
+
seenSoFar: this.zoneRefusalEventCount
|
|
36360
|
+
});
|
|
36361
|
+
}
|
|
36362
|
+
}
|
|
36363
|
+
/**
|
|
36364
|
+
* Collapse identical refusals. A refused box is refused on EVERY analysed
|
|
36365
|
+
* frame, so a subject that does not leave — the parcel at the door, the
|
|
36366
|
+
* parked car — is an unbounded emitter. The key includes a coarse box, so a
|
|
36367
|
+
* subject that actually moves keeps reporting while a stationary one does
|
|
36368
|
+
* not.
|
|
36369
|
+
*/
|
|
36370
|
+
shouldReportZoneRefusal(r, memberships, timestamp) {
|
|
36371
|
+
const cell = (v) => Math.round(v / ZONE_REFUSAL_BOX_CELL_PX);
|
|
36372
|
+
const key = [
|
|
36373
|
+
r.detClass,
|
|
36374
|
+
r.reason,
|
|
36375
|
+
memberships.map((m) => m.zoneId).join("+"),
|
|
36376
|
+
cell(r.bbox.x),
|
|
36377
|
+
cell(r.bbox.y),
|
|
36378
|
+
cell(r.bbox.width),
|
|
36379
|
+
cell(r.bbox.height)
|
|
36380
|
+
].join("|");
|
|
36381
|
+
const last = this.zoneRefusalLastReport.get(key);
|
|
36382
|
+
if (last !== void 0 && timestamp - last < ZONE_REFUSAL_THROTTLE_MS) return false;
|
|
36383
|
+
this.zoneRefusalLastReport.set(key, timestamp);
|
|
36384
|
+
if (this.zoneRefusalLastReport.size > ZONE_REFUSAL_KEY_LIMIT) {
|
|
36385
|
+
for (const [k, t] of this.zoneRefusalLastReport) if (timestamp - t >= ZONE_REFUSAL_THROTTLE_MS) this.zoneRefusalLastReport.delete(k);
|
|
36331
36386
|
}
|
|
36332
|
-
return
|
|
36387
|
+
return true;
|
|
36333
36388
|
}
|
|
36334
|
-
/** How many zone
|
|
36335
|
-
*
|
|
36389
|
+
/** How many refusals-in-zone this processor has SEEN — the measure that says
|
|
36390
|
+
* whether this path is a trickle or a storm, before any throttling. */
|
|
36336
36391
|
zoneRefusalEventsEmitted() {
|
|
36337
36392
|
return this.zoneRefusalEventCount;
|
|
36338
36393
|
}
|
|
@@ -36737,11 +36792,10 @@ var FrameProcessor = class {
|
|
|
36737
36792
|
frameHeight
|
|
36738
36793
|
};
|
|
36739
36794
|
};
|
|
36740
|
-
|
|
36795
|
+
this.reportRefusedDetectionsInZones(timestamp, frameWidth, frameHeight);
|
|
36741
36796
|
const withTrackId = rawEvents.filter((e) => e.detection.trackId);
|
|
36742
36797
|
const objectEvents = withTrackId.filter((e) => e.type !== "object.detected").map((e) => toObjectEvent(e));
|
|
36743
36798
|
const appearanceEvents = withTrackId.filter((e) => e.type === "object.detected").map((e) => toObjectEvent(e, "entered"));
|
|
36744
|
-
objectEvents.push(...zoneRefusalEvents);
|
|
36745
36799
|
return {
|
|
36746
36800
|
deviceId: this.deviceId,
|
|
36747
36801
|
timestamp,
|
|
@@ -77554,6 +77608,15 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
77554
77608
|
}
|
|
77555
77609
|
});
|
|
77556
77610
|
});
|
|
77611
|
+
p.setZoneRefusalObserver((observation) => {
|
|
77612
|
+
this.ctx.logger.info("detection refused inside a watched zone — not notified", {
|
|
77613
|
+
tags: { deviceId },
|
|
77614
|
+
meta: {
|
|
77615
|
+
source,
|
|
77616
|
+
...observation
|
|
77617
|
+
}
|
|
77618
|
+
});
|
|
77619
|
+
});
|
|
77557
77620
|
p.setSpawnBesideCoastingObserver((record) => {
|
|
77558
77621
|
this.ctx.logger.info("tracker: new id minted beside a live coasting track", {
|
|
77559
77622
|
tags: { deviceId },
|
|
@@ -36066,6 +36066,17 @@ function mapObjectStateToTrackState(s) {
|
|
|
36066
36066
|
default: return "new";
|
|
36067
36067
|
}
|
|
36068
36068
|
}
|
|
36069
|
+
/**
|
|
36070
|
+
* How long an identical refusal stays collapsed. A stationary subject is
|
|
36071
|
+
* refused on every analysed frame; at ~2 fps of analysis, 60 s turns a parked
|
|
36072
|
+
* car's 120 lines into one and still reports a subject that comes back later.
|
|
36073
|
+
*/
|
|
36074
|
+
var ZONE_REFUSAL_THROTTLE_MS = 6e4;
|
|
36075
|
+
/** Box quantisation for the collapse key: a subject that moves less than this
|
|
36076
|
+
* is the same sighting for diagnostic purposes. */
|
|
36077
|
+
var ZONE_REFUSAL_BOX_CELL_PX = 64;
|
|
36078
|
+
/** Bound on the collapse map; stale keys are swept when it is exceeded. */
|
|
36079
|
+
var ZONE_REFUSAL_KEY_LIMIT = 512;
|
|
36069
36080
|
var FrameProcessor = class {
|
|
36070
36081
|
deviceId;
|
|
36071
36082
|
/** Detection source this processor handles (`pipeline` | `onboard`).
|
|
@@ -36137,8 +36148,11 @@ var FrameProcessor = class {
|
|
|
36137
36148
|
* as before?" answered first. See `zone-detection-emits-without-a-track`.
|
|
36138
36149
|
*/
|
|
36139
36150
|
refusalsThisFrame = [];
|
|
36140
|
-
/** Running count of
|
|
36151
|
+
/** Running count of refusals-in-zone SEEN — never the throttled number. */
|
|
36141
36152
|
zoneRefusalEventCount = 0;
|
|
36153
|
+
zoneRefusalObserver = null;
|
|
36154
|
+
/** Last report time per collapsed key, for {@link ZONE_REFUSAL_THROTTLE_MS}. */
|
|
36155
|
+
zoneRefusalLastReport = /* @__PURE__ */ new Map();
|
|
36142
36156
|
zoneRefusalSampler = new SpawnRefusalSampler();
|
|
36143
36157
|
/** Optional stationary-object gate (parked-object suppression). Null until
|
|
36144
36158
|
* the addon wires it via {@link setStationaryGate}. */
|
|
@@ -36239,6 +36253,14 @@ var FrameProcessor = class {
|
|
|
36239
36253
|
this.tracker.setSpawnRefusalObserver(observer);
|
|
36240
36254
|
this.spawnRefusalObserver = observer;
|
|
36241
36255
|
}
|
|
36256
|
+
/**
|
|
36257
|
+
* Where a refused-inside-a-zone detection goes. Diagnostics only: this is
|
|
36258
|
+
* deliberately NOT the event stream, so nothing downstream can alert on a
|
|
36259
|
+
* box the confirmation gate threw away (D426).
|
|
36260
|
+
*/
|
|
36261
|
+
setZoneRefusalObserver(observer) {
|
|
36262
|
+
this.zoneRefusalObserver = observer;
|
|
36263
|
+
}
|
|
36242
36264
|
setBindObserver(observer) {
|
|
36243
36265
|
this.tracker.setBindObserver(observer);
|
|
36244
36266
|
}
|
|
@@ -36274,40 +36296,73 @@ var FrameProcessor = class {
|
|
|
36274
36296
|
* operator's own detection rules already said the box does not count on this
|
|
36275
36297
|
* camera, and re-admitting it here would overrule a decision they made.
|
|
36276
36298
|
*/
|
|
36277
|
-
|
|
36299
|
+
/**
|
|
36300
|
+
* Report every detection the tracker refused that fell INSIDE a watched
|
|
36301
|
+
* zone. Diagnostics only — see the spec
|
|
36302
|
+
* `zone-refusal-is-observed-never-notified` for the ten hours of production
|
|
36303
|
+
* that decided this: 1473 of 1511 such refusals were one stationary parcel
|
|
36304
|
+
* re-offered every frame, and both notifications this path ever produced
|
|
36305
|
+
* were phantoms the confirmation gate had correctly rejected.
|
|
36306
|
+
*
|
|
36307
|
+
* Drains {@link refusalsThisFrame}. `zone-filtered` is skipped: a box the
|
|
36308
|
+
* zone filter already removed was never in a zone.
|
|
36309
|
+
*/
|
|
36310
|
+
reportRefusedDetectionsInZones(timestamp, frameWidth, frameHeight) {
|
|
36278
36311
|
const refusals = this.refusalsThisFrame;
|
|
36279
36312
|
this.refusalsThisFrame = [];
|
|
36280
|
-
if (refusals.length === 0 || this.zones.length === 0) return
|
|
36281
|
-
const
|
|
36313
|
+
if (refusals.length === 0 || this.zones.length === 0) return;
|
|
36314
|
+
const observer = this.zoneRefusalObserver;
|
|
36315
|
+
if (observer === null) return;
|
|
36282
36316
|
for (const r of refusals) {
|
|
36283
36317
|
if (r.reason === "zone-filtered") continue;
|
|
36284
36318
|
const { memberships } = this.zoneEngine.splitDetectionZones(r.bbox, this.zones, frameWidth, frameHeight, void 0, void 0, void 0, this.zoneMembershipMinOverlap);
|
|
36285
36319
|
if (memberships.length === 0) continue;
|
|
36286
36320
|
this.zoneRefusalEventCount += 1;
|
|
36287
|
-
|
|
36288
|
-
|
|
36289
|
-
kind: "object",
|
|
36321
|
+
if (!this.shouldReportZoneRefusal(r, memberships, timestamp)) continue;
|
|
36322
|
+
observer({
|
|
36290
36323
|
deviceId: this.deviceId,
|
|
36291
36324
|
timestamp,
|
|
36292
|
-
frameId: frame.frameId,
|
|
36293
36325
|
className: r.detClass,
|
|
36294
36326
|
confidence: r.detScore,
|
|
36295
36327
|
bbox: r.bbox,
|
|
36296
|
-
|
|
36297
|
-
|
|
36298
|
-
zones: memberships.map((m) => m.zoneId),
|
|
36299
|
-
zoneOverlaps: memberships.map((m) => ({
|
|
36328
|
+
reason: r.reason,
|
|
36329
|
+
zones: memberships.map((m) => ({
|
|
36300
36330
|
zoneId: m.zoneId,
|
|
36301
36331
|
...m.zoneName === void 0 ? {} : { zoneName: m.zoneName },
|
|
36302
36332
|
overlapPct: Math.round(m.overlap * 1e3) / 10
|
|
36303
36333
|
})),
|
|
36304
|
-
|
|
36305
|
-
});
|
|
36334
|
+
seenSoFar: this.zoneRefusalEventCount
|
|
36335
|
+
});
|
|
36336
|
+
}
|
|
36337
|
+
}
|
|
36338
|
+
/**
|
|
36339
|
+
* Collapse identical refusals. A refused box is refused on EVERY analysed
|
|
36340
|
+
* frame, so a subject that does not leave — the parcel at the door, the
|
|
36341
|
+
* parked car — is an unbounded emitter. The key includes a coarse box, so a
|
|
36342
|
+
* subject that actually moves keeps reporting while a stationary one does
|
|
36343
|
+
* not.
|
|
36344
|
+
*/
|
|
36345
|
+
shouldReportZoneRefusal(r, memberships, timestamp) {
|
|
36346
|
+
const cell = (v) => Math.round(v / ZONE_REFUSAL_BOX_CELL_PX);
|
|
36347
|
+
const key = [
|
|
36348
|
+
r.detClass,
|
|
36349
|
+
r.reason,
|
|
36350
|
+
memberships.map((m) => m.zoneId).join("+"),
|
|
36351
|
+
cell(r.bbox.x),
|
|
36352
|
+
cell(r.bbox.y),
|
|
36353
|
+
cell(r.bbox.width),
|
|
36354
|
+
cell(r.bbox.height)
|
|
36355
|
+
].join("|");
|
|
36356
|
+
const last = this.zoneRefusalLastReport.get(key);
|
|
36357
|
+
if (last !== void 0 && timestamp - last < ZONE_REFUSAL_THROTTLE_MS) return false;
|
|
36358
|
+
this.zoneRefusalLastReport.set(key, timestamp);
|
|
36359
|
+
if (this.zoneRefusalLastReport.size > ZONE_REFUSAL_KEY_LIMIT) {
|
|
36360
|
+
for (const [k, t] of this.zoneRefusalLastReport) if (timestamp - t >= ZONE_REFUSAL_THROTTLE_MS) this.zoneRefusalLastReport.delete(k);
|
|
36306
36361
|
}
|
|
36307
|
-
return
|
|
36362
|
+
return true;
|
|
36308
36363
|
}
|
|
36309
|
-
/** How many zone
|
|
36310
|
-
*
|
|
36364
|
+
/** How many refusals-in-zone this processor has SEEN — the measure that says
|
|
36365
|
+
* whether this path is a trickle or a storm, before any throttling. */
|
|
36311
36366
|
zoneRefusalEventsEmitted() {
|
|
36312
36367
|
return this.zoneRefusalEventCount;
|
|
36313
36368
|
}
|
|
@@ -36712,11 +36767,10 @@ var FrameProcessor = class {
|
|
|
36712
36767
|
frameHeight
|
|
36713
36768
|
};
|
|
36714
36769
|
};
|
|
36715
|
-
|
|
36770
|
+
this.reportRefusedDetectionsInZones(timestamp, frameWidth, frameHeight);
|
|
36716
36771
|
const withTrackId = rawEvents.filter((e) => e.detection.trackId);
|
|
36717
36772
|
const objectEvents = withTrackId.filter((e) => e.type !== "object.detected").map((e) => toObjectEvent(e));
|
|
36718
36773
|
const appearanceEvents = withTrackId.filter((e) => e.type === "object.detected").map((e) => toObjectEvent(e, "entered"));
|
|
36719
|
-
objectEvents.push(...zoneRefusalEvents);
|
|
36720
36774
|
return {
|
|
36721
36775
|
deviceId: this.deviceId,
|
|
36722
36776
|
timestamp,
|
|
@@ -77480,6 +77534,15 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
77480
77534
|
}
|
|
77481
77535
|
});
|
|
77482
77536
|
});
|
|
77537
|
+
p.setZoneRefusalObserver((observation) => {
|
|
77538
|
+
this.ctx.logger.info("detection refused inside a watched zone — not notified", {
|
|
77539
|
+
tags: { deviceId },
|
|
77540
|
+
meta: {
|
|
77541
|
+
source,
|
|
77542
|
+
...observation
|
|
77543
|
+
}
|
|
77544
|
+
});
|
|
77545
|
+
});
|
|
77483
77546
|
p.setSpawnBesideCoastingObserver((record) => {
|
|
77484
77547
|
this.ctx.logger.info("tracker: new id minted beside a live coasting track", {
|
|
77485
77548
|
tags: { deviceId },
|
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.217",
|
|
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",
|