@camstack/addon-pipeline 1.2.195 → 1.2.196
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/pipeline-runner/index.js +106 -24
- package/dist/pipeline-runner/index.mjs +106 -24
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-B17jLw_E.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BMsB2MVD.mjs} +3 -3
- package/dist/stream-broker/{hostInit-C6ku1nL0.mjs → hostInit-CZig_taJ.mjs} +3 -3
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/package.json +1 -1
|
@@ -5156,12 +5156,6 @@ var PipelineRunner = class {
|
|
|
5156
5156
|
return null;
|
|
5157
5157
|
}
|
|
5158
5158
|
};
|
|
5159
|
-
/**
|
|
5160
|
-
* How much of a region must lie OUTSIDE every full-frame detection for the
|
|
5161
|
-
* pass to run on it. A person's own blob (dilated, with shadow) is covered
|
|
5162
|
-
* 70–90% by the person box; a person-and-dog blob is covered ~55% by it.
|
|
5163
|
-
*/
|
|
5164
|
-
var RECOVERY_MIN_UNCOVERED_FRACTION = .35;
|
|
5165
5159
|
function area(r) {
|
|
5166
5160
|
return Math.max(0, r.w) * Math.max(0, r.h);
|
|
5167
5161
|
}
|
|
@@ -5209,25 +5203,53 @@ function cropsForRegion(region, native) {
|
|
|
5209
5203
|
return [squareAround(alongX ? centreX - quarter : centreX, alongX ? centreY : centreY - quarter, side, native), squareAround(alongX ? centreX + quarter : centreX, alongX ? centreY : centreY + quarter, side, native)];
|
|
5210
5204
|
}
|
|
5211
5205
|
/**
|
|
5212
|
-
* The
|
|
5206
|
+
* The ONE place the two region gates are decided. `planRecoveryCrops` filters
|
|
5207
|
+
* on it and {@link countRecoverySkips} counts it, so the report can never
|
|
5208
|
+
* describe a gate the planner does not actually apply.
|
|
5209
|
+
*/
|
|
5210
|
+
function classifyRegion(region, uncovered) {
|
|
5211
|
+
if (area(region) > .25) return "too-large";
|
|
5212
|
+
if (uncovered < .35) return "covered";
|
|
5213
|
+
return "eligible";
|
|
5214
|
+
}
|
|
5215
|
+
/**
|
|
5216
|
+
* WHY A FRAME PLANNED NO CROP, AS A NUMBER (D378).
|
|
5213
5217
|
*
|
|
5214
|
-
*
|
|
5215
|
-
*
|
|
5216
|
-
*
|
|
5217
|
-
*
|
|
5218
|
-
*
|
|
5219
|
-
*
|
|
5218
|
+
* The pass report counts the passes that RAN, so "the second pass stops running
|
|
5219
|
+
* when a person is beside the dog" — a plausible reading of
|
|
5220
|
+
* {@link RECOVERY_MAX_REGION_FRAME_FRACTION} and
|
|
5221
|
+
* {@link RECOVERY_MIN_UNCOVERED_FRACTION}, and a live hypothesis on camera 615
|
|
5222
|
+
* — could not be confirmed or killed from the logs at all. This classifies the
|
|
5223
|
+
* regions a frame offered, against the same predicate the planner filters on.
|
|
5220
5224
|
*/
|
|
5225
|
+
function countRecoverySkips(input) {
|
|
5226
|
+
let tooLarge = 0;
|
|
5227
|
+
let covered = 0;
|
|
5228
|
+
let eligible = 0;
|
|
5229
|
+
for (const region of input.regions) switch (classifyRegion(region, uncoveredFraction(region, input.detections))) {
|
|
5230
|
+
case "too-large":
|
|
5231
|
+
tooLarge += 1;
|
|
5232
|
+
break;
|
|
5233
|
+
case "covered":
|
|
5234
|
+
covered += 1;
|
|
5235
|
+
break;
|
|
5236
|
+
case "eligible":
|
|
5237
|
+
eligible += 1;
|
|
5238
|
+
break;
|
|
5239
|
+
}
|
|
5240
|
+
return {
|
|
5241
|
+
tooLarge,
|
|
5242
|
+
covered,
|
|
5243
|
+
eligible
|
|
5244
|
+
};
|
|
5245
|
+
}
|
|
5221
5246
|
function planRecoveryCrops(input) {
|
|
5222
5247
|
const { native } = input;
|
|
5223
5248
|
if (Math.min(native.width, native.height) < 640) return [];
|
|
5224
5249
|
const ranked = input.regions.map((region) => ({
|
|
5225
5250
|
region,
|
|
5226
5251
|
uncovered: uncoveredFraction(region, input.detections)
|
|
5227
|
-
})).filter(({ region, uncovered }) =>
|
|
5228
|
-
if (area(region) > .25) return false;
|
|
5229
|
-
return uncovered >= RECOVERY_MIN_UNCOVERED_FRACTION;
|
|
5230
|
-
}).toSorted((a, b) => area(b.region) * b.uncovered - area(a.region) * a.uncovered);
|
|
5252
|
+
})).filter(({ region, uncovered }) => classifyRegion(region, uncovered) === "eligible").toSorted((a, b) => area(b.region) * b.uncovered - area(a.region) * a.uncovered);
|
|
5231
5253
|
const planned = [];
|
|
5232
5254
|
for (const { region } of ranked) for (const crop of cropsForRegion(region, native)) {
|
|
5233
5255
|
if (planned.length >= 2) return planned;
|
|
@@ -5258,6 +5280,11 @@ function remapCropDetections(detections, crop, view, result, native) {
|
|
|
5258
5280
|
if (x <= 2 || y <= 2 || x + width >= view.width - 2 || y + height >= view.height - 2) continue;
|
|
5259
5281
|
out.push({
|
|
5260
5282
|
...detection,
|
|
5283
|
+
nativeRecovery: {
|
|
5284
|
+
score: detection.score,
|
|
5285
|
+
cropSidePx: crop.side,
|
|
5286
|
+
viewPx: view.width
|
|
5287
|
+
},
|
|
5261
5288
|
bbox: {
|
|
5262
5289
|
x: (crop.left + x * viewToNative) * nativeToResultX,
|
|
5263
5290
|
y: (crop.top + y * viewToNative) * nativeToResultY,
|
|
@@ -5922,6 +5949,15 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
5922
5949
|
/** Per-camera counters since the last recovery report line. */
|
|
5923
5950
|
recoveryStats = /* @__PURE__ */ new Map();
|
|
5924
5951
|
/**
|
|
5952
|
+
* Per-camera counters for the frames on which the pass did NOT run, since the
|
|
5953
|
+
* last report line (D378). "The second pass stops running when a person is
|
|
5954
|
+
* beside the dog" was a live hypothesis on camera 615 and the logs could not
|
|
5955
|
+
* answer it: a report that counts only the passes that ran cannot say why the
|
|
5956
|
+
* others did not. `rateLimited` is the cadence cap, `noMotion` an empty or
|
|
5957
|
+
* stale motion mirror, and `tooLarge` / `covered` the two region gates.
|
|
5958
|
+
*/
|
|
5959
|
+
recoverySkips = /* @__PURE__ */ new Map();
|
|
5960
|
+
/**
|
|
5925
5961
|
* Gates the root-plane `package-detection` step to motion-settle edges +
|
|
5926
5962
|
* a low-cadence safety poll instead of every frame (packages are
|
|
5927
5963
|
* stationary — per-frame parcel inference is wasted cost). Consulted in
|
|
@@ -7645,6 +7681,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
7645
7681
|
this.motionRegions.forget(deviceId);
|
|
7646
7682
|
this.lastRecoveryAt.delete(deviceId);
|
|
7647
7683
|
this.recoveryStats.delete(deviceId);
|
|
7684
|
+
this.recoverySkips.delete(deviceId);
|
|
7648
7685
|
const warnPrefix = `${deviceId}:`;
|
|
7649
7686
|
for (const key of this.warnedNoJumpDevice) if (key.startsWith(warnPrefix)) this.warnedNoJumpDevice.delete(key);
|
|
7650
7687
|
this.attached.delete(deviceId);
|
|
@@ -8413,9 +8450,15 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
8413
8450
|
const api = this.ctx?.api;
|
|
8414
8451
|
if (!api || !frame.frameRef) return result;
|
|
8415
8452
|
const now = Date.now();
|
|
8416
|
-
if (now - (this.lastRecoveryAt.get(deviceId) ?? 0) < 500)
|
|
8453
|
+
if (now - (this.lastRecoveryAt.get(deviceId) ?? 0) < 500) {
|
|
8454
|
+
this.addRecoverySkips(deviceId, { rateLimited: 1 });
|
|
8455
|
+
return result;
|
|
8456
|
+
}
|
|
8417
8457
|
const regions = this.motionRegions.fresh(deviceId, now);
|
|
8418
|
-
if (regions.length === 0)
|
|
8458
|
+
if (regions.length === 0) {
|
|
8459
|
+
this.addRecoverySkips(deviceId, { noMotion: 1 });
|
|
8460
|
+
return result;
|
|
8461
|
+
}
|
|
8419
8462
|
const step = selectRecoveryStep(steps);
|
|
8420
8463
|
if (step === null) return result;
|
|
8421
8464
|
if (result.width <= 0 || result.height <= 0) return result;
|
|
@@ -8423,7 +8466,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
8423
8466
|
width: frame.frameRef.width,
|
|
8424
8467
|
height: frame.frameRef.height
|
|
8425
8468
|
};
|
|
8426
|
-
const
|
|
8469
|
+
const planInput = {
|
|
8427
8470
|
regions,
|
|
8428
8471
|
detections: result.detections.filter((d) => d.kind === "first-level").map((d) => ({
|
|
8429
8472
|
x: d.bbox.x / result.width,
|
|
@@ -8432,8 +8475,16 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
8432
8475
|
h: d.bbox.height / result.height
|
|
8433
8476
|
})),
|
|
8434
8477
|
native
|
|
8435
|
-
}
|
|
8436
|
-
|
|
8478
|
+
};
|
|
8479
|
+
const crops = planRecoveryCrops(planInput);
|
|
8480
|
+
if (crops.length === 0) {
|
|
8481
|
+
const skipped = countRecoverySkips(planInput);
|
|
8482
|
+
this.addRecoverySkips(deviceId, {
|
|
8483
|
+
tooLarge: skipped.tooLarge,
|
|
8484
|
+
covered: skipped.covered
|
|
8485
|
+
});
|
|
8486
|
+
return result;
|
|
8487
|
+
}
|
|
8437
8488
|
this.lastRecoveryAt.set(deviceId, now);
|
|
8438
8489
|
const log = this.ctx.logger.withTags({ deviceId });
|
|
8439
8490
|
const started = Date.now();
|
|
@@ -8473,7 +8524,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
8473
8524
|
recovered: added,
|
|
8474
8525
|
ms: Date.now() - started,
|
|
8475
8526
|
now,
|
|
8476
|
-
classes: fused.slice(result.detections.length).map((d) => d.macroClass)
|
|
8527
|
+
classes: fused.slice(result.detections.length).map((d) => `${d.macroClass}:${d.score.toFixed(3)}`)
|
|
8477
8528
|
});
|
|
8478
8529
|
return added > 0 ? {
|
|
8479
8530
|
...result,
|
|
@@ -8522,6 +8573,26 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
8522
8573
|
lease.release(reason);
|
|
8523
8574
|
}
|
|
8524
8575
|
}
|
|
8576
|
+
/**
|
|
8577
|
+
* Fold a frame's non-run into the per-camera skip counters, drained by the
|
|
8578
|
+
* next report line (D378). Counters, never lines: a skip happens several
|
|
8579
|
+
* times a second per camera and a line per skip would be the noisiest thing
|
|
8580
|
+
* in the log.
|
|
8581
|
+
*/
|
|
8582
|
+
addRecoverySkips(deviceId, delta) {
|
|
8583
|
+
const current = this.recoverySkips.get(deviceId) ?? {
|
|
8584
|
+
rateLimited: 0,
|
|
8585
|
+
noMotion: 0,
|
|
8586
|
+
tooLarge: 0,
|
|
8587
|
+
covered: 0
|
|
8588
|
+
};
|
|
8589
|
+
this.recoverySkips.set(deviceId, {
|
|
8590
|
+
rateLimited: current.rateLimited + (delta.rateLimited ?? 0),
|
|
8591
|
+
noMotion: current.noMotion + (delta.noMotion ?? 0),
|
|
8592
|
+
tooLarge: current.tooLarge + (delta.tooLarge ?? 0),
|
|
8593
|
+
covered: current.covered + (delta.covered ?? 0)
|
|
8594
|
+
});
|
|
8595
|
+
}
|
|
8525
8596
|
/** Aggregate the pass per camera and report once per {@link RECOVERY_LOG_INTERVAL_MS}. */
|
|
8526
8597
|
reportRecovery(deviceId, sample) {
|
|
8527
8598
|
const stats = this.recoveryStats.get(deviceId) ?? {
|
|
@@ -8548,6 +8619,12 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
8548
8619
|
}
|
|
8549
8620
|
});
|
|
8550
8621
|
if (sample.now - stats.reportedAt >= RECOVERY_LOG_INTERVAL_MS) {
|
|
8622
|
+
const skips = this.recoverySkips.get(deviceId) ?? {
|
|
8623
|
+
rateLimited: 0,
|
|
8624
|
+
noMotion: 0,
|
|
8625
|
+
tooLarge: 0,
|
|
8626
|
+
covered: 0
|
|
8627
|
+
};
|
|
8551
8628
|
this.ctx.logger.info("small-subject recovery: pass report", {
|
|
8552
8629
|
tags: { deviceId },
|
|
8553
8630
|
meta: {
|
|
@@ -8555,9 +8632,14 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
8555
8632
|
crops: next.crops,
|
|
8556
8633
|
recovered: next.recovered,
|
|
8557
8634
|
avgMsPerPass: next.passes > 0 ? Math.round(next.ms / next.passes) : 0,
|
|
8558
|
-
windowMs: sample.now - stats.reportedAt
|
|
8635
|
+
windowMs: sample.now - stats.reportedAt,
|
|
8636
|
+
skipRateLimited: skips.rateLimited,
|
|
8637
|
+
skipNoMotion: skips.noMotion,
|
|
8638
|
+
skipTooLarge: skips.tooLarge,
|
|
8639
|
+
skipCovered: skips.covered
|
|
8559
8640
|
}
|
|
8560
8641
|
});
|
|
8642
|
+
this.recoverySkips.delete(deviceId);
|
|
8561
8643
|
this.recoveryStats.set(deviceId, {
|
|
8562
8644
|
passes: 0,
|
|
8563
8645
|
crops: 0,
|
|
@@ -5149,12 +5149,6 @@ var PipelineRunner = class {
|
|
|
5149
5149
|
return null;
|
|
5150
5150
|
}
|
|
5151
5151
|
};
|
|
5152
|
-
/**
|
|
5153
|
-
* How much of a region must lie OUTSIDE every full-frame detection for the
|
|
5154
|
-
* pass to run on it. A person's own blob (dilated, with shadow) is covered
|
|
5155
|
-
* 70–90% by the person box; a person-and-dog blob is covered ~55% by it.
|
|
5156
|
-
*/
|
|
5157
|
-
var RECOVERY_MIN_UNCOVERED_FRACTION = .35;
|
|
5158
5152
|
function area(r) {
|
|
5159
5153
|
return Math.max(0, r.w) * Math.max(0, r.h);
|
|
5160
5154
|
}
|
|
@@ -5202,25 +5196,53 @@ function cropsForRegion(region, native) {
|
|
|
5202
5196
|
return [squareAround(alongX ? centreX - quarter : centreX, alongX ? centreY : centreY - quarter, side, native), squareAround(alongX ? centreX + quarter : centreX, alongX ? centreY : centreY + quarter, side, native)];
|
|
5203
5197
|
}
|
|
5204
5198
|
/**
|
|
5205
|
-
* The
|
|
5199
|
+
* The ONE place the two region gates are decided. `planRecoveryCrops` filters
|
|
5200
|
+
* on it and {@link countRecoverySkips} counts it, so the report can never
|
|
5201
|
+
* describe a gate the planner does not actually apply.
|
|
5202
|
+
*/
|
|
5203
|
+
function classifyRegion(region, uncovered) {
|
|
5204
|
+
if (area(region) > .25) return "too-large";
|
|
5205
|
+
if (uncovered < .35) return "covered";
|
|
5206
|
+
return "eligible";
|
|
5207
|
+
}
|
|
5208
|
+
/**
|
|
5209
|
+
* WHY A FRAME PLANNED NO CROP, AS A NUMBER (D378).
|
|
5206
5210
|
*
|
|
5207
|
-
*
|
|
5208
|
-
*
|
|
5209
|
-
*
|
|
5210
|
-
*
|
|
5211
|
-
*
|
|
5212
|
-
*
|
|
5211
|
+
* The pass report counts the passes that RAN, so "the second pass stops running
|
|
5212
|
+
* when a person is beside the dog" — a plausible reading of
|
|
5213
|
+
* {@link RECOVERY_MAX_REGION_FRAME_FRACTION} and
|
|
5214
|
+
* {@link RECOVERY_MIN_UNCOVERED_FRACTION}, and a live hypothesis on camera 615
|
|
5215
|
+
* — could not be confirmed or killed from the logs at all. This classifies the
|
|
5216
|
+
* regions a frame offered, against the same predicate the planner filters on.
|
|
5213
5217
|
*/
|
|
5218
|
+
function countRecoverySkips(input) {
|
|
5219
|
+
let tooLarge = 0;
|
|
5220
|
+
let covered = 0;
|
|
5221
|
+
let eligible = 0;
|
|
5222
|
+
for (const region of input.regions) switch (classifyRegion(region, uncoveredFraction(region, input.detections))) {
|
|
5223
|
+
case "too-large":
|
|
5224
|
+
tooLarge += 1;
|
|
5225
|
+
break;
|
|
5226
|
+
case "covered":
|
|
5227
|
+
covered += 1;
|
|
5228
|
+
break;
|
|
5229
|
+
case "eligible":
|
|
5230
|
+
eligible += 1;
|
|
5231
|
+
break;
|
|
5232
|
+
}
|
|
5233
|
+
return {
|
|
5234
|
+
tooLarge,
|
|
5235
|
+
covered,
|
|
5236
|
+
eligible
|
|
5237
|
+
};
|
|
5238
|
+
}
|
|
5214
5239
|
function planRecoveryCrops(input) {
|
|
5215
5240
|
const { native } = input;
|
|
5216
5241
|
if (Math.min(native.width, native.height) < 640) return [];
|
|
5217
5242
|
const ranked = input.regions.map((region) => ({
|
|
5218
5243
|
region,
|
|
5219
5244
|
uncovered: uncoveredFraction(region, input.detections)
|
|
5220
|
-
})).filter(({ region, uncovered }) =>
|
|
5221
|
-
if (area(region) > .25) return false;
|
|
5222
|
-
return uncovered >= RECOVERY_MIN_UNCOVERED_FRACTION;
|
|
5223
|
-
}).toSorted((a, b) => area(b.region) * b.uncovered - area(a.region) * a.uncovered);
|
|
5245
|
+
})).filter(({ region, uncovered }) => classifyRegion(region, uncovered) === "eligible").toSorted((a, b) => area(b.region) * b.uncovered - area(a.region) * a.uncovered);
|
|
5224
5246
|
const planned = [];
|
|
5225
5247
|
for (const { region } of ranked) for (const crop of cropsForRegion(region, native)) {
|
|
5226
5248
|
if (planned.length >= 2) return planned;
|
|
@@ -5251,6 +5273,11 @@ function remapCropDetections(detections, crop, view, result, native) {
|
|
|
5251
5273
|
if (x <= 2 || y <= 2 || x + width >= view.width - 2 || y + height >= view.height - 2) continue;
|
|
5252
5274
|
out.push({
|
|
5253
5275
|
...detection,
|
|
5276
|
+
nativeRecovery: {
|
|
5277
|
+
score: detection.score,
|
|
5278
|
+
cropSidePx: crop.side,
|
|
5279
|
+
viewPx: view.width
|
|
5280
|
+
},
|
|
5254
5281
|
bbox: {
|
|
5255
5282
|
x: (crop.left + x * viewToNative) * nativeToResultX,
|
|
5256
5283
|
y: (crop.top + y * viewToNative) * nativeToResultY,
|
|
@@ -5915,6 +5942,15 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
5915
5942
|
/** Per-camera counters since the last recovery report line. */
|
|
5916
5943
|
recoveryStats = /* @__PURE__ */ new Map();
|
|
5917
5944
|
/**
|
|
5945
|
+
* Per-camera counters for the frames on which the pass did NOT run, since the
|
|
5946
|
+
* last report line (D378). "The second pass stops running when a person is
|
|
5947
|
+
* beside the dog" was a live hypothesis on camera 615 and the logs could not
|
|
5948
|
+
* answer it: a report that counts only the passes that ran cannot say why the
|
|
5949
|
+
* others did not. `rateLimited` is the cadence cap, `noMotion` an empty or
|
|
5950
|
+
* stale motion mirror, and `tooLarge` / `covered` the two region gates.
|
|
5951
|
+
*/
|
|
5952
|
+
recoverySkips = /* @__PURE__ */ new Map();
|
|
5953
|
+
/**
|
|
5918
5954
|
* Gates the root-plane `package-detection` step to motion-settle edges +
|
|
5919
5955
|
* a low-cadence safety poll instead of every frame (packages are
|
|
5920
5956
|
* stationary — per-frame parcel inference is wasted cost). Consulted in
|
|
@@ -7638,6 +7674,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
7638
7674
|
this.motionRegions.forget(deviceId);
|
|
7639
7675
|
this.lastRecoveryAt.delete(deviceId);
|
|
7640
7676
|
this.recoveryStats.delete(deviceId);
|
|
7677
|
+
this.recoverySkips.delete(deviceId);
|
|
7641
7678
|
const warnPrefix = `${deviceId}:`;
|
|
7642
7679
|
for (const key of this.warnedNoJumpDevice) if (key.startsWith(warnPrefix)) this.warnedNoJumpDevice.delete(key);
|
|
7643
7680
|
this.attached.delete(deviceId);
|
|
@@ -8406,9 +8443,15 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
8406
8443
|
const api = this.ctx?.api;
|
|
8407
8444
|
if (!api || !frame.frameRef) return result;
|
|
8408
8445
|
const now = Date.now();
|
|
8409
|
-
if (now - (this.lastRecoveryAt.get(deviceId) ?? 0) < 500)
|
|
8446
|
+
if (now - (this.lastRecoveryAt.get(deviceId) ?? 0) < 500) {
|
|
8447
|
+
this.addRecoverySkips(deviceId, { rateLimited: 1 });
|
|
8448
|
+
return result;
|
|
8449
|
+
}
|
|
8410
8450
|
const regions = this.motionRegions.fresh(deviceId, now);
|
|
8411
|
-
if (regions.length === 0)
|
|
8451
|
+
if (regions.length === 0) {
|
|
8452
|
+
this.addRecoverySkips(deviceId, { noMotion: 1 });
|
|
8453
|
+
return result;
|
|
8454
|
+
}
|
|
8412
8455
|
const step = selectRecoveryStep(steps);
|
|
8413
8456
|
if (step === null) return result;
|
|
8414
8457
|
if (result.width <= 0 || result.height <= 0) return result;
|
|
@@ -8416,7 +8459,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
8416
8459
|
width: frame.frameRef.width,
|
|
8417
8460
|
height: frame.frameRef.height
|
|
8418
8461
|
};
|
|
8419
|
-
const
|
|
8462
|
+
const planInput = {
|
|
8420
8463
|
regions,
|
|
8421
8464
|
detections: result.detections.filter((d) => d.kind === "first-level").map((d) => ({
|
|
8422
8465
|
x: d.bbox.x / result.width,
|
|
@@ -8425,8 +8468,16 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
8425
8468
|
h: d.bbox.height / result.height
|
|
8426
8469
|
})),
|
|
8427
8470
|
native
|
|
8428
|
-
}
|
|
8429
|
-
|
|
8471
|
+
};
|
|
8472
|
+
const crops = planRecoveryCrops(planInput);
|
|
8473
|
+
if (crops.length === 0) {
|
|
8474
|
+
const skipped = countRecoverySkips(planInput);
|
|
8475
|
+
this.addRecoverySkips(deviceId, {
|
|
8476
|
+
tooLarge: skipped.tooLarge,
|
|
8477
|
+
covered: skipped.covered
|
|
8478
|
+
});
|
|
8479
|
+
return result;
|
|
8480
|
+
}
|
|
8430
8481
|
this.lastRecoveryAt.set(deviceId, now);
|
|
8431
8482
|
const log = this.ctx.logger.withTags({ deviceId });
|
|
8432
8483
|
const started = Date.now();
|
|
@@ -8466,7 +8517,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
8466
8517
|
recovered: added,
|
|
8467
8518
|
ms: Date.now() - started,
|
|
8468
8519
|
now,
|
|
8469
|
-
classes: fused.slice(result.detections.length).map((d) => d.macroClass)
|
|
8520
|
+
classes: fused.slice(result.detections.length).map((d) => `${d.macroClass}:${d.score.toFixed(3)}`)
|
|
8470
8521
|
});
|
|
8471
8522
|
return added > 0 ? {
|
|
8472
8523
|
...result,
|
|
@@ -8515,6 +8566,26 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
8515
8566
|
lease.release(reason);
|
|
8516
8567
|
}
|
|
8517
8568
|
}
|
|
8569
|
+
/**
|
|
8570
|
+
* Fold a frame's non-run into the per-camera skip counters, drained by the
|
|
8571
|
+
* next report line (D378). Counters, never lines: a skip happens several
|
|
8572
|
+
* times a second per camera and a line per skip would be the noisiest thing
|
|
8573
|
+
* in the log.
|
|
8574
|
+
*/
|
|
8575
|
+
addRecoverySkips(deviceId, delta) {
|
|
8576
|
+
const current = this.recoverySkips.get(deviceId) ?? {
|
|
8577
|
+
rateLimited: 0,
|
|
8578
|
+
noMotion: 0,
|
|
8579
|
+
tooLarge: 0,
|
|
8580
|
+
covered: 0
|
|
8581
|
+
};
|
|
8582
|
+
this.recoverySkips.set(deviceId, {
|
|
8583
|
+
rateLimited: current.rateLimited + (delta.rateLimited ?? 0),
|
|
8584
|
+
noMotion: current.noMotion + (delta.noMotion ?? 0),
|
|
8585
|
+
tooLarge: current.tooLarge + (delta.tooLarge ?? 0),
|
|
8586
|
+
covered: current.covered + (delta.covered ?? 0)
|
|
8587
|
+
});
|
|
8588
|
+
}
|
|
8518
8589
|
/** Aggregate the pass per camera and report once per {@link RECOVERY_LOG_INTERVAL_MS}. */
|
|
8519
8590
|
reportRecovery(deviceId, sample) {
|
|
8520
8591
|
const stats = this.recoveryStats.get(deviceId) ?? {
|
|
@@ -8541,6 +8612,12 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
8541
8612
|
}
|
|
8542
8613
|
});
|
|
8543
8614
|
if (sample.now - stats.reportedAt >= RECOVERY_LOG_INTERVAL_MS) {
|
|
8615
|
+
const skips = this.recoverySkips.get(deviceId) ?? {
|
|
8616
|
+
rateLimited: 0,
|
|
8617
|
+
noMotion: 0,
|
|
8618
|
+
tooLarge: 0,
|
|
8619
|
+
covered: 0
|
|
8620
|
+
};
|
|
8544
8621
|
this.ctx.logger.info("small-subject recovery: pass report", {
|
|
8545
8622
|
tags: { deviceId },
|
|
8546
8623
|
meta: {
|
|
@@ -8548,9 +8625,14 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
8548
8625
|
crops: next.crops,
|
|
8549
8626
|
recovered: next.recovered,
|
|
8550
8627
|
avgMsPerPass: next.passes > 0 ? Math.round(next.ms / next.passes) : 0,
|
|
8551
|
-
windowMs: sample.now - stats.reportedAt
|
|
8628
|
+
windowMs: sample.now - stats.reportedAt,
|
|
8629
|
+
skipRateLimited: skips.rateLimited,
|
|
8630
|
+
skipNoMotion: skips.noMotion,
|
|
8631
|
+
skipTooLarge: skips.tooLarge,
|
|
8632
|
+
skipCovered: skips.covered
|
|
8552
8633
|
}
|
|
8553
8634
|
});
|
|
8635
|
+
this.recoverySkips.delete(deviceId);
|
|
8554
8636
|
this.recoveryStats.set(deviceId, {
|
|
8555
8637
|
passes: 0,
|
|
8556
8638
|
crops: 0,
|
|
@@ -3,7 +3,7 @@ import "./dist-CYZr2fwk.mjs";
|
|
|
3
3
|
var e = {
|
|
4
4
|
"@camstack/sdk": {
|
|
5
5
|
name: "@camstack/sdk",
|
|
6
|
-
version: "1.2.
|
|
6
|
+
version: "1.2.78",
|
|
7
7
|
scope: ["default"],
|
|
8
8
|
loaded: !1,
|
|
9
9
|
from: "addon_stream_broker_widgets",
|
|
@@ -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.158",
|
|
22
22
|
scope: ["default"],
|
|
23
23
|
loaded: !1,
|
|
24
24
|
from: "addon_stream_broker_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.126",
|
|
37
37
|
scope: ["default"],
|
|
38
38
|
loaded: !1,
|
|
39
39
|
from: "addon_stream_broker_widgets",
|
|
@@ -36,7 +36,7 @@ async function r() {
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"@camstack/types": {
|
|
39
|
-
version: "1.2.
|
|
39
|
+
version: "1.2.158",
|
|
40
40
|
scope: "default",
|
|
41
41
|
shareConfig: {
|
|
42
42
|
singleton: !0,
|
|
@@ -45,7 +45,7 @@ async function r() {
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"@camstack/sdk": {
|
|
48
|
-
version: "1.2.
|
|
48
|
+
version: "1.2.78",
|
|
49
49
|
scope: "default",
|
|
50
50
|
shareConfig: {
|
|
51
51
|
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.126",
|
|
85
85
|
scope: "default",
|
|
86
86
|
shareConfig: {
|
|
87
87
|
singleton: !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_stream_broker_widgets-
|
|
33
|
+
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BMsB2MVD.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-pipeline",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.196",
|
|
4
4
|
"description": "Pipeline bundle — runner, detection, motion, audio + stream broker. Multi-entry npm package shipping pipeline addons under a single bundle.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|