@camstack/addon-vision 0.1.0

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.
Files changed (109) hide show
  1. package/dist/addons/animal-classifier/index.d.mts +25 -0
  2. package/dist/addons/animal-classifier/index.d.ts +25 -0
  3. package/dist/addons/animal-classifier/index.js +652 -0
  4. package/dist/addons/animal-classifier/index.js.map +1 -0
  5. package/dist/addons/animal-classifier/index.mjs +10 -0
  6. package/dist/addons/animal-classifier/index.mjs.map +1 -0
  7. package/dist/addons/audio-classification/index.d.mts +31 -0
  8. package/dist/addons/audio-classification/index.d.ts +31 -0
  9. package/dist/addons/audio-classification/index.js +572 -0
  10. package/dist/addons/audio-classification/index.js.map +1 -0
  11. package/dist/addons/audio-classification/index.mjs +8 -0
  12. package/dist/addons/audio-classification/index.mjs.map +1 -0
  13. package/dist/addons/bird-global-classifier/index.d.mts +26 -0
  14. package/dist/addons/bird-global-classifier/index.d.ts +26 -0
  15. package/dist/addons/bird-global-classifier/index.js +658 -0
  16. package/dist/addons/bird-global-classifier/index.js.map +1 -0
  17. package/dist/addons/bird-global-classifier/index.mjs +10 -0
  18. package/dist/addons/bird-global-classifier/index.mjs.map +1 -0
  19. package/dist/addons/bird-nabirds-classifier/index.d.mts +28 -0
  20. package/dist/addons/bird-nabirds-classifier/index.d.ts +28 -0
  21. package/dist/addons/bird-nabirds-classifier/index.js +700 -0
  22. package/dist/addons/bird-nabirds-classifier/index.js.map +1 -0
  23. package/dist/addons/bird-nabirds-classifier/index.mjs +10 -0
  24. package/dist/addons/bird-nabirds-classifier/index.mjs.map +1 -0
  25. package/dist/addons/camera-native-detection/index.d.mts +32 -0
  26. package/dist/addons/camera-native-detection/index.d.ts +32 -0
  27. package/dist/addons/camera-native-detection/index.js +99 -0
  28. package/dist/addons/camera-native-detection/index.js.map +1 -0
  29. package/dist/addons/camera-native-detection/index.mjs +7 -0
  30. package/dist/addons/camera-native-detection/index.mjs.map +1 -0
  31. package/dist/addons/face-detection/index.d.mts +24 -0
  32. package/dist/addons/face-detection/index.d.ts +24 -0
  33. package/dist/addons/face-detection/index.js +720 -0
  34. package/dist/addons/face-detection/index.js.map +1 -0
  35. package/dist/addons/face-detection/index.mjs +10 -0
  36. package/dist/addons/face-detection/index.mjs.map +1 -0
  37. package/dist/addons/face-recognition/index.d.mts +24 -0
  38. package/dist/addons/face-recognition/index.d.ts +24 -0
  39. package/dist/addons/face-recognition/index.js +603 -0
  40. package/dist/addons/face-recognition/index.js.map +1 -0
  41. package/dist/addons/face-recognition/index.mjs +9 -0
  42. package/dist/addons/face-recognition/index.mjs.map +1 -0
  43. package/dist/addons/motion-detection/index.d.mts +26 -0
  44. package/dist/addons/motion-detection/index.d.ts +26 -0
  45. package/dist/addons/motion-detection/index.js +273 -0
  46. package/dist/addons/motion-detection/index.js.map +1 -0
  47. package/dist/addons/motion-detection/index.mjs +8 -0
  48. package/dist/addons/motion-detection/index.mjs.map +1 -0
  49. package/dist/addons/object-detection/index.d.mts +26 -0
  50. package/dist/addons/object-detection/index.d.ts +26 -0
  51. package/dist/addons/object-detection/index.js +1214 -0
  52. package/dist/addons/object-detection/index.js.map +1 -0
  53. package/dist/addons/object-detection/index.mjs +10 -0
  54. package/dist/addons/object-detection/index.mjs.map +1 -0
  55. package/dist/addons/plate-detection/index.d.mts +25 -0
  56. package/dist/addons/plate-detection/index.d.ts +25 -0
  57. package/dist/addons/plate-detection/index.js +646 -0
  58. package/dist/addons/plate-detection/index.js.map +1 -0
  59. package/dist/addons/plate-detection/index.mjs +10 -0
  60. package/dist/addons/plate-detection/index.mjs.map +1 -0
  61. package/dist/addons/plate-recognition/index.d.mts +25 -0
  62. package/dist/addons/plate-recognition/index.d.ts +25 -0
  63. package/dist/addons/plate-recognition/index.js +648 -0
  64. package/dist/addons/plate-recognition/index.js.map +1 -0
  65. package/dist/addons/plate-recognition/index.mjs +9 -0
  66. package/dist/addons/plate-recognition/index.mjs.map +1 -0
  67. package/dist/chunk-3MQFUDRU.mjs +260 -0
  68. package/dist/chunk-3MQFUDRU.mjs.map +1 -0
  69. package/dist/chunk-5AIQSN32.mjs +227 -0
  70. package/dist/chunk-5AIQSN32.mjs.map +1 -0
  71. package/dist/chunk-5JJZGKL7.mjs +186 -0
  72. package/dist/chunk-5JJZGKL7.mjs.map +1 -0
  73. package/dist/chunk-6OR5TE7A.mjs +101 -0
  74. package/dist/chunk-6OR5TE7A.mjs.map +1 -0
  75. package/dist/chunk-AYBFB7ID.mjs +763 -0
  76. package/dist/chunk-AYBFB7ID.mjs.map +1 -0
  77. package/dist/chunk-B3R66MPF.mjs +219 -0
  78. package/dist/chunk-B3R66MPF.mjs.map +1 -0
  79. package/dist/chunk-DTOAB2CE.mjs +79 -0
  80. package/dist/chunk-DTOAB2CE.mjs.map +1 -0
  81. package/dist/chunk-ISOIDU4U.mjs +54 -0
  82. package/dist/chunk-ISOIDU4U.mjs.map +1 -0
  83. package/dist/chunk-J4WRYHHY.mjs +212 -0
  84. package/dist/chunk-J4WRYHHY.mjs.map +1 -0
  85. package/dist/chunk-KUO2BVFY.mjs +90 -0
  86. package/dist/chunk-KUO2BVFY.mjs.map +1 -0
  87. package/dist/chunk-LPI42WL6.mjs +324 -0
  88. package/dist/chunk-LPI42WL6.mjs.map +1 -0
  89. package/dist/chunk-MEVASN3P.mjs +305 -0
  90. package/dist/chunk-MEVASN3P.mjs.map +1 -0
  91. package/dist/chunk-PDSHDDPV.mjs +255 -0
  92. package/dist/chunk-PDSHDDPV.mjs.map +1 -0
  93. package/dist/chunk-Q3SQOYG6.mjs +218 -0
  94. package/dist/chunk-Q3SQOYG6.mjs.map +1 -0
  95. package/dist/chunk-QIMDG34B.mjs +229 -0
  96. package/dist/chunk-QIMDG34B.mjs.map +1 -0
  97. package/dist/index.d.mts +171 -0
  98. package/dist/index.d.ts +171 -0
  99. package/dist/index.js +3463 -0
  100. package/dist/index.js.map +1 -0
  101. package/dist/index.mjs +111 -0
  102. package/dist/index.mjs.map +1 -0
  103. package/package.json +49 -0
  104. package/python/__pycache__/coreml_inference.cpython-313.pyc +0 -0
  105. package/python/__pycache__/openvino_inference.cpython-313.pyc +0 -0
  106. package/python/__pycache__/pytorch_inference.cpython-313.pyc +0 -0
  107. package/python/coreml_inference.py +319 -0
  108. package/python/openvino_inference.py +247 -0
  109. package/python/pytorch_inference.py +255 -0
