@camstack/addon-pipeline 1.1.52 → 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 +191 -12
- package/dist/detection-pipeline/index.mjs +191 -12
- package/dist/{dist-B-pQhc30.js → dist-BbaoC680.js} +111 -7
- package/dist/{dist-DM6-eQRs.mjs → dist-CEcTeu1h.mjs} +111 -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-DivMEw6Y.mjs → remote-source-plane-CNCqC_XG.mjs} +1 -1
- package/dist/{remote-source-plane-dSe5NlH9.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-D6UvBYwr.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-DpF_eilj.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-DRCvljqU.js → MaskShapeCanvas-DI4BY7W2-CA2Hd6xx.js} +1 -1
- package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-DmQAEfWC.js → MotionZonesSettings-NcxxQN8r-DUN1VeC7.js} +1 -1
- package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-DNJ5RGGN.js → PrivacyMaskSettings-APgPLF7p-BqZIAU5s.js} +1 -1
- package/embed-dist/assets/index-CC06JBcl.css +2 -0
- package/embed-dist/assets/{index-BHKG2Pw-.js → index-CMkYypxB.js} +10 -10
- package/embed-dist/index.html +2 -2
- package/package.json +1 -1
- package/python/inference_pool.py +42 -1
- package/python/test_inference_pool_layout.py +44 -0
- 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
- package/embed-dist/assets/index-JqUY2p33.css +0 -2
|
@@ -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");
|
|
@@ -1543,11 +1543,16 @@ var STEP_CLIP_EMBEDDING = new PipelineStepBase({
|
|
|
1543
1543
|
slot: "classifier",
|
|
1544
1544
|
postprocessor: "clip",
|
|
1545
1545
|
extractMode: "crop-roi",
|
|
1546
|
-
inputClasses:
|
|
1546
|
+
inputClasses: [
|
|
1547
|
+
"person",
|
|
1548
|
+
"vehicle",
|
|
1549
|
+
"animal"
|
|
1550
|
+
],
|
|
1547
1551
|
outputClasses: ["embedding"],
|
|
1548
1552
|
models: [...CLIP_EMBEDDING_MODELS],
|
|
1549
1553
|
defaultModelId: "mobileclip-s1",
|
|
1550
1554
|
enabledByDefault: true,
|
|
1555
|
+
backfillIntoExistingOverrides: true,
|
|
1551
1556
|
defaultConfidence: 0,
|
|
1552
1557
|
defaultMinParentScore: .4,
|
|
1553
1558
|
group: "CLIP"
|
|
@@ -3718,6 +3723,93 @@ function bboxShortSide(bbox) {
|
|
|
3718
3723
|
return Math.min(bbox[2] - bbox[0], bbox[3] - bbox[1]);
|
|
3719
3724
|
}
|
|
3720
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
|
+
/**
|
|
3721
3813
|
* Produce an aligned 112x112 JPEG of a face from the full-frame JPEG and the
|
|
3722
3814
|
* face's 5 landmarks (in absolute image-space coordinates). Decodes a padded
|
|
3723
3815
|
* region around the face bbox to raw RGB, warps to the ArcFace template, and
|
|
@@ -4132,15 +4224,42 @@ function formatTopResult(output) {
|
|
|
4132
4224
|
/**
|
|
4133
4225
|
* Default minimum short-side (px, source-frame resolution) of a face bbox for
|
|
4134
4226
|
* which a recognition embedding is attempted. Faces smaller than this upscale
|
|
4135
|
-
* into
|
|
4227
|
+
* into weaker embeddings; we skip them rather than pollute clustering. Override
|
|
4136
4228
|
* per-step via `settings.minFaceSize`.
|
|
4229
|
+
*
|
|
4230
|
+
* Lowered 40 → 28 (2026-07-14): detection runs on a downscaled frame (e.g.
|
|
4231
|
+
* 640×360), so an indoor person's face is often 28-38px there and was being
|
|
4232
|
+
* skipped entirely — no faces collected. 28px still warps acceptably onto the
|
|
4233
|
+
* 112×112 ArcFace input; the honest below-threshold count is surfaced by the
|
|
4234
|
+
* throttled "too small to embed" diagnostic (with the actual `faceShortSidePx`).
|
|
4235
|
+
* The quality-preserving alternative is to run face-detection at a higher input
|
|
4236
|
+
* resolution — a deeper change tracked separately.
|
|
4137
4237
|
*/
|
|
4138
|
-
var DEFAULT_MIN_FACE_SIZE_PX =
|
|
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;
|
|
4139
4247
|
var PipelineExecutor = class {
|
|
4140
4248
|
opts;
|
|
4141
4249
|
constructor(opts) {
|
|
4142
4250
|
this.opts = opts;
|
|
4143
4251
|
}
|
|
4252
|
+
/** Per-key last-warn timestamps, so a persistently-failing/skipping child
|
|
4253
|
+
* step surfaces a diagnostic without spamming the log every frame. */
|
|
4254
|
+
warnThrottleAt = /* @__PURE__ */ new Map();
|
|
4255
|
+
throttledWarn(key, everyMs, message, meta) {
|
|
4256
|
+
const logger = this.opts.logger;
|
|
4257
|
+
if (!logger) return;
|
|
4258
|
+
const now = Date.now();
|
|
4259
|
+
if (now - (this.warnThrottleAt.get(key) ?? 0) < everyMs) return;
|
|
4260
|
+
this.warnThrottleAt.set(key, now);
|
|
4261
|
+
logger.warn(message, { meta });
|
|
4262
|
+
}
|
|
4144
4263
|
/**
|
|
4145
4264
|
* Execute the full pipeline tree against a frame.
|
|
4146
4265
|
*
|
|
@@ -4154,7 +4273,7 @@ var PipelineExecutor = class {
|
|
|
4154
4273
|
*
|
|
4155
4274
|
* @returns FrameResult + optional trace
|
|
4156
4275
|
*/
|
|
4157
|
-
async run(tree, rootInput, fullFrameJpegProvider, imageWidth, imageHeight, deviceId, runOpts) {
|
|
4276
|
+
async run(tree, rootInput, fullFrameJpegProvider, imageWidth, imageHeight, deviceId, runOpts, nativeFaceCropProvider) {
|
|
4158
4277
|
const startMs = Date.now();
|
|
4159
4278
|
const verbosity = runOpts?.traceVerbosity ?? "off";
|
|
4160
4279
|
const traceBuilder = new ExecutionTraceBuilder(verbosity, deviceId, imageWidth, imageHeight, this.opts.engineRuntime);
|
|
@@ -4192,7 +4311,7 @@ var PipelineExecutor = class {
|
|
|
4192
4311
|
continue;
|
|
4193
4312
|
}
|
|
4194
4313
|
try {
|
|
4195
|
-
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);
|
|
4196
4315
|
} catch (err) {
|
|
4197
4316
|
this.opts.logger?.warn("Pipeline child execution failed — keeping parent detection", {
|
|
4198
4317
|
tags: { deviceId },
|
|
@@ -4294,7 +4413,7 @@ var PipelineExecutor = class {
|
|
|
4294
4413
|
});
|
|
4295
4414
|
return output;
|
|
4296
4415
|
}
|
|
4297
|
-
async executeChildren(children, parentDetection, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg) {
|
|
4416
|
+
async executeChildren(children, parentDetection, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, nativeFaceCropProvider) {
|
|
4298
4417
|
for (const child of children) {
|
|
4299
4418
|
if (!this.matchesInputClasses(parentDetection.macroClass, child.inputClasses)) continue;
|
|
4300
4419
|
const minParentScore = child.settings?.minParentScore ?? child.definition?.defaultMinParentScore;
|
|
@@ -4309,10 +4428,30 @@ var PipelineExecutor = class {
|
|
|
4309
4428
|
let cropH;
|
|
4310
4429
|
if (modelEntry?.faceAlignment) {
|
|
4311
4430
|
const minFaceSize = typeof child.settings?.["minFaceSize"] === "number" ? child.settings["minFaceSize"] : DEFAULT_MIN_FACE_SIZE_PX;
|
|
4312
|
-
|
|
4431
|
+
const faceShortSide = bboxShortSide(parentDetection.bbox);
|
|
4432
|
+
if (faceShortSide < minFaceSize) {
|
|
4433
|
+
this.throttledWarn(`face-small:${child.stepId}`, 3e4, "face detected but too small to embed — skipping", {
|
|
4434
|
+
step: child.stepId,
|
|
4435
|
+
model: child.modelId,
|
|
4436
|
+
faceShortSidePx: Math.round(faceShortSide),
|
|
4437
|
+
minFaceSizePx: minFaceSize
|
|
4438
|
+
});
|
|
4439
|
+
continue;
|
|
4440
|
+
}
|
|
4313
4441
|
const lms = parentDetection.landmarks;
|
|
4314
4442
|
if (lms && lms.length >= 5) {
|
|
4315
|
-
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
|
+
});
|
|
4316
4455
|
cropJpegBuf = aligned.jpeg;
|
|
4317
4456
|
cropW = aligned.width;
|
|
4318
4457
|
cropH = aligned.height;
|
|
@@ -4346,10 +4485,16 @@ var PipelineExecutor = class {
|
|
|
4346
4485
|
y: l.y + py1
|
|
4347
4486
|
}));
|
|
4348
4487
|
}
|
|
4349
|
-
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);
|
|
4350
4489
|
}
|
|
4351
4490
|
}
|
|
4352
|
-
} catch {
|
|
4491
|
+
} catch (err) {
|
|
4492
|
+
this.throttledWarn(`child-fail:${child.stepId}`, 15e3, "pipeline child step failed", {
|
|
4493
|
+
step: child.stepId,
|
|
4494
|
+
model: child.modelId,
|
|
4495
|
+
error: require_dist.errMsg(err)
|
|
4496
|
+
});
|
|
4497
|
+
}
|
|
4353
4498
|
}
|
|
4354
4499
|
}
|
|
4355
4500
|
matchesInputClasses(className, inputClasses) {
|
|
@@ -5832,6 +5977,38 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5832
5977
|
debug: { totalInferenceMs: 0 }
|
|
5833
5978
|
};
|
|
5834
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
|
+
}
|
|
5835
6012
|
async runPipeline(input, onProgress) {
|
|
5836
6013
|
const nodeId = this.addonCtx?.kernel?.localNodeId ?? "hub";
|
|
5837
6014
|
const sessionId = input.sessionId ?? `run-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
@@ -6088,7 +6265,8 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
6088
6265
|
const effectiveDeviceId = input.deviceId ?? 0;
|
|
6089
6266
|
const deviceOverrides = effectiveDeviceId > 0 ? await this.readDeviceStore(effectiveDeviceId) : {};
|
|
6090
6267
|
const effectiveTree = Object.keys(deviceOverrides).length > 0 ? applyDeviceOverridesToTree(tree, "object-detection", deviceOverrides) : tree;
|
|
6091
|
-
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);
|
|
6092
6270
|
if (isRuntime) {
|
|
6093
6271
|
if (trace && this.eventBus) this.eventBus.emit(require_dist.createEvent(require_dist.EventCategory.PipelineTrace, {
|
|
6094
6272
|
type: "device",
|
|
@@ -7173,6 +7351,7 @@ function buildSchemaSlots(format, modelsDir, customByStep) {
|
|
|
7173
7351
|
defaultModelId: step.defaultModelId,
|
|
7174
7352
|
...step.defaultModelIdByFormat ? { defaultModelIdByFormat: { ...step.defaultModelIdByFormat } } : {},
|
|
7175
7353
|
...step.enabledByDefault !== void 0 ? { enabledByDefault: step.enabledByDefault } : {},
|
|
7354
|
+
...step.backfillIntoExistingOverrides !== void 0 ? { backfillIntoExistingOverrides: step.backfillIntoExistingOverrides } : {},
|
|
7176
7355
|
defaultConfidence: step.defaultConfidence,
|
|
7177
7356
|
...step.group ? { group: step.group } : {},
|
|
7178
7357
|
...configSchema.length > 0 ? { configSchema: [...configSchema] } : {}
|
|
@@ -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";
|
|
@@ -1535,11 +1535,16 @@ var STEP_CLIP_EMBEDDING = new PipelineStepBase({
|
|
|
1535
1535
|
slot: "classifier",
|
|
1536
1536
|
postprocessor: "clip",
|
|
1537
1537
|
extractMode: "crop-roi",
|
|
1538
|
-
inputClasses:
|
|
1538
|
+
inputClasses: [
|
|
1539
|
+
"person",
|
|
1540
|
+
"vehicle",
|
|
1541
|
+
"animal"
|
|
1542
|
+
],
|
|
1539
1543
|
outputClasses: ["embedding"],
|
|
1540
1544
|
models: [...CLIP_EMBEDDING_MODELS],
|
|
1541
1545
|
defaultModelId: "mobileclip-s1",
|
|
1542
1546
|
enabledByDefault: true,
|
|
1547
|
+
backfillIntoExistingOverrides: true,
|
|
1543
1548
|
defaultConfidence: 0,
|
|
1544
1549
|
defaultMinParentScore: .4,
|
|
1545
1550
|
group: "CLIP"
|
|
@@ -3710,6 +3715,93 @@ function bboxShortSide(bbox) {
|
|
|
3710
3715
|
return Math.min(bbox[2] - bbox[0], bbox[3] - bbox[1]);
|
|
3711
3716
|
}
|
|
3712
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
|
+
/**
|
|
3713
3805
|
* Produce an aligned 112x112 JPEG of a face from the full-frame JPEG and the
|
|
3714
3806
|
* face's 5 landmarks (in absolute image-space coordinates). Decodes a padded
|
|
3715
3807
|
* region around the face bbox to raw RGB, warps to the ArcFace template, and
|
|
@@ -4124,15 +4216,42 @@ function formatTopResult(output) {
|
|
|
4124
4216
|
/**
|
|
4125
4217
|
* Default minimum short-side (px, source-frame resolution) of a face bbox for
|
|
4126
4218
|
* which a recognition embedding is attempted. Faces smaller than this upscale
|
|
4127
|
-
* into
|
|
4219
|
+
* into weaker embeddings; we skip them rather than pollute clustering. Override
|
|
4128
4220
|
* per-step via `settings.minFaceSize`.
|
|
4221
|
+
*
|
|
4222
|
+
* Lowered 40 → 28 (2026-07-14): detection runs on a downscaled frame (e.g.
|
|
4223
|
+
* 640×360), so an indoor person's face is often 28-38px there and was being
|
|
4224
|
+
* skipped entirely — no faces collected. 28px still warps acceptably onto the
|
|
4225
|
+
* 112×112 ArcFace input; the honest below-threshold count is surfaced by the
|
|
4226
|
+
* throttled "too small to embed" diagnostic (with the actual `faceShortSidePx`).
|
|
4227
|
+
* The quality-preserving alternative is to run face-detection at a higher input
|
|
4228
|
+
* resolution — a deeper change tracked separately.
|
|
4129
4229
|
*/
|
|
4130
|
-
var DEFAULT_MIN_FACE_SIZE_PX =
|
|
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;
|
|
4131
4239
|
var PipelineExecutor = class {
|
|
4132
4240
|
opts;
|
|
4133
4241
|
constructor(opts) {
|
|
4134
4242
|
this.opts = opts;
|
|
4135
4243
|
}
|
|
4244
|
+
/** Per-key last-warn timestamps, so a persistently-failing/skipping child
|
|
4245
|
+
* step surfaces a diagnostic without spamming the log every frame. */
|
|
4246
|
+
warnThrottleAt = /* @__PURE__ */ new Map();
|
|
4247
|
+
throttledWarn(key, everyMs, message, meta) {
|
|
4248
|
+
const logger = this.opts.logger;
|
|
4249
|
+
if (!logger) return;
|
|
4250
|
+
const now = Date.now();
|
|
4251
|
+
if (now - (this.warnThrottleAt.get(key) ?? 0) < everyMs) return;
|
|
4252
|
+
this.warnThrottleAt.set(key, now);
|
|
4253
|
+
logger.warn(message, { meta });
|
|
4254
|
+
}
|
|
4136
4255
|
/**
|
|
4137
4256
|
* Execute the full pipeline tree against a frame.
|
|
4138
4257
|
*
|
|
@@ -4146,7 +4265,7 @@ var PipelineExecutor = class {
|
|
|
4146
4265
|
*
|
|
4147
4266
|
* @returns FrameResult + optional trace
|
|
4148
4267
|
*/
|
|
4149
|
-
async run(tree, rootInput, fullFrameJpegProvider, imageWidth, imageHeight, deviceId, runOpts) {
|
|
4268
|
+
async run(tree, rootInput, fullFrameJpegProvider, imageWidth, imageHeight, deviceId, runOpts, nativeFaceCropProvider) {
|
|
4150
4269
|
const startMs = Date.now();
|
|
4151
4270
|
const verbosity = runOpts?.traceVerbosity ?? "off";
|
|
4152
4271
|
const traceBuilder = new ExecutionTraceBuilder(verbosity, deviceId, imageWidth, imageHeight, this.opts.engineRuntime);
|
|
@@ -4184,7 +4303,7 @@ var PipelineExecutor = class {
|
|
|
4184
4303
|
continue;
|
|
4185
4304
|
}
|
|
4186
4305
|
try {
|
|
4187
|
-
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);
|
|
4188
4307
|
} catch (err) {
|
|
4189
4308
|
this.opts.logger?.warn("Pipeline child execution failed — keeping parent detection", {
|
|
4190
4309
|
tags: { deviceId },
|
|
@@ -4286,7 +4405,7 @@ var PipelineExecutor = class {
|
|
|
4286
4405
|
});
|
|
4287
4406
|
return output;
|
|
4288
4407
|
}
|
|
4289
|
-
async executeChildren(children, parentDetection, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg) {
|
|
4408
|
+
async executeChildren(children, parentDetection, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, nativeFaceCropProvider) {
|
|
4290
4409
|
for (const child of children) {
|
|
4291
4410
|
if (!this.matchesInputClasses(parentDetection.macroClass, child.inputClasses)) continue;
|
|
4292
4411
|
const minParentScore = child.settings?.minParentScore ?? child.definition?.defaultMinParentScore;
|
|
@@ -4301,10 +4420,30 @@ var PipelineExecutor = class {
|
|
|
4301
4420
|
let cropH;
|
|
4302
4421
|
if (modelEntry?.faceAlignment) {
|
|
4303
4422
|
const minFaceSize = typeof child.settings?.["minFaceSize"] === "number" ? child.settings["minFaceSize"] : DEFAULT_MIN_FACE_SIZE_PX;
|
|
4304
|
-
|
|
4423
|
+
const faceShortSide = bboxShortSide(parentDetection.bbox);
|
|
4424
|
+
if (faceShortSide < minFaceSize) {
|
|
4425
|
+
this.throttledWarn(`face-small:${child.stepId}`, 3e4, "face detected but too small to embed — skipping", {
|
|
4426
|
+
step: child.stepId,
|
|
4427
|
+
model: child.modelId,
|
|
4428
|
+
faceShortSidePx: Math.round(faceShortSide),
|
|
4429
|
+
minFaceSizePx: minFaceSize
|
|
4430
|
+
});
|
|
4431
|
+
continue;
|
|
4432
|
+
}
|
|
4305
4433
|
const lms = parentDetection.landmarks;
|
|
4306
4434
|
if (lms && lms.length >= 5) {
|
|
4307
|
-
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
|
+
});
|
|
4308
4447
|
cropJpegBuf = aligned.jpeg;
|
|
4309
4448
|
cropW = aligned.width;
|
|
4310
4449
|
cropH = aligned.height;
|
|
@@ -4338,10 +4477,16 @@ var PipelineExecutor = class {
|
|
|
4338
4477
|
y: l.y + py1
|
|
4339
4478
|
}));
|
|
4340
4479
|
}
|
|
4341
|
-
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);
|
|
4342
4481
|
}
|
|
4343
4482
|
}
|
|
4344
|
-
} catch {
|
|
4483
|
+
} catch (err) {
|
|
4484
|
+
this.throttledWarn(`child-fail:${child.stepId}`, 15e3, "pipeline child step failed", {
|
|
4485
|
+
step: child.stepId,
|
|
4486
|
+
model: child.modelId,
|
|
4487
|
+
error: errMsg(err)
|
|
4488
|
+
});
|
|
4489
|
+
}
|
|
4345
4490
|
}
|
|
4346
4491
|
}
|
|
4347
4492
|
matchesInputClasses(className, inputClasses) {
|
|
@@ -5824,6 +5969,38 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5824
5969
|
debug: { totalInferenceMs: 0 }
|
|
5825
5970
|
};
|
|
5826
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
|
+
}
|
|
5827
6004
|
async runPipeline(input, onProgress) {
|
|
5828
6005
|
const nodeId = this.addonCtx?.kernel?.localNodeId ?? "hub";
|
|
5829
6006
|
const sessionId = input.sessionId ?? `run-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
@@ -6080,7 +6257,8 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
6080
6257
|
const effectiveDeviceId = input.deviceId ?? 0;
|
|
6081
6258
|
const deviceOverrides = effectiveDeviceId > 0 ? await this.readDeviceStore(effectiveDeviceId) : {};
|
|
6082
6259
|
const effectiveTree = Object.keys(deviceOverrides).length > 0 ? applyDeviceOverridesToTree(tree, "object-detection", deviceOverrides) : tree;
|
|
6083
|
-
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);
|
|
6084
6262
|
if (isRuntime) {
|
|
6085
6263
|
if (trace && this.eventBus) this.eventBus.emit(createEvent(EventCategory.PipelineTrace, {
|
|
6086
6264
|
type: "device",
|
|
@@ -7165,6 +7343,7 @@ function buildSchemaSlots(format, modelsDir, customByStep) {
|
|
|
7165
7343
|
defaultModelId: step.defaultModelId,
|
|
7166
7344
|
...step.defaultModelIdByFormat ? { defaultModelIdByFormat: { ...step.defaultModelIdByFormat } } : {},
|
|
7167
7345
|
...step.enabledByDefault !== void 0 ? { enabledByDefault: step.enabledByDefault } : {},
|
|
7346
|
+
...step.backfillIntoExistingOverrides !== void 0 ? { backfillIntoExistingOverrides: step.backfillIntoExistingOverrides } : {},
|
|
7168
7347
|
defaultConfidence: step.defaultConfidence,
|
|
7169
7348
|
...step.group ? { group: step.group } : {},
|
|
7170
7349
|
...configSchema.length > 0 ? { configSchema: [...configSchema] } : {}
|