@camstack/addon-pipeline 1.2.329 → 1.2.330
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-B6gKGnfx.js} +39 -67
- package/dist/{default-detection-model-j0nBpRoB.mjs → default-detection-model-CPDT_eFT.mjs} +39 -67
- package/dist/detection-pipeline/index.js +197 -31
- package/dist/detection-pipeline/index.mjs +197 -31
- package/dist/{dist-BlIWhjnu.js → dist-CMeICL07.js} +771 -464
- package/dist/{dist-0HoA3wAi.mjs → dist-CuPEyZY2.mjs} +726 -437
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/{node-HOMWRPzW.mjs → node-USq5jYwq.mjs} +1 -1
- package/dist/{node-BPpVMnZy.js → node-X_0Tka7-.js} +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-07XYts8V.mjs} +1 -1
- package/dist/{process-memory-BW06vOIQ.js → process-memory-o4LFWbBN.js} +1 -1
- package/dist/recorder/index.js +7 -7
- package/dist/recorder/index.mjs +7 -7
- package/dist/{segment-demux-js-BU2MXpln.mjs → segment-demux-js-Bq8DrH0b.mjs} +2 -2
- package/dist/{segment-demux-js-Dl52Zz8w.js → segment-demux-js-FqaQjG7n.js} +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-EYRUYv_r.mjs} +2 -2
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BnP7C_HO.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-CR0--kRv.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-iGdBpvGc.mjs} +2 -2
- package/dist/stream-broker/index.js +4 -4
- package/dist/stream-broker/index.mjs +4 -4
- 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
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-
|
|
3
|
-
import { _ as getStep, c as macroGateVerdict, d as isSessionDecodeReadGone, f as createSessionDecodeCoordinator, l as rootObjectGate, o as placeDetailStep, p as resolveFrameViewGeometry, r as CustomModelCatalog, s as stepsForElectedPool, u as localFrameRegistry, v as getStepDefinition } from "../default-detection-model-
|
|
1
|
+
import { A as NativeLeaseAdmissionSchema, An as customAction, Cn as errMsg, Gt as pipelineRunnerCapability, Hn as _enum, Ht as pickDetailCropConvention, Jn as number, Kn as lazy, Ln as nodePin, Ot as linkedStepOf, Q as cameraOwnsMotionViaSensor, Qn as string, T as FailureCounters, Tn as BaseAddon, Un as array, Ut as pickNativeLeaseOverride, Wn as boolean, Yn as object, _ as DEFAULT_DETAIL_CROP_CONVENTION, a as BackgroundRunRegistry, ct as deriveDetailCropRect, er as EventCategory, in as rootSuppliesOf, j as NativeLeaseSettingsSchema, jn as defineCustomActions, kn as createEvent, kt as loadContributionCapability, m as CROP_SUPPLY_SOURCES, q as allowedSuppliedEffect, qn as literal, rn as resolveRootSupply, ut as deriveRootFaceRefineRect, y as DEFAULT_NATIVE_LEASE_SETTINGS, yt as failureContributionCapability, z as ROOT_SUPPLIABLE_STEP_IDS } from "../dist-CuPEyZY2.mjs";
|
|
2
|
+
import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-USq5jYwq.mjs";
|
|
3
|
+
import { _ as getStep, c as macroGateVerdict, d as isSessionDecodeReadGone, f as createSessionDecodeCoordinator, l as rootObjectGate, o as placeDetailStep, p as resolveFrameViewGeometry, r as CustomModelCatalog, s as stepsForElectedPool, u as localFrameRegistry, v as getStepDefinition } from "../default-detection-model-CPDT_eFT.mjs";
|
|
4
4
|
import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
|
|
5
5
|
import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
|
|
6
6
|
import { c as withDetectionIntent, o as preRollRequestOf, t as DETECTION_PRE_ROLL_REQUEST_MS } from "../restream-intent-DyGmLAU9.mjs";
|
|
@@ -1969,33 +1969,34 @@ function onboardRootDetections(slot, frame, gate) {
|
|
|
1969
1969
|
};
|
|
1970
1970
|
}
|
|
1971
1971
|
//#endregion
|
|
1972
|
-
//#region src/pipeline-runner/
|
|
1972
|
+
//#region src/pipeline-runner/root-supply.ts
|
|
1973
1973
|
/**
|
|
1974
|
-
* The runner's half of
|
|
1974
|
+
* The runner's half of root-model supply (D715, D730).
|
|
1975
1975
|
*
|
|
1976
1976
|
* The runner holds the camera's WHOLE step tree (the attach config), so it is
|
|
1977
|
-
* the one place that can answer "
|
|
1978
|
-
* root model's declaration (`
|
|
1979
|
-
* step's own `
|
|
1980
|
-
* consumers read that
|
|
1981
|
-
*
|
|
1982
|
-
* -
|
|
1983
|
-
*
|
|
1984
|
-
*
|
|
1985
|
-
*
|
|
1986
|
-
*
|
|
1987
|
-
*
|
|
1988
|
-
*
|
|
1989
|
-
*
|
|
1990
|
-
*
|
|
1977
|
+
* the one place that can answer "does this camera use the root's output for
|
|
1978
|
+
* this step?": the root model's declaration (`rootSuppliesOf`) against the
|
|
1979
|
+
* step's own `useRootModel` flag, through the one decision `resolveRootSupply`.
|
|
1980
|
+
* Four consumers read that answer:
|
|
1981
|
+
*
|
|
1982
|
+
* - linked labels — effective ⇒ raised to the detection's score (the root is
|
|
1983
|
+
* the one writer), or DROPPED when the step's class list switched that
|
|
1984
|
+
* class off (the detection stays at its macro); otherwise the score-0
|
|
1985
|
+
* fallback the executor wrote;
|
|
1986
|
+
* - root child boxes — a `plate` / `face` detail the root drew is kept only
|
|
1987
|
+
* when its crop step's supply is effective;
|
|
1988
|
+
* - the `detailSteps` announce — an effective step carries `filledBy` +
|
|
1989
|
+
* `filledAs`, so post-analysis does not infer it;
|
|
1990
|
+
* - `package-detection` — effective ⇒ the package root is not dispatched and
|
|
1991
|
+
* the root's own `package` boxes ARE the package result.
|
|
1991
1992
|
*
|
|
1992
1993
|
* Pure: the caller passes the model resolver; no I/O, no clock.
|
|
1993
1994
|
*/
|
|
1994
|
-
/** The root step the
|
|
1995
|
-
var
|
|
1995
|
+
/** The root step the supplies are read from — the always-on camera root. */
|
|
1996
|
+
var ROOT_SUPPLY_ROOT_STEP_ID = "object-detection";
|
|
1996
1997
|
var PACKAGE_STEP_ID = "package-detection";
|
|
1997
1998
|
var PACKAGE_THRESHOLD_KEY = "minConfidencePackage";
|
|
1998
|
-
var
|
|
1999
|
+
var EMPTY_ROOT_SUPPLY_PLAN = { states: /* @__PURE__ */ new Map() };
|
|
1999
2000
|
function findStep(steps, addonId) {
|
|
2000
2001
|
for (const step of steps) {
|
|
2001
2002
|
if (step.addonId === addonId) return step;
|
|
@@ -2004,70 +2005,108 @@ function findStep(steps, addonId) {
|
|
|
2004
2005
|
}
|
|
2005
2006
|
}
|
|
2006
2007
|
/**
|
|
2007
|
-
* Plan a camera's
|
|
2008
|
-
* model runs the
|
|
2008
|
+
* Plan a camera's root supplies from its configured tree. A root with no
|
|
2009
|
+
* pinned model runs the step's default — a COCO model, which supplies the
|
|
2010
|
+
* animal and vehicle classifiers like any other root.
|
|
2009
2011
|
*/
|
|
2010
|
-
function
|
|
2011
|
-
if (steps === void 0) return
|
|
2012
|
+
function planRootSupplies(steps, resolveModel) {
|
|
2013
|
+
if (steps === void 0) return EMPTY_ROOT_SUPPLY_PLAN;
|
|
2012
2014
|
const root = steps.find((s) => s.addonId === "object-detection" && s.enabled !== false);
|
|
2013
|
-
if (root
|
|
2014
|
-
const
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
...
|
|
2025
|
-
|
|
2015
|
+
if (root === void 0) return EMPTY_ROOT_SUPPLY_PLAN;
|
|
2016
|
+
const definition = getStepDefinition(ROOT_SUPPLY_ROOT_STEP_ID);
|
|
2017
|
+
const rootModelId = root.modelId ?? definition.defaultModelId;
|
|
2018
|
+
const supplies = rootSuppliesOf(resolveModel(ROOT_SUPPLY_ROOT_STEP_ID, rootModelId), {
|
|
2019
|
+
labels: definition.labels ?? [],
|
|
2020
|
+
...definition.classMap !== void 0 ? { classMap: definition.classMap } : {}
|
|
2021
|
+
});
|
|
2022
|
+
const states = /* @__PURE__ */ new Map();
|
|
2023
|
+
for (const stepId of ROOT_SUPPLIABLE_STEP_IDS) {
|
|
2024
|
+
const settings = findStep(steps, stepId)?.settings;
|
|
2025
|
+
states.set(stepId, {
|
|
2026
|
+
...resolveRootSupply(stepId, settings, supplies),
|
|
2027
|
+
...settings !== void 0 ? { settings } : {}
|
|
2028
|
+
});
|
|
2026
2029
|
}
|
|
2027
2030
|
return {
|
|
2028
|
-
rootModelId
|
|
2029
|
-
|
|
2031
|
+
rootModelId,
|
|
2032
|
+
states
|
|
2030
2033
|
};
|
|
2031
2034
|
}
|
|
2032
|
-
/**
|
|
2033
|
-
function
|
|
2035
|
+
/** The steps whose supply is effective, with the model and the kind. */
|
|
2036
|
+
function suppliedSteps(plan) {
|
|
2034
2037
|
const out = /* @__PURE__ */ new Map();
|
|
2035
2038
|
if (plan.rootModelId === void 0) return out;
|
|
2036
|
-
for (const [stepId,
|
|
2039
|
+
for (const [stepId, state] of plan.states) if (state.effective && state.supply !== void 0) out.set(stepId, {
|
|
2040
|
+
modelId: plan.rootModelId,
|
|
2041
|
+
kind: state.supply.kind
|
|
2042
|
+
});
|
|
2037
2043
|
return out;
|
|
2038
2044
|
}
|
|
2039
|
-
|
|
2045
|
+
/** The steps whose supply just BECAME effective between two plans. */
|
|
2046
|
+
function newlySuppliedSteps(previous, next) {
|
|
2047
|
+
const out = [];
|
|
2048
|
+
for (const [stepId, state] of next.states) if (state.effective && previous?.states.get(stepId)?.effective !== true) out.push(stepId);
|
|
2049
|
+
return out;
|
|
2050
|
+
}
|
|
2051
|
+
function authoritativeLabels(detection, plan) {
|
|
2040
2052
|
let changed = false;
|
|
2041
|
-
const labels =
|
|
2053
|
+
const labels = [];
|
|
2054
|
+
for (const label of detection.labels) {
|
|
2042
2055
|
const stepId = linkedStepOf(label.stepId);
|
|
2043
|
-
|
|
2044
|
-
if (
|
|
2056
|
+
const state = stepId !== void 0 ? plan.states.get(stepId) : void 0;
|
|
2057
|
+
if (state === void 0 || !state.effective) {
|
|
2058
|
+
labels.push(label);
|
|
2059
|
+
continue;
|
|
2060
|
+
}
|
|
2061
|
+
if (!allowedSuppliedEffect(label.label, state.settings)) {
|
|
2062
|
+
changed = true;
|
|
2063
|
+
continue;
|
|
2064
|
+
}
|
|
2065
|
+
if (label.score === detection.score) {
|
|
2066
|
+
labels.push(label);
|
|
2067
|
+
continue;
|
|
2068
|
+
}
|
|
2045
2069
|
changed = true;
|
|
2046
|
-
|
|
2070
|
+
labels.push({
|
|
2047
2071
|
...label,
|
|
2048
2072
|
score: detection.score
|
|
2049
|
-
};
|
|
2050
|
-
}
|
|
2073
|
+
});
|
|
2074
|
+
}
|
|
2051
2075
|
return changed ? labels : void 0;
|
|
2052
2076
|
}
|
|
2077
|
+
/** Whether a root child box survives: only under an effective crop supply. */
|
|
2078
|
+
function keepsDetection(detection, plan) {
|
|
2079
|
+
const supplied = detection.rootSupplied;
|
|
2080
|
+
if (supplied === void 0 || detection.kind !== "detail") return true;
|
|
2081
|
+
const stepId = ROOT_SUPPLIABLE_STEP_IDS.find((id) => id === supplied.stepId);
|
|
2082
|
+
return stepId !== void 0 && plan.states.get(stepId)?.effective === true;
|
|
2083
|
+
}
|
|
2053
2084
|
/**
|
|
2054
|
-
*
|
|
2055
|
-
* authoritative
|
|
2056
|
-
*
|
|
2057
|
-
* nothing changes.
|
|
2085
|
+
* Apply the plan to a frame: linked labels of an effective step become
|
|
2086
|
+
* authoritative (or are dropped by its class list), and root child boxes of a
|
|
2087
|
+
* crop step that is not effective are removed. Never mutates; returns the
|
|
2088
|
+
* input when nothing changes.
|
|
2058
2089
|
*/
|
|
2059
|
-
function
|
|
2060
|
-
if (plan.
|
|
2090
|
+
function applyRootSupplyAuthority(result, plan) {
|
|
2091
|
+
if (plan.states.size === 0) return result;
|
|
2061
2092
|
let changed = false;
|
|
2062
|
-
const detections =
|
|
2063
|
-
|
|
2064
|
-
if (
|
|
2093
|
+
const detections = [];
|
|
2094
|
+
for (const d of result.detections) {
|
|
2095
|
+
if (!keepsDetection(d, plan)) {
|
|
2096
|
+
changed = true;
|
|
2097
|
+
continue;
|
|
2098
|
+
}
|
|
2099
|
+
const labels = authoritativeLabels(d, plan);
|
|
2100
|
+
if (labels === void 0) {
|
|
2101
|
+
detections.push(d);
|
|
2102
|
+
continue;
|
|
2103
|
+
}
|
|
2065
2104
|
changed = true;
|
|
2066
|
-
|
|
2105
|
+
detections.push({
|
|
2067
2106
|
...d,
|
|
2068
2107
|
labels
|
|
2069
|
-
};
|
|
2070
|
-
}
|
|
2108
|
+
});
|
|
2109
|
+
}
|
|
2071
2110
|
return changed ? {
|
|
2072
2111
|
...result,
|
|
2073
2112
|
detections
|
|
@@ -2075,7 +2114,7 @@ function applyLabelLinkAuthority(result, plan) {
|
|
|
2075
2114
|
}
|
|
2076
2115
|
/** The root step's schema default for its class allow-list. */
|
|
2077
2116
|
function rootDefaultMacroClasses() {
|
|
2078
|
-
const field = getStep(
|
|
2117
|
+
const field = getStep(ROOT_SUPPLY_ROOT_STEP_ID).getConfigSchema().find((f) => "key" in f && f.key === "enabledMacroClasses");
|
|
2079
2118
|
const value = field !== void 0 && "default" in field ? field.default : void 0;
|
|
2080
2119
|
return Array.isArray(value) ? value : [];
|
|
2081
2120
|
}
|
|
@@ -2096,18 +2135,19 @@ function withPackageAdmitted(root, packageStep) {
|
|
|
2096
2135
|
};
|
|
2097
2136
|
}
|
|
2098
2137
|
/**
|
|
2099
|
-
* When the root
|
|
2100
|
-
*
|
|
2101
|
-
*
|
|
2102
|
-
*
|
|
2138
|
+
* When the root's `package` supply is effective for this camera, drop the
|
|
2139
|
+
* package root from the dispatch and let the camera root report packages: its
|
|
2140
|
+
* gate admits `package` at the package step's threshold. Otherwise (the root
|
|
2141
|
+
* declares no `package`, or the flag is off) the tree is returned untouched
|
|
2142
|
+
* and the package step runs its own model.
|
|
2103
2143
|
*/
|
|
2104
2144
|
function applyPackageFill(steps, plan) {
|
|
2105
|
-
if (plan.
|
|
2145
|
+
if (plan.states.get(PACKAGE_STEP_ID)?.effective !== true) return {
|
|
2106
2146
|
steps,
|
|
2107
2147
|
filledByRoot: false
|
|
2108
2148
|
};
|
|
2109
2149
|
const packageStep = steps.find((s) => s.addonId === PACKAGE_STEP_ID);
|
|
2110
|
-
if (packageStep === void 0) return {
|
|
2150
|
+
if (packageStep === void 0 || packageStep.enabled === false) return {
|
|
2111
2151
|
steps,
|
|
2112
2152
|
filledByRoot: false
|
|
2113
2153
|
};
|
|
@@ -3288,6 +3328,150 @@ var FaceOutsideParentLog = class {
|
|
|
3288
3328
|
}
|
|
3289
3329
|
};
|
|
3290
3330
|
//#endregion
|
|
3331
|
+
//#region src/pipeline-runner/root-face-refine.ts
|
|
3332
|
+
/**
|
|
3333
|
+
* Root face refinement (D731) — how a ROOT model's `face` box becomes a face
|
|
3334
|
+
* the recogniser can use, without ever embedding an unaligned crop.
|
|
3335
|
+
*
|
|
3336
|
+
* A root face box has no landmarks. It is used as a LOCATION only:
|
|
3337
|
+
*
|
|
3338
|
+
* 1. one face-scoped square around it (`deriveRootFaceRefineRect`, box ×1.6)
|
|
3339
|
+
* is cut from the frame's NATIVE surface — the native lease or nothing;
|
|
3340
|
+
* 2. the square is shrunk to {@link ROOT_FACE_REFINE_INPUT_PX} and laid in the
|
|
3341
|
+
* top-left of a black canvas the size of the face detector's input, so the
|
|
3342
|
+
* detector runs at an effective 160 px input on its own 640 graph — the
|
|
3343
|
+
* network is convolutional, the black margin contains nothing, and no 160 px
|
|
3344
|
+
* build of the model is needed. The canvas maps back to the frame through
|
|
3345
|
+
* a "virtual" frame rectangle 640/160 times the square, so every existing
|
|
3346
|
+
* crop→frame mapping (results, native child re-cuts) stays exact;
|
|
3347
|
+
* 3. the detector's best face in the square is kept — its landmarks come from
|
|
3348
|
+
* the refinement input, where the face is large, which is the space the
|
|
3349
|
+
* landmark-precision gate then measures it in;
|
|
3350
|
+
* 4. that face is handed back to the SAME step as its output
|
|
3351
|
+
* (`suppliedRoot`), so the embedder child aligns it through the existing
|
|
3352
|
+
* native-crop alignment, exactly like a detected face.
|
|
3353
|
+
*
|
|
3354
|
+
* No face in the square ⇒ nothing is embedded, and the drop is logged.
|
|
3355
|
+
*/
|
|
3356
|
+
/** The face detector's input side for the step as it will run. */
|
|
3357
|
+
function detectorInputPx(step) {
|
|
3358
|
+
const def = getStepDefinition(step.addonId);
|
|
3359
|
+
const modelId = step.modelId ?? def.defaultModelId;
|
|
3360
|
+
return def.models.find((m) => m.id === modelId)?.inputSize.width ?? 160;
|
|
3361
|
+
}
|
|
3362
|
+
/** Lay the refinement square, shrunk to the refine input, on a black canvas. */
|
|
3363
|
+
async function buildRefineCanvas(squareJpeg, canvasPx) {
|
|
3364
|
+
const sharp = await getSharp();
|
|
3365
|
+
const small = await sharp(squareJpeg).resize(160, 160, { fit: "fill" }).toBuffer();
|
|
3366
|
+
const pad = Math.max(0, canvasPx - 160);
|
|
3367
|
+
return sharp(small).extend({
|
|
3368
|
+
top: 0,
|
|
3369
|
+
left: 0,
|
|
3370
|
+
bottom: pad,
|
|
3371
|
+
right: pad,
|
|
3372
|
+
background: {
|
|
3373
|
+
r: 0,
|
|
3374
|
+
g: 0,
|
|
3375
|
+
b: 0
|
|
3376
|
+
}
|
|
3377
|
+
}).jpeg({ quality: 92 }).toBuffer();
|
|
3378
|
+
}
|
|
3379
|
+
/** The best face of a refinement pass, or `undefined` when it found none. */
|
|
3380
|
+
function bestFace(detections) {
|
|
3381
|
+
return detections.filter((d) => d.macroClass === "face").reduce((best, d) => best === void 0 || d.score > best.score ? d : best, void 0);
|
|
3382
|
+
}
|
|
3383
|
+
/** The canvas-space face as the step's own output for the second pass. */
|
|
3384
|
+
function refinedAsSupplied(face, stepId, modelId) {
|
|
3385
|
+
return {
|
|
3386
|
+
stepId,
|
|
3387
|
+
modelId,
|
|
3388
|
+
detections: [{
|
|
3389
|
+
bbox: [
|
|
3390
|
+
face.bbox.x,
|
|
3391
|
+
face.bbox.y,
|
|
3392
|
+
face.bbox.x + face.bbox.width,
|
|
3393
|
+
face.bbox.y + face.bbox.height
|
|
3394
|
+
],
|
|
3395
|
+
score: face.score,
|
|
3396
|
+
...face.landmarks !== void 0 ? { landmarks: face.landmarks.map((p) => ({
|
|
3397
|
+
x: p.x,
|
|
3398
|
+
y: p.y
|
|
3399
|
+
})) } : {}
|
|
3400
|
+
}]
|
|
3401
|
+
};
|
|
3402
|
+
}
|
|
3403
|
+
/**
|
|
3404
|
+
* Refine the root's face box for one step and run the requested chain on the
|
|
3405
|
+
* face it finds. `null` ⇒ no pixels (the native lease is the only source);
|
|
3406
|
+
* an empty list ⇒ the square held no face. Every drop is logged.
|
|
3407
|
+
*/
|
|
3408
|
+
async function refineRootFace(deps, input, supplied, step, requested, deviceKey) {
|
|
3409
|
+
const handle = input.frameHandle;
|
|
3410
|
+
const tags = { deviceId: input.deviceId };
|
|
3411
|
+
if (handle === void 0) {
|
|
3412
|
+
deps.logger.warn("root face refinement needs the native frame — no frame handle, dropped", {
|
|
3413
|
+
tags,
|
|
3414
|
+
meta: { stepId: step.addonId }
|
|
3415
|
+
});
|
|
3416
|
+
return null;
|
|
3417
|
+
}
|
|
3418
|
+
const box = supplied.boxes.reduce((best, b) => b.score > best.score ? b : best);
|
|
3419
|
+
const square = deriveRootFaceRefineRect(box.bbox, handle.width, handle.height);
|
|
3420
|
+
const native = await deps.getNativeCrop(input.deviceId, handle, normalizeRect(square, handle.width, handle.height));
|
|
3421
|
+
if (native === null || isNativeCropMiss(native)) {
|
|
3422
|
+
deps.logger.warn("root face refinement: the native square missed — dropped", {
|
|
3423
|
+
tags,
|
|
3424
|
+
meta: {
|
|
3425
|
+
stepId: step.addonId,
|
|
3426
|
+
nativeMissReason: native === null ? null : native.miss
|
|
3427
|
+
}
|
|
3428
|
+
});
|
|
3429
|
+
return null;
|
|
3430
|
+
}
|
|
3431
|
+
const canvasPx = detectorInputPx(step);
|
|
3432
|
+
const scale = canvasPx / 160;
|
|
3433
|
+
const virtualFrameSpace = {
|
|
3434
|
+
x: native.frameSpace.x,
|
|
3435
|
+
y: native.frameSpace.y,
|
|
3436
|
+
w: native.frameSpace.w * scale,
|
|
3437
|
+
h: native.frameSpace.h * scale
|
|
3438
|
+
};
|
|
3439
|
+
const crop = {
|
|
3440
|
+
jpeg: await buildRefineCanvas(native.jpeg, canvasPx),
|
|
3441
|
+
width: canvasPx,
|
|
3442
|
+
height: canvasPx,
|
|
3443
|
+
frameSpace: virtualFrameSpace,
|
|
3444
|
+
source: "native"
|
|
3445
|
+
};
|
|
3446
|
+
const nativeCropRef = {
|
|
3447
|
+
handle,
|
|
3448
|
+
cropFrameSpace: virtualFrameSpace
|
|
3449
|
+
};
|
|
3450
|
+
const detectorOnly = {
|
|
3451
|
+
...step,
|
|
3452
|
+
children: []
|
|
3453
|
+
};
|
|
3454
|
+
const found = await deps.runPipeline([detectorOnly], crop.jpeg, input.deviceId, deviceKey, nativeCropRef);
|
|
3455
|
+
const face = found === null ? void 0 : bestFace(found.detections);
|
|
3456
|
+
if (face === void 0) {
|
|
3457
|
+
deps.logger.warn("root face refinement found no face in the square — dropped, never embedded unaligned", {
|
|
3458
|
+
tags,
|
|
3459
|
+
meta: {
|
|
3460
|
+
stepId: step.addonId,
|
|
3461
|
+
rootModelId: supplied.modelId,
|
|
3462
|
+
rootScore: box.score
|
|
3463
|
+
}
|
|
3464
|
+
});
|
|
3465
|
+
return [];
|
|
3466
|
+
}
|
|
3467
|
+
const chain = requested === void 0 ? step : pruneChildStepsToRequested(step, requested);
|
|
3468
|
+
if (chain.children === void 0 || chain.children.length === 0) return [toDetailResult(face, step.addonId, crop)];
|
|
3469
|
+
const modelId = step.modelId ?? getStepDefinition(step.addonId).defaultModelId;
|
|
3470
|
+
const embedded = await deps.runPipeline([chain], crop.jpeg, input.deviceId, deviceKey, nativeCropRef, refinedAsSupplied(face, step.addonId, modelId));
|
|
3471
|
+
if (embedded === null) return [toDetailResult(face, step.addonId, crop)];
|
|
3472
|
+
return embedded.detections.map((d) => toDetailResult(d, step.addonId, crop));
|
|
3473
|
+
}
|
|
3474
|
+
//#endregion
|
|
3291
3475
|
//#region src/pipeline-runner/detail-subtree.ts
|
|
3292
3476
|
/** Did the subtree run refuse because the camera is not attached (park lapsed)? */
|
|
3293
3477
|
function isDetailNotAttachedRefusal(outcome) {
|
|
@@ -3380,6 +3564,43 @@ function mapBboxToFrameSpace(bbox, crop) {
|
|
|
3380
3564
|
h: bbox.height * scaleY
|
|
3381
3565
|
};
|
|
3382
3566
|
}
|
|
3567
|
+
/**
|
|
3568
|
+
* Map the root's FRAME-space boxes (D730) into the crop's pixels — the inverse
|
|
3569
|
+
* of {@link mapBboxToFrameSpace} — clamped to the crop. A box with nothing left
|
|
3570
|
+
* inside the crop is dropped. `null` when no box survives.
|
|
3571
|
+
*/
|
|
3572
|
+
function suppliedRootForCrop(supplied, crop) {
|
|
3573
|
+
const scaleX = crop.width / crop.frameSpace.w;
|
|
3574
|
+
const scaleY = crop.height / crop.frameSpace.h;
|
|
3575
|
+
const detections = [];
|
|
3576
|
+
for (const box of supplied.boxes) {
|
|
3577
|
+
const x1 = Math.max(0, (box.bbox.x - crop.frameSpace.x) * scaleX);
|
|
3578
|
+
const y1 = Math.max(0, (box.bbox.y - crop.frameSpace.y) * scaleY);
|
|
3579
|
+
const x2 = Math.min(crop.width, (box.bbox.x + box.bbox.w - crop.frameSpace.x) * scaleX);
|
|
3580
|
+
const y2 = Math.min(crop.height, (box.bbox.y + box.bbox.h - crop.frameSpace.y) * scaleY);
|
|
3581
|
+
if (x2 <= x1 || y2 <= y1) continue;
|
|
3582
|
+
detections.push({
|
|
3583
|
+
bbox: [
|
|
3584
|
+
x1,
|
|
3585
|
+
y1,
|
|
3586
|
+
x2,
|
|
3587
|
+
y2
|
|
3588
|
+
],
|
|
3589
|
+
score: box.score
|
|
3590
|
+
});
|
|
3591
|
+
}
|
|
3592
|
+
const [first, ...rest] = detections;
|
|
3593
|
+
if (first === void 0) return null;
|
|
3594
|
+
return {
|
|
3595
|
+
stepId: supplied.stepId,
|
|
3596
|
+
modelId: supplied.modelId,
|
|
3597
|
+
detections: [first, ...rest]
|
|
3598
|
+
};
|
|
3599
|
+
}
|
|
3600
|
+
/** Whether the root's box for this step is a location to refine in (a face). */
|
|
3601
|
+
function refinesSuppliedStep(stepId) {
|
|
3602
|
+
return (stepId === "plate-detection" || stepId === "face-detection") && CROP_SUPPLY_SOURCES[stepId].use === "refine";
|
|
3603
|
+
}
|
|
3383
3604
|
/** Base64-encode a normalized float32 embedding vector. */
|
|
3384
3605
|
function encodeEmbeddingBase64(embedding) {
|
|
3385
3606
|
return Buffer.from(Float32Array.from(embedding).buffer).toString("base64");
|
|
@@ -3583,6 +3804,18 @@ async function runDetailSubtree(deps, input) {
|
|
|
3583
3804
|
}
|
|
3584
3805
|
const requested = input.steps;
|
|
3585
3806
|
const steps = requested === void 0 ? matched : matched.map((step) => pruneChildStepsToRequested(step, requested));
|
|
3807
|
+
const supplied = input.supplied;
|
|
3808
|
+
if (supplied !== void 0 && refinesSuppliedStep(supplied.stepId)) {
|
|
3809
|
+
const details = [];
|
|
3810
|
+
for (const step of matched) {
|
|
3811
|
+
if (step.addonId !== supplied.stepId) continue;
|
|
3812
|
+
const placement = deps.placeStep?.(input.deviceId, step);
|
|
3813
|
+
const refined = await refineRootFace(deps, input, supplied, placement?.step ?? step, requested, placement?.deviceKey);
|
|
3814
|
+
if (refined === null) return null;
|
|
3815
|
+
details.push(...refined);
|
|
3816
|
+
}
|
|
3817
|
+
return { details };
|
|
3818
|
+
}
|
|
3586
3819
|
const { crop, nativeMiss } = await resolveCrop(deps, input, deps.resolveCropConvention(input.deviceId));
|
|
3587
3820
|
if (!crop) {
|
|
3588
3821
|
deps.logger.warn("runDetailSubtree: no crop source resolved — the dispatch produced nothing", {
|
|
@@ -3611,7 +3844,18 @@ async function runDetailSubtree(deps, input) {
|
|
|
3611
3844
|
let unjudgedFaces = 0;
|
|
3612
3845
|
for (const step of steps) {
|
|
3613
3846
|
const placement = deps.placeStep?.(input.deviceId, step);
|
|
3614
|
-
const
|
|
3847
|
+
const suppliedRoot = input.supplied !== void 0 && input.supplied.stepId === step.addonId ? suppliedRootForCrop(input.supplied, crop) : void 0;
|
|
3848
|
+
if (suppliedRoot === null) {
|
|
3849
|
+
deps.logger.warn("runDetailSubtree: the root-supplied boxes fall outside the crop — nothing to read", {
|
|
3850
|
+
tags: { deviceId: input.deviceId },
|
|
3851
|
+
meta: {
|
|
3852
|
+
stepId: step.addonId,
|
|
3853
|
+
parentClass: input.parent.className
|
|
3854
|
+
}
|
|
3855
|
+
});
|
|
3856
|
+
continue;
|
|
3857
|
+
}
|
|
3858
|
+
const result = await deps.runPipeline([placement?.step ?? step], crop.jpeg, input.deviceId, placement?.deviceKey, nativeCropRef, ...suppliedRoot !== void 0 ? [suppliedRoot] : []);
|
|
3615
3859
|
if (!result) continue;
|
|
3616
3860
|
for (const detection of result.detections) {
|
|
3617
3861
|
const detail = toDetailResult(detection, step.addonId, crop);
|
|
@@ -7334,15 +7578,15 @@ function isDetailPlaneStepId(stepId) {
|
|
|
7334
7578
|
* Extracted as a pure function so the unit suite can exercise the
|
|
7335
7579
|
* derivation without instantiating the full addon.
|
|
7336
7580
|
*/
|
|
7337
|
-
function deriveDetailSteps(steps, plan =
|
|
7581
|
+
function deriveDetailSteps(steps, plan = EMPTY_ROOT_SUPPLY_PLAN) {
|
|
7338
7582
|
const stepIds = /* @__PURE__ */ new Set();
|
|
7339
7583
|
collectEnabledStepIds(steps, stepIds);
|
|
7340
|
-
const
|
|
7584
|
+
const supplied = suppliedSteps(plan);
|
|
7341
7585
|
const announces = [];
|
|
7342
7586
|
for (const stepId of stepIds) {
|
|
7343
7587
|
const def = tryGetStepDefinition(stepId);
|
|
7344
7588
|
if (!def || def.inputClasses === null) continue;
|
|
7345
|
-
const
|
|
7589
|
+
const by = supplied.get(def.id);
|
|
7346
7590
|
announces.push({
|
|
7347
7591
|
stepId: def.id,
|
|
7348
7592
|
inputClasses: def.inputClasses,
|
|
@@ -7350,7 +7594,10 @@ function deriveDetailSteps(steps, plan = EMPTY_LABEL_LINK_PLAN) {
|
|
|
7350
7594
|
trigger: "once",
|
|
7351
7595
|
maxPerTrack: 3
|
|
7352
7596
|
},
|
|
7353
|
-
...
|
|
7597
|
+
...by !== void 0 ? {
|
|
7598
|
+
filledBy: by.modelId,
|
|
7599
|
+
filledAs: by.kind
|
|
7600
|
+
} : {}
|
|
7354
7601
|
});
|
|
7355
7602
|
}
|
|
7356
7603
|
return announces;
|
|
@@ -8482,7 +8729,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
8482
8729
|
if (!ctx.api) throw new Error("runCapacityRamp: ctx.api unavailable");
|
|
8483
8730
|
if (this.capacityRuns.get(input.sessionId)?.status.state === "running") throw new Error(`runCapacityRamp: session '${input.sessionId}' is already running`);
|
|
8484
8731
|
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`);
|
|
8485
|
-
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
|
|
8732
|
+
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.`);
|
|
8486
8733
|
const nonIdle = (this.runner?.getAllCameraMetrics() ?? []).filter((m) => m.phase !== "idle");
|
|
8487
8734
|
const contaminated = nonIdle.length > 0;
|
|
8488
8735
|
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.`);
|
|
@@ -9532,7 +9779,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
9532
9779
|
const outcome = await runDetailSubtree({
|
|
9533
9780
|
resolveChildSteps: (deviceId, parentClassName, filter) => this.resolveDetailChildSteps(deviceId, parentClassName, filter),
|
|
9534
9781
|
getNativeCrop: (deviceId, handle, paddedBbox) => this.getNativeCropForDetail(deviceId, handle, paddedBbox),
|
|
9535
|
-
runPipeline: (steps, imageJpeg, deviceId, deviceKey, nativeCropRef) => this.runDetailPipeline(steps, imageJpeg, deviceId, deviceKey, nativeCropRef),
|
|
9782
|
+
runPipeline: (steps, imageJpeg, deviceId, deviceKey, nativeCropRef, suppliedRoot) => this.runDetailPipeline(steps, imageJpeg, deviceId, deviceKey, nativeCropRef, suppliedRoot),
|
|
9536
9783
|
placeStep: (deviceId, step) => this.placeDetailStepForCamera(deviceId, step),
|
|
9537
9784
|
resolveCropConvention: () => convention,
|
|
9538
9785
|
logger: this.detailSubtreeLogger(),
|
|
@@ -9667,39 +9914,42 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
9667
9914
|
*/
|
|
9668
9915
|
customModels = new CustomModelCatalog({ warn: (message, extras) => this.ctxIfReady?.logger.warn(message, extras) });
|
|
9669
9916
|
/**
|
|
9670
|
-
* Per camera, the
|
|
9917
|
+
* Per camera, the root-supply plan (D730) and the announce derived from it,
|
|
9671
9918
|
* keyed on the two inputs it reads: the attach tree and the custom-model
|
|
9672
9919
|
* snapshot (a Frigate+ root is a custom model). Recomputed when either
|
|
9673
9920
|
* changes identity — at most once per catalog refresh.
|
|
9674
9921
|
*/
|
|
9675
|
-
|
|
9676
|
-
/**
|
|
9677
|
-
|
|
9922
|
+
rootSupplyCache = /* @__PURE__ */ new Map();
|
|
9923
|
+
/**
|
|
9924
|
+
* The camera's root-supply plan. One INFO per (camera, step) when a step
|
|
9925
|
+
* BECOMES supplied by the root — a COCO root now stops dispatching the
|
|
9926
|
+
* animal and vehicle classifiers by default, and that must be readable per
|
|
9927
|
+
* camera rather than inferred from a missing dispatch (D391).
|
|
9928
|
+
*/
|
|
9929
|
+
rootSupplyPlanFor(deviceId, steps) {
|
|
9678
9930
|
const byStep = this.customModels.current();
|
|
9679
|
-
const cached = this.
|
|
9931
|
+
const cached = this.rootSupplyCache.get(deviceId);
|
|
9680
9932
|
if (cached !== void 0 && cached.steps === steps && cached.byStep === byStep) return cached.plan;
|
|
9681
|
-
const plan =
|
|
9933
|
+
const plan = planRootSupplies(steps, (stepId, modelId) => this.customModels.stepDefinition(stepId)?.models.find((m) => m.id === modelId));
|
|
9682
9934
|
const announces = deriveDetailSteps(steps, plan);
|
|
9683
|
-
this.
|
|
9935
|
+
this.rootSupplyCache.set(deviceId, {
|
|
9684
9936
|
steps,
|
|
9685
9937
|
byStep,
|
|
9686
9938
|
plan,
|
|
9687
9939
|
announces
|
|
9688
9940
|
});
|
|
9689
|
-
|
|
9690
|
-
|
|
9691
|
-
|
|
9692
|
-
|
|
9693
|
-
|
|
9694
|
-
steps: [...plan.decisions].map(([stepId, d]) => ({
|
|
9941
|
+
for (const stepId of newlySuppliedSteps(cached?.plan, plan)) {
|
|
9942
|
+
const state = plan.states.get(stepId);
|
|
9943
|
+
this.ctxIfReady?.logger.info("root model supplies a pipeline step — the step is not dispatched", {
|
|
9944
|
+
tags: { deviceId },
|
|
9945
|
+
meta: {
|
|
9695
9946
|
stepId,
|
|
9696
|
-
|
|
9697
|
-
|
|
9698
|
-
|
|
9699
|
-
|
|
9700
|
-
|
|
9701
|
-
|
|
9702
|
-
});
|
|
9947
|
+
rootModelId: plan.rootModelId ?? null,
|
|
9948
|
+
kind: state?.supply?.kind ?? null,
|
|
9949
|
+
uncovered: state?.supply?.kind === "label" ? state.supply.uncovered : []
|
|
9950
|
+
}
|
|
9951
|
+
});
|
|
9952
|
+
}
|
|
9703
9953
|
return plan;
|
|
9704
9954
|
}
|
|
9705
9955
|
cropConvention = new CropConventionSource({ logger: {
|
|
@@ -9967,7 +10217,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
9967
10217
|
};
|
|
9968
10218
|
}
|
|
9969
10219
|
/** Real `runPipeline` dep: node-local `pipelineExecutor.runPipeline` on the `'full'` plane. */
|
|
9970
|
-
async runDetailPipeline(steps, imageJpeg, deviceId, stepDeviceKey, nativeCropRef) {
|
|
10220
|
+
async runDetailPipeline(steps, imageJpeg, deviceId, stepDeviceKey, nativeCropRef, suppliedRoot) {
|
|
9971
10221
|
const api = this.ctxIfReady?.api;
|
|
9972
10222
|
if (!api) return null;
|
|
9973
10223
|
const detailDeviceKey = stepDeviceKey ?? this.attached.get(deviceId)?.config?.deviceKey ?? this.freshParkedDetail(deviceId)?.deviceKey;
|
|
@@ -9978,7 +10228,8 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
9978
10228
|
deviceId,
|
|
9979
10229
|
...detailDeviceKey ? { deviceKey: detailDeviceKey } : {},
|
|
9980
10230
|
plane: "full",
|
|
9981
|
-
...nativeCropRef ? { nativeCropRef } : {}
|
|
10231
|
+
...nativeCropRef ? { nativeCropRef } : {},
|
|
10232
|
+
...suppliedRoot ? { suppliedRoot } : {}
|
|
9982
10233
|
});
|
|
9983
10234
|
} catch (err) {
|
|
9984
10235
|
this.ctxIfReady?.logger.warn("runDetailSubtree: runPipeline failed", {
|
|
@@ -10097,7 +10348,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
10097
10348
|
this.lastDecodeUnexpectedEndAt.delete(deviceId);
|
|
10098
10349
|
this.packageGovernor.forget(deviceId);
|
|
10099
10350
|
this.packageGateSkips.delete(deviceId);
|
|
10100
|
-
this.
|
|
10351
|
+
this.rootSupplyCache.delete(deviceId);
|
|
10101
10352
|
this.packageZoneUnionByDevice.delete(deviceId);
|
|
10102
10353
|
this.packageZoneUnionAt.delete(deviceId);
|
|
10103
10354
|
this.lastMotionNearPackageZone.delete(deviceId);
|
|
@@ -10813,8 +11064,8 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
10813
11064
|
return null;
|
|
10814
11065
|
}
|
|
10815
11066
|
if (steps.length === 0) return null;
|
|
10816
|
-
const
|
|
10817
|
-
const packageFill = applyPackageFill(steps,
|
|
11067
|
+
const rootSupplyPlan = this.rootSupplyPlanFor(deviceId, steps);
|
|
11068
|
+
const packageFill = applyPackageFill(steps, rootSupplyPlan);
|
|
10818
11069
|
let dispatchSteps = packageFill.steps;
|
|
10819
11070
|
if (dispatchSteps.some((s) => s.addonId === "package-detection" && s.enabled !== false)) {
|
|
10820
11071
|
this.refreshPackageZoneUnion(deviceId);
|
|
@@ -10903,7 +11154,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
10903
11154
|
return null;
|
|
10904
11155
|
}
|
|
10905
11156
|
if (result === null) return null;
|
|
10906
|
-
const looked = withPackageLook(
|
|
11157
|
+
const looked = withPackageLook(applyRootSupplyAuthority(result, rootSupplyPlan), dispatchSteps, packageFill.filledByRoot);
|
|
10907
11158
|
return this.recoverSmallSubjects(deviceId, frame, looked, dispatchSteps, camConfig?.deviceKey);
|
|
10908
11159
|
}
|
|
10909
11160
|
/**
|
|
@@ -11600,7 +11851,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
11600
11851
|
emitInferenceResult(deviceId, frame, result, handle, source, occasion) {
|
|
11601
11852
|
if (!this.ctx?.eventBus) return;
|
|
11602
11853
|
const capturedAt = frame.capturedAt;
|
|
11603
|
-
const detailSteps = this.
|
|
11854
|
+
const detailSteps = this.rootSupplyCache.get(deviceId)?.announces ?? this.attached.get(deviceId)?.detailSteps;
|
|
11604
11855
|
const payload = {
|
|
11605
11856
|
deviceId,
|
|
11606
11857
|
frame: result,
|