@@ -0,0 +1,572 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/addons/audio-classification/index.ts
31
+ var audio_classification_exports = {};
32
+ __export(audio_classification_exports, {
33
+ default: () => AudioClassificationAddon
34
+ });
35
+ module.exports = __toCommonJS(audio_classification_exports);
36
+
37
+ // src/catalogs/audio-classification-models.ts
38
+ var import_types = require("@camstack/types");
39
+ var HF_REPO = "camstack/camstack-models";
40
+ var AUDIO_LABELS = [
41
+ { id: "audio", name: "Audio Event" }
42
+ ];
43
+ var AUDIO_CLASSIFICATION_MODELS = [
44
+ {
45
+ id: "yamnet",
46
+ name: "YAMNet",
47
+ description: "YAMNet \u2014 audio event classification from raw waveform",
48
+ inputSize: { width: 1, height: 16e3 },
49
+ labels: AUDIO_LABELS,
50
+ formats: {
51
+ onnx: {
52
+ url: (0, import_types.hfModelUrl)(HF_REPO, "audioClassification/yamnet/onnx/camstack-yamnet.onnx"),
53
+ sizeMB: 15
54
+ },
55
+ openvino: {
56
+ url: (0, import_types.hfModelUrl)(HF_REPO, "audioClassification/yamnet/openvino/camstack-yamnet.xml"),
57
+ sizeMB: 8
58
+ }
59
+ }
60
+ }
61
+ ];
62
+
63
+ // src/shared/postprocess/yamnet.ts
64
+ function yamnetPostprocess(output, numFrames, numClasses, classNames, minScore) {
65
+ const avgScores = new Float32Array(numClasses);
66
+ for (let f = 0; f < numFrames; f++) {
67
+ for (let c = 0; c < numClasses; c++) {
68
+ const prev = avgScores[c] ?? 0;
69
+ avgScores[c] = prev + (output[f * numClasses + c] ?? 0);
70
+ }
71
+ }
72
+ if (numFrames > 0) {
73
+ for (let c = 0; c < numClasses; c++) {
74
+ const val = avgScores[c] ?? 0;
75
+ avgScores[c] = val / numFrames;
76
+ }
77
+ }
78
+ const results = [];
79
+ for (let c = 0; c < numClasses; c++) {
80
+ const score = avgScores[c];
81
+ if (score >= minScore) {
82
+ results.push({
83
+ className: classNames[c] ?? String(c),
84
+ score
85
+ });
86
+ }
87
+ }
88
+ return results.sort((a, b) => b.score - a.score);
89
+ }
90
+
91
+ // src/shared/engine-resolver.ts
92
+ var fs = __toESM(require("fs"));
93
+ var path2 = __toESM(require("path"));
94
+
95
+ // src/shared/node-engine.ts
96
+ var path = __toESM(require("path"));
97
+ var BACKEND_TO_PROVIDER = {
98
+ cpu: "cpu",
99
+ coreml: "coreml",
100
+ cuda: "cuda",
101
+ tensorrt: "tensorrt",
102
+ dml: "dml"
103
+ };
104
+ var BACKEND_TO_DEVICE = {
105
+ cpu: "cpu",
106
+ coreml: "gpu-mps",
107
+ cuda: "gpu-cuda",
108
+ tensorrt: "tensorrt"
109
+ };
110
+ var NodeInferenceEngine = class {
111
+ constructor(modelPath, backend) {
112
+ this.modelPath = modelPath;
113
+ this.backend = backend;
114
+ this.device = BACKEND_TO_DEVICE[backend] ?? "cpu";
115
+ }
116
+ runtime = "onnx";
117
+ device;
118
+ session = null;
119
+ async initialize() {
120
+ const ort = await import("onnxruntime-node");
121
+ const provider = BACKEND_TO_PROVIDER[this.backend] ?? "cpu";
122
+ const absModelPath = path.isAbsolute(this.modelPath) ? this.modelPath : path.resolve(process.cwd(), this.modelPath);
123
+ const sessionOptions = {
124
+ executionProviders: [provider]
125
+ };
126
+ this.session = await ort.InferenceSession.create(absModelPath, sessionOptions);
127
+ }
128
+ async run(input, inputShape) {
129
+ if (!this.session) {
130
+ throw new Error("NodeInferenceEngine: not initialized \u2014 call initialize() first");
131
+ }
132
+ const ort = await import("onnxruntime-node");
133
+ const sess = this.session;
134
+ const inputName = sess.inputNames[0];
135
+ const tensor = new ort.Tensor("float32", input, [...inputShape]);
136
+ const feeds = { [inputName]: tensor };
137
+ const results = await sess.run(feeds);
138
+ const outputName = sess.outputNames[0];
139
+ const outputTensor = results[outputName];
140
+ return outputTensor.data;
141
+ }
142
+ async runMultiOutput(input, inputShape) {
143
+ if (!this.session) {
144
+ throw new Error("NodeInferenceEngine: not initialized \u2014 call initialize() first");
145
+ }
146
+ const ort = await import("onnxruntime-node");
147
+ const sess = this.session;
148
+ const inputName = sess.inputNames[0];
149
+ const tensor = new ort.Tensor("float32", input, [...inputShape]);
150
+ const feeds = { [inputName]: tensor };
151
+ const results = await sess.run(feeds);
152
+ const out = {};
153
+ for (const name of sess.outputNames) {
154
+ out[name] = results[name].data;
155
+ }
156
+ return out;
157
+ }
158
+ async dispose() {
159
+ this.session = null;
160
+ }
161
+ };
162
+
163
+ // src/shared/python-engine.ts
164
+ var import_node_child_process = require("child_process");
165
+ var PythonInferenceEngine = class {
166
+ constructor(pythonPath, scriptPath, runtime, modelPath, extraArgs = []) {
167
+ this.pythonPath = pythonPath;
168
+ this.scriptPath = scriptPath;
169
+ this.modelPath = modelPath;
170
+ this.extraArgs = extraArgs;
171
+ this.runtime = runtime;
172
+ const runtimeDeviceMap = {
173
+ onnx: "cpu",
174
+ coreml: "gpu-mps",
175
+ pytorch: "cpu",
176
+ openvino: "cpu",
177
+ tflite: "cpu"
178
+ };
179
+ this.device = runtimeDeviceMap[runtime];
180
+ }
181
+ runtime;
182
+ device;
183
+ process = null;
184
+ receiveBuffer = Buffer.alloc(0);
185
+ pendingResolve = null;
186
+ pendingReject = null;
187
+ async initialize() {
188
+ const args = [this.scriptPath, this.modelPath, ...this.extraArgs];
189
+ this.process = (0, import_node_child_process.spawn)(this.pythonPath, args, {
190
+ stdio: ["pipe", "pipe", "pipe"]
191
+ });
192
+ if (!this.process.stdout || !this.process.stdin) {
193
+ throw new Error("PythonInferenceEngine: failed to create process pipes");
194
+ }
195
+ this.process.stderr?.on("data", (chunk) => {
196
+ process.stderr.write(`[python-engine] ${chunk.toString()}`);
197
+ });
198
+ this.process.on("error", (err) => {
199
+ this.pendingReject?.(err);
200
+ this.pendingReject = null;
201
+ this.pendingResolve = null;
202
+ });
203
+ this.process.on("exit", (code) => {
204
+ if (code !== 0) {
205
+ const err = new Error(`PythonInferenceEngine: process exited with code ${code}`);
206
+ this.pendingReject?.(err);
207
+ this.pendingReject = null;
208
+ this.pendingResolve = null;
209
+ }
210
+ });
211
+ this.process.stdout.on("data", (chunk) => {
212
+ this.receiveBuffer = Buffer.concat([this.receiveBuffer, chunk]);
213
+ this._tryReceive();
214
+ });
215
+ await new Promise((resolve2, reject) => {
216
+ const timeout = setTimeout(() => resolve2(), 2e3);
217
+ this.process?.on("error", (err) => {
218
+ clearTimeout(timeout);
219
+ reject(err);
220
+ });
221
+ this.process?.on("exit", (code) => {
222
+ clearTimeout(timeout);
223
+ if (code !== 0) {
224
+ reject(new Error(`PythonInferenceEngine: process exited early with code ${code}`));
225
+ }
226
+ });
227
+ });
228
+ }
229
+ _tryReceive() {
230
+ if (this.receiveBuffer.length < 4) return;
231
+ const length = this.receiveBuffer.readUInt32LE(0);
232
+ if (this.receiveBuffer.length < 4 + length) return;
233
+ const jsonBytes = this.receiveBuffer.subarray(4, 4 + length);
234
+ this.receiveBuffer = this.receiveBuffer.subarray(4 + length);
235
+ const resolve2 = this.pendingResolve;
236
+ const reject = this.pendingReject;
237
+ this.pendingResolve = null;
238
+ this.pendingReject = null;
239
+ if (!resolve2) return;
240
+ try {
241
+ const parsed = JSON.parse(jsonBytes.toString("utf8"));
242
+ resolve2(parsed);
243
+ } catch (err) {
244
+ reject?.(err instanceof Error ? err : new Error(String(err)));
245
+ }
246
+ }
247
+ /** Send JPEG buffer, receive JSON detection results */
248
+ async runJpeg(jpeg) {
249
+ if (!this.process?.stdin) {
250
+ throw new Error("PythonInferenceEngine: process not initialized");
251
+ }
252
+ return new Promise((resolve2, reject) => {
253
+ this.pendingResolve = resolve2;
254
+ this.pendingReject = reject;
255
+ const lengthBuf = Buffer.allocUnsafe(4);
256
+ lengthBuf.writeUInt32LE(jpeg.length, 0);
257
+ this.process.stdin.write(Buffer.concat([lengthBuf, jpeg]));
258
+ });
259
+ }
260
+ /** IInferenceEngine.run — wraps runJpeg for compatibility */
261
+ async run(_input, _inputShape) {
262
+ throw new Error(
263
+ "PythonInferenceEngine: use runJpeg() directly \u2014 this engine operates on JPEG input"
264
+ );
265
+ }
266
+ /** IInferenceEngine.runMultiOutput — not supported by Python engine (operates on JPEG input) */
267
+ async runMultiOutput(_input, _inputShape) {
268
+ throw new Error(
269
+ "PythonInferenceEngine: runMultiOutput() is not supported \u2014 this engine operates on JPEG input"
270
+ );
271
+ }
272
+ async dispose() {
273
+ if (this.process) {
274
+ this.process.stdin?.end();
275
+ this.process.kill("SIGTERM");
276
+ this.process = null;
277
+ }
278
+ }
279
+ };
280
+
281
+ // src/shared/engine-resolver.ts
282
+ var AUTO_BACKEND_PRIORITY = ["coreml", "cuda", "tensorrt", "cpu"];
283
+ var BACKEND_TO_FORMAT = {
284
+ cpu: "onnx",
285
+ coreml: "coreml",
286
+ cuda: "onnx",
287
+ tensorrt: "onnx"
288
+ };
289
+ var RUNTIME_TO_FORMAT = {
290
+ onnx: "onnx",
291
+ coreml: "coreml",
292
+ openvino: "openvino",
293
+ tflite: "tflite",
294
+ pytorch: "pt"
295
+ };
296
+ function modelFilePath(modelsDir, modelEntry, format) {
297
+ const formatEntry = modelEntry.formats[format];
298
+ if (!formatEntry) {
299
+ throw new Error(`Model ${modelEntry.id} has no ${format} format`);
300
+ }
301
+ const urlParts = formatEntry.url.split("/");
302
+ const filename = urlParts[urlParts.length - 1] ?? `${modelEntry.id}.${format}`;
303
+ return path2.join(modelsDir, filename);
304
+ }
305
+ function modelExists(filePath) {
306
+ try {
307
+ return fs.existsSync(filePath);
308
+ } catch {
309
+ return false;
310
+ }
311
+ }
312
+ async function resolveEngine(options) {
313
+ const { runtime, backend, modelEntry, modelsDir, downloadModel } = options;
314
+ let selectedFormat;
315
+ let selectedBackend;
316
+ if (runtime === "auto") {
317
+ const available = await probeOnnxBackends();
318
+ let chosen = null;
319
+ for (const b of AUTO_BACKEND_PRIORITY) {
320
+ if (!available.includes(b)) continue;
321
+ const fmt = BACKEND_TO_FORMAT[b];
322
+ if (!fmt) continue;
323
+ if (!modelEntry.formats[fmt]) continue;
324
+ chosen = { backend: b, format: fmt };
325
+ break;
326
+ }
327
+ if (!chosen) {
328
+ throw new Error(
329
+ `resolveEngine: no compatible backend found for model ${modelEntry.id}. Available backends: ${available.join(", ")}`
330
+ );
331
+ }
332
+ selectedFormat = chosen.format;
333
+ selectedBackend = chosen.backend;
334
+ } else {
335
+ const fmt = RUNTIME_TO_FORMAT[runtime];
336
+ if (!fmt) {
337
+ throw new Error(`resolveEngine: unsupported runtime "${runtime}"`);
338
+ }
339
+ if (!modelEntry.formats[fmt]) {
340
+ throw new Error(
341
+ `resolveEngine: model ${modelEntry.id} has no ${fmt} format for runtime ${runtime}`
342
+ );
343
+ }
344
+ selectedFormat = fmt;
345
+ selectedBackend = runtime === "onnx" ? backend || "cpu" : runtime;
346
+ }
347
+ let modelPath = modelFilePath(modelsDir, modelEntry, selectedFormat);
348
+ if (!modelExists(modelPath)) {
349
+ if (downloadModel) {
350
+ const formatEntry = modelEntry.formats[selectedFormat];
351
+ modelPath = await downloadModel(formatEntry.url, modelsDir);
352
+ } else {
353
+ throw new Error(
354
+ `resolveEngine: model file not found at ${modelPath} and no downloadModel function provided`
355
+ );
356
+ }
357
+ }
358
+ if (selectedFormat === "onnx" || selectedFormat === "coreml") {
359
+ const engine = new NodeInferenceEngine(modelPath, selectedBackend);
360
+ await engine.initialize();
361
+ return { engine, format: selectedFormat, modelPath };
362
+ }
363
+ const { pythonPath } = options;
364
+ const PYTHON_SCRIPT_MAP = {
365
+ coreml: "coreml_inference.py",
366
+ pytorch: "pytorch_inference.py",
367
+ openvino: "openvino_inference.py"
368
+ };
369
+ const effectiveRuntime = runtime === "auto" ? selectedBackend : runtime;
370
+ const scriptName = PYTHON_SCRIPT_MAP[effectiveRuntime];
371
+ if (scriptName && pythonPath) {
372
+ const scriptPath = path2.join(__dirname, "../../python", scriptName);
373
+ const inputSize = Math.max(modelEntry.inputSize.width, modelEntry.inputSize.height);
374
+ const engine = new PythonInferenceEngine(pythonPath, scriptPath, effectiveRuntime, modelPath, [
375
+ `--input-size=${inputSize}`,
376
+ `--confidence=0.25`
377
+ ]);
378
+ await engine.initialize();
379
+ return { engine, format: selectedFormat, modelPath };
380
+ }
381
+ const fallbackPath = modelFilePath(modelsDir, modelEntry, "onnx");
382
+ if (modelEntry.formats["onnx"] && modelExists(fallbackPath)) {
383
+ const engine = new NodeInferenceEngine(fallbackPath, "cpu");
384
+ await engine.initialize();
385
+ return { engine, format: "onnx", modelPath: fallbackPath };
386
+ }
387
+ throw new Error(
388
+ `resolveEngine: format ${selectedFormat} is not yet supported by NodeInferenceEngine, no Python runtime is available, and no ONNX fallback exists`
389
+ );
390
+ }
391
+ async function probeOnnxBackends() {
392
+ const available = ["cpu"];
393
+ try {
394
+ const ort = await import("onnxruntime-node");
395
+ const providers = ort.env?.webgl?.disabled !== void 0 ? ort.InferenceSession?.getAvailableProviders?.() ?? [] : [];
396
+ for (const p of providers) {
397
+ const normalized = p.toLowerCase().replace("executionprovider", "");
398
+ if (normalized === "coreml") available.push("coreml");
399
+ else if (normalized === "cuda") available.push("cuda");
400
+ else if (normalized === "tensorrt") available.push("tensorrt");
401
+ }
402
+ } catch {
403
+ }
404
+ if (process.platform === "darwin" && !available.includes("coreml")) {
405
+ available.push("coreml");
406
+ }
407
+ return [...new Set(available)];
408
+ }
409
+
410
+ // src/addons/audio-classification/index.ts
411
+ var YAMNET_NUM_CLASSES = 521;
412
+ var AUDIO_EVENT_LABEL = { id: "audio-event", name: "Audio Event" };
413
+ var AUDIO_LABELS2 = [AUDIO_EVENT_LABEL];
414
+ var AUDIO_CLASS_MAP = { mapping: {}, preserveOriginal: true };
415
+ var AudioClassificationAddon = class {
416
+ id = "audio-classification";
417
+ slot = "classifier";
418
+ inputClasses = null;
419
+ outputClasses = ["audio-event:*"];
420
+ slotPriority = 0;
421
+ manifest = {
422
+ id: "audio-classification",
423
+ name: "Audio Classification",
424
+ version: "0.1.0",
425
+ description: "YAMNet-based audio event classification from audio waveform",
426
+ packageName: "@camstack/addon-vision",
427
+ slot: "classifier",
428
+ inputClasses: void 0,
429
+ outputClasses: ["audio-event:*"],
430
+ supportsCustomModels: false,
431
+ mayRequirePython: false,
432
+ defaultConfig: {
433
+ modelId: "yamnet",
434
+ runtime: "auto",
435
+ backend: "cpu",
436
+ minScore: 0.3
437
+ }
438
+ };
439
+ engine;
440
+ modelEntry;
441
+ minScore = 0.3;
442
+ async initialize(ctx) {
443
+ const cfg = ctx.addonConfig;
444
+ const modelId = cfg["modelId"] ?? "yamnet";
445
+ const runtime = cfg["runtime"] ?? "auto";
446
+ const backend = cfg["backend"] ?? "cpu";
447
+ this.minScore = cfg["minScore"] ?? 0.3;
448
+ const entry = AUDIO_CLASSIFICATION_MODELS.find((m) => m.id === modelId);
449
+ if (!entry) {
450
+ throw new Error(`AudioClassificationAddon: unknown modelId "${modelId}"`);
451
+ }
452
+ this.modelEntry = entry;
453
+ const resolved = await resolveEngine({
454
+ runtime,
455
+ backend,
456
+ modelEntry: entry,
457
+ modelsDir: ctx.locationPaths.models
458
+ });
459
+ this.engine = resolved.engine;
460
+ }
461
+ /**
462
+ * classify() receives a CropInput but internally treats input.frame.data as raw audio context.
463
+ * For audio, the actual audio chunk data should be stored in frame.data as a Float32Array
464
+ * serialized into a Buffer (little-endian float32 samples at 16 kHz).
465
+ *
466
+ * The CropInput.roi is not used for audio — it is ignored.
467
+ */
468
+ async classify(input) {
469
+ const start = Date.now();
470
+ const buf = input.frame.data;
471
+ const numSamples = Math.floor(buf.length / 4);
472
+ const audioData = new Float32Array(numSamples);
473
+ for (let i = 0; i < numSamples; i++) {
474
+ audioData[i] = buf.readFloatLE(i * 4);
475
+ }
476
+ const output = await this.engine.run(audioData, [numSamples]);
477
+ const numFrames = output.length / YAMNET_NUM_CLASSES;
478
+ const classNames = this.modelEntry.labels.map((l) => l.id);
479
+ while (classNames.length < YAMNET_NUM_CLASSES) {
480
+ classNames.push(`class_${classNames.length}`);
481
+ }
482
+ const results = yamnetPostprocess(
483
+ output,
484
+ Math.round(numFrames),
485
+ YAMNET_NUM_CLASSES,
486
+ classNames,
487
+ this.minScore
488
+ );
489
+ const classifications = results.map((r) => ({
490
+ class: `audio-event:${r.className}`,
491
+ score: r.score
492
+ }));
493
+ return {
494
+ classifications,
495
+ inferenceMs: Date.now() - start,
496
+ modelId: this.modelEntry.id
497
+ };
498
+ }
499
+ async shutdown() {
500
+ await this.engine?.dispose();
501
+ }
502
+ getConfigSchema() {
503
+ return {
504
+ sections: [
505
+ {
506
+ id: "runtime",
507
+ title: "Runtime",
508
+ columns: 2,
509
+ fields: [
510
+ {
511
+ key: "runtime",
512
+ label: "Runtime",
513
+ type: "select",
514
+ options: [
515
+ { value: "auto", label: "Auto (recommended)" },
516
+ { value: "onnx", label: "ONNX Runtime" },
517
+ { value: "openvino", label: "OpenVINO (Intel)" }
518
+ ]
519
+ },
520
+ {
521
+ key: "backend",
522
+ label: "Backend",
523
+ type: "select",
524
+ dependsOn: { runtime: "onnx" },
525
+ options: [
526
+ { value: "cpu", label: "CPU" },
527
+ { value: "cuda", label: "CUDA (NVIDIA)" }
528
+ ]
529
+ }
530
+ ]
531
+ },
532
+ {
533
+ id: "thresholds",
534
+ title: "Classification Settings",
535
+ columns: 1,
536
+ fields: [
537
+ {
538
+ key: "minScore",
539
+ label: "Minimum Score",
540
+ type: "slider",
541
+ min: 0.05,
542
+ max: 1,
543
+ step: 0.05,
544
+ default: 0.3
545
+ }
546
+ ]
547
+ }
548
+ ]
549
+ };
550
+ }
551
+ getClassMap() {
552
+ return AUDIO_CLASS_MAP;
553
+ }
554
+ getModelCatalog() {
555
+ return [...AUDIO_CLASSIFICATION_MODELS];
556
+ }
557
+ getAvailableModels() {
558
+ return [];
559
+ }
560
+ getActiveLabels() {
561
+ return AUDIO_LABELS2;
562
+ }
563
+ async probe() {
564
+ return {
565
+ available: true,
566
+ runtime: this.engine?.runtime ?? "onnx",
567
+ device: this.engine?.device ?? "cpu",
568
+ capabilities: ["fp32"]
569
+ };
570
+ }
571
+ };
572
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/addons/audio-classification/index.ts","../../../src/catalogs/audio-classification-models.ts","../../../src/shared/postprocess/yamnet.ts","../../../src/shared/engine-resolver.ts","../../../src/shared/node-engine.ts","../../../src/shared/python-engine.ts"],"sourcesContent":["import type {\n IClassifierProvider,\n IDetectionAddon,\n AddonManifest,\n AddonContext,\n CropInput,\n ClassifierOutput,\n ConfigUISchema,\n ClassMapDefinition,\n ProbeResult,\n ModelCatalogEntry,\n DetectionModel,\n LabelDefinition,\n IInferenceEngine,\n} from '@camstack/types'\nimport { AUDIO_CLASSIFICATION_MODELS } from '../../catalogs/audio-classification-models.js'\nimport { yamnetPostprocess } from '../../shared/postprocess/yamnet.js'\nimport { resolveEngine } from '../../shared/engine-resolver.js'\n\n// YAMNet recognizes 521 audio classes from the AudioSet ontology.\n// This is the top-level subset used for filtering. Full class list lives in the model catalog.\nconst YAMNET_NUM_CLASSES = 521\n\nconst AUDIO_EVENT_LABEL: LabelDefinition = { id: 'audio-event', name: 'Audio Event' }\nconst AUDIO_LABELS: readonly LabelDefinition[] = [AUDIO_EVENT_LABEL]\nconst AUDIO_CLASS_MAP: ClassMapDefinition = { mapping: {}, preserveOriginal: true }\n\nexport default class AudioClassificationAddon implements IClassifierProvider, IDetectionAddon {\n readonly id = 'audio-classification'\n readonly slot = 'classifier' as const\n readonly inputClasses: readonly string[] | null = null\n readonly outputClasses = ['audio-event:*'] as const\n readonly slotPriority = 0\n readonly manifest: AddonManifest = {\n id: 'audio-classification',\n name: 'Audio Classification',\n version: '0.1.0',\n description: 'YAMNet-based audio event classification from audio waveform',\n packageName: '@camstack/addon-vision',\n slot: 'classifier',\n inputClasses: undefined,\n outputClasses: ['audio-event:*'],\n supportsCustomModels: false,\n mayRequirePython: false,\n defaultConfig: {\n modelId: 'yamnet',\n runtime: 'auto',\n backend: 'cpu',\n minScore: 0.3,\n },\n }\n\n private engine!: IInferenceEngine\n private modelEntry!: ModelCatalogEntry\n private minScore = 0.3\n\n async initialize(ctx: AddonContext): Promise<void> {\n const cfg = ctx.addonConfig\n const modelId = (cfg['modelId'] as string | undefined) ?? 'yamnet'\n const runtime = (cfg['runtime'] as string | undefined) ?? 'auto'\n const backend = (cfg['backend'] as string | undefined) ?? 'cpu'\n this.minScore = (cfg['minScore'] as number | undefined) ?? 0.3\n\n const entry = AUDIO_CLASSIFICATION_MODELS.find((m) => m.id === modelId)\n if (!entry) {\n throw new Error(`AudioClassificationAddon: unknown modelId \"${modelId}\"`)\n }\n this.modelEntry = entry\n\n const resolved = await resolveEngine({\n runtime: runtime as 'auto',\n backend,\n modelEntry: entry,\n modelsDir: ctx.locationPaths.models,\n })\n this.engine = resolved.engine\n }\n\n /**\n * classify() receives a CropInput but internally treats input.frame.data as raw audio context.\n * For audio, the actual audio chunk data should be stored in frame.data as a Float32Array\n * serialized into a Buffer (little-endian float32 samples at 16 kHz).\n *\n * The CropInput.roi is not used for audio — it is ignored.\n */\n async classify(input: CropInput): Promise<ClassifierOutput> {\n const start = Date.now()\n\n // Extract raw float32 audio samples from the buffer\n const buf = input.frame.data\n const numSamples = Math.floor(buf.length / 4)\n const audioData = new Float32Array(numSamples)\n for (let i = 0; i < numSamples; i++) {\n audioData[i] = buf.readFloatLE(i * 4)\n }\n\n // YAMNet expects 1D waveform at 16 kHz\n const output = await this.engine.run(audioData, [numSamples])\n\n // YAMNet output shape: [numFrames, numClasses]\n const numFrames = output.length / YAMNET_NUM_CLASSES\n\n // Use model label ids as class names (or index strings if catalog is sparse)\n const classNames: string[] = this.modelEntry.labels.map((l) => l.id)\n // Pad to full 521 classes if the catalog only has a subset\n while (classNames.length < YAMNET_NUM_CLASSES) {\n classNames.push(`class_${classNames.length}`)\n }\n\n const results = yamnetPostprocess(\n output,\n Math.round(numFrames),\n YAMNET_NUM_CLASSES,\n classNames,\n this.minScore,\n )\n\n const classifications = results.map((r) => ({\n class: `audio-event:${r.className}`,\n score: r.score,\n }))\n\n return {\n classifications,\n inferenceMs: Date.now() - start,\n modelId: this.modelEntry.id,\n }\n }\n\n async shutdown(): Promise<void> {\n await this.engine?.dispose()\n }\n\n getConfigSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'runtime',\n title: 'Runtime',\n columns: 2,\n fields: [\n {\n key: 'runtime',\n label: 'Runtime',\n type: 'select',\n options: [\n { value: 'auto', label: 'Auto (recommended)' },\n { value: 'onnx', label: 'ONNX Runtime' },\n { value: 'openvino', label: 'OpenVINO (Intel)' },\n ],\n },\n {\n key: 'backend',\n label: 'Backend',\n type: 'select',\n dependsOn: { runtime: 'onnx' },\n options: [\n { value: 'cpu', label: 'CPU' },\n { value: 'cuda', label: 'CUDA (NVIDIA)' },\n ],\n },\n ],\n },\n {\n id: 'thresholds',\n title: 'Classification Settings',\n columns: 1,\n fields: [\n {\n key: 'minScore',\n label: 'Minimum Score',\n type: 'slider',\n min: 0.05,\n max: 1.0,\n step: 0.05,\n default: 0.3,\n },\n ],\n },\n ],\n }\n }\n\n getClassMap(): ClassMapDefinition {\n return AUDIO_CLASS_MAP\n }\n\n getModelCatalog(): ModelCatalogEntry[] {\n return [...AUDIO_CLASSIFICATION_MODELS]\n }\n\n getAvailableModels(): DetectionModel[] {\n return []\n }\n\n getActiveLabels(): readonly LabelDefinition[] {\n return AUDIO_LABELS\n }\n\n async probe(): Promise<ProbeResult> {\n return {\n available: true,\n runtime: this.engine?.runtime ?? 'onnx',\n device: this.engine?.device ?? 'cpu',\n capabilities: ['fp32'],\n }\n }\n}\n","import type { ModelCatalogEntry, LabelDefinition } from '@camstack/types'\nimport { hfModelUrl } from '@camstack/types'\n\nconst HF_REPO = 'camstack/camstack-models'\n\nconst AUDIO_LABELS: readonly LabelDefinition[] = [\n { id: 'audio', name: 'Audio Event' },\n] as const\n\nexport const AUDIO_CLASSIFICATION_MODELS: readonly ModelCatalogEntry[] = [\n {\n id: 'yamnet',\n name: 'YAMNet',\n description: 'YAMNet — audio event classification from raw waveform',\n inputSize: { width: 1, height: 16000 },\n labels: AUDIO_LABELS,\n formats: {\n onnx: {\n url: hfModelUrl(HF_REPO, 'audioClassification/yamnet/onnx/camstack-yamnet.onnx'),\n sizeMB: 15,\n },\n openvino: {\n url: hfModelUrl(HF_REPO, 'audioClassification/yamnet/openvino/camstack-yamnet.xml'),\n sizeMB: 8,\n },\n },\n },\n] as const\n","export interface AudioClassification {\n readonly className: string\n readonly score: number\n}\n\n/** Average YAMNET scores across frames, return top classes above threshold */\nexport function yamnetPostprocess(\n output: Float32Array,\n numFrames: number,\n numClasses: number,\n classNames: readonly string[],\n minScore: number,\n): AudioClassification[] {\n // Average across frames\n const avgScores = new Float32Array(numClasses)\n for (let f = 0; f < numFrames; f++) {\n for (let c = 0; c < numClasses; c++) {\n const prev = avgScores[c] ?? 0\n avgScores[c] = prev + (output[f * numClasses + c] ?? 0)\n }\n }\n if (numFrames > 0) {\n for (let c = 0; c < numClasses; c++) {\n const val = avgScores[c] ?? 0\n avgScores[c] = val / numFrames\n }\n }\n\n // Collect classes above threshold\n const results: AudioClassification[] = []\n for (let c = 0; c < numClasses; c++) {\n const score = avgScores[c]!\n if (score >= minScore) {\n results.push({\n className: classNames[c] ?? String(c),\n score,\n })\n }\n }\n\n // Sort descending by score\n return results.sort((a, b) => b.score - a.score)\n}\n","// TODO: Wire PythonInferenceEngine for PyTorch/OpenVINO/TFLite runtimes\n// Currently falls back to ONNX CPU when non-ONNX runtime is requested.\n// See: packages/addon-vision/python/ for stub implementations.\n//\n// WHY THIS FILE USES RAW FILESYSTEM PATHS (modelsDir: string):\n//\n// Model files must be loaded via absolute filesystem paths because inference\n// engines (ONNX Runtime, CoreML, etc.) require direct file access — they do\n// not accept Buffer or stream inputs. This is fundamentally different from\n// user data (recordings, media) where IAddonFileStorage abstraction makes\n// sense. Models are closer to binaries or compiled artifacts that must live\n// on the local filesystem at runtime.\n//\n// IAddonFileStorage.readFile() returns a Buffer, but onnxruntime-node's\n// InferenceSession.create() only accepts a file path string or a Uint8Array\n// loaded into memory. For large models (hundreds of MB), loading into a\n// Uint8Array is impractical. Therefore, modelsDir stays as a raw string path\n// and is intentionally NOT replaced with IAddonFileStorage here.\n\nimport type {\n IInferenceEngine,\n DetectionRuntime,\n ModelCatalogEntry,\n ModelFormat,\n} from '@camstack/types'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { NodeInferenceEngine } from './node-engine.js'\nimport { PythonInferenceEngine } from './python-engine.js'\n\nexport interface EngineResolverOptions {\n readonly runtime: DetectionRuntime | 'auto'\n readonly backend: string\n readonly modelEntry: ModelCatalogEntry\n readonly modelsDir: string\n readonly pythonPath?: string\n readonly downloadModel?: (url: string, destDir: string) => Promise<string>\n}\n\nexport interface ResolvedEngine {\n readonly engine: IInferenceEngine\n readonly format: ModelFormat\n readonly modelPath: string\n}\n\n/** Priority order for auto-selection of ONNX backends */\nconst AUTO_BACKEND_PRIORITY = ['coreml', 'cuda', 'tensorrt', 'cpu'] as const\n\n/** Map backend names to the model format they require */\nconst BACKEND_TO_FORMAT: Readonly<Record<string, ModelFormat>> = {\n cpu: 'onnx',\n coreml: 'coreml',\n cuda: 'onnx',\n tensorrt: 'onnx',\n} as const\n\n/** Map DetectionRuntime to ModelFormat */\nconst RUNTIME_TO_FORMAT: Readonly<Partial<Record<DetectionRuntime, ModelFormat>>> = {\n onnx: 'onnx',\n coreml: 'coreml',\n openvino: 'openvino',\n tflite: 'tflite',\n pytorch: 'pt',\n} as const\n\nfunction modelFilePath(modelsDir: string, modelEntry: ModelCatalogEntry, format: ModelFormat): string {\n const formatEntry = modelEntry.formats[format]\n if (!formatEntry) {\n throw new Error(`Model ${modelEntry.id} has no ${format} format`)\n }\n // Derive filename from URL\n const urlParts = formatEntry.url.split('/')\n const filename = urlParts[urlParts.length - 1] ?? `${modelEntry.id}.${format}`\n return path.join(modelsDir, filename)\n}\n\nfunction modelExists(filePath: string): boolean {\n try {\n return fs.existsSync(filePath)\n } catch {\n return false\n }\n}\n\nexport async function resolveEngine(options: EngineResolverOptions): Promise<ResolvedEngine> {\n const { runtime, backend, modelEntry, modelsDir, downloadModel } = options\n\n let selectedFormat: ModelFormat\n let selectedBackend: string\n\n if (runtime === 'auto') {\n // Probe available ONNX backends and pick best\n const available = await probeOnnxBackends()\n\n // Pick first priority backend that has a corresponding model format available\n let chosen: { backend: string; format: ModelFormat } | null = null\n\n for (const b of AUTO_BACKEND_PRIORITY) {\n if (!available.includes(b)) continue\n const fmt = BACKEND_TO_FORMAT[b]\n if (!fmt) continue\n if (!modelEntry.formats[fmt]) continue\n chosen = { backend: b, format: fmt }\n break\n }\n\n if (!chosen) {\n throw new Error(\n `resolveEngine: no compatible backend found for model ${modelEntry.id}. Available backends: ${available.join(', ')}`,\n )\n }\n\n selectedFormat = chosen.format\n selectedBackend = chosen.backend\n } else {\n // Explicit runtime requested\n const fmt = RUNTIME_TO_FORMAT[runtime]\n if (!fmt) {\n throw new Error(`resolveEngine: unsupported runtime \"${runtime}\"`)\n }\n if (!modelEntry.formats[fmt]) {\n throw new Error(\n `resolveEngine: model ${modelEntry.id} has no ${fmt} format for runtime ${runtime}`,\n )\n }\n selectedFormat = fmt\n // For onnx runtime, use the provided backend; otherwise use the runtime name\n selectedBackend = runtime === 'onnx' ? (backend || 'cpu') : runtime\n }\n\n // Resolve model path\n let modelPath = modelFilePath(modelsDir, modelEntry, selectedFormat)\n\n if (!modelExists(modelPath)) {\n if (downloadModel) {\n const formatEntry = modelEntry.formats[selectedFormat]!\n modelPath = await downloadModel(formatEntry.url, modelsDir)\n } else {\n throw new Error(\n `resolveEngine: model file not found at ${modelPath} and no downloadModel function provided`,\n )\n }\n }\n\n // Only ONNX runtime is handled by NodeInferenceEngine currently\n if (selectedFormat === 'onnx' || selectedFormat === 'coreml') {\n const engine = new NodeInferenceEngine(modelPath, selectedBackend)\n await engine.initialize()\n return { engine, format: selectedFormat, modelPath }\n }\n\n // For non-ONNX/CoreML formats, try PythonInferenceEngine when a python binary is available\n const { pythonPath } = options\n const PYTHON_SCRIPT_MAP: Readonly<Record<string, string>> = {\n coreml: 'coreml_inference.py',\n pytorch: 'pytorch_inference.py',\n openvino: 'openvino_inference.py',\n } as const\n\n const effectiveRuntime = runtime === 'auto' ? selectedBackend : runtime\n const scriptName = PYTHON_SCRIPT_MAP[effectiveRuntime]\n\n if (scriptName && pythonPath) {\n const scriptPath = path.join(__dirname, '../../python', scriptName)\n const inputSize = Math.max(modelEntry.inputSize.width, modelEntry.inputSize.height)\n const engine = new PythonInferenceEngine(pythonPath, scriptPath, effectiveRuntime as DetectionRuntime, modelPath, [\n `--input-size=${inputSize}`,\n `--confidence=0.25`,\n ])\n await engine.initialize()\n return { engine, format: selectedFormat, modelPath }\n }\n\n // Final fallback: use ONNX CPU if available\n const fallbackPath = modelFilePath(modelsDir, modelEntry, 'onnx')\n if (modelEntry.formats['onnx'] && modelExists(fallbackPath)) {\n const engine = new NodeInferenceEngine(fallbackPath, 'cpu')\n await engine.initialize()\n return { engine, format: 'onnx', modelPath: fallbackPath }\n }\n\n throw new Error(\n `resolveEngine: format ${selectedFormat} is not yet supported by NodeInferenceEngine, ` +\n `no Python runtime is available, and no ONNX fallback exists`,\n )\n}\n\n/** Probe which ONNX execution providers are available on this system */\nexport async function probeOnnxBackends(): Promise<string[]> {\n const available: string[] = ['cpu'] // CPU is always available\n\n try {\n const ort = await import('onnxruntime-node')\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const providers: string[] = (ort as any).env?.webgl?.disabled !== undefined\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ? ((ort as any).InferenceSession?.getAvailableProviders?.() ?? [])\n : []\n\n for (const p of providers) {\n const normalized = p.toLowerCase().replace('executionprovider', '')\n if (normalized === 'coreml') available.push('coreml')\n else if (normalized === 'cuda') available.push('cuda')\n else if (normalized === 'tensorrt') available.push('tensorrt')\n }\n } catch {\n // onnxruntime-node may not be installed; CPU only\n }\n\n // Platform-specific hints when getAvailableProviders isn't exposed\n if (process.platform === 'darwin' && !available.includes('coreml')) {\n available.push('coreml')\n }\n\n return [...new Set(available)]\n}\n","import type { IInferenceEngine, DetectionRuntime, DetectionDevice } from '@camstack/types'\nimport * as path from 'node:path'\n\nconst BACKEND_TO_PROVIDER: Readonly<Record<string, string>> = {\n cpu: 'cpu',\n coreml: 'coreml',\n cuda: 'cuda',\n tensorrt: 'tensorrt',\n dml: 'dml',\n} as const\n\nconst BACKEND_TO_DEVICE: Readonly<Record<string, DetectionDevice>> = {\n cpu: 'cpu',\n coreml: 'gpu-mps',\n cuda: 'gpu-cuda',\n tensorrt: 'tensorrt',\n} as const\n\nexport class NodeInferenceEngine implements IInferenceEngine {\n readonly runtime: DetectionRuntime = 'onnx'\n readonly device: DetectionDevice\n private session: unknown = null\n\n constructor(\n private readonly modelPath: string,\n private readonly backend: string,\n ) {\n this.device = (BACKEND_TO_DEVICE[backend] ?? 'cpu') as DetectionDevice\n }\n\n async initialize(): Promise<void> {\n const ort = await import('onnxruntime-node')\n const provider = BACKEND_TO_PROVIDER[this.backend] ?? 'cpu'\n\n // Resolve absolute path\n const absModelPath = path.isAbsolute(this.modelPath)\n ? this.modelPath\n : path.resolve(process.cwd(), this.modelPath)\n\n const sessionOptions: Record<string, unknown> = {\n executionProviders: [provider],\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this.session = await (ort as any).InferenceSession.create(absModelPath, sessionOptions)\n }\n\n async run(input: Float32Array, inputShape: readonly number[]): Promise<Float32Array> {\n if (!this.session) {\n throw new Error('NodeInferenceEngine: not initialized — call initialize() first')\n }\n\n const ort = await import('onnxruntime-node')\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const sess = this.session as any\n\n // Get the first input name\n const inputName: string = sess.inputNames[0]\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const tensor = new (ort as any).Tensor('float32', input, [...inputShape])\n const feeds: Record<string, unknown> = { [inputName]: tensor }\n\n const results = await sess.run(feeds)\n const outputName: string = sess.outputNames[0]\n const outputTensor = results[outputName]\n\n return outputTensor.data as Float32Array\n }\n\n async runMultiOutput(\n input: Float32Array,\n inputShape: readonly number[],\n ): Promise<Record<string, Float32Array>> {\n if (!this.session) {\n throw new Error('NodeInferenceEngine: not initialized — call initialize() first')\n }\n\n const ort = await import('onnxruntime-node')\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const sess = this.session as any\n\n const inputName: string = sess.inputNames[0]\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const tensor = new (ort as any).Tensor('float32', input, [...inputShape])\n const feeds: Record<string, unknown> = { [inputName]: tensor }\n\n const results = await sess.run(feeds)\n\n const out: Record<string, Float32Array> = {}\n for (const name of sess.outputNames as string[]) {\n out[name] = results[name].data as Float32Array\n }\n return out\n }\n\n async dispose(): Promise<void> {\n // onnxruntime-node sessions don't have explicit close in all versions\n // but we clear the reference\n this.session = null\n }\n}\n","import type { IInferenceEngine, DetectionRuntime, DetectionDevice } from '@camstack/types'\nimport { spawn, type ChildProcess } from 'node:child_process'\n\nexport class PythonInferenceEngine implements IInferenceEngine {\n readonly runtime: DetectionRuntime\n readonly device: DetectionDevice\n private process: ChildProcess | null = null\n private receiveBuffer: Buffer = Buffer.alloc(0)\n private pendingResolve: ((value: Record<string, unknown>) => void) | null = null\n private pendingReject: ((reason: Error) => void) | null = null\n\n constructor(\n private readonly pythonPath: string,\n private readonly scriptPath: string,\n runtime: DetectionRuntime,\n private readonly modelPath: string,\n private readonly extraArgs: readonly string[] = [],\n ) {\n this.runtime = runtime\n // Determine device from runtime\n const runtimeDeviceMap: Readonly<Record<DetectionRuntime, DetectionDevice>> = {\n onnx: 'cpu',\n coreml: 'gpu-mps',\n pytorch: 'cpu',\n openvino: 'cpu',\n tflite: 'cpu',\n }\n this.device = runtimeDeviceMap[runtime]\n }\n\n async initialize(): Promise<void> {\n const args = [this.scriptPath, this.modelPath, ...this.extraArgs]\n this.process = spawn(this.pythonPath, args, {\n stdio: ['pipe', 'pipe', 'pipe'],\n })\n\n if (!this.process.stdout || !this.process.stdin) {\n throw new Error('PythonInferenceEngine: failed to create process pipes')\n }\n\n this.process.stderr?.on('data', (chunk: Buffer) => {\n // Log stderr from python process for debugging\n process.stderr.write(`[python-engine] ${chunk.toString()}`)\n })\n\n this.process.on('error', (err) => {\n this.pendingReject?.(err)\n this.pendingReject = null\n this.pendingResolve = null\n })\n\n this.process.on('exit', (code) => {\n if (code !== 0) {\n const err = new Error(`PythonInferenceEngine: process exited with code ${code}`)\n this.pendingReject?.(err)\n this.pendingReject = null\n this.pendingResolve = null\n }\n })\n\n this.process.stdout.on('data', (chunk: Buffer) => {\n this.receiveBuffer = Buffer.concat([this.receiveBuffer, chunk])\n this._tryReceive()\n })\n\n // Give the process a moment to start up and load the model\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => resolve(), 2000)\n this.process?.on('error', (err) => {\n clearTimeout(timeout)\n reject(err)\n })\n this.process?.on('exit', (code) => {\n clearTimeout(timeout)\n if (code !== 0) {\n reject(new Error(`PythonInferenceEngine: process exited early with code ${code}`))\n }\n })\n })\n }\n\n private _tryReceive(): void {\n // Binary IPC: [4 bytes LE uint32 length][JSON bytes]\n if (this.receiveBuffer.length < 4) return\n\n const length = this.receiveBuffer.readUInt32LE(0)\n if (this.receiveBuffer.length < 4 + length) return\n\n const jsonBytes = this.receiveBuffer.subarray(4, 4 + length)\n this.receiveBuffer = this.receiveBuffer.subarray(4 + length)\n\n const resolve = this.pendingResolve\n const reject = this.pendingReject\n this.pendingResolve = null\n this.pendingReject = null\n\n if (!resolve) return\n\n try {\n const parsed = JSON.parse(jsonBytes.toString('utf8')) as Record<string, unknown>\n resolve(parsed)\n } catch (err) {\n reject?.(err instanceof Error ? err : new Error(String(err)))\n }\n }\n\n /** Send JPEG buffer, receive JSON detection results */\n async runJpeg(jpeg: Buffer): Promise<Record<string, unknown>> {\n if (!this.process?.stdin) {\n throw new Error('PythonInferenceEngine: process not initialized')\n }\n\n return new Promise<Record<string, unknown>>((resolve, reject) => {\n this.pendingResolve = resolve\n this.pendingReject = reject\n\n // Binary IPC: [4 bytes LE uint32 length][JPEG bytes]\n const lengthBuf = Buffer.allocUnsafe(4)\n lengthBuf.writeUInt32LE(jpeg.length, 0)\n this.process!.stdin!.write(Buffer.concat([lengthBuf, jpeg]))\n })\n }\n\n /** IInferenceEngine.run — wraps runJpeg for compatibility */\n async run(_input: Float32Array, _inputShape: readonly number[]): Promise<Float32Array> {\n throw new Error(\n 'PythonInferenceEngine: use runJpeg() directly — this engine operates on JPEG input',\n )\n }\n\n /** IInferenceEngine.runMultiOutput — not supported by Python engine (operates on JPEG input) */\n async runMultiOutput(\n _input: Float32Array,\n _inputShape: readonly number[],\n ): Promise<Record<string, Float32Array>> {\n throw new Error(\n 'PythonInferenceEngine: runMultiOutput() is not supported — this engine operates on JPEG input',\n )\n }\n\n async dispose(): Promise<void> {\n if (this.process) {\n this.process.stdin?.end()\n this.process.kill('SIGTERM')\n this.process = null\n }\n }\n}\n"],"mappings":"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","names":["path","resolve","AUDIO_LABELS"]}
@@ -0,0 +1,8 @@
1
+ import {
2
+ AudioClassificationAddon
3
+ } from "../../chunk-5AIQSN32.mjs";
4
+ import "../../chunk-LPI42WL6.mjs";
5
+ export {
6
+ AudioClassificationAddon as default
7
+ };
8
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}