@camstack/addon-pipeline 1.1.65 → 1.1.67
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 +83 -22
- package/dist/detection-pipeline/index.mjs +83 -22
- package/dist/{dist-WW3gH8hF.js → dist-BxxVKcnw.js} +56 -5
- package/dist/{dist-D6tHM9GA.mjs → dist-DkGHtAaK.mjs} +56 -5
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +88 -22
- package/dist/pipeline-runner/index.mjs +88 -22
- package/dist/recorder/index.js +141 -1
- package/dist/recorder/index.mjs +141 -1
- package/dist/session-decode/decode-worker-child.js +185 -14
- package/dist/session-decode/decode-worker-child.mjs +185 -14
- package/dist/{step-definitions-CXZpwwT0.js → step-definitions-CAs0RD2N.js} +127 -8
- package/dist/{step-definitions-Dbsm64_I.mjs → step-definitions-Delhq28w.mjs} +127 -8
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CbU_HuOC.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CfFsU700.mjs} +2 -2
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-D68Lj69v.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-DlALQ_wp.mjs +26 -0
- package/dist/stream-broker/{hostInit-QaDEOY0Z.mjs → hostInit-D-S5fCDx.mjs} +2 -2
- package/dist/stream-broker/index.js +1 -1
- package/dist/stream-broker/index.mjs +1 -1
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-C7H0kVAH.js → MaskShapeCanvas-DI4BY7W2-CRvzj7Of.js} +1 -1
- package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-CHfEFlEF.js → MotionZonesSettings-NcxxQN8r-CBgvPUB-.js} +1 -1
- package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-Bc-trVxX.js → PrivacyMaskSettings-APgPLF7p-DH07fe1k.js} +1 -1
- package/embed-dist/assets/index-BkoPcGtQ.js +114 -0
- package/embed-dist/assets/index-D2pF9Z3W.css +2 -0
- package/embed-dist/index.html +2 -2
- package/package.json +1 -1
- package/python/inference_pool.py +392 -45
- package/python/postprocessors/softmax.py +17 -5
- package/python/postprocessors/test_softmax.py +52 -0
- package/python/test_inference_pool_ov_ppp.py +117 -0
- package/python/test_inference_pool_preprocess.py +93 -1
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CxZvj8X1.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-BzNJyeNE.mjs +0 -26
- package/embed-dist/assets/index-Bm9LaQPH.css +0 -2
- package/embed-dist/assets/index-eZGm_itM.js +0 -81
|
@@ -2,6 +2,133 @@ import { n as isWorkerRequest } from "../worker-protocol-CyVJTZEO.mjs";
|
|
|
2
2
|
import { pathToFileURL } from "node:url";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
5
|
+
//#region src/session-decode/color-conversion.ts
|
|
6
|
+
/**
|
|
7
|
+
* Explicit YUV→RGB colorspace/range resolution for the session-decode worker.
|
|
8
|
+
*
|
|
9
|
+
* WHY THIS EXISTS (RC-5, `docs/superpowers/specs/2026-07-19-track-media-bugs-analysis.md`):
|
|
10
|
+
* an intermittent ~one-GOP DESATURATED stretch was measured on cam 615 — the
|
|
11
|
+
* classic "limited-range YUV interpreted as full-range" signature (lifted
|
|
12
|
+
* blacks, washed color, JPEG size collapse). Root cause: NONE of the decode
|
|
13
|
+
* worker's YUV→RGB conversion sites pinned an explicit colorspace/range, so the
|
|
14
|
+
* conversion depended on whatever `colorspace`/`color_range` metadata each
|
|
15
|
+
* decoded frame happened to carry. A GOP replayed/decoded by a DIFFERENT path
|
|
16
|
+
* (hwaccel-cascade / keyframe-seed fallback, see `docs/design/decode-path.md`)
|
|
17
|
+
* can carry UNSPECIFIED (or a different) range, which the libav auto-inserted
|
|
18
|
+
* converter then guesses differently than its neighbours → one washed-out GOP,
|
|
19
|
+
* then a snap back.
|
|
20
|
+
*
|
|
21
|
+
* The fix is to resolve every frame's source color to an EXPLICIT
|
|
22
|
+
* `(matrix, range)` — using the frame's own tags when present, and a STABLE
|
|
23
|
+
* default (bt709 / limited, the typical H.264/H.265 HD signalling) when they
|
|
24
|
+
* are unspecified — and to pin the conversion to that + full-range RGB output
|
|
25
|
+
* IDENTICALLY at every site. An unspecified/fallback GOP then converts exactly
|
|
26
|
+
* like its neighbours instead of being auto-guessed, so the desaturation cannot
|
|
27
|
+
* recur regardless of which decode path produced the GOP.
|
|
28
|
+
*
|
|
29
|
+
* These functions operate on the raw FFmpeg `AVColorSpace` / `AVColorRange`
|
|
30
|
+
* enum integers (stable ABI values) so they are unit-testable without loading
|
|
31
|
+
* node-av. node-av's branded `AVColorSpace`/`AVColorRange` are `number`
|
|
32
|
+
* subtypes and pass through directly.
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* FFmpeg `AVColorSpace` enum values we map to a libswscale/`scale`-filter
|
|
36
|
+
* `in_color_matrix` token. Stable ABI integers (libavutil `pixfmt.h`).
|
|
37
|
+
*/
|
|
38
|
+
var AVCOL_SPC_BT709 = 1;
|
|
39
|
+
var AVCOL_SPC_FCC = 4;
|
|
40
|
+
var AVCOL_SPC_BT470BG = 5;
|
|
41
|
+
var AVCOL_SPC_SMPTE170M = 6;
|
|
42
|
+
var AVCOL_SPC_SMPTE240M = 7;
|
|
43
|
+
var AVCOL_SPC_BT2020_NCL = 9;
|
|
44
|
+
var AVCOL_SPC_BT2020_CL = 10;
|
|
45
|
+
/** FFmpeg `AVColorRange` enum values. Stable ABI integers. */
|
|
46
|
+
var AVCOL_RANGE_MPEG = 1;
|
|
47
|
+
var AVCOL_RANGE_JPEG = 2;
|
|
48
|
+
/**
|
|
49
|
+
* Default source matrix when the frame carries no (or an unusable) colorspace
|
|
50
|
+
* tag — bt709, the near-universal signalling for HD H.264/H.265 IP cameras.
|
|
51
|
+
*/
|
|
52
|
+
var DEFAULT_COLOR_MATRIX = "bt709";
|
|
53
|
+
/**
|
|
54
|
+
* Default source range when the frame carries no (or an unspecified) range tag
|
|
55
|
+
* — limited/mpeg, the near-universal signalling for camera video. Guessing
|
|
56
|
+
* full here is the exact mistake that produced the desaturated GOP.
|
|
57
|
+
*/
|
|
58
|
+
var DEFAULT_COLOR_RANGE = "limited";
|
|
59
|
+
/**
|
|
60
|
+
* Output RGB range — always FULL (0-255). Every RGB conversion site converges
|
|
61
|
+
* on this so downstream media/inference sees a single, path-independent RGB
|
|
62
|
+
* contract.
|
|
63
|
+
*/
|
|
64
|
+
var OUTPUT_RGB_RANGE = "full";
|
|
65
|
+
/** Map an `AVColorSpace` enum integer to a `scale`-filter matrix token. */
|
|
66
|
+
function resolveColorMatrix(colorSpace) {
|
|
67
|
+
switch (colorSpace) {
|
|
68
|
+
case AVCOL_SPC_BT709: return "bt709";
|
|
69
|
+
case AVCOL_SPC_BT470BG:
|
|
70
|
+
case AVCOL_SPC_SMPTE170M: return "bt601";
|
|
71
|
+
case AVCOL_SPC_FCC: return "fcc";
|
|
72
|
+
case AVCOL_SPC_SMPTE240M: return "smpte240m";
|
|
73
|
+
case AVCOL_SPC_BT2020_NCL:
|
|
74
|
+
case AVCOL_SPC_BT2020_CL: return "bt2020nc";
|
|
75
|
+
default: return DEFAULT_COLOR_MATRIX;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Map an `AVColorRange` enum integer to a `scale`-filter range token. */
|
|
79
|
+
function resolveColorRange(colorRange) {
|
|
80
|
+
switch (colorRange) {
|
|
81
|
+
case AVCOL_RANGE_JPEG: return "full";
|
|
82
|
+
case AVCOL_RANGE_MPEG: return "limited";
|
|
83
|
+
default: return DEFAULT_COLOR_RANGE;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Resolve a frame's `(colorSpace, colorRange)` tags to the explicit
|
|
88
|
+
* `(matrix, range)` every conversion site pins — applying the stable defaults
|
|
89
|
+
* when a tag is unspecified/unusable.
|
|
90
|
+
*/
|
|
91
|
+
function resolveSourceColor(colorSpace, colorRange) {
|
|
92
|
+
return {
|
|
93
|
+
inColorMatrix: resolveColorMatrix(colorSpace),
|
|
94
|
+
inRange: resolveColorRange(colorRange),
|
|
95
|
+
colorSpaceDefaulted: !(colorSpace === AVCOL_SPC_BT709 || colorSpace === AVCOL_SPC_BT470BG || colorSpace === AVCOL_SPC_SMPTE170M || colorSpace === AVCOL_SPC_FCC || colorSpace === AVCOL_SPC_SMPTE240M || colorSpace === AVCOL_SPC_BT2020_NCL || colorSpace === AVCOL_SPC_BT2020_CL),
|
|
96
|
+
colorRangeDefaulted: !(colorRange === AVCOL_RANGE_JPEG || colorRange === AVCOL_RANGE_MPEG),
|
|
97
|
+
rawColorSpace: colorSpace,
|
|
98
|
+
rawColorRange: colorRange
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The `scale`-filter option fragment that pins the explicit YUV→(full)RGB
|
|
103
|
+
* conversion — appended to a `scale=...` filter in the GPU/CPU filtergraph
|
|
104
|
+
* (`scale=W:H:<fragment>` or `scale=<fragment>` for an identity-size convert).
|
|
105
|
+
* Always converges output on full-range RGB; `out_color_matrix` is intentionally
|
|
106
|
+
* omitted (irrelevant for an RGB/gray packed output).
|
|
107
|
+
*/
|
|
108
|
+
function colorConvertFilterOptions(resolved) {
|
|
109
|
+
return `in_color_matrix=${resolved.inColorMatrix}:in_range=${resolved.inRange}:out_range=${OUTPUT_RGB_RANGE}`;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* A stable cache key for the resolved source color — folded into the GPU
|
|
113
|
+
* filtergraph key so a mid-session colorspace/range change rebuilds the graph
|
|
114
|
+
* (and re-emits the provenance transition marker) instead of reusing a graph
|
|
115
|
+
* pinned to the old color.
|
|
116
|
+
*/
|
|
117
|
+
function sourceColorKey(resolved) {
|
|
118
|
+
return `${resolved.inColorMatrix}:${resolved.inRange}`;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* One-line provenance marker: which decode/scale path produced a frame + the
|
|
122
|
+
* (raw and resolved) source color it was converted with. Emitted on a color
|
|
123
|
+
* TRANSITION (rare, diagnostic-gold: pinpoints an intermittent recurrence) and,
|
|
124
|
+
* behind the debug flag, per frame. Includes a trailing newline for `emitStderr`.
|
|
125
|
+
*/
|
|
126
|
+
function colorProvenanceLine(path, resolved, transition) {
|
|
127
|
+
const spaceTag = resolved.colorSpaceDefaulted ? `${resolved.rawColorSpace}->${resolved.inColorMatrix}(default)` : `${resolved.rawColorSpace}->${resolved.inColorMatrix}`;
|
|
128
|
+
const rangeTag = resolved.colorRangeDefaulted ? `${resolved.rawColorRange}->${resolved.inRange}(default)` : `${resolved.rawColorRange}->${resolved.inRange}`;
|
|
129
|
+
return `session-decode color${transition ? " TRANSITION" : ""} {path:${path}, colorSpace:${spaceTag}, colorRange:${rangeTag}, out:rgb-${OUTPUT_RGB_RANGE}}\n`;
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
5
132
|
//#region src/session-decode/crop-geometry.ts
|
|
6
133
|
/** Clamp to [min,max] then floor to the nearest even number (YUV420P chroma is 2x2 subsampled). */
|
|
7
134
|
function clampEven(value, min, max) {
|
|
@@ -456,12 +583,18 @@ var NATIVE_LEASE_BUDGET_BYTES = (() => {
|
|
|
456
583
|
/**
|
|
457
584
|
* TTL (ms) after which a native-frame lease is treated as a miss and reclaimed.
|
|
458
585
|
* The backstop that bounds in-flight RAM even if the explicit `releaseNativeLease`
|
|
459
|
-
* is dropped
|
|
460
|
-
*
|
|
586
|
+
* is dropped. Must comfortably exceed the FULL late-crop horizon: detection
|
|
587
|
+
* inference + the cross-process inference-result hop to hub post-analysis +
|
|
588
|
+
* tracking + the tRPC crop round-trip back — on a busy camera with many
|
|
589
|
+
* concurrent tracks that is well past the old 500 ms (which the busiest cameras'
|
|
590
|
+
* subject crops outran, missing to the ≤640 fallback). Raised to 1200 ms; still
|
|
591
|
+
* bounded by the per-session {@link NATIVE_LEASE_BUDGET_BYTES} ceiling and the
|
|
592
|
+
* {@link NATIVE_LEASE_ACTIVITY_WINDOW_MS} demand gate, so idle cameras retain
|
|
593
|
+
* nothing. `CAMSTACK_SESSION_NATIVE_LEASE_TTL_MS` (default 1200).
|
|
461
594
|
*/
|
|
462
595
|
var NATIVE_LEASE_TTL_MS = (() => {
|
|
463
596
|
const raw = Number(process.env["CAMSTACK_SESSION_NATIVE_LEASE_TTL_MS"]);
|
|
464
|
-
return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) :
|
|
597
|
+
return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 1200;
|
|
465
598
|
})();
|
|
466
599
|
/**
|
|
467
600
|
* Demand window (ms) for the lease-capture {@link LeaseActivityGate}: eager
|
|
@@ -628,9 +761,10 @@ function backendToHwDeviceConst(backend, consts) {
|
|
|
628
761
|
* It is correct and never crashes; it just forgoes the GPU win for that rare
|
|
629
762
|
* call — exactly the safety/behaviour trade-off the task calls for.
|
|
630
763
|
*/
|
|
631
|
-
function buildGpuFilterDescription(scaleFilter, region, target, fullFrame, pixel) {
|
|
632
|
-
|
|
633
|
-
return
|
|
764
|
+
function buildGpuFilterDescription(scaleFilter, region, target, fullFrame, pixel, color) {
|
|
765
|
+
const convert = `scale=${colorConvertFilterOptions(color)}`;
|
|
766
|
+
if (fullFrame) return `${scaleFilter}=w=${target.width}:h=${target.height},hwdownload,format=nv12,${convert},format=${pixel}`;
|
|
767
|
+
return `hwdownload,format=nv12,crop=${region.width}:${region.height}:${region.left}:${region.top},scale=${target.width}:${target.height}:${colorConvertFilterOptions(color)},format=${pixel}`;
|
|
634
768
|
}
|
|
635
769
|
/**
|
|
636
770
|
* Copy a single-plane packed frame (rgb24 / gray8 out of the filtergraph) into
|
|
@@ -761,6 +895,15 @@ var DecodeWorkerChild = class {
|
|
|
761
895
|
/** Throttled native-crop hit/miss counters, emitted with the throughput line. */
|
|
762
896
|
nativeCropHits = 0;
|
|
763
897
|
nativeCropMisses = 0;
|
|
898
|
+
/**
|
|
899
|
+
* Last `(path, rawColorSpace, rawColorRange)` provenance key emitted — a
|
|
900
|
+
* change re-emits a one-line TRANSITION marker (RC-5 diagnostic: pinpoints an
|
|
901
|
+
* intermittent desaturation recurrence by showing the exact GOP whose color
|
|
902
|
+
* tags drifted). See {@link logColorProvenance}.
|
|
903
|
+
*/
|
|
904
|
+
lastColorProvenanceKey = "";
|
|
905
|
+
/** Per-frame color provenance logging (opt-in; the transition marker is always on). */
|
|
906
|
+
colorDebug = process.env["CAMSTACK_DECODE_COLOR_DEBUG"] === "1";
|
|
764
907
|
/** Scrypted-style throughput counters (plain integers, no per-frame allocation). */
|
|
765
908
|
framesDecoded = 0;
|
|
766
909
|
/** Child-side skips only (HW-guard drops + probe/degrade frees); slot drops add {@link FrameSlot.droppedCount}. */
|
|
@@ -961,6 +1104,28 @@ var DecodeWorkerChild = class {
|
|
|
961
1104
|
return this.nativeRing.get(frameId) ?? this.frames.toBuffer(frameId);
|
|
962
1105
|
}
|
|
963
1106
|
/**
|
|
1107
|
+
* Resolve a decoded frame's `(colorSpace, colorRange)` tags to the EXPLICIT
|
|
1108
|
+
* `(matrix, range)` every RGB conversion site pins — the RC-5 fix. `colorSpace`
|
|
1109
|
+
* / `colorRange` are node-av branded `number` subtypes, so they pass to the
|
|
1110
|
+
* plain-number helper directly.
|
|
1111
|
+
*/
|
|
1112
|
+
resolveFrameColor(frame) {
|
|
1113
|
+
return resolveSourceColor(frame.colorSpace, frame.colorRange);
|
|
1114
|
+
}
|
|
1115
|
+
/**
|
|
1116
|
+
* Emit the color-provenance marker: which decode/scale path produced this
|
|
1117
|
+
* frame + its raw and resolved source color. Always logs on a TRANSITION (the
|
|
1118
|
+
* path or the frame's raw color tags changed vs the last emit — rare, and the
|
|
1119
|
+
* exact signal a desaturation recurrence leaves); logs every frame only under
|
|
1120
|
+
* the `CAMSTACK_DECODE_COLOR_DEBUG` flag.
|
|
1121
|
+
*/
|
|
1122
|
+
logColorProvenance(path, color) {
|
|
1123
|
+
const key = `${path}|${color.rawColorSpace}|${color.rawColorRange}`;
|
|
1124
|
+
const transition = key !== this.lastColorProvenanceKey;
|
|
1125
|
+
if (transition) this.lastColorProvenanceKey = key;
|
|
1126
|
+
if (transition || this.colorDebug) this.emitStderr(colorProvenanceLine(path, color, transition));
|
|
1127
|
+
}
|
|
1128
|
+
/**
|
|
964
1129
|
* Retain a just-superseded DELIVERED frame for later native crops. Transfers
|
|
965
1130
|
* ownership from the {@link FrameSlot} (the caller MUST NOT free when this
|
|
966
1131
|
* returns `true`).
|
|
@@ -1043,6 +1208,7 @@ var DecodeWorkerChild = class {
|
|
|
1043
1208
|
const nav = this.nav;
|
|
1044
1209
|
const C = this.consts;
|
|
1045
1210
|
if (!nav || !C) throw new Error("node-av not initialized");
|
|
1211
|
+
this.logColorProvenance("sw-crop", this.resolveFrameColor(frame));
|
|
1046
1212
|
const { region, target } = resolved;
|
|
1047
1213
|
const planes = frame.data;
|
|
1048
1214
|
const strides = frame.linesize;
|
|
@@ -1081,7 +1247,9 @@ var DecodeWorkerChild = class {
|
|
|
1081
1247
|
*/
|
|
1082
1248
|
nativeHwCropToBuffer(frame, resolved) {
|
|
1083
1249
|
const { region, target } = resolved;
|
|
1084
|
-
const
|
|
1250
|
+
const color = this.resolveFrameColor(frame);
|
|
1251
|
+
this.logColorProvenance("hw-crop", color);
|
|
1252
|
+
const outputs = this.ensureNativeCropHwFilter(region, target, color).processAllSync(frame);
|
|
1085
1253
|
const first = outputs[0];
|
|
1086
1254
|
if (!first) {
|
|
1087
1255
|
for (const out of outputs) out.free();
|
|
@@ -1115,17 +1283,17 @@ var DecodeWorkerChild = class {
|
|
|
1115
1283
|
return scaler;
|
|
1116
1284
|
}
|
|
1117
1285
|
/** Build-once / reuse the dedicated native-crop GPU filtergraph (rgb out). */
|
|
1118
|
-
ensureNativeCropHwFilter(region, target) {
|
|
1286
|
+
ensureNativeCropHwFilter(region, target, color) {
|
|
1119
1287
|
const nav = this.nav;
|
|
1120
1288
|
const scaleFilter = this.gpuScaleFilter;
|
|
1121
1289
|
if (!nav || !scaleFilter || !this.hwContext) throw new Error("decode-worker-child: native GPU crop requested without a HW context");
|
|
1122
|
-
const key = `${region.left},${region.top},${region.width},${region.height}->${target.width}x${target.height}`;
|
|
1290
|
+
const key = `${region.left},${region.top},${region.width},${region.height}->${target.width}x${target.height}:${sourceColorKey(color)}`;
|
|
1123
1291
|
const cached = this.nativeCropHwFilter;
|
|
1124
1292
|
if (cached && key === this.nativeCropHwFilterKey) return cached;
|
|
1125
1293
|
this.nativeCropHwFilter?.close();
|
|
1126
1294
|
this.nativeCropHwFilter = null;
|
|
1127
1295
|
this.nativeCropHwFilterKey = "";
|
|
1128
|
-
const description = buildGpuFilterDescription(scaleFilter, region, target, false, "rgb24");
|
|
1296
|
+
const description = buildGpuFilterDescription(scaleFilter, region, target, false, "rgb24", color);
|
|
1129
1297
|
const filter = nav.FilterAPI.create(description, { hardware: this.hwContext });
|
|
1130
1298
|
this.nativeCropHwFilter = filter;
|
|
1131
1299
|
this.nativeCropHwFilterKey = key;
|
|
@@ -1334,6 +1502,7 @@ var DecodeWorkerChild = class {
|
|
|
1334
1502
|
const format = opts?.format ?? this.sessionFormat;
|
|
1335
1503
|
const dstFmt = format === "gray" ? C.AV_PIX_FMT_GRAY8 : C.AV_PIX_FMT_RGB24;
|
|
1336
1504
|
const channels = format === "gray" ? 1 : 3;
|
|
1505
|
+
this.logColorProvenance(isFullFrameRegion(region, frame.width, frame.height) ? "sw-fullframe" : "sw-crop", this.resolveFrameColor(frame));
|
|
1337
1506
|
const planes = frame.data;
|
|
1338
1507
|
const strides = frame.linesize;
|
|
1339
1508
|
const yPlane = planes?.[0];
|
|
@@ -1405,7 +1574,9 @@ var DecodeWorkerChild = class {
|
|
|
1405
1574
|
const target = resolveResizeTarget(region.width, region.height, opts?.resize);
|
|
1406
1575
|
const format = opts?.format ?? this.sessionFormat;
|
|
1407
1576
|
const channels = format === "gray" ? 1 : 3;
|
|
1408
|
-
const
|
|
1577
|
+
const color = this.resolveFrameColor(frame);
|
|
1578
|
+
this.logColorProvenance(isFullFrameRegion(region, frame.width, frame.height) ? "hw-fullframe" : "hw-crop", color);
|
|
1579
|
+
const outputs = this.ensureHwFilter(region, target, frame.width, frame.height, format, color).processAllSync(frame);
|
|
1409
1580
|
const first = outputs[0];
|
|
1410
1581
|
if (!first) throw new Error("GPU filtergraph produced no frame");
|
|
1411
1582
|
try {
|
|
@@ -1427,19 +1598,19 @@ var DecodeWorkerChild = class {
|
|
|
1427
1598
|
* only when the key changes; detection's ROI + resize target are stable, so
|
|
1428
1599
|
* this is a per-worker singleton in the steady state.
|
|
1429
1600
|
*/
|
|
1430
|
-
ensureHwFilter(region, target, srcW, srcH, format) {
|
|
1601
|
+
ensureHwFilter(region, target, srcW, srcH, format, color) {
|
|
1431
1602
|
const nav = this.nav;
|
|
1432
1603
|
const scaleFilter = this.gpuScaleFilter;
|
|
1433
1604
|
if (!nav || !scaleFilter || !this.hwContext) throw new Error("decode-worker-child: GPU filter requested without a HW context");
|
|
1434
1605
|
const fullFrame = isFullFrameRegion(region, srcW, srcH);
|
|
1435
1606
|
const pixel = pixelFilterName(format);
|
|
1436
|
-
const key = `${region.left},${region.top},${region.width},${region.height}->${target.width}x${target.height}:${pixel}`;
|
|
1607
|
+
const key = `${region.left},${region.top},${region.width},${region.height}->${target.width}x${target.height}:${pixel}:${sourceColorKey(color)}`;
|
|
1437
1608
|
const cached = this.hwFilter;
|
|
1438
1609
|
if (cached && key === this.hwFilterKey) return cached;
|
|
1439
1610
|
this.hwFilter?.close();
|
|
1440
1611
|
this.hwFilter = null;
|
|
1441
1612
|
this.hwFilterKey = "";
|
|
1442
|
-
const description = buildGpuFilterDescription(scaleFilter, region, target, fullFrame, pixel);
|
|
1613
|
+
const description = buildGpuFilterDescription(scaleFilter, region, target, fullFrame, pixel, color);
|
|
1443
1614
|
const filter = nav.FilterAPI.create(description, { hardware: this.hwContext });
|
|
1444
1615
|
this.hwFilter = filter;
|
|
1445
1616
|
this.hwFilterKey = key;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_dist = require("./dist-
|
|
1
|
+
const require_dist = require("./dist-BxxVKcnw.js");
|
|
2
2
|
//#region src/detection-pipeline/registry/model-catalogs.ts
|
|
3
3
|
var HF_REPO = "camstack/camstack-models";
|
|
4
4
|
var HF_SCRYPTED = "scrypted/plugin-models";
|
|
@@ -764,9 +764,45 @@ var PLATE_OCR_MODELS = [{
|
|
|
764
764
|
}
|
|
765
765
|
}];
|
|
766
766
|
var ANIMAL_CLASSIFIER_MODELS = [{
|
|
767
|
+
id: "animal-classifier",
|
|
768
|
+
name: "Animal Classifier (8)",
|
|
769
|
+
description: "EfficientNet-Lite0 animal classifier — bird, cat, cow, deer, dog, horse, sheep, squirrel (Open Images V7, publicly distributable)",
|
|
770
|
+
inputSize: {
|
|
771
|
+
width: 224,
|
|
772
|
+
height: 224
|
|
773
|
+
},
|
|
774
|
+
inputLayout: "nchw",
|
|
775
|
+
inputNormalization: "imagenet",
|
|
776
|
+
license: "Apache-2.0",
|
|
777
|
+
labels: [{
|
|
778
|
+
id: "animal-type",
|
|
779
|
+
name: "Animal Type"
|
|
780
|
+
}],
|
|
781
|
+
preprocessMode: "resize",
|
|
782
|
+
formats: {
|
|
783
|
+
onnx: {
|
|
784
|
+
url: hf("animalClassification/animal-classifier/onnx/animal-classifier.onnx"),
|
|
785
|
+
sizeMB: 13
|
|
786
|
+
},
|
|
787
|
+
coreml: {
|
|
788
|
+
url: hf("animalClassification/animal-classifier/coreml/animal-classifier.mlpackage"),
|
|
789
|
+
sizeMB: 6.5,
|
|
790
|
+
isDirectory: true,
|
|
791
|
+
files: [...MLPACKAGE_FILES],
|
|
792
|
+
runtimes: ["python"]
|
|
793
|
+
},
|
|
794
|
+
openvino: ovFormat(hf("animalClassification/animal-classifier/openvino/animal-classifier.xml"), 6.9)
|
|
795
|
+
},
|
|
796
|
+
extraFiles: [{
|
|
797
|
+
url: hf("animalClassification/animal-classifier/onnx/animal-classifier-labels.json"),
|
|
798
|
+
filename: "animal-classifier-labels.json",
|
|
799
|
+
sizeMB: .01
|
|
800
|
+
}]
|
|
801
|
+
}, {
|
|
767
802
|
id: "animals-10",
|
|
768
803
|
name: "Animal Classifier (10)",
|
|
769
804
|
description: "ViT-based animal type classifier — cat, cow, dog, dolphin, eagle, panda, horse, monkey, sheep, spider",
|
|
805
|
+
legacy: true,
|
|
770
806
|
inputSize: {
|
|
771
807
|
width: 224,
|
|
772
808
|
height: 224
|
|
@@ -793,9 +829,46 @@ var ANIMAL_CLASSIFIER_MODELS = [{
|
|
|
793
829
|
}
|
|
794
830
|
}];
|
|
795
831
|
var BIRD_CLASSIFIER_MODELS = [{
|
|
832
|
+
id: "bird-classifier",
|
|
833
|
+
name: "Bird Classifier (AIY 965)",
|
|
834
|
+
description: "Google AIY Birds V1 (MobileNetV2) — 964 species + background; publicly distributable (Apache-2.0)",
|
|
835
|
+
inputSize: {
|
|
836
|
+
width: 224,
|
|
837
|
+
height: 224
|
|
838
|
+
},
|
|
839
|
+
inputLayout: "nhwc",
|
|
840
|
+
inputNormalization: "none",
|
|
841
|
+
outputProbabilities: true,
|
|
842
|
+
license: "Apache-2.0",
|
|
843
|
+
labels: [{
|
|
844
|
+
id: "species",
|
|
845
|
+
name: "Bird Species"
|
|
846
|
+
}],
|
|
847
|
+
preprocessMode: "resize",
|
|
848
|
+
formats: {
|
|
849
|
+
onnx: {
|
|
850
|
+
url: hf("animalClassification/bird-classifier/onnx/bird-classifier.onnx"),
|
|
851
|
+
sizeMB: 14
|
|
852
|
+
},
|
|
853
|
+
coreml: {
|
|
854
|
+
url: hf("animalClassification/bird-classifier/coreml/bird-classifier.mlpackage"),
|
|
855
|
+
sizeMB: 6.8,
|
|
856
|
+
isDirectory: true,
|
|
857
|
+
files: [...MLPACKAGE_FILES],
|
|
858
|
+
runtimes: ["python"]
|
|
859
|
+
},
|
|
860
|
+
openvino: ovFormat(hf("animalClassification/bird-classifier/openvino/bird-classifier.xml"), 14)
|
|
861
|
+
},
|
|
862
|
+
extraFiles: [{
|
|
863
|
+
url: hf("animalClassification/bird-classifier/onnx/bird-classifier-labels.json"),
|
|
864
|
+
filename: "bird-classifier-labels.json",
|
|
865
|
+
sizeMB: .03
|
|
866
|
+
}]
|
|
867
|
+
}, {
|
|
796
868
|
id: "bird-nabirds-404",
|
|
797
869
|
name: "NABirds (404 species)",
|
|
798
870
|
description: "ResNet50 trained on NABirds — 404 North American species",
|
|
871
|
+
legacy: true,
|
|
799
872
|
inputSize: {
|
|
800
873
|
width: 224,
|
|
801
874
|
height: 224
|
|
@@ -827,9 +900,44 @@ var BIRD_CLASSIFIER_MODELS = [{
|
|
|
827
900
|
}]
|
|
828
901
|
}];
|
|
829
902
|
var VEHICLE_CLASSIFIER_MODELS = [{
|
|
903
|
+
id: "vehicle-type-v1",
|
|
904
|
+
name: "Vehicle Type (8)",
|
|
905
|
+
description: "MobileNetV3-Large vehicle type classifier — Ambulance, Bicycle, Bus, Car, Motorcycle, Taxi, Truck, Van (val 89.4%)",
|
|
906
|
+
inputSize: {
|
|
907
|
+
width: 224,
|
|
908
|
+
height: 224
|
|
909
|
+
},
|
|
910
|
+
inputLayout: "nchw",
|
|
911
|
+
inputNormalization: "imagenet",
|
|
912
|
+
labels: [{
|
|
913
|
+
id: "vehicle-type",
|
|
914
|
+
name: "Vehicle Type"
|
|
915
|
+
}],
|
|
916
|
+
preprocessMode: "resize",
|
|
917
|
+
formats: {
|
|
918
|
+
onnx: {
|
|
919
|
+
url: hf("vehicleClassification/vehicle-type-v1/onnx/vehicle-type-v1.onnx"),
|
|
920
|
+
sizeMB: 17
|
|
921
|
+
},
|
|
922
|
+
coreml: {
|
|
923
|
+
url: hf("vehicleClassification/vehicle-type-v1/coreml/vehicle-type-v1.mlpackage"),
|
|
924
|
+
sizeMB: 8.1,
|
|
925
|
+
isDirectory: true,
|
|
926
|
+
files: [...MLPACKAGE_FILES],
|
|
927
|
+
runtimes: ["python"]
|
|
928
|
+
},
|
|
929
|
+
openvino: ovFormat(hf("vehicleClassification/vehicle-type-v1/openvino/vehicle-type-v1.xml"), 8.7)
|
|
930
|
+
},
|
|
931
|
+
extraFiles: [{
|
|
932
|
+
url: hf("vehicleClassification/vehicle-type-v1/onnx/vehicle-type-v1-labels.json"),
|
|
933
|
+
filename: "vehicle-type-v1-labels.json",
|
|
934
|
+
sizeMB: .01
|
|
935
|
+
}]
|
|
936
|
+
}, {
|
|
830
937
|
id: "vehicle-type-efficientnet",
|
|
831
938
|
name: "Vehicle Type (EfficientNet)",
|
|
832
939
|
description: "EfficientNet-B4 vehicle make/model/year classifier — 8,949 classes from VMMRdb",
|
|
940
|
+
legacy: true,
|
|
833
941
|
inputSize: {
|
|
834
942
|
width: 380,
|
|
835
943
|
height: 380
|
|
@@ -1105,6 +1213,16 @@ var ANIMAL_10_LABELS = [
|
|
|
1105
1213
|
"sheep",
|
|
1106
1214
|
"spider"
|
|
1107
1215
|
];
|
|
1216
|
+
var ANIMAL_CLASSIFIER_LABELS = [
|
|
1217
|
+
"bird",
|
|
1218
|
+
"cat",
|
|
1219
|
+
"cow",
|
|
1220
|
+
"deer",
|
|
1221
|
+
"dog",
|
|
1222
|
+
"horse",
|
|
1223
|
+
"sheep",
|
|
1224
|
+
"squirrel"
|
|
1225
|
+
];
|
|
1108
1226
|
var VGG_G2_CHARSET = [
|
|
1109
1227
|
"",
|
|
1110
1228
|
..."0123456789".split(""),
|
|
@@ -1303,9 +1421,9 @@ var AnimalClassifierStep = class {
|
|
|
1303
1421
|
inputClasses: ["animal"],
|
|
1304
1422
|
outputClasses: ["animal-type"],
|
|
1305
1423
|
models: [...ANIMAL_CLASSIFIER_MODELS],
|
|
1306
|
-
defaultModelId: "
|
|
1424
|
+
defaultModelId: "animal-classifier",
|
|
1307
1425
|
enabledByDefault: false,
|
|
1308
|
-
defaultConfidence: .
|
|
1426
|
+
defaultConfidence: .5,
|
|
1309
1427
|
labels: [...ANIMAL_10_LABELS],
|
|
1310
1428
|
cadence: {
|
|
1311
1429
|
trigger: "once",
|
|
@@ -1319,7 +1437,7 @@ var AnimalClassifierStep = class {
|
|
|
1319
1437
|
key: "enabledClasses",
|
|
1320
1438
|
label: "Species",
|
|
1321
1439
|
description: "Which animal species to keep (empty = all). The classifier still scores every label but only the selected classes are returned on the detection.",
|
|
1322
|
-
options:
|
|
1440
|
+
options: ANIMAL_CLASSIFIER_LABELS.map((l) => ({
|
|
1323
1441
|
value: l,
|
|
1324
1442
|
label: l
|
|
1325
1443
|
})),
|
|
@@ -1465,9 +1583,10 @@ var STEP_BIRD_CLASSIFIER = new ClassifierWithMinConfidence({
|
|
|
1465
1583
|
inputClasses: ["animal"],
|
|
1466
1584
|
outputClasses: ["species"],
|
|
1467
1585
|
models: [...BIRD_CLASSIFIER_MODELS],
|
|
1468
|
-
defaultModelId: "bird-
|
|
1586
|
+
defaultModelId: "bird-classifier",
|
|
1469
1587
|
enabledByDefault: false,
|
|
1470
|
-
defaultConfidence: .
|
|
1588
|
+
defaultConfidence: .6,
|
|
1589
|
+
rejectClasses: ["background"],
|
|
1471
1590
|
cadence: {
|
|
1472
1591
|
trigger: "once",
|
|
1473
1592
|
maxPerTrack: 3,
|
|
@@ -1483,9 +1602,9 @@ var STEP_VEHICLE_CLASSIFIER = new ClassifierWithMinConfidence({
|
|
|
1483
1602
|
inputClasses: ["vehicle"],
|
|
1484
1603
|
outputClasses: ["vehicle-type"],
|
|
1485
1604
|
models: [...VEHICLE_CLASSIFIER_MODELS],
|
|
1486
|
-
defaultModelId: "vehicle-type-
|
|
1605
|
+
defaultModelId: "vehicle-type-v1",
|
|
1487
1606
|
enabledByDefault: false,
|
|
1488
|
-
defaultConfidence: .
|
|
1607
|
+
defaultConfidence: .5,
|
|
1489
1608
|
cadence: {
|
|
1490
1609
|
trigger: "once",
|
|
1491
1610
|
maxPerTrack: 3,
|