@camstack/addon-pipeline 1.2.329 → 1.2.331
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 +2 -2
- package/dist/audio-analyzer/index.mjs +2 -2
- package/dist/{default-detection-model-ChH7Y219.js → default-detection-model-D3SnO2hN.js} +39 -67
- package/dist/{default-detection-model-j0nBpRoB.mjs → default-detection-model-DELTCQgv.mjs} +39 -67
- package/dist/detection-pipeline/index.js +197 -31
- package/dist/detection-pipeline/index.mjs +197 -31
- package/dist/{dist-0HoA3wAi.mjs → dist-CNXhBzn6.mjs} +847 -439
- package/dist/{dist-BlIWhjnu.js → dist-DrUx3EGR.js} +892 -466
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/{node-BPpVMnZy.js → node-B4qy3lnV.js} +1 -1
- package/dist/{node-HOMWRPzW.mjs → node-BmxmlYLx.mjs} +1 -1
- package/dist/pipeline-runner/index.js +355 -104
- package/dist/pipeline-runner/index.mjs +355 -104
- package/dist/{process-memory-DuF8pa2w.mjs → process-memory-BGc5PzZI.mjs} +1 -1
- package/dist/{process-memory-BW06vOIQ.js → process-memory-CRtWJbjV.js} +1 -1
- package/dist/recorder/index.js +7 -7
- package/dist/recorder/index.mjs +7 -7
- package/dist/{segment-demux-js-Dl52Zz8w.js → segment-demux-js-CpSgZmSR.js} +2 -2
- package/dist/{segment-demux-js-BU2MXpln.mjs → segment-demux-js-VqsLAgQ0.mjs} +2 -2
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-vNfS65In.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BnncdmtJ.mjs} +2 -2
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-2jlYV34n.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-Bt8aa0wk.mjs +26 -0
- package/dist/stream-broker/demux-worker-child.js +1 -1
- package/dist/stream-broker/demux-worker-child.mjs +1 -1
- package/dist/stream-broker/{hostInit-Ciacp783.mjs → hostInit-CF_2Rlu0.mjs} +2 -2
- package/dist/stream-broker/index.js +713 -80
- package/dist/stream-broker/index.mjs +713 -80
- package/dist/stream-broker/remoteEntry.js +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-DfeHvW4k.mjs +0 -26
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BpXPNINd.mjs +0 -26
|
@@ -3,9 +3,9 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../chunk-emK7D4bc.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
7
|
-
const require_node = require("../node-
|
|
8
|
-
const require_default_detection_model = require("../default-detection-model-
|
|
6
|
+
const require_dist = require("../dist-DrUx3EGR.js");
|
|
7
|
+
const require_node = require("../node-B4qy3lnV.js");
|
|
8
|
+
const require_default_detection_model = require("../default-detection-model-D3SnO2hN.js");
|
|
9
9
|
const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
|
|
10
10
|
const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
|
|
11
11
|
const require_restream_intent = require("../restream-intent-7uDNqMKs.js");
|
|
@@ -1976,33 +1976,34 @@ function onboardRootDetections(slot, frame, gate) {
|
|
|
1976
1976
|
};
|
|
1977
1977
|
}
|
|
1978
1978
|
//#endregion
|
|
1979
|
-
//#region src/pipeline-runner/
|
|
1979
|
+
//#region src/pipeline-runner/root-supply.ts
|
|
1980
1980
|
/**
|
|
1981
|
-
* The runner's half of
|
|
1981
|
+
* The runner's half of root-model supply (D715, D730).
|
|
1982
1982
|
*
|
|
1983
1983
|
* The runner holds the camera's WHOLE step tree (the attach config), so it is
|
|
1984
|
-
* the one place that can answer "
|
|
1985
|
-
* root model's declaration (`
|
|
1986
|
-
* step's own `
|
|
1987
|
-
* consumers read that
|
|
1988
|
-
*
|
|
1989
|
-
* -
|
|
1990
|
-
*
|
|
1991
|
-
*
|
|
1992
|
-
*
|
|
1993
|
-
*
|
|
1994
|
-
*
|
|
1995
|
-
*
|
|
1996
|
-
*
|
|
1997
|
-
*
|
|
1984
|
+
* the one place that can answer "does this camera use the root's output for
|
|
1985
|
+
* this step?": the root model's declaration (`rootSuppliesOf`) against the
|
|
1986
|
+
* step's own `useRootModel` flag, through the one decision `resolveRootSupply`.
|
|
1987
|
+
* Four consumers read that answer:
|
|
1988
|
+
*
|
|
1989
|
+
* - linked labels — effective ⇒ raised to the detection's score (the root is
|
|
1990
|
+
* the one writer), or DROPPED when the step's class list switched that
|
|
1991
|
+
* class off (the detection stays at its macro); otherwise the score-0
|
|
1992
|
+
* fallback the executor wrote;
|
|
1993
|
+
* - root child boxes — a `plate` / `face` detail the root drew is kept only
|
|
1994
|
+
* when its crop step's supply is effective;
|
|
1995
|
+
* - the `detailSteps` announce — an effective step carries `filledBy` +
|
|
1996
|
+
* `filledAs`, so post-analysis does not infer it;
|
|
1997
|
+
* - `package-detection` — effective ⇒ the package root is not dispatched and
|
|
1998
|
+
* the root's own `package` boxes ARE the package result.
|
|
1998
1999
|
*
|
|
1999
2000
|
* Pure: the caller passes the model resolver; no I/O, no clock.
|
|
2000
2001
|
*/
|
|
2001
|
-
/** The root step the
|
|
2002
|
-
var
|
|
2002
|
+
/** The root step the supplies are read from — the always-on camera root. */
|
|
2003
|
+
var ROOT_SUPPLY_ROOT_STEP_ID = "object-detection";
|
|
2003
2004
|
var PACKAGE_STEP_ID = "package-detection";
|
|
2004
2005
|
var PACKAGE_THRESHOLD_KEY = "minConfidencePackage";
|
|
2005
|
-
var
|
|
2006
|
+
var EMPTY_ROOT_SUPPLY_PLAN = { states: /* @__PURE__ */ new Map() };
|
|
2006
2007
|
function findStep(steps, addonId) {
|
|
2007
2008
|
for (const step of steps) {
|
|
2008
2009
|
if (step.addonId === addonId) return step;
|
|
@@ -2011,70 +2012,108 @@ function findStep(steps, addonId) {
|
|
|
2011
2012
|
}
|
|
2012
2013
|
}
|
|
2013
2014
|
/**
|
|
2014
|
-
* Plan a camera's
|
|
2015
|
-
* model runs the
|
|
2015
|
+
* Plan a camera's root supplies from its configured tree. A root with no
|
|
2016
|
+
* pinned model runs the step's default — a COCO model, which supplies the
|
|
2017
|
+
* animal and vehicle classifiers like any other root.
|
|
2016
2018
|
*/
|
|
2017
|
-
function
|
|
2018
|
-
if (steps === void 0) return
|
|
2019
|
+
function planRootSupplies(steps, resolveModel) {
|
|
2020
|
+
if (steps === void 0) return EMPTY_ROOT_SUPPLY_PLAN;
|
|
2019
2021
|
const root = steps.find((s) => s.addonId === "object-detection" && s.enabled !== false);
|
|
2020
|
-
if (root
|
|
2021
|
-
const
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
...
|
|
2032
|
-
|
|
2022
|
+
if (root === void 0) return EMPTY_ROOT_SUPPLY_PLAN;
|
|
2023
|
+
const definition = require_default_detection_model.getStepDefinition(ROOT_SUPPLY_ROOT_STEP_ID);
|
|
2024
|
+
const rootModelId = root.modelId ?? definition.defaultModelId;
|
|
2025
|
+
const supplies = require_dist.rootSuppliesOf(resolveModel(ROOT_SUPPLY_ROOT_STEP_ID, rootModelId), {
|
|
2026
|
+
labels: definition.labels ?? [],
|
|
2027
|
+
...definition.classMap !== void 0 ? { classMap: definition.classMap } : {}
|
|
2028
|
+
});
|
|
2029
|
+
const states = /* @__PURE__ */ new Map();
|
|
2030
|
+
for (const stepId of require_dist.ROOT_SUPPLIABLE_STEP_IDS) {
|
|
2031
|
+
const settings = findStep(steps, stepId)?.settings;
|
|
2032
|
+
states.set(stepId, {
|
|
2033
|
+
...require_dist.resolveRootSupply(stepId, settings, supplies),
|
|
2034
|
+
...settings !== void 0 ? { settings } : {}
|
|
2035
|
+
});
|
|
2033
2036
|
}
|
|
2034
2037
|
return {
|
|
2035
|
-
rootModelId
|
|
2036
|
-
|
|
2038
|
+
rootModelId,
|
|
2039
|
+
states
|
|
2037
2040
|
};
|
|
2038
2041
|
}
|
|
2039
|
-
/**
|
|
2040
|
-
function
|
|
2042
|
+
/** The steps whose supply is effective, with the model and the kind. */
|
|
2043
|
+
function suppliedSteps(plan) {
|
|
2041
2044
|
const out = /* @__PURE__ */ new Map();
|
|
2042
2045
|
if (plan.rootModelId === void 0) return out;
|
|
2043
|
-
for (const [stepId,
|
|
2046
|
+
for (const [stepId, state] of plan.states) if (state.effective && state.supply !== void 0) out.set(stepId, {
|
|
2047
|
+
modelId: plan.rootModelId,
|
|
2048
|
+
kind: state.supply.kind
|
|
2049
|
+
});
|
|
2044
2050
|
return out;
|
|
2045
2051
|
}
|
|
2046
|
-
|
|
2052
|
+
/** The steps whose supply just BECAME effective between two plans. */
|
|
2053
|
+
function newlySuppliedSteps(previous, next) {
|
|
2054
|
+
const out = [];
|
|
2055
|
+
for (const [stepId, state] of next.states) if (state.effective && previous?.states.get(stepId)?.effective !== true) out.push(stepId);
|
|
2056
|
+
return out;
|
|
2057
|
+
}
|
|
2058
|
+
function authoritativeLabels(detection, plan) {
|
|
2047
2059
|
let changed = false;
|
|
2048
|
-
const labels =
|
|
2060
|
+
const labels = [];
|
|
2061
|
+
for (const label of detection.labels) {
|
|
2049
2062
|
const stepId = require_dist.linkedStepOf(label.stepId);
|
|
2050
|
-
|
|
2051
|
-
if (
|
|
2063
|
+
const state = stepId !== void 0 ? plan.states.get(stepId) : void 0;
|
|
2064
|
+
if (state === void 0 || !state.effective) {
|
|
2065
|
+
labels.push(label);
|
|
2066
|
+
continue;
|
|
2067
|
+
}
|
|
2068
|
+
if (!require_dist.allowedSuppliedEffect(label.label, state.settings)) {
|
|
2069
|
+
changed = true;
|
|
2070
|
+
continue;
|
|
2071
|
+
}
|
|
2072
|
+
if (label.score === detection.score) {
|
|
2073
|
+
labels.push(label);
|
|
2074
|
+
continue;
|
|
2075
|
+
}
|
|
2052
2076
|
changed = true;
|
|
2053
|
-
|
|
2077
|
+
labels.push({
|
|
2054
2078
|
...label,
|
|
2055
2079
|
score: detection.score
|
|
2056
|
-
};
|
|
2057
|
-
}
|
|
2080
|
+
});
|
|
2081
|
+
}
|
|
2058
2082
|
return changed ? labels : void 0;
|
|
2059
2083
|
}
|
|
2084
|
+
/** Whether a root child box survives: only under an effective crop supply. */
|
|
2085
|
+
function keepsDetection(detection, plan) {
|
|
2086
|
+
const supplied = detection.rootSupplied;
|
|
2087
|
+
if (supplied === void 0 || detection.kind !== "detail") return true;
|
|
2088
|
+
const stepId = require_dist.ROOT_SUPPLIABLE_STEP_IDS.find((id) => id === supplied.stepId);
|
|
2089
|
+
return stepId !== void 0 && plan.states.get(stepId)?.effective === true;
|
|
2090
|
+
}
|
|
2060
2091
|
/**
|
|
2061
|
-
*
|
|
2062
|
-
* authoritative
|
|
2063
|
-
*
|
|
2064
|
-
* nothing changes.
|
|
2092
|
+
* Apply the plan to a frame: linked labels of an effective step become
|
|
2093
|
+
* authoritative (or are dropped by its class list), and root child boxes of a
|
|
2094
|
+
* crop step that is not effective are removed. Never mutates; returns the
|
|
2095
|
+
* input when nothing changes.
|
|
2065
2096
|
*/
|
|
2066
|
-
function
|
|
2067
|
-
if (plan.
|
|
2097
|
+
function applyRootSupplyAuthority(result, plan) {
|
|
2098
|
+
if (plan.states.size === 0) return result;
|
|
2068
2099
|
let changed = false;
|
|
2069
|
-
const detections =
|
|
2070
|
-
|
|
2071
|
-
if (
|
|
2100
|
+
const detections = [];
|
|
2101
|
+
for (const d of result.detections) {
|
|
2102
|
+
if (!keepsDetection(d, plan)) {
|
|
2103
|
+
changed = true;
|
|
2104
|
+
continue;
|
|
2105
|
+
}
|
|
2106
|
+
const labels = authoritativeLabels(d, plan);
|
|
2107
|
+
if (labels === void 0) {
|
|
2108
|
+
detections.push(d);
|
|
2109
|
+
continue;
|
|
2110
|
+
}
|
|
2072
2111
|
changed = true;
|
|
2073
|
-
|
|
2112
|
+
detections.push({
|
|
2074
2113
|
...d,
|
|
2075
2114
|
labels
|
|
2076
|
-
};
|
|
2077
|
-
}
|
|
2115
|
+
});
|
|
2116
|
+
}
|
|
2078
2117
|
return changed ? {
|
|
2079
2118
|
...result,
|
|
2080
2119
|
detections
|
|
@@ -2082,7 +2121,7 @@ function applyLabelLinkAuthority(result, plan) {
|
|
|
2082
2121
|
}
|
|
2083
2122
|
/** The root step's schema default for its class allow-list. */
|
|
2084
2123
|
function rootDefaultMacroClasses() {
|
|
2085
|
-
const field = require_default_detection_model.getStep(
|
|
2124
|
+
const field = require_default_detection_model.getStep(ROOT_SUPPLY_ROOT_STEP_ID).getConfigSchema().find((f) => "key" in f && f.key === "enabledMacroClasses");
|
|
2086
2125
|
const value = field !== void 0 && "default" in field ? field.default : void 0;
|
|
2087
2126
|
return Array.isArray(value) ? value : [];
|
|
2088
2127
|
}
|
|
@@ -2103,18 +2142,19 @@ function withPackageAdmitted(root, packageStep) {
|
|
|
2103
2142
|
};
|
|
2104
2143
|
}
|
|
2105
2144
|
/**
|
|
2106
|
-
* When the root
|
|
2107
|
-
*
|
|
2108
|
-
*
|
|
2109
|
-
*
|
|
2145
|
+
* When the root's `package` supply is effective for this camera, drop the
|
|
2146
|
+
* package root from the dispatch and let the camera root report packages: its
|
|
2147
|
+
* gate admits `package` at the package step's threshold. Otherwise (the root
|
|
2148
|
+
* declares no `package`, or the flag is off) the tree is returned untouched
|
|
2149
|
+
* and the package step runs its own model.
|
|
2110
2150
|
*/
|
|
2111
2151
|
function applyPackageFill(steps, plan) {
|
|
2112
|
-
if (plan.
|
|
2152
|
+
if (plan.states.get(PACKAGE_STEP_ID)?.effective !== true) return {
|
|
2113
2153
|
steps,
|
|
2114
2154
|
filledByRoot: false
|
|
2115
2155
|
};
|
|
2116
2156
|
const packageStep = steps.find((s) => s.addonId === PACKAGE_STEP_ID);
|
|
2117
|
-
if (packageStep === void 0) return {
|
|
2157
|
+
if (packageStep === void 0 || packageStep.enabled === false) return {
|
|
2118
2158
|
steps,
|
|
2119
2159
|
filledByRoot: false
|
|
2120
2160
|
};
|
|
@@ -3296,6 +3336,150 @@ var FaceOutsideParentLog = class {
|
|
|
3296
3336
|
}
|
|
3297
3337
|
};
|
|
3298
3338
|
//#endregion
|
|
3339
|
+
//#region src/pipeline-runner/root-face-refine.ts
|
|
3340
|
+
/**
|
|
3341
|
+
* Root face refinement (D731) — how a ROOT model's `face` box becomes a face
|
|
3342
|
+
* the recogniser can use, without ever embedding an unaligned crop.
|
|
3343
|
+
*
|
|
3344
|
+
* A root face box has no landmarks. It is used as a LOCATION only:
|
|
3345
|
+
*
|
|
3346
|
+
* 1. one face-scoped square around it (`deriveRootFaceRefineRect`, box ×1.6)
|
|
3347
|
+
* is cut from the frame's NATIVE surface — the native lease or nothing;
|
|
3348
|
+
* 2. the square is shrunk to {@link ROOT_FACE_REFINE_INPUT_PX} and laid in the
|
|
3349
|
+
* top-left of a black canvas the size of the face detector's input, so the
|
|
3350
|
+
* detector runs at an effective 160 px input on its own 640 graph — the
|
|
3351
|
+
* network is convolutional, the black margin contains nothing, and no 160 px
|
|
3352
|
+
* build of the model is needed. The canvas maps back to the frame through
|
|
3353
|
+
* a "virtual" frame rectangle 640/160 times the square, so every existing
|
|
3354
|
+
* crop→frame mapping (results, native child re-cuts) stays exact;
|
|
3355
|
+
* 3. the detector's best face in the square is kept — its landmarks come from
|
|
3356
|
+
* the refinement input, where the face is large, which is the space the
|
|
3357
|
+
* landmark-precision gate then measures it in;
|
|
3358
|
+
* 4. that face is handed back to the SAME step as its output
|
|
3359
|
+
* (`suppliedRoot`), so the embedder child aligns it through the existing
|
|
3360
|
+
* native-crop alignment, exactly like a detected face.
|
|
3361
|
+
*
|
|
3362
|
+
* No face in the square ⇒ nothing is embedded, and the drop is logged.
|
|
3363
|
+
*/
|
|
3364
|
+
/** The face detector's input side for the step as it will run. */
|
|
3365
|
+
function detectorInputPx(step) {
|
|
3366
|
+
const def = require_default_detection_model.getStepDefinition(step.addonId);
|
|
3367
|
+
const modelId = step.modelId ?? def.defaultModelId;
|
|
3368
|
+
return def.models.find((m) => m.id === modelId)?.inputSize.width ?? 160;
|
|
3369
|
+
}
|
|
3370
|
+
/** Lay the refinement square, shrunk to the refine input, on a black canvas. */
|
|
3371
|
+
async function buildRefineCanvas(squareJpeg, canvasPx) {
|
|
3372
|
+
const sharp = await require_lazy_sharp.getSharp();
|
|
3373
|
+
const small = await sharp(squareJpeg).resize(160, 160, { fit: "fill" }).toBuffer();
|
|
3374
|
+
const pad = Math.max(0, canvasPx - 160);
|
|
3375
|
+
return sharp(small).extend({
|
|
3376
|
+
top: 0,
|
|
3377
|
+
left: 0,
|
|
3378
|
+
bottom: pad,
|
|
3379
|
+
right: pad,
|
|
3380
|
+
background: {
|
|
3381
|
+
r: 0,
|
|
3382
|
+
g: 0,
|
|
3383
|
+
b: 0
|
|
3384
|
+
}
|
|
3385
|
+
}).jpeg({ quality: 92 }).toBuffer();
|
|
3386
|
+
}
|
|
3387
|
+
/** The best face of a refinement pass, or `undefined` when it found none. */
|
|
3388
|
+
function bestFace(detections) {
|
|
3389
|
+
return detections.filter((d) => d.macroClass === "face").reduce((best, d) => best === void 0 || d.score > best.score ? d : best, void 0);
|
|
3390
|
+
}
|
|
3391
|
+
/** The canvas-space face as the step's own output for the second pass. */
|
|
3392
|
+
function refinedAsSupplied(face, stepId, modelId) {
|
|
3393
|
+
return {
|
|
3394
|
+
stepId,
|
|
3395
|
+
modelId,
|
|
3396
|
+
detections: [{
|
|
3397
|
+
bbox: [
|
|
3398
|
+
face.bbox.x,
|
|
3399
|
+
face.bbox.y,
|
|
3400
|
+
face.bbox.x + face.bbox.width,
|
|
3401
|
+
face.bbox.y + face.bbox.height
|
|
3402
|
+
],
|
|
3403
|
+
score: face.score,
|
|
3404
|
+
...face.landmarks !== void 0 ? { landmarks: face.landmarks.map((p) => ({
|
|
3405
|
+
x: p.x,
|
|
3406
|
+
y: p.y
|
|
3407
|
+
})) } : {}
|
|
3408
|
+
}]
|
|
3409
|
+
};
|
|
3410
|
+
}
|
|
3411
|
+
/**
|
|
3412
|
+
* Refine the root's face box for one step and run the requested chain on the
|
|
3413
|
+
* face it finds. `null` ⇒ no pixels (the native lease is the only source);
|
|
3414
|
+
* an empty list ⇒ the square held no face. Every drop is logged.
|
|
3415
|
+
*/
|
|
3416
|
+
async function refineRootFace(deps, input, supplied, step, requested, deviceKey) {
|
|
3417
|
+
const handle = input.frameHandle;
|
|
3418
|
+
const tags = { deviceId: input.deviceId };
|
|
3419
|
+
if (handle === void 0) {
|
|
3420
|
+
deps.logger.warn("root face refinement needs the native frame — no frame handle, dropped", {
|
|
3421
|
+
tags,
|
|
3422
|
+
meta: { stepId: step.addonId }
|
|
3423
|
+
});
|
|
3424
|
+
return null;
|
|
3425
|
+
}
|
|
3426
|
+
const box = supplied.boxes.reduce((best, b) => b.score > best.score ? b : best);
|
|
3427
|
+
const square = require_dist.deriveRootFaceRefineRect(box.bbox, handle.width, handle.height);
|
|
3428
|
+
const native = await deps.getNativeCrop(input.deviceId, handle, normalizeRect(square, handle.width, handle.height));
|
|
3429
|
+
if (native === null || isNativeCropMiss(native)) {
|
|
3430
|
+
deps.logger.warn("root face refinement: the native square missed — dropped", {
|
|
3431
|
+
tags,
|
|
3432
|
+
meta: {
|
|
3433
|
+
stepId: step.addonId,
|
|
3434
|
+
nativeMissReason: native === null ? null : native.miss
|
|
3435
|
+
}
|
|
3436
|
+
});
|
|
3437
|
+
return null;
|
|
3438
|
+
}
|
|
3439
|
+
const canvasPx = detectorInputPx(step);
|
|
3440
|
+
const scale = canvasPx / 160;
|
|
3441
|
+
const virtualFrameSpace = {
|
|
3442
|
+
x: native.frameSpace.x,
|
|
3443
|
+
y: native.frameSpace.y,
|
|
3444
|
+
w: native.frameSpace.w * scale,
|
|
3445
|
+
h: native.frameSpace.h * scale
|
|
3446
|
+
};
|
|
3447
|
+
const crop = {
|
|
3448
|
+
jpeg: await buildRefineCanvas(native.jpeg, canvasPx),
|
|
3449
|
+
width: canvasPx,
|
|
3450
|
+
height: canvasPx,
|
|
3451
|
+
frameSpace: virtualFrameSpace,
|
|
3452
|
+
source: "native"
|
|
3453
|
+
};
|
|
3454
|
+
const nativeCropRef = {
|
|
3455
|
+
handle,
|
|
3456
|
+
cropFrameSpace: virtualFrameSpace
|
|
3457
|
+
};
|
|
3458
|
+
const detectorOnly = {
|
|
3459
|
+
...step,
|
|
3460
|
+
children: []
|
|
3461
|
+
};
|
|
3462
|
+
const found = await deps.runPipeline([detectorOnly], crop.jpeg, input.deviceId, deviceKey, nativeCropRef);
|
|
3463
|
+
const face = found === null ? void 0 : bestFace(found.detections);
|
|
3464
|
+
if (face === void 0) {
|
|
3465
|
+
deps.logger.warn("root face refinement found no face in the square — dropped, never embedded unaligned", {
|
|
3466
|
+
tags,
|
|
3467
|
+
meta: {
|
|
3468
|
+
stepId: step.addonId,
|
|
3469
|
+
rootModelId: supplied.modelId,
|
|
3470
|
+
rootScore: box.score
|
|
3471
|
+
}
|
|
3472
|
+
});
|
|
3473
|
+
return [];
|
|
3474
|
+
}
|
|
3475
|
+
const chain = requested === void 0 ? step : pruneChildStepsToRequested(step, requested);
|
|
3476
|
+
if (chain.children === void 0 || chain.children.length === 0) return [toDetailResult(face, step.addonId, crop)];
|
|
3477
|
+
const modelId = step.modelId ?? require_default_detection_model.getStepDefinition(step.addonId).defaultModelId;
|
|
3478
|
+
const embedded = await deps.runPipeline([chain], crop.jpeg, input.deviceId, deviceKey, nativeCropRef, refinedAsSupplied(face, step.addonId, modelId));
|
|
3479
|
+
if (embedded === null) return [toDetailResult(face, step.addonId, crop)];
|
|
3480
|
+
return embedded.detections.map((d) => toDetailResult(d, step.addonId, crop));
|
|
3481
|
+
}
|
|
3482
|
+
//#endregion
|
|
3299
3483
|
//#region src/pipeline-runner/detail-subtree.ts
|
|
3300
3484
|
/** Did the subtree run refuse because the camera is not attached (park lapsed)? */
|
|
3301
3485
|
function isDetailNotAttachedRefusal(outcome) {
|
|
@@ -3388,6 +3572,43 @@ function mapBboxToFrameSpace(bbox, crop) {
|
|
|
3388
3572
|
h: bbox.height * scaleY
|
|
3389
3573
|
};
|
|
3390
3574
|
}
|
|
3575
|
+
/**
|
|
3576
|
+
* Map the root's FRAME-space boxes (D730) into the crop's pixels — the inverse
|
|
3577
|
+
* of {@link mapBboxToFrameSpace} — clamped to the crop. A box with nothing left
|
|
3578
|
+
* inside the crop is dropped. `null` when no box survives.
|
|
3579
|
+
*/
|
|
3580
|
+
function suppliedRootForCrop(supplied, crop) {
|
|
3581
|
+
const scaleX = crop.width / crop.frameSpace.w;
|
|
3582
|
+
const scaleY = crop.height / crop.frameSpace.h;
|
|
3583
|
+
const detections = [];
|
|
3584
|
+
for (const box of supplied.boxes) {
|
|
3585
|
+
const x1 = Math.max(0, (box.bbox.x - crop.frameSpace.x) * scaleX);
|
|
3586
|
+
const y1 = Math.max(0, (box.bbox.y - crop.frameSpace.y) * scaleY);
|
|
3587
|
+
const x2 = Math.min(crop.width, (box.bbox.x + box.bbox.w - crop.frameSpace.x) * scaleX);
|
|
3588
|
+
const y2 = Math.min(crop.height, (box.bbox.y + box.bbox.h - crop.frameSpace.y) * scaleY);
|
|
3589
|
+
if (x2 <= x1 || y2 <= y1) continue;
|
|
3590
|
+
detections.push({
|
|
3591
|
+
bbox: [
|
|
3592
|
+
x1,
|
|
3593
|
+
y1,
|
|
3594
|
+
x2,
|
|
3595
|
+
y2
|
|
3596
|
+
],
|
|
3597
|
+
score: box.score
|
|
3598
|
+
});
|
|
3599
|
+
}
|
|
3600
|
+
const [first, ...rest] = detections;
|
|
3601
|
+
if (first === void 0) return null;
|
|
3602
|
+
return {
|
|
3603
|
+
stepId: supplied.stepId,
|
|
3604
|
+
modelId: supplied.modelId,
|
|
3605
|
+
detections: [first, ...rest]
|
|
3606
|
+
};
|
|
3607
|
+
}
|
|
3608
|
+
/** Whether the root's box for this step is a location to refine in (a face). */
|
|
3609
|
+
function refinesSuppliedStep(stepId) {
|
|
3610
|
+
return (stepId === "plate-detection" || stepId === "face-detection") && require_dist.CROP_SUPPLY_SOURCES[stepId].use === "refine";
|
|
3611
|
+
}
|
|
3391
3612
|
/** Base64-encode a normalized float32 embedding vector. */
|
|
3392
3613
|
function encodeEmbeddingBase64(embedding) {
|
|
3393
3614
|
return Buffer.from(Float32Array.from(embedding).buffer).toString("base64");
|
|
@@ -3591,6 +3812,18 @@ async function runDetailSubtree(deps, input) {
|
|
|
3591
3812
|
}
|
|
3592
3813
|
const requested = input.steps;
|
|
3593
3814
|
const steps = requested === void 0 ? matched : matched.map((step) => pruneChildStepsToRequested(step, requested));
|
|
3815
|
+
const supplied = input.supplied;
|
|
3816
|
+
if (supplied !== void 0 && refinesSuppliedStep(supplied.stepId)) {
|
|
3817
|
+
const details = [];
|
|
3818
|
+
for (const step of matched) {
|
|
3819
|
+
if (step.addonId !== supplied.stepId) continue;
|
|
3820
|
+
const placement = deps.placeStep?.(input.deviceId, step);
|
|
3821
|
+
const refined = await refineRootFace(deps, input, supplied, placement?.step ?? step, requested, placement?.deviceKey);
|
|
3822
|
+
if (refined === null) return null;
|
|
3823
|
+
details.push(...refined);
|
|
3824
|
+
}
|
|
3825
|
+
return { details };
|
|
3826
|
+
}
|
|
3594
3827
|
const { crop, nativeMiss } = await resolveCrop(deps, input, deps.resolveCropConvention(input.deviceId));
|
|
3595
3828
|
if (!crop) {
|
|
3596
3829
|
deps.logger.warn("runDetailSubtree: no crop source resolved — the dispatch produced nothing", {
|
|
@@ -3619,7 +3852,18 @@ async function runDetailSubtree(deps, input) {
|
|
|
3619
3852
|
let unjudgedFaces = 0;
|
|
3620
3853
|
for (const step of steps) {
|
|
3621
3854
|
const placement = deps.placeStep?.(input.deviceId, step);
|
|
3622
|
-
const
|
|
3855
|
+
const suppliedRoot = input.supplied !== void 0 && input.supplied.stepId === step.addonId ? suppliedRootForCrop(input.supplied, crop) : void 0;
|
|
3856
|
+
if (suppliedRoot === null) {
|
|
3857
|
+
deps.logger.warn("runDetailSubtree: the root-supplied boxes fall outside the crop — nothing to read", {
|
|
3858
|
+
tags: { deviceId: input.deviceId },
|
|
3859
|
+
meta: {
|
|
3860
|
+
stepId: step.addonId,
|
|
3861
|
+
parentClass: input.parent.className
|
|
3862
|
+
}
|
|
3863
|
+
});
|
|
3864
|
+
continue;
|
|
3865
|
+
}
|
|
3866
|
+
const result = await deps.runPipeline([placement?.step ?? step], crop.jpeg, input.deviceId, placement?.deviceKey, nativeCropRef, ...suppliedRoot !== void 0 ? [suppliedRoot] : []);
|
|
3623
3867
|
if (!result) continue;
|
|
3624
3868
|
for (const detection of result.detections) {
|
|
3625
3869
|
const detail = toDetailResult(detection, step.addonId, crop);
|
|
@@ -7342,15 +7586,15 @@ function isDetailPlaneStepId(stepId) {
|
|
|
7342
7586
|
* Extracted as a pure function so the unit suite can exercise the
|
|
7343
7587
|
* derivation without instantiating the full addon.
|
|
7344
7588
|
*/
|
|
7345
|
-
function deriveDetailSteps(steps, plan =
|
|
7589
|
+
function deriveDetailSteps(steps, plan = EMPTY_ROOT_SUPPLY_PLAN) {
|
|
7346
7590
|
const stepIds = /* @__PURE__ */ new Set();
|
|
7347
7591
|
collectEnabledStepIds(steps, stepIds);
|
|
7348
|
-
const
|
|
7592
|
+
const supplied = suppliedSteps(plan);
|
|
7349
7593
|
const announces = [];
|
|
7350
7594
|
for (const stepId of stepIds) {
|
|
7351
7595
|
const def = tryGetStepDefinition(stepId);
|
|
7352
7596
|
if (!def || def.inputClasses === null) continue;
|
|
7353
|
-
const
|
|
7597
|
+
const by = supplied.get(def.id);
|
|
7354
7598
|
announces.push({
|
|
7355
7599
|
stepId: def.id,
|
|
7356
7600
|
inputClasses: def.inputClasses,
|
|
@@ -7358,7 +7602,10 @@ function deriveDetailSteps(steps, plan = EMPTY_LABEL_LINK_PLAN) {
|
|
|
7358
7602
|
trigger: "once",
|
|
7359
7603
|
maxPerTrack: 3
|
|
7360
7604
|
},
|
|
7361
|
-
...
|
|
7605
|
+
...by !== void 0 ? {
|
|
7606
|
+
filledBy: by.modelId,
|
|
7607
|
+
filledAs: by.kind
|
|
7608
|
+
} : {}
|
|
7362
7609
|
});
|
|
7363
7610
|
}
|
|
7364
7611
|
return announces;
|
|
@@ -8490,7 +8737,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
8490
8737
|
if (!ctx.api) throw new Error("runCapacityRamp: ctx.api unavailable");
|
|
8491
8738
|
if (this.capacityRuns.get(input.sessionId)?.status.state === "running") throw new Error(`runCapacityRamp: session '${input.sessionId}' is already running`);
|
|
8492
8739
|
for (const [id, entry] of this.capacityRuns) if (entry.status.state === "running") throw new Error(`runCapacityRamp: ramp '${id}' is already running on this node — one at a time, the measurement owns the node`);
|
|
8493
|
-
if (input.nodeId !== void 0 && input.nodeId !== this.nodeId) throw new Error(`runCapacityRamp: driver is node-local — this runner is '${this.nodeId}', refusing nodeId '${input.nodeId}'. Frames must not cross a node boundary per call
|
|
8740
|
+
if (input.nodeId !== void 0 && input.nodeId !== this.nodeId) throw new Error(`runCapacityRamp: driver is node-local — this runner is '${this.nodeId}', refusing nodeId '${input.nodeId}'. Frames must not cross a node boundary per call.`);
|
|
8494
8741
|
const nonIdle = (this.runner?.getAllCameraMetrics() ?? []).filter((m) => m.phase !== "idle");
|
|
8495
8742
|
const contaminated = nonIdle.length > 0;
|
|
8496
8743
|
if (contaminated && input.allowActiveCameras !== true) throw new Error(`runCapacityRamp: node not quiet — ${nonIdle.length} camera(s) not idle (deviceIds ${nonIdle.map((m) => m.deviceId).join(", ")}). Pause detection first (scripts/capacity/pause-restore.mjs) or pass allowActiveCameras: true to run a knowingly contaminated measurement.`);
|
|
@@ -9540,7 +9787,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
9540
9787
|
const outcome = await runDetailSubtree({
|
|
9541
9788
|
resolveChildSteps: (deviceId, parentClassName, filter) => this.resolveDetailChildSteps(deviceId, parentClassName, filter),
|
|
9542
9789
|
getNativeCrop: (deviceId, handle, paddedBbox) => this.getNativeCropForDetail(deviceId, handle, paddedBbox),
|
|
9543
|
-
runPipeline: (steps, imageJpeg, deviceId, deviceKey, nativeCropRef) => this.runDetailPipeline(steps, imageJpeg, deviceId, deviceKey, nativeCropRef),
|
|
9790
|
+
runPipeline: (steps, imageJpeg, deviceId, deviceKey, nativeCropRef, suppliedRoot) => this.runDetailPipeline(steps, imageJpeg, deviceId, deviceKey, nativeCropRef, suppliedRoot),
|
|
9544
9791
|
placeStep: (deviceId, step) => this.placeDetailStepForCamera(deviceId, step),
|
|
9545
9792
|
resolveCropConvention: () => convention,
|
|
9546
9793
|
logger: this.detailSubtreeLogger(),
|
|
@@ -9675,39 +9922,42 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
9675
9922
|
*/
|
|
9676
9923
|
customModels = new require_default_detection_model.CustomModelCatalog({ warn: (message, extras) => this.ctxIfReady?.logger.warn(message, extras) });
|
|
9677
9924
|
/**
|
|
9678
|
-
* Per camera, the
|
|
9925
|
+
* Per camera, the root-supply plan (D730) and the announce derived from it,
|
|
9679
9926
|
* keyed on the two inputs it reads: the attach tree and the custom-model
|
|
9680
9927
|
* snapshot (a Frigate+ root is a custom model). Recomputed when either
|
|
9681
9928
|
* changes identity — at most once per catalog refresh.
|
|
9682
9929
|
*/
|
|
9683
|
-
|
|
9684
|
-
/**
|
|
9685
|
-
|
|
9930
|
+
rootSupplyCache = /* @__PURE__ */ new Map();
|
|
9931
|
+
/**
|
|
9932
|
+
* The camera's root-supply plan. One INFO per (camera, step) when a step
|
|
9933
|
+
* BECOMES supplied by the root — a COCO root now stops dispatching the
|
|
9934
|
+
* animal and vehicle classifiers by default, and that must be readable per
|
|
9935
|
+
* camera rather than inferred from a missing dispatch (D391).
|
|
9936
|
+
*/
|
|
9937
|
+
rootSupplyPlanFor(deviceId, steps) {
|
|
9686
9938
|
const byStep = this.customModels.current();
|
|
9687
|
-
const cached = this.
|
|
9939
|
+
const cached = this.rootSupplyCache.get(deviceId);
|
|
9688
9940
|
if (cached !== void 0 && cached.steps === steps && cached.byStep === byStep) return cached.plan;
|
|
9689
|
-
const plan =
|
|
9941
|
+
const plan = planRootSupplies(steps, (stepId, modelId) => this.customModels.stepDefinition(stepId)?.models.find((m) => m.id === modelId));
|
|
9690
9942
|
const announces = deriveDetailSteps(steps, plan);
|
|
9691
|
-
this.
|
|
9943
|
+
this.rootSupplyCache.set(deviceId, {
|
|
9692
9944
|
steps,
|
|
9693
9945
|
byStep,
|
|
9694
9946
|
plan,
|
|
9695
9947
|
announces
|
|
9696
9948
|
});
|
|
9697
|
-
|
|
9698
|
-
|
|
9699
|
-
|
|
9700
|
-
|
|
9701
|
-
|
|
9702
|
-
steps: [...plan.decisions].map(([stepId, d]) => ({
|
|
9949
|
+
for (const stepId of newlySuppliedSteps(cached?.plan, plan)) {
|
|
9950
|
+
const state = plan.states.get(stepId);
|
|
9951
|
+
this.ctxIfReady?.logger.info("root model supplies a pipeline step — the step is not dispatched", {
|
|
9952
|
+
tags: { deviceId },
|
|
9953
|
+
meta: {
|
|
9703
9954
|
stepId,
|
|
9704
|
-
|
|
9705
|
-
|
|
9706
|
-
|
|
9707
|
-
|
|
9708
|
-
|
|
9709
|
-
|
|
9710
|
-
});
|
|
9955
|
+
rootModelId: plan.rootModelId ?? null,
|
|
9956
|
+
kind: state?.supply?.kind ?? null,
|
|
9957
|
+
uncovered: state?.supply?.kind === "label" ? state.supply.uncovered : []
|
|
9958
|
+
}
|
|
9959
|
+
});
|
|
9960
|
+
}
|
|
9711
9961
|
return plan;
|
|
9712
9962
|
}
|
|
9713
9963
|
cropConvention = new CropConventionSource({ logger: {
|
|
@@ -9975,7 +10225,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
9975
10225
|
};
|
|
9976
10226
|
}
|
|
9977
10227
|
/** Real `runPipeline` dep: node-local `pipelineExecutor.runPipeline` on the `'full'` plane. */
|
|
9978
|
-
async runDetailPipeline(steps, imageJpeg, deviceId, stepDeviceKey, nativeCropRef) {
|
|
10228
|
+
async runDetailPipeline(steps, imageJpeg, deviceId, stepDeviceKey, nativeCropRef, suppliedRoot) {
|
|
9979
10229
|
const api = this.ctxIfReady?.api;
|
|
9980
10230
|
if (!api) return null;
|
|
9981
10231
|
const detailDeviceKey = stepDeviceKey ?? this.attached.get(deviceId)?.config?.deviceKey ?? this.freshParkedDetail(deviceId)?.deviceKey;
|
|
@@ -9986,7 +10236,8 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
9986
10236
|
deviceId,
|
|
9987
10237
|
...detailDeviceKey ? { deviceKey: detailDeviceKey } : {},
|
|
9988
10238
|
plane: "full",
|
|
9989
|
-
...nativeCropRef ? { nativeCropRef } : {}
|
|
10239
|
+
...nativeCropRef ? { nativeCropRef } : {},
|
|
10240
|
+
...suppliedRoot ? { suppliedRoot } : {}
|
|
9990
10241
|
});
|
|
9991
10242
|
} catch (err) {
|
|
9992
10243
|
this.ctxIfReady?.logger.warn("runDetailSubtree: runPipeline failed", {
|
|
@@ -10105,7 +10356,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
10105
10356
|
this.lastDecodeUnexpectedEndAt.delete(deviceId);
|
|
10106
10357
|
this.packageGovernor.forget(deviceId);
|
|
10107
10358
|
this.packageGateSkips.delete(deviceId);
|
|
10108
|
-
this.
|
|
10359
|
+
this.rootSupplyCache.delete(deviceId);
|
|
10109
10360
|
this.packageZoneUnionByDevice.delete(deviceId);
|
|
10110
10361
|
this.packageZoneUnionAt.delete(deviceId);
|
|
10111
10362
|
this.lastMotionNearPackageZone.delete(deviceId);
|
|
@@ -10821,8 +11072,8 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
10821
11072
|
return null;
|
|
10822
11073
|
}
|
|
10823
11074
|
if (steps.length === 0) return null;
|
|
10824
|
-
const
|
|
10825
|
-
const packageFill = applyPackageFill(steps,
|
|
11075
|
+
const rootSupplyPlan = this.rootSupplyPlanFor(deviceId, steps);
|
|
11076
|
+
const packageFill = applyPackageFill(steps, rootSupplyPlan);
|
|
10826
11077
|
let dispatchSteps = packageFill.steps;
|
|
10827
11078
|
if (dispatchSteps.some((s) => s.addonId === "package-detection" && s.enabled !== false)) {
|
|
10828
11079
|
this.refreshPackageZoneUnion(deviceId);
|
|
@@ -10911,7 +11162,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
10911
11162
|
return null;
|
|
10912
11163
|
}
|
|
10913
11164
|
if (result === null) return null;
|
|
10914
|
-
const looked = withPackageLook(
|
|
11165
|
+
const looked = withPackageLook(applyRootSupplyAuthority(result, rootSupplyPlan), dispatchSteps, packageFill.filledByRoot);
|
|
10915
11166
|
return this.recoverSmallSubjects(deviceId, frame, looked, dispatchSteps, camConfig?.deviceKey);
|
|
10916
11167
|
}
|
|
10917
11168
|
/**
|
|
@@ -11608,7 +11859,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
11608
11859
|
emitInferenceResult(deviceId, frame, result, handle, source, occasion) {
|
|
11609
11860
|
if (!this.ctx?.eventBus) return;
|
|
11610
11861
|
const capturedAt = frame.capturedAt;
|
|
11611
|
-
const detailSteps = this.
|
|
11862
|
+
const detailSteps = this.rootSupplyCache.get(deviceId)?.announces ?? this.attached.get(deviceId)?.detailSteps;
|
|
11612
11863
|
const payload = {
|
|
11613
11864
|
deviceId,
|
|
11614
11865
|
frame: result,
|