@camstack/addon-pipeline 1.1.53 → 1.1.54
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/audio-analyzer/index.js +1 -1
- package/dist/audio-analyzer/index.mjs +1 -1
- package/dist/detection-pipeline/index.js +146 -7
- package/dist/detection-pipeline/index.mjs +146 -7
- package/dist/{dist-DWb62H5U.js → dist-BbaoC680.js} +110 -7
- package/dist/{dist-BalxNwOt.mjs → dist-CEcTeu1h.mjs} +110 -7
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +114 -5
- package/dist/pipeline-runner/index.mjs +114 -5
- package/dist/recorder/index.js +1 -1
- package/dist/recorder/index.mjs +1 -1
- package/dist/{remote-source-plane-BCJW5CvF.mjs → remote-source-plane-CNCqC_XG.mjs} +1 -1
- package/dist/{remote-source-plane-CZpzIVro.js → remote-source-plane-vN45KFSm.js} +1 -1
- package/dist/session-decode/decode-worker-child.js +319 -35
- package/dist/session-decode/decode-worker-child.mjs +319 -35
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DIN3CcRP.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-HS0tc6Sa.mjs} +3 -3
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-B3gTdHEh.mjs +26 -0
- package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-C9fwKMfg.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-B_v5r5ya.mjs} +1 -1
- package/dist/stream-broker/{hostInit-CGUEq--X.mjs → hostInit-DgohfEDA.mjs} +3 -3
- package/dist/stream-broker/index.js +2 -2
- package/dist/stream-broker/index.mjs +2 -2
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/dist/{worker-protocol-pk7qdYXt.mjs → worker-protocol-DGIt_waM.mjs} +6 -0
- package/dist/{worker-protocol-BCfO8gUF.js → worker-protocol-DkL6GDxe.js} +6 -0
- package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-CqOe8eYa.js → MaskShapeCanvas-DI4BY7W2-CA2Hd6xx.js} +1 -1
- package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-CZyLeUnd.js → MotionZonesSettings-NcxxQN8r-DUN1VeC7.js} +1 -1
- package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-Cn0hGZnu.js → PrivacyMaskSettings-APgPLF7p-BqZIAU5s.js} +1 -1
- package/embed-dist/assets/{index-DRl4XYjA.js → index-CMkYypxB.js} +10 -10
- package/embed-dist/index.html +1 -1
- package/package.json +1 -1
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-o4tu_xuc.mjs +0 -26
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../chunk-D6vf50IK.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
6
|
+
const require_dist = require("../dist-BbaoC680.js");
|
|
7
7
|
const require_node_topology_platform = require("../node-topology-platform-CFZ7F4xW.js");
|
|
8
8
|
const require_model_download_service_C_IHWnXx = require("../model-download-service-C-IHWnXx-BLXzxpRB.js");
|
|
9
9
|
let node_fs = require("node:fs");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __require } from "../chunk-BdkLduGY.mjs";
|
|
2
|
-
import { C as mapAudioLabelToMacro, I as errMsg, L as BaseAddon, U as hydrateSchema, h as audioAnalyzerCapability, l as HF_BASE_URL, m as audioAnalysisCapability, n as AUDIO_BACKEND_CHOICES, o as DEFAULT_AUDIO_ANALYZER_CONFIG } from "../dist-
|
|
2
|
+
import { C as mapAudioLabelToMacro, I as errMsg, L as BaseAddon, U as hydrateSchema, h as audioAnalyzerCapability, l as HF_BASE_URL, m as audioAnalysisCapability, n as AUDIO_BACKEND_CHOICES, o as DEFAULT_AUDIO_ANALYZER_CONFIG } from "../dist-CEcTeu1h.mjs";
|
|
3
3
|
import { t as pickNodePlatformArch } from "../node-topology-platform-BkR_k6WT.mjs";
|
|
4
4
|
import { r as downloadFile } from "../model-download-service-C-IHWnXx-BPy6aoAx.mjs";
|
|
5
5
|
import * as fs from "node:fs";
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../chunk-D6vf50IK.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
6
|
+
const require_dist = require("../dist-BbaoC680.js");
|
|
7
7
|
const require_node_topology_platform = require("../node-topology-platform-CFZ7F4xW.js");
|
|
8
8
|
const require_model_download_service_C_IHWnXx = require("../model-download-service-C-IHWnXx-BLXzxpRB.js");
|
|
9
9
|
let _camstack_shm_ring = require("@camstack/shm-ring");
|
|
@@ -3723,6 +3723,93 @@ function bboxShortSide(bbox) {
|
|
|
3723
3723
|
return Math.min(bbox[2] - bbox[0], bbox[3] - bbox[1]);
|
|
3724
3724
|
}
|
|
3725
3725
|
/**
|
|
3726
|
+
* Padded region (clamped to the frame) around a face bbox — the area a native
|
|
3727
|
+
* crop must cover so the warp has room around a tilted / edge face. Mirrors the
|
|
3728
|
+
* padding {@link alignFaceCrop} applies internally (`padRatio` default 0.4), so
|
|
3729
|
+
* the native and fallback paths sample the same neighbourhood.
|
|
3730
|
+
*/
|
|
3731
|
+
function paddedFaceRegion(faceBbox, imageWidth, imageHeight, padRatio = .4) {
|
|
3732
|
+
const bw = faceBbox[2] - faceBbox[0];
|
|
3733
|
+
const bh = faceBbox[3] - faceBbox[1];
|
|
3734
|
+
const padX = bw * padRatio;
|
|
3735
|
+
const padY = bh * padRatio;
|
|
3736
|
+
return {
|
|
3737
|
+
left: Math.max(0, Math.floor(faceBbox[0] - padX)),
|
|
3738
|
+
top: Math.max(0, Math.floor(faceBbox[1] - padY)),
|
|
3739
|
+
right: Math.min(imageWidth, Math.ceil(faceBbox[2] + padX)),
|
|
3740
|
+
bottom: Math.min(imageHeight, Math.ceil(faceBbox[3] + padY))
|
|
3741
|
+
};
|
|
3742
|
+
}
|
|
3743
|
+
/**
|
|
3744
|
+
* Warp a raw RGB crop of `region` (native-resolution pixels of exactly that
|
|
3745
|
+
* detection-space region) so `landmarksImageSpace` land on the ArcFace
|
|
3746
|
+
* template. Landmarks are mapped from image space into the crop's local pixel
|
|
3747
|
+
* space by the per-axis region→crop scale (handles `maxWidth` downscale of the
|
|
3748
|
+
* native crop). Channels default to RGB (3) — the format the decode worker's
|
|
3749
|
+
* native surface produces.
|
|
3750
|
+
*/
|
|
3751
|
+
async function alignFaceCropFromRgb(rgb, region, landmarksImageSpace, opts = {}) {
|
|
3752
|
+
const outSize = opts.outSize ?? 112;
|
|
3753
|
+
const quality = opts.quality ?? 95;
|
|
3754
|
+
const channels = opts.channels ?? 3;
|
|
3755
|
+
const regionW = region.right - region.left;
|
|
3756
|
+
const regionH = region.bottom - region.top;
|
|
3757
|
+
if (regionW < 1 || regionH < 1) throw new Error(`alignFaceCropFromRgb: degenerate region ${regionW}x${regionH}`);
|
|
3758
|
+
if (rgb.width < 2 || rgb.height < 2) throw new Error(`alignFaceCropFromRgb: degenerate crop ${rgb.width}x${rgb.height}`);
|
|
3759
|
+
const scaleX = rgb.width / regionW;
|
|
3760
|
+
const scaleY = rgb.height / regionH;
|
|
3761
|
+
const localLandmarks = landmarksImageSpace.map((l) => ({
|
|
3762
|
+
x: (l.x - region.left) * scaleX,
|
|
3763
|
+
y: (l.y - region.top) * scaleY
|
|
3764
|
+
}));
|
|
3765
|
+
const aligned = warpToTemplate({
|
|
3766
|
+
data: rgb.bytes,
|
|
3767
|
+
width: rgb.width,
|
|
3768
|
+
height: rgb.height,
|
|
3769
|
+
channels
|
|
3770
|
+
}, localLandmarks, opts.template ?? ARCFACE_TEMPLATE_112, outSize);
|
|
3771
|
+
return {
|
|
3772
|
+
jpeg: await (0, sharp.default)(Buffer.from(aligned.data), { raw: {
|
|
3773
|
+
width: aligned.width,
|
|
3774
|
+
height: aligned.height,
|
|
3775
|
+
channels: aligned.channels
|
|
3776
|
+
} }).jpeg({ quality }).toBuffer(),
|
|
3777
|
+
width: aligned.width,
|
|
3778
|
+
height: aligned.height
|
|
3779
|
+
};
|
|
3780
|
+
}
|
|
3781
|
+
/**
|
|
3782
|
+
* Produce a landmark-aligned face crop, preferring a NATIVE-resolution crop
|
|
3783
|
+
* (design B — the quality win) and FALLING BACK to today's downscaled
|
|
3784
|
+
* full-frame alignment on any native miss/error (never worse than before).
|
|
3785
|
+
*
|
|
3786
|
+
* The native path requests the padded face region from `nativeCropProvider`
|
|
3787
|
+
* (a best-effort local cap round-trip bound to this frame), warps the native
|
|
3788
|
+
* pixels; the fallback path re-uses {@link alignFaceCrop} on the downscaled
|
|
3789
|
+
* JPEG. ArcFace input is a fixed `outSize`×`outSize` either way — only the crop
|
|
3790
|
+
* SOURCE changes, so model cost is unchanged.
|
|
3791
|
+
*/
|
|
3792
|
+
async function buildAlignedFaceCrop(args) {
|
|
3793
|
+
if (args.nativeCropProvider) try {
|
|
3794
|
+
const region = paddedFaceRegion(args.faceBbox, args.imageWidth, args.imageHeight);
|
|
3795
|
+
const roi = {
|
|
3796
|
+
x: region.left / args.imageWidth,
|
|
3797
|
+
y: region.top / args.imageHeight,
|
|
3798
|
+
w: (region.right - region.left) / args.imageWidth,
|
|
3799
|
+
h: (region.bottom - region.top) / args.imageHeight
|
|
3800
|
+
};
|
|
3801
|
+
const native = await args.nativeCropProvider(roi, args.nativeMaxWidth);
|
|
3802
|
+
if (native && native.width >= 2 && native.height >= 2 && native.bytes.length >= native.width * native.height * 3) return {
|
|
3803
|
+
...await alignFaceCropFromRgb(native, region, args.landmarksImageSpace, { outSize: args.outSize }),
|
|
3804
|
+
source: "native"
|
|
3805
|
+
};
|
|
3806
|
+
} catch {}
|
|
3807
|
+
return {
|
|
3808
|
+
...await alignFaceCrop(args.fullFrameJpeg, args.faceBbox, args.landmarksImageSpace, args.imageWidth, args.imageHeight, { outSize: args.outSize }),
|
|
3809
|
+
source: "fallback"
|
|
3810
|
+
};
|
|
3811
|
+
}
|
|
3812
|
+
/**
|
|
3726
3813
|
* Produce an aligned 112x112 JPEG of a face from the full-frame JPEG and the
|
|
3727
3814
|
* face's 5 landmarks (in absolute image-space coordinates). Decodes a padded
|
|
3728
3815
|
* region around the face bbox to raw RGB, warps to the ArcFace template, and
|
|
@@ -4149,6 +4236,14 @@ function formatTopResult(output) {
|
|
|
4149
4236
|
* resolution — a deeper change tracked separately.
|
|
4150
4237
|
*/
|
|
4151
4238
|
var DEFAULT_MIN_FACE_SIZE_PX = 28;
|
|
4239
|
+
/**
|
|
4240
|
+
* Cap (px) on the width of the NATIVE face crop fetched for embedding (design
|
|
4241
|
+
* B). The arcface input is a fixed 112² warp, so a wider native crop buys no
|
|
4242
|
+
* embedding quality past a modest margin but does inflate the per-face transport
|
|
4243
|
+
* of the best-effort native-crop round-trip. 320px keeps a distant face well
|
|
4244
|
+
* above the downscaled detection resolution while bounding the crop payload.
|
|
4245
|
+
*/
|
|
4246
|
+
var NATIVE_FACE_CROP_MAX_WIDTH = 320;
|
|
4152
4247
|
var PipelineExecutor = class {
|
|
4153
4248
|
opts;
|
|
4154
4249
|
constructor(opts) {
|
|
@@ -4178,7 +4273,7 @@ var PipelineExecutor = class {
|
|
|
4178
4273
|
*
|
|
4179
4274
|
* @returns FrameResult + optional trace
|
|
4180
4275
|
*/
|
|
4181
|
-
async run(tree, rootInput, fullFrameJpegProvider, imageWidth, imageHeight, deviceId, runOpts) {
|
|
4276
|
+
async run(tree, rootInput, fullFrameJpegProvider, imageWidth, imageHeight, deviceId, runOpts, nativeFaceCropProvider) {
|
|
4182
4277
|
const startMs = Date.now();
|
|
4183
4278
|
const verbosity = runOpts?.traceVerbosity ?? "off";
|
|
4184
4279
|
const traceBuilder = new ExecutionTraceBuilder(verbosity, deviceId, imageWidth, imageHeight, this.opts.engineRuntime);
|
|
@@ -4216,7 +4311,7 @@ var PipelineExecutor = class {
|
|
|
4216
4311
|
continue;
|
|
4217
4312
|
}
|
|
4218
4313
|
try {
|
|
4219
|
-
await this.executeChildren(rootStep.children, mutable, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg);
|
|
4314
|
+
await this.executeChildren(rootStep.children, mutable, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, nativeFaceCropProvider);
|
|
4220
4315
|
} catch (err) {
|
|
4221
4316
|
this.opts.logger?.warn("Pipeline child execution failed — keeping parent detection", {
|
|
4222
4317
|
tags: { deviceId },
|
|
@@ -4318,7 +4413,7 @@ var PipelineExecutor = class {
|
|
|
4318
4413
|
});
|
|
4319
4414
|
return output;
|
|
4320
4415
|
}
|
|
4321
|
-
async executeChildren(children, parentDetection, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg) {
|
|
4416
|
+
async executeChildren(children, parentDetection, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, nativeFaceCropProvider) {
|
|
4322
4417
|
for (const child of children) {
|
|
4323
4418
|
if (!this.matchesInputClasses(parentDetection.macroClass, child.inputClasses)) continue;
|
|
4324
4419
|
const minParentScore = child.settings?.minParentScore ?? child.definition?.defaultMinParentScore;
|
|
@@ -4345,7 +4440,18 @@ var PipelineExecutor = class {
|
|
|
4345
4440
|
}
|
|
4346
4441
|
const lms = parentDetection.landmarks;
|
|
4347
4442
|
if (lms && lms.length >= 5) {
|
|
4348
|
-
const aligned = await
|
|
4443
|
+
const aligned = await buildAlignedFaceCrop({
|
|
4444
|
+
fullFrameJpeg,
|
|
4445
|
+
faceBbox: parentDetection.bbox,
|
|
4446
|
+
landmarksImageSpace: lms,
|
|
4447
|
+
imageWidth,
|
|
4448
|
+
imageHeight,
|
|
4449
|
+
outSize: modelEntry.inputSize.width,
|
|
4450
|
+
...nativeFaceCropProvider ? {
|
|
4451
|
+
nativeCropProvider: nativeFaceCropProvider,
|
|
4452
|
+
nativeMaxWidth: NATIVE_FACE_CROP_MAX_WIDTH
|
|
4453
|
+
} : {}
|
|
4454
|
+
});
|
|
4349
4455
|
cropJpegBuf = aligned.jpeg;
|
|
4350
4456
|
cropW = aligned.width;
|
|
4351
4457
|
cropH = aligned.height;
|
|
@@ -4379,7 +4485,7 @@ var PipelineExecutor = class {
|
|
|
4379
4485
|
y: l.y + py1
|
|
4380
4486
|
}));
|
|
4381
4487
|
}
|
|
4382
|
-
await this.executeChildren(child.children, detail, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg);
|
|
4488
|
+
await this.executeChildren(child.children, detail, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, nativeFaceCropProvider);
|
|
4383
4489
|
}
|
|
4384
4490
|
}
|
|
4385
4491
|
} catch (err) {
|
|
@@ -5871,6 +5977,38 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5871
5977
|
debug: { totalInferenceMs: 0 }
|
|
5872
5978
|
};
|
|
5873
5979
|
}
|
|
5980
|
+
/**
|
|
5981
|
+
* Build the design-B native-face-crop provider for a runtime frame. Present
|
|
5982
|
+
* ONLY when the frame arrived as a retention `frameHandle` (the key the owning
|
|
5983
|
+
* pipeline-runner registered the frame's native surface under) and the sibling
|
|
5984
|
+
* `pipeline-runner` cap is reachable via `ctx.api`. Each call is a best-effort
|
|
5985
|
+
* round-trip pinned to the frame's OWNING node (`nodePin(handle.nodeId)` — the
|
|
5986
|
+
* node that decoded it, i.e. THIS node for a local frame, never a cross-node
|
|
5987
|
+
* hop) that returns the native ROI or `null` on a miss; the executor then
|
|
5988
|
+
* falls back to the downscaled crop, so behaviour is never worse than before.
|
|
5989
|
+
*/
|
|
5990
|
+
buildNativeFaceCropProvider(handle) {
|
|
5991
|
+
if (!handle) return void 0;
|
|
5992
|
+
const api = this.addonCtx?.api;
|
|
5993
|
+
if (!api) return void 0;
|
|
5994
|
+
return async (roi, maxWidth) => {
|
|
5995
|
+
try {
|
|
5996
|
+
const native = await api.pipelineRunner.getNativeCrop.query({
|
|
5997
|
+
handle,
|
|
5998
|
+
bbox: roi,
|
|
5999
|
+
...maxWidth !== void 0 ? { maxWidth } : {}
|
|
6000
|
+
}, require_dist.nodePin(handle.nodeId));
|
|
6001
|
+
if (!native || native.width <= 0 || native.height <= 0) return null;
|
|
6002
|
+
return {
|
|
6003
|
+
bytes: native.bytes,
|
|
6004
|
+
width: native.width,
|
|
6005
|
+
height: native.height
|
|
6006
|
+
};
|
|
6007
|
+
} catch {
|
|
6008
|
+
return null;
|
|
6009
|
+
}
|
|
6010
|
+
};
|
|
6011
|
+
}
|
|
5874
6012
|
async runPipeline(input, onProgress) {
|
|
5875
6013
|
const nodeId = this.addonCtx?.kernel?.localNodeId ?? "hub";
|
|
5876
6014
|
const sessionId = input.sessionId ?? `run-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
@@ -6127,7 +6265,8 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
6127
6265
|
const effectiveDeviceId = input.deviceId ?? 0;
|
|
6128
6266
|
const deviceOverrides = effectiveDeviceId > 0 ? await this.readDeviceStore(effectiveDeviceId) : {};
|
|
6129
6267
|
const effectiveTree = Object.keys(deviceOverrides).length > 0 ? applyDeviceOverridesToTree(tree, "object-detection", deviceOverrides) : tree;
|
|
6130
|
-
const
|
|
6268
|
+
const nativeFaceCropProvider = this.buildNativeFaceCropProvider(input.frameHandle);
|
|
6269
|
+
const { result, trace } = await executor.run(effectiveTree, rootInput, jpegProvider, imageWidth, imageHeight, effectiveDeviceId, { traceVerbosity: isRuntime ? this.eventBus ? "summary" : "off" : "full" }, nativeFaceCropProvider);
|
|
6131
6270
|
if (isRuntime) {
|
|
6132
6271
|
if (trace && this.eventBus) this.eventBus.emit(require_dist.createEvent(require_dist.EventCategory.PipelineTrace, {
|
|
6133
6272
|
type: "device",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __require } from "../chunk-BdkLduGY.mjs";
|
|
2
|
-
import { H as createEvent, I as errMsg, L as BaseAddon, O as pipelineExecutorCapability, P as supportedRuntimes$1, S as hfModelUrl, U as hydrateSchema, V as EventCategory, X as sleep, a as COCO_TO_MACRO, b as detectionPipelineCapability, f as YAMNET_TO_MACRO, i as COCO_80_LABELS, j as runtimeDevices$1, q as parseJsonUnknown, r as AUDIO_MACRO_LABELS, t as APPLE_SA_TO_MACRO, v as defaultDeviceFor$1, x as evaluateZoneRules } from "../dist-
|
|
2
|
+
import { D as nodePin, H as createEvent, I as errMsg, L as BaseAddon, O as pipelineExecutorCapability, P as supportedRuntimes$1, S as hfModelUrl, U as hydrateSchema, V as EventCategory, X as sleep, a as COCO_TO_MACRO, b as detectionPipelineCapability, f as YAMNET_TO_MACRO, i as COCO_80_LABELS, j as runtimeDevices$1, q as parseJsonUnknown, r as AUDIO_MACRO_LABELS, t as APPLE_SA_TO_MACRO, v as defaultDeviceFor$1, x as evaluateZoneRules } from "../dist-CEcTeu1h.mjs";
|
|
3
3
|
import { t as pickNodePlatformArch } from "../node-topology-platform-BkR_k6WT.mjs";
|
|
4
4
|
import { a as isModelDownloaded, i as ensureModel, n as deleteModelFromDisk } from "../model-download-service-C-IHWnXx-BPy6aoAx.mjs";
|
|
5
5
|
import { FrameRingReaderCache } from "@camstack/shm-ring";
|
|
@@ -3715,6 +3715,93 @@ function bboxShortSide(bbox) {
|
|
|
3715
3715
|
return Math.min(bbox[2] - bbox[0], bbox[3] - bbox[1]);
|
|
3716
3716
|
}
|
|
3717
3717
|
/**
|
|
3718
|
+
* Padded region (clamped to the frame) around a face bbox — the area a native
|
|
3719
|
+
* crop must cover so the warp has room around a tilted / edge face. Mirrors the
|
|
3720
|
+
* padding {@link alignFaceCrop} applies internally (`padRatio` default 0.4), so
|
|
3721
|
+
* the native and fallback paths sample the same neighbourhood.
|
|
3722
|
+
*/
|
|
3723
|
+
function paddedFaceRegion(faceBbox, imageWidth, imageHeight, padRatio = .4) {
|
|
3724
|
+
const bw = faceBbox[2] - faceBbox[0];
|
|
3725
|
+
const bh = faceBbox[3] - faceBbox[1];
|
|
3726
|
+
const padX = bw * padRatio;
|
|
3727
|
+
const padY = bh * padRatio;
|
|
3728
|
+
return {
|
|
3729
|
+
left: Math.max(0, Math.floor(faceBbox[0] - padX)),
|
|
3730
|
+
top: Math.max(0, Math.floor(faceBbox[1] - padY)),
|
|
3731
|
+
right: Math.min(imageWidth, Math.ceil(faceBbox[2] + padX)),
|
|
3732
|
+
bottom: Math.min(imageHeight, Math.ceil(faceBbox[3] + padY))
|
|
3733
|
+
};
|
|
3734
|
+
}
|
|
3735
|
+
/**
|
|
3736
|
+
* Warp a raw RGB crop of `region` (native-resolution pixels of exactly that
|
|
3737
|
+
* detection-space region) so `landmarksImageSpace` land on the ArcFace
|
|
3738
|
+
* template. Landmarks are mapped from image space into the crop's local pixel
|
|
3739
|
+
* space by the per-axis region→crop scale (handles `maxWidth` downscale of the
|
|
3740
|
+
* native crop). Channels default to RGB (3) — the format the decode worker's
|
|
3741
|
+
* native surface produces.
|
|
3742
|
+
*/
|
|
3743
|
+
async function alignFaceCropFromRgb(rgb, region, landmarksImageSpace, opts = {}) {
|
|
3744
|
+
const outSize = opts.outSize ?? 112;
|
|
3745
|
+
const quality = opts.quality ?? 95;
|
|
3746
|
+
const channels = opts.channels ?? 3;
|
|
3747
|
+
const regionW = region.right - region.left;
|
|
3748
|
+
const regionH = region.bottom - region.top;
|
|
3749
|
+
if (regionW < 1 || regionH < 1) throw new Error(`alignFaceCropFromRgb: degenerate region ${regionW}x${regionH}`);
|
|
3750
|
+
if (rgb.width < 2 || rgb.height < 2) throw new Error(`alignFaceCropFromRgb: degenerate crop ${rgb.width}x${rgb.height}`);
|
|
3751
|
+
const scaleX = rgb.width / regionW;
|
|
3752
|
+
const scaleY = rgb.height / regionH;
|
|
3753
|
+
const localLandmarks = landmarksImageSpace.map((l) => ({
|
|
3754
|
+
x: (l.x - region.left) * scaleX,
|
|
3755
|
+
y: (l.y - region.top) * scaleY
|
|
3756
|
+
}));
|
|
3757
|
+
const aligned = warpToTemplate({
|
|
3758
|
+
data: rgb.bytes,
|
|
3759
|
+
width: rgb.width,
|
|
3760
|
+
height: rgb.height,
|
|
3761
|
+
channels
|
|
3762
|
+
}, localLandmarks, opts.template ?? ARCFACE_TEMPLATE_112, outSize);
|
|
3763
|
+
return {
|
|
3764
|
+
jpeg: await sharp(Buffer.from(aligned.data), { raw: {
|
|
3765
|
+
width: aligned.width,
|
|
3766
|
+
height: aligned.height,
|
|
3767
|
+
channels: aligned.channels
|
|
3768
|
+
} }).jpeg({ quality }).toBuffer(),
|
|
3769
|
+
width: aligned.width,
|
|
3770
|
+
height: aligned.height
|
|
3771
|
+
};
|
|
3772
|
+
}
|
|
3773
|
+
/**
|
|
3774
|
+
* Produce a landmark-aligned face crop, preferring a NATIVE-resolution crop
|
|
3775
|
+
* (design B — the quality win) and FALLING BACK to today's downscaled
|
|
3776
|
+
* full-frame alignment on any native miss/error (never worse than before).
|
|
3777
|
+
*
|
|
3778
|
+
* The native path requests the padded face region from `nativeCropProvider`
|
|
3779
|
+
* (a best-effort local cap round-trip bound to this frame), warps the native
|
|
3780
|
+
* pixels; the fallback path re-uses {@link alignFaceCrop} on the downscaled
|
|
3781
|
+
* JPEG. ArcFace input is a fixed `outSize`×`outSize` either way — only the crop
|
|
3782
|
+
* SOURCE changes, so model cost is unchanged.
|
|
3783
|
+
*/
|
|
3784
|
+
async function buildAlignedFaceCrop(args) {
|
|
3785
|
+
if (args.nativeCropProvider) try {
|
|
3786
|
+
const region = paddedFaceRegion(args.faceBbox, args.imageWidth, args.imageHeight);
|
|
3787
|
+
const roi = {
|
|
3788
|
+
x: region.left / args.imageWidth,
|
|
3789
|
+
y: region.top / args.imageHeight,
|
|
3790
|
+
w: (region.right - region.left) / args.imageWidth,
|
|
3791
|
+
h: (region.bottom - region.top) / args.imageHeight
|
|
3792
|
+
};
|
|
3793
|
+
const native = await args.nativeCropProvider(roi, args.nativeMaxWidth);
|
|
3794
|
+
if (native && native.width >= 2 && native.height >= 2 && native.bytes.length >= native.width * native.height * 3) return {
|
|
3795
|
+
...await alignFaceCropFromRgb(native, region, args.landmarksImageSpace, { outSize: args.outSize }),
|
|
3796
|
+
source: "native"
|
|
3797
|
+
};
|
|
3798
|
+
} catch {}
|
|
3799
|
+
return {
|
|
3800
|
+
...await alignFaceCrop(args.fullFrameJpeg, args.faceBbox, args.landmarksImageSpace, args.imageWidth, args.imageHeight, { outSize: args.outSize }),
|
|
3801
|
+
source: "fallback"
|
|
3802
|
+
};
|
|
3803
|
+
}
|
|
3804
|
+
/**
|
|
3718
3805
|
* Produce an aligned 112x112 JPEG of a face from the full-frame JPEG and the
|
|
3719
3806
|
* face's 5 landmarks (in absolute image-space coordinates). Decodes a padded
|
|
3720
3807
|
* region around the face bbox to raw RGB, warps to the ArcFace template, and
|
|
@@ -4141,6 +4228,14 @@ function formatTopResult(output) {
|
|
|
4141
4228
|
* resolution — a deeper change tracked separately.
|
|
4142
4229
|
*/
|
|
4143
4230
|
var DEFAULT_MIN_FACE_SIZE_PX = 28;
|
|
4231
|
+
/**
|
|
4232
|
+
* Cap (px) on the width of the NATIVE face crop fetched for embedding (design
|
|
4233
|
+
* B). The arcface input is a fixed 112² warp, so a wider native crop buys no
|
|
4234
|
+
* embedding quality past a modest margin but does inflate the per-face transport
|
|
4235
|
+
* of the best-effort native-crop round-trip. 320px keeps a distant face well
|
|
4236
|
+
* above the downscaled detection resolution while bounding the crop payload.
|
|
4237
|
+
*/
|
|
4238
|
+
var NATIVE_FACE_CROP_MAX_WIDTH = 320;
|
|
4144
4239
|
var PipelineExecutor = class {
|
|
4145
4240
|
opts;
|
|
4146
4241
|
constructor(opts) {
|
|
@@ -4170,7 +4265,7 @@ var PipelineExecutor = class {
|
|
|
4170
4265
|
*
|
|
4171
4266
|
* @returns FrameResult + optional trace
|
|
4172
4267
|
*/
|
|
4173
|
-
async run(tree, rootInput, fullFrameJpegProvider, imageWidth, imageHeight, deviceId, runOpts) {
|
|
4268
|
+
async run(tree, rootInput, fullFrameJpegProvider, imageWidth, imageHeight, deviceId, runOpts, nativeFaceCropProvider) {
|
|
4174
4269
|
const startMs = Date.now();
|
|
4175
4270
|
const verbosity = runOpts?.traceVerbosity ?? "off";
|
|
4176
4271
|
const traceBuilder = new ExecutionTraceBuilder(verbosity, deviceId, imageWidth, imageHeight, this.opts.engineRuntime);
|
|
@@ -4208,7 +4303,7 @@ var PipelineExecutor = class {
|
|
|
4208
4303
|
continue;
|
|
4209
4304
|
}
|
|
4210
4305
|
try {
|
|
4211
|
-
await this.executeChildren(rootStep.children, mutable, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg);
|
|
4306
|
+
await this.executeChildren(rootStep.children, mutable, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, nativeFaceCropProvider);
|
|
4212
4307
|
} catch (err) {
|
|
4213
4308
|
this.opts.logger?.warn("Pipeline child execution failed — keeping parent detection", {
|
|
4214
4309
|
tags: { deviceId },
|
|
@@ -4310,7 +4405,7 @@ var PipelineExecutor = class {
|
|
|
4310
4405
|
});
|
|
4311
4406
|
return output;
|
|
4312
4407
|
}
|
|
4313
|
-
async executeChildren(children, parentDetection, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg) {
|
|
4408
|
+
async executeChildren(children, parentDetection, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, nativeFaceCropProvider) {
|
|
4314
4409
|
for (const child of children) {
|
|
4315
4410
|
if (!this.matchesInputClasses(parentDetection.macroClass, child.inputClasses)) continue;
|
|
4316
4411
|
const minParentScore = child.settings?.minParentScore ?? child.definition?.defaultMinParentScore;
|
|
@@ -4337,7 +4432,18 @@ var PipelineExecutor = class {
|
|
|
4337
4432
|
}
|
|
4338
4433
|
const lms = parentDetection.landmarks;
|
|
4339
4434
|
if (lms && lms.length >= 5) {
|
|
4340
|
-
const aligned = await
|
|
4435
|
+
const aligned = await buildAlignedFaceCrop({
|
|
4436
|
+
fullFrameJpeg,
|
|
4437
|
+
faceBbox: parentDetection.bbox,
|
|
4438
|
+
landmarksImageSpace: lms,
|
|
4439
|
+
imageWidth,
|
|
4440
|
+
imageHeight,
|
|
4441
|
+
outSize: modelEntry.inputSize.width,
|
|
4442
|
+
...nativeFaceCropProvider ? {
|
|
4443
|
+
nativeCropProvider: nativeFaceCropProvider,
|
|
4444
|
+
nativeMaxWidth: NATIVE_FACE_CROP_MAX_WIDTH
|
|
4445
|
+
} : {}
|
|
4446
|
+
});
|
|
4341
4447
|
cropJpegBuf = aligned.jpeg;
|
|
4342
4448
|
cropW = aligned.width;
|
|
4343
4449
|
cropH = aligned.height;
|
|
@@ -4371,7 +4477,7 @@ var PipelineExecutor = class {
|
|
|
4371
4477
|
y: l.y + py1
|
|
4372
4478
|
}));
|
|
4373
4479
|
}
|
|
4374
|
-
await this.executeChildren(child.children, detail, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg);
|
|
4480
|
+
await this.executeChildren(child.children, detail, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, nativeFaceCropProvider);
|
|
4375
4481
|
}
|
|
4376
4482
|
}
|
|
4377
4483
|
} catch (err) {
|
|
@@ -5863,6 +5969,38 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5863
5969
|
debug: { totalInferenceMs: 0 }
|
|
5864
5970
|
};
|
|
5865
5971
|
}
|
|
5972
|
+
/**
|
|
5973
|
+
* Build the design-B native-face-crop provider for a runtime frame. Present
|
|
5974
|
+
* ONLY when the frame arrived as a retention `frameHandle` (the key the owning
|
|
5975
|
+
* pipeline-runner registered the frame's native surface under) and the sibling
|
|
5976
|
+
* `pipeline-runner` cap is reachable via `ctx.api`. Each call is a best-effort
|
|
5977
|
+
* round-trip pinned to the frame's OWNING node (`nodePin(handle.nodeId)` — the
|
|
5978
|
+
* node that decoded it, i.e. THIS node for a local frame, never a cross-node
|
|
5979
|
+
* hop) that returns the native ROI or `null` on a miss; the executor then
|
|
5980
|
+
* falls back to the downscaled crop, so behaviour is never worse than before.
|
|
5981
|
+
*/
|
|
5982
|
+
buildNativeFaceCropProvider(handle) {
|
|
5983
|
+
if (!handle) return void 0;
|
|
5984
|
+
const api = this.addonCtx?.api;
|
|
5985
|
+
if (!api) return void 0;
|
|
5986
|
+
return async (roi, maxWidth) => {
|
|
5987
|
+
try {
|
|
5988
|
+
const native = await api.pipelineRunner.getNativeCrop.query({
|
|
5989
|
+
handle,
|
|
5990
|
+
bbox: roi,
|
|
5991
|
+
...maxWidth !== void 0 ? { maxWidth } : {}
|
|
5992
|
+
}, nodePin(handle.nodeId));
|
|
5993
|
+
if (!native || native.width <= 0 || native.height <= 0) return null;
|
|
5994
|
+
return {
|
|
5995
|
+
bytes: native.bytes,
|
|
5996
|
+
width: native.width,
|
|
5997
|
+
height: native.height
|
|
5998
|
+
};
|
|
5999
|
+
} catch {
|
|
6000
|
+
return null;
|
|
6001
|
+
}
|
|
6002
|
+
};
|
|
6003
|
+
}
|
|
5866
6004
|
async runPipeline(input, onProgress) {
|
|
5867
6005
|
const nodeId = this.addonCtx?.kernel?.localNodeId ?? "hub";
|
|
5868
6006
|
const sessionId = input.sessionId ?? `run-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
@@ -6119,7 +6257,8 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
6119
6257
|
const effectiveDeviceId = input.deviceId ?? 0;
|
|
6120
6258
|
const deviceOverrides = effectiveDeviceId > 0 ? await this.readDeviceStore(effectiveDeviceId) : {};
|
|
6121
6259
|
const effectiveTree = Object.keys(deviceOverrides).length > 0 ? applyDeviceOverridesToTree(tree, "object-detection", deviceOverrides) : tree;
|
|
6122
|
-
const
|
|
6260
|
+
const nativeFaceCropProvider = this.buildNativeFaceCropProvider(input.frameHandle);
|
|
6261
|
+
const { result, trace } = await executor.run(effectiveTree, rootInput, jpegProvider, imageWidth, imageHeight, effectiveDeviceId, { traceVerbosity: isRuntime ? this.eventBus ? "summary" : "off" : "full" }, nativeFaceCropProvider);
|
|
6123
6262
|
if (isRuntime) {
|
|
6124
6263
|
if (trace && this.eventBus) this.eventBus.emit(createEvent(EventCategory.PipelineTrace, {
|
|
6125
6264
|
type: "device",
|
|
@@ -4627,7 +4627,7 @@ function _instanceof(cls, params = {}) {
|
|
|
4627
4627
|
return inst;
|
|
4628
4628
|
}
|
|
4629
4629
|
//#endregion
|
|
4630
|
-
//#region ../types/dist/sleep-
|
|
4630
|
+
//#region ../types/dist/sleep-BC9Yqte7.mjs
|
|
4631
4631
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
4632
4632
|
EventCategory["SystemBoot"] = "system.boot";
|
|
4633
4633
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -11890,6 +11890,25 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(ZoneSchema).read
|
|
|
11890
11890
|
auth: "admin"
|
|
11891
11891
|
}), object({ zones: array(ZoneSchema).readonly() });
|
|
11892
11892
|
/**
|
|
11893
|
+
* A bounding box in NORMALIZED [0,1] frame coordinates for `getNativeCrop`. The
|
|
11894
|
+
* decode worker resolves it against the RETAINED native frame's real pixel dims,
|
|
11895
|
+
* so the caller supplies only the detection-res bbox divided by the detection
|
|
11896
|
+
* dims — no native resolution to plumb.
|
|
11897
|
+
*/
|
|
11898
|
+
var NativeCropBboxSchema = object({
|
|
11899
|
+
x: number(),
|
|
11900
|
+
y: number(),
|
|
11901
|
+
w: number(),
|
|
11902
|
+
h: number()
|
|
11903
|
+
});
|
|
11904
|
+
/** Result of a best-effort native-resolution crop (`getNativeCrop`). */
|
|
11905
|
+
var NativeCropResultSchema = object({
|
|
11906
|
+
/** Packed rgb (24-bit) pixels of the crop. */
|
|
11907
|
+
bytes: _instanceof(Uint8Array),
|
|
11908
|
+
width: number().int().positive(),
|
|
11909
|
+
height: number().int().positive()
|
|
11910
|
+
});
|
|
11911
|
+
/**
|
|
11893
11912
|
* Per-camera tunable ranges + defaults. Single source of truth used
|
|
11894
11913
|
* by both the Zod data schema (validation + default fallback) and
|
|
11895
11914
|
* the device settings UI (slider min/max/step). Touch one place and
|
|
@@ -12193,7 +12212,22 @@ var pipelineRunnerCapability = {
|
|
|
12193
12212
|
/** All per-camera metrics in one round-trip. */
|
|
12194
12213
|
getAllCameraMetrics: method(_void(), array(CameraMetricsWithDeviceIdSchema).readonly()),
|
|
12195
12214
|
/** List the deviceIds currently attached to this runner. */
|
|
12196
|
-
getLocalCameras: method(_void(), array(number()).readonly())
|
|
12215
|
+
getLocalCameras: method(_void(), array(number()).readonly()),
|
|
12216
|
+
/**
|
|
12217
|
+
* Best-effort NATIVE-resolution crop of a retention-ring frame. Given the
|
|
12218
|
+
* `FrameHandle` that rode an inference-result event and a normalized `bbox`,
|
|
12219
|
+
* the runner asks the decode worker still holding that frame's NATIVE
|
|
12220
|
+
* surface to GPU/CPU-crop ONLY the ROI at native res and download just the
|
|
12221
|
+
* crop. Returns `null` on a miss (handle not registered, or the worker's
|
|
12222
|
+
* tiny native-retention ring already evicted the frame) — the caller falls
|
|
12223
|
+
* back to a detection-frame crop. Routed to the frame's owning node by
|
|
12224
|
+
* `handle.nodeId`; NEVER ships a full native frame.
|
|
12225
|
+
*/
|
|
12226
|
+
getNativeCrop: method(object({
|
|
12227
|
+
handle: FrameHandleSchema,
|
|
12228
|
+
bbox: NativeCropBboxSchema,
|
|
12229
|
+
maxWidth: number().int().positive().optional()
|
|
12230
|
+
}), NativeCropResultSchema.nullable())
|
|
12197
12231
|
}
|
|
12198
12232
|
};
|
|
12199
12233
|
object({
|
|
@@ -16443,7 +16477,17 @@ var TrackSchema = object({
|
|
|
16443
16477
|
/** Cumulative normalized distance travelled (0..1 units = full frame width). */
|
|
16444
16478
|
totalDistance: number(),
|
|
16445
16479
|
state: TrackStateSchema,
|
|
16446
|
-
active: boolean()
|
|
16480
|
+
active: boolean(),
|
|
16481
|
+
/** Deterministic key-event importance score in [0,1] (server-computed at
|
|
16482
|
+
* track expiry, recomputed on late label). Absent on legacy rows written
|
|
16483
|
+
* before scoring shipped — consumers degrade to absence / compute-on-read. */
|
|
16484
|
+
importance: number().optional(),
|
|
16485
|
+
/** Id of the track's highest-confidence ObjectEvent (its representative
|
|
16486
|
+
* "best" frame). Absent when the track produced no object events. */
|
|
16487
|
+
bestEventId: string().optional(),
|
|
16488
|
+
/** Tag of the importance sub-signal that dominated the score
|
|
16489
|
+
* (identity|dwell|proximity|class|confidence|travel|zone). */
|
|
16490
|
+
importanceReason: string().optional()
|
|
16447
16491
|
});
|
|
16448
16492
|
var BaseEventFields = {
|
|
16449
16493
|
id: string(),
|
|
@@ -16508,8 +16552,18 @@ var ObjectEventSchema = object({
|
|
|
16508
16552
|
frameHeight: number().optional(),
|
|
16509
16553
|
/** MediaStore key for the crop attached to this event (if any). */
|
|
16510
16554
|
mediaKey: string().optional(),
|
|
16555
|
+
/** Design B: MediaStore key of the track's native-resolution key frame (the
|
|
16556
|
+
* best-detection full frame). Resolve via the event-media data-plane
|
|
16557
|
+
* (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
|
|
16558
|
+
* draws `bbox` over the native frame. Absent on legacy rows / non-decoded
|
|
16559
|
+
* sources — consumers fall back to `mediaKey` (the tight crop). */
|
|
16560
|
+
keyFrameMediaKey: string().optional(),
|
|
16511
16561
|
/** Populated by B5 (recording playback URL for this event). */
|
|
16512
|
-
mediaUrl: string().optional()
|
|
16562
|
+
mediaUrl: string().optional(),
|
|
16563
|
+
/** The parent track's key-event importance [0,1], propagated to every object
|
|
16564
|
+
* event of the track (so an event row can be sorted by importance without a
|
|
16565
|
+
* track join). Absent on legacy rows / before the track was scored. */
|
|
16566
|
+
importance: number().optional()
|
|
16513
16567
|
});
|
|
16514
16568
|
var AudioEventSchema = object({
|
|
16515
16569
|
...BaseEventFields,
|
|
@@ -16533,7 +16587,8 @@ var MediaFileKindEnum = _enum([
|
|
|
16533
16587
|
"fullFrame",
|
|
16534
16588
|
"fullFrameBoxed",
|
|
16535
16589
|
"faceCrop",
|
|
16536
|
-
"plateCrop"
|
|
16590
|
+
"plateCrop",
|
|
16591
|
+
"keyFrame"
|
|
16537
16592
|
]);
|
|
16538
16593
|
var MediaFileSchema = object({
|
|
16539
16594
|
key: string(),
|
|
@@ -16554,6 +16609,32 @@ var DeviceEventQueryInput = object({
|
|
|
16554
16609
|
projection: _enum(["full", "slim"]).optional()
|
|
16555
16610
|
});
|
|
16556
16611
|
var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
|
|
16612
|
+
var KeyEventQueryInput = object({
|
|
16613
|
+
deviceId: number(),
|
|
16614
|
+
/** Window lower bound (track firstSeen ≥ since). */
|
|
16615
|
+
since: number(),
|
|
16616
|
+
/** Window upper bound (track firstSeen ≤ until). */
|
|
16617
|
+
until: number(),
|
|
16618
|
+
limit: number().int().min(1).max(200).default(50),
|
|
16619
|
+
/** Drop tracks scoring below this importance. */
|
|
16620
|
+
minImportance: number().min(0).max(1).optional(),
|
|
16621
|
+
/** Restrict to a single class (e.g. 'person'). */
|
|
16622
|
+
classFilter: string().optional()
|
|
16623
|
+
});
|
|
16624
|
+
var KeyEventSchema = object({
|
|
16625
|
+
/** The representative event id (the track's best ObjectEvent, else its trackId). */
|
|
16626
|
+
id: string(),
|
|
16627
|
+
trackId: string(),
|
|
16628
|
+
/** Track start time (firstSeen). */
|
|
16629
|
+
timestamp: number(),
|
|
16630
|
+
className: string(),
|
|
16631
|
+
label: string().optional(),
|
|
16632
|
+
importance: number(),
|
|
16633
|
+
/** Highest-confidence ObjectEvent id for the track (empty when none). */
|
|
16634
|
+
bestEventId: string(),
|
|
16635
|
+
/** Track lifetime in ms (lastSeen - firstSeen). */
|
|
16636
|
+
windowMs: number().optional()
|
|
16637
|
+
});
|
|
16557
16638
|
var TrackedDetectionSchema = object({
|
|
16558
16639
|
trackId: string(),
|
|
16559
16640
|
className: string(),
|
|
@@ -16583,7 +16664,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
16583
16664
|
}), array(TrackSchema).readonly()), method(object({ deviceId: number() }), _void(), {
|
|
16584
16665
|
kind: "mutation",
|
|
16585
16666
|
auth: "admin"
|
|
16586
|
-
}), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({
|
|
16667
|
+
}), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
|
|
16587
16668
|
deviceId: number(),
|
|
16588
16669
|
since: number(),
|
|
16589
16670
|
until: number(),
|
|
@@ -18595,7 +18676,17 @@ var FaceInfoSchema = object({
|
|
|
18595
18676
|
recognizedIdentityId: string().optional(),
|
|
18596
18677
|
identityName: string().optional(),
|
|
18597
18678
|
assigned: boolean(),
|
|
18598
|
-
base64: string().optional()
|
|
18679
|
+
base64: string().optional(),
|
|
18680
|
+
/** Design B: the face bbox (pixel space) on the key frame — lets a detail
|
|
18681
|
+
* view draw the box over the native `keyFrameMediaKey` frame. Absent on
|
|
18682
|
+
* legacy rows written before design B. */
|
|
18683
|
+
faceBbox: BoundingBoxSchema.optional(),
|
|
18684
|
+
/** Design B: MediaStore key of the track's native-resolution key frame.
|
|
18685
|
+
* Fetch the native JPEG via the event-media data-plane
|
|
18686
|
+
* (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
|
|
18687
|
+
* track produced no key frame (e.g. native/onboard source) — the UI falls
|
|
18688
|
+
* back to the inline `base64` face crop. */
|
|
18689
|
+
keyFrameMediaKey: string().optional()
|
|
18599
18690
|
});
|
|
18600
18691
|
var FaceFilterEnum = _enum([
|
|
18601
18692
|
"unassigned",
|
|
@@ -22747,6 +22838,12 @@ Object.freeze({
|
|
|
22747
22838
|
addonId: null,
|
|
22748
22839
|
access: "view"
|
|
22749
22840
|
},
|
|
22841
|
+
"pipelineAnalytics.getKeyEvents": {
|
|
22842
|
+
capName: "pipeline-analytics",
|
|
22843
|
+
capScope: "device",
|
|
22844
|
+
addonId: null,
|
|
22845
|
+
access: "view"
|
|
22846
|
+
},
|
|
22750
22847
|
"pipelineAnalytics.getMotionEvents": {
|
|
22751
22848
|
capName: "pipeline-analytics",
|
|
22752
22849
|
capScope: "device",
|
|
@@ -23275,6 +23372,12 @@ Object.freeze({
|
|
|
23275
23372
|
addonId: null,
|
|
23276
23373
|
access: "view"
|
|
23277
23374
|
},
|
|
23375
|
+
"pipelineRunner.getNativeCrop": {
|
|
23376
|
+
capName: "pipeline-runner",
|
|
23377
|
+
capScope: "system",
|
|
23378
|
+
addonId: null,
|
|
23379
|
+
access: "view"
|
|
23380
|
+
},
|
|
23278
23381
|
"pipelineRunner.reportMotion": {
|
|
23279
23382
|
capName: "pipeline-runner",
|
|
23280
23383
|
capScope: "system",
|