@camstack/addon-vision 0.1.1 → 0.1.3
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/addons/animal-classifier/index.js +999 -823
- package/dist/addons/animal-classifier/index.js.map +1 -1
- package/dist/addons/animal-classifier/index.mjs +242 -7
- package/dist/addons/animal-classifier/index.mjs.map +1 -1
- package/dist/addons/audio-classification/index.js +501 -379
- package/dist/addons/audio-classification/index.js.map +1 -1
- package/dist/addons/audio-classification/index.mjs +224 -4
- package/dist/addons/audio-classification/index.mjs.map +1 -1
- package/dist/addons/bird-global-classifier/index.js +1002 -826
- package/dist/addons/bird-global-classifier/index.js.map +1 -1
- package/dist/addons/bird-global-classifier/index.mjs +248 -7
- package/dist/addons/bird-global-classifier/index.mjs.map +1 -1
- package/dist/addons/bird-nabirds-classifier/index.js +1002 -826
- package/dist/addons/bird-nabirds-classifier/index.js.map +1 -1
- package/dist/addons/bird-nabirds-classifier/index.mjs +289 -7
- package/dist/addons/bird-nabirds-classifier/index.mjs.map +1 -1
- package/dist/addons/face-detection/index.js +1196 -935
- package/dist/addons/face-detection/index.js.map +1 -1
- package/dist/addons/face-detection/index.mjs +227 -7
- package/dist/addons/face-detection/index.mjs.map +1 -1
- package/dist/addons/face-recognition/index.js +1003 -808
- package/dist/addons/face-recognition/index.js.map +1 -1
- package/dist/addons/face-recognition/index.mjs +197 -6
- package/dist/addons/face-recognition/index.mjs.map +1 -1
- package/dist/addons/motion-detection/index.js +214 -111
- package/dist/addons/motion-detection/index.js.map +1 -1
- package/dist/addons/motion-detection/index.mjs +12 -9
- package/dist/addons/motion-detection/index.mjs.map +1 -1
- package/dist/addons/object-detection/index.js +1287 -1083
- package/dist/addons/object-detection/index.js.map +1 -1
- package/dist/addons/object-detection/index.mjs +373 -7
- package/dist/addons/object-detection/index.mjs.map +1 -1
- package/dist/addons/plate-detection/index.js +1075 -869
- package/dist/addons/plate-detection/index.js.map +1 -1
- package/dist/addons/plate-detection/index.mjs +230 -7
- package/dist/addons/plate-detection/index.mjs.map +1 -1
- package/dist/addons/plate-recognition/index.js +684 -506
- package/dist/addons/plate-recognition/index.js.map +1 -1
- package/dist/addons/plate-recognition/index.mjs +244 -5
- package/dist/addons/plate-recognition/index.mjs.map +1 -1
- package/dist/addons/segmentation-refiner/index.js +967 -791
- package/dist/addons/segmentation-refiner/index.js.map +1 -1
- package/dist/addons/segmentation-refiner/index.mjs +21 -17
- package/dist/addons/segmentation-refiner/index.mjs.map +1 -1
- package/dist/addons/vehicle-classifier/index.js +581 -411
- package/dist/addons/vehicle-classifier/index.js.map +1 -1
- package/dist/addons/vehicle-classifier/index.mjs +20 -16
- package/dist/addons/vehicle-classifier/index.mjs.map +1 -1
- package/dist/chunk-2YMA6QOV.mjs +193 -0
- package/dist/chunk-2YMA6QOV.mjs.map +1 -0
- package/dist/chunk-3IIFBJCD.mjs +45 -0
- package/dist/chunk-BS4DKYGN.mjs +48 -0
- package/dist/{chunk-7DYHXUPZ.mjs.map → chunk-BS4DKYGN.mjs.map} +1 -1
- package/dist/chunk-DE7I3VHO.mjs +106 -0
- package/dist/{chunk-KUO2BVFY.mjs.map → chunk-DE7I3VHO.mjs.map} +1 -1
- package/dist/chunk-F6D2OZ36.mjs +89 -0
- package/dist/chunk-F6D2OZ36.mjs.map +1 -0
- package/dist/chunk-GAOIFQDX.mjs +59 -0
- package/dist/chunk-GAOIFQDX.mjs.map +1 -0
- package/dist/chunk-HUIX2XVR.mjs +159 -0
- package/dist/chunk-HUIX2XVR.mjs.map +1 -0
- package/dist/chunk-K36R6HWY.mjs +51 -0
- package/dist/{chunk-XZ6ZMXXU.mjs.map → chunk-K36R6HWY.mjs.map} +1 -1
- package/dist/chunk-MBTAI3WE.mjs +78 -0
- package/dist/chunk-MBTAI3WE.mjs.map +1 -0
- package/dist/chunk-MGT6RUVX.mjs +423 -0
- package/dist/{chunk-BP7H4NFS.mjs.map → chunk-MGT6RUVX.mjs.map} +1 -1
- package/dist/chunk-PIFS7AIT.mjs +446 -0
- package/dist/chunk-PIFS7AIT.mjs.map +1 -0
- package/dist/chunk-WG66JYYW.mjs +116 -0
- package/dist/{chunk-22BHCDT5.mjs.map → chunk-WG66JYYW.mjs.map} +1 -1
- package/dist/chunk-XD7WGXHZ.mjs +82 -0
- package/dist/{chunk-DUN6XU3N.mjs.map → chunk-XD7WGXHZ.mjs.map} +1 -1
- package/dist/chunk-YYDM6V2F.mjs +113 -0
- package/dist/{chunk-BR2FPGOX.mjs.map → chunk-YYDM6V2F.mjs.map} +1 -1
- package/dist/chunk-ZK7P3TZN.mjs +286 -0
- package/dist/chunk-ZK7P3TZN.mjs.map +1 -0
- package/dist/index.js +4443 -3925
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2698 -250
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -3
- package/dist/chunk-22BHCDT5.mjs +0 -101
- package/dist/chunk-6DJZZR64.mjs +0 -336
- package/dist/chunk-6DJZZR64.mjs.map +0 -1
- package/dist/chunk-7DYHXUPZ.mjs +0 -36
- package/dist/chunk-BJTO5JO5.mjs +0 -11
- package/dist/chunk-BP7H4NFS.mjs +0 -412
- package/dist/chunk-BR2FPGOX.mjs +0 -98
- package/dist/chunk-DNQNGDR4.mjs +0 -256
- package/dist/chunk-DNQNGDR4.mjs.map +0 -1
- package/dist/chunk-DUN6XU3N.mjs +0 -72
- package/dist/chunk-EPNWLSCG.mjs +0 -387
- package/dist/chunk-EPNWLSCG.mjs.map +0 -1
- package/dist/chunk-G32RCIUI.mjs +0 -645
- package/dist/chunk-G32RCIUI.mjs.map +0 -1
- package/dist/chunk-GR65KM6X.mjs +0 -289
- package/dist/chunk-GR65KM6X.mjs.map +0 -1
- package/dist/chunk-H7LMBTS5.mjs +0 -276
- package/dist/chunk-H7LMBTS5.mjs.map +0 -1
- package/dist/chunk-IK4XIQPC.mjs +0 -242
- package/dist/chunk-IK4XIQPC.mjs.map +0 -1
- package/dist/chunk-J6VNIIYX.mjs +0 -269
- package/dist/chunk-J6VNIIYX.mjs.map +0 -1
- package/dist/chunk-KUO2BVFY.mjs +0 -90
- package/dist/chunk-ML2JX43J.mjs +0 -248
- package/dist/chunk-ML2JX43J.mjs.map +0 -1
- package/dist/chunk-WUMV524J.mjs +0 -379
- package/dist/chunk-WUMV524J.mjs.map +0 -1
- package/dist/chunk-XZ6ZMXXU.mjs +0 -39
- /package/dist/{chunk-BJTO5JO5.mjs.map → chunk-3IIFBJCD.mjs.map} +0 -0
package/dist/chunk-H7LMBTS5.mjs
DELETED
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
resolveEngine
|
|
3
|
-
} from "./chunk-6DJZZR64.mjs";
|
|
4
|
-
|
|
5
|
-
// src/catalogs/audio-classification-models.ts
|
|
6
|
-
import { hfModelUrl } from "@camstack/types";
|
|
7
|
-
var HF_REPO = "camstack/camstack-models";
|
|
8
|
-
var AUDIO_LABELS = [
|
|
9
|
-
{ id: "audio", name: "Audio Event" }
|
|
10
|
-
];
|
|
11
|
-
var AUDIO_CLASSIFICATION_MODELS = [
|
|
12
|
-
{
|
|
13
|
-
id: "yamnet",
|
|
14
|
-
name: "YAMNet",
|
|
15
|
-
description: "YAMNet \u2014 audio event classification from raw waveform",
|
|
16
|
-
inputSize: { width: 1, height: 16e3 },
|
|
17
|
-
labels: AUDIO_LABELS,
|
|
18
|
-
formats: {
|
|
19
|
-
onnx: {
|
|
20
|
-
url: hfModelUrl(HF_REPO, "audioClassification/yamnet/onnx/camstack-yamnet.onnx"),
|
|
21
|
-
sizeMB: 15
|
|
22
|
-
},
|
|
23
|
-
openvino: {
|
|
24
|
-
url: hfModelUrl(HF_REPO, "audioClassification/yamnet/openvino/camstack-yamnet.xml"),
|
|
25
|
-
sizeMB: 8
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
];
|
|
30
|
-
|
|
31
|
-
// src/shared/postprocess/yamnet.ts
|
|
32
|
-
function yamnetPostprocess(output, numFrames, numClasses, classNames, minScore) {
|
|
33
|
-
const avgScores = new Float32Array(numClasses);
|
|
34
|
-
for (let f = 0; f < numFrames; f++) {
|
|
35
|
-
for (let c = 0; c < numClasses; c++) {
|
|
36
|
-
const prev = avgScores[c] ?? 0;
|
|
37
|
-
avgScores[c] = prev + (output[f * numClasses + c] ?? 0);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
if (numFrames > 0) {
|
|
41
|
-
for (let c = 0; c < numClasses; c++) {
|
|
42
|
-
const val = avgScores[c] ?? 0;
|
|
43
|
-
avgScores[c] = val / numFrames;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
const results = [];
|
|
47
|
-
for (let c = 0; c < numClasses; c++) {
|
|
48
|
-
const score = avgScores[c];
|
|
49
|
-
if (score >= minScore) {
|
|
50
|
-
results.push({
|
|
51
|
-
className: classNames[c] ?? String(c),
|
|
52
|
-
score
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return results.sort((a, b) => b.score - a.score);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// src/addons/audio-classification/index.ts
|
|
60
|
-
var YAMNET_NUM_CLASSES = 521;
|
|
61
|
-
var AUDIO_EVENT_LABEL = { id: "audio-event", name: "Audio Event" };
|
|
62
|
-
var AUDIO_LABELS2 = [AUDIO_EVENT_LABEL];
|
|
63
|
-
var AUDIO_CLASS_MAP = { mapping: {}, preserveOriginal: true };
|
|
64
|
-
var AudioClassificationAddon = class {
|
|
65
|
-
id = "audio-classification";
|
|
66
|
-
slot = "classifier";
|
|
67
|
-
inputClasses = null;
|
|
68
|
-
outputClasses = ["audio-event:*"];
|
|
69
|
-
slotPriority = 0;
|
|
70
|
-
manifest = {
|
|
71
|
-
id: "audio-classification",
|
|
72
|
-
name: "Audio Classification",
|
|
73
|
-
version: "0.1.0",
|
|
74
|
-
description: "YAMNet-based audio event classification from audio waveform",
|
|
75
|
-
slot: "classifier",
|
|
76
|
-
labelOutputType: "classification",
|
|
77
|
-
inputClasses: void 0,
|
|
78
|
-
outputClasses: ["audio-event:*"],
|
|
79
|
-
supportsCustomModels: false,
|
|
80
|
-
mayRequirePython: false,
|
|
81
|
-
defaultConfig: {
|
|
82
|
-
modelId: "yamnet",
|
|
83
|
-
runtime: "node",
|
|
84
|
-
backend: "cpu",
|
|
85
|
-
minScore: 0.3
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
engine = null;
|
|
89
|
-
modelEntry;
|
|
90
|
-
minScore = 0.3;
|
|
91
|
-
resolvedConfig = null;
|
|
92
|
-
ctx = null;
|
|
93
|
-
getModelRequirements() {
|
|
94
|
-
return AUDIO_CLASSIFICATION_MODELS.map((m) => ({
|
|
95
|
-
modelId: m.id,
|
|
96
|
-
name: m.name,
|
|
97
|
-
minRAM_MB: 100,
|
|
98
|
-
accuracyScore: 80,
|
|
99
|
-
formats: Object.keys(m.formats)
|
|
100
|
-
}));
|
|
101
|
-
}
|
|
102
|
-
configure(config) {
|
|
103
|
-
this.resolvedConfig = config;
|
|
104
|
-
}
|
|
105
|
-
async initialize(ctx) {
|
|
106
|
-
this.ctx = ctx;
|
|
107
|
-
const cfg = ctx.addonConfig;
|
|
108
|
-
const modelId = cfg["modelId"] ?? this.resolvedConfig?.modelId ?? "yamnet";
|
|
109
|
-
this.minScore = cfg["minScore"] ?? 0.3;
|
|
110
|
-
const entry = AUDIO_CLASSIFICATION_MODELS.find((m) => m.id === modelId);
|
|
111
|
-
if (!entry) {
|
|
112
|
-
throw new Error(`AudioClassificationAddon: unknown modelId "${modelId}"`);
|
|
113
|
-
}
|
|
114
|
-
this.modelEntry = entry;
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* classify() receives a CropInput but internally treats input.frame.data as raw audio context.
|
|
118
|
-
* For audio, the actual audio chunk data should be stored in frame.data as a Float32Array
|
|
119
|
-
* serialized into a Buffer (little-endian float32 samples at 16 kHz).
|
|
120
|
-
*
|
|
121
|
-
* The CropInput.roi is not used for audio — it is ignored.
|
|
122
|
-
*/
|
|
123
|
-
async classify(input) {
|
|
124
|
-
if (!this.engine) await this.ensureEngine();
|
|
125
|
-
const start = Date.now();
|
|
126
|
-
const buf = input.frame.data;
|
|
127
|
-
const numSamples = Math.floor(buf.length / 4);
|
|
128
|
-
const audioData = new Float32Array(numSamples);
|
|
129
|
-
for (let i = 0; i < numSamples; i++) {
|
|
130
|
-
audioData[i] = buf.readFloatLE(i * 4);
|
|
131
|
-
}
|
|
132
|
-
const output = await this.engine.run(audioData, [numSamples]);
|
|
133
|
-
const numFrames = output.length / YAMNET_NUM_CLASSES;
|
|
134
|
-
const classNames = this.modelEntry.labels.map((l) => l.id);
|
|
135
|
-
while (classNames.length < YAMNET_NUM_CLASSES) {
|
|
136
|
-
classNames.push(`class_${classNames.length}`);
|
|
137
|
-
}
|
|
138
|
-
const results = yamnetPostprocess(
|
|
139
|
-
output,
|
|
140
|
-
Math.round(numFrames),
|
|
141
|
-
YAMNET_NUM_CLASSES,
|
|
142
|
-
classNames,
|
|
143
|
-
this.minScore
|
|
144
|
-
);
|
|
145
|
-
const classifications = results.map((r) => ({
|
|
146
|
-
class: `audio-event:${r.className}`,
|
|
147
|
-
score: r.score
|
|
148
|
-
}));
|
|
149
|
-
return {
|
|
150
|
-
classifications,
|
|
151
|
-
inferenceMs: Date.now() - start,
|
|
152
|
-
modelId: this.modelEntry.id
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
async ensureEngine() {
|
|
156
|
-
const config = this.resolvedConfig;
|
|
157
|
-
const modelId = config?.modelId ?? this.modelEntry.id;
|
|
158
|
-
const runtime = config?.runtime === "python" ? "coreml" : config?.runtime === "node" ? "onnx" : "auto";
|
|
159
|
-
const backend = config?.backend ?? "cpu";
|
|
160
|
-
const format = config?.format ?? "onnx";
|
|
161
|
-
const entry = AUDIO_CLASSIFICATION_MODELS.find((m) => m.id === modelId) ?? this.modelEntry;
|
|
162
|
-
this.modelEntry = entry;
|
|
163
|
-
const modelsDir = this.ctx.models?.getModelsDir() ?? this.ctx.locationPaths.models;
|
|
164
|
-
if (this.ctx.models) {
|
|
165
|
-
await this.ctx.models.ensure(modelId, format);
|
|
166
|
-
}
|
|
167
|
-
const resolved = await resolveEngine({
|
|
168
|
-
runtime,
|
|
169
|
-
backend,
|
|
170
|
-
modelEntry: entry,
|
|
171
|
-
modelsDir,
|
|
172
|
-
models: this.ctx.models
|
|
173
|
-
});
|
|
174
|
-
this.engine = resolved.engine;
|
|
175
|
-
}
|
|
176
|
-
async shutdown() {
|
|
177
|
-
await this.engine?.dispose();
|
|
178
|
-
}
|
|
179
|
-
getConfigSchema() {
|
|
180
|
-
return {
|
|
181
|
-
sections: [
|
|
182
|
-
{
|
|
183
|
-
id: "model",
|
|
184
|
-
title: "Model",
|
|
185
|
-
columns: 1,
|
|
186
|
-
fields: [
|
|
187
|
-
{
|
|
188
|
-
key: "modelId",
|
|
189
|
-
label: "Model",
|
|
190
|
-
type: "model-selector",
|
|
191
|
-
catalog: [...AUDIO_CLASSIFICATION_MODELS],
|
|
192
|
-
allowCustom: false,
|
|
193
|
-
allowConversion: false,
|
|
194
|
-
acceptFormats: ["onnx", "openvino"],
|
|
195
|
-
requiredMetadata: ["inputSize", "labels", "outputFormat"],
|
|
196
|
-
outputFormatHint: "classification"
|
|
197
|
-
}
|
|
198
|
-
]
|
|
199
|
-
},
|
|
200
|
-
{
|
|
201
|
-
id: "runtime",
|
|
202
|
-
title: "Runtime",
|
|
203
|
-
columns: 2,
|
|
204
|
-
fields: [
|
|
205
|
-
{
|
|
206
|
-
key: "runtime",
|
|
207
|
-
label: "Runtime",
|
|
208
|
-
type: "select",
|
|
209
|
-
options: [
|
|
210
|
-
{ value: "auto", label: "Auto" },
|
|
211
|
-
{ value: "onnx", label: "ONNX Runtime" },
|
|
212
|
-
{ value: "coreml", label: "CoreML (Apple)" },
|
|
213
|
-
{ value: "openvino", label: "OpenVINO (Intel)" }
|
|
214
|
-
]
|
|
215
|
-
},
|
|
216
|
-
{
|
|
217
|
-
key: "backend",
|
|
218
|
-
label: "Backend",
|
|
219
|
-
type: "select",
|
|
220
|
-
showWhen: { field: "runtime", equals: "onnx" },
|
|
221
|
-
options: [
|
|
222
|
-
{ value: "auto", label: "Auto" },
|
|
223
|
-
{ value: "cpu", label: "CPU" },
|
|
224
|
-
{ value: "coreml", label: "CoreML" },
|
|
225
|
-
{ value: "cuda", label: "CUDA (NVIDIA)" }
|
|
226
|
-
]
|
|
227
|
-
}
|
|
228
|
-
]
|
|
229
|
-
},
|
|
230
|
-
{
|
|
231
|
-
id: "thresholds",
|
|
232
|
-
title: "Classification Settings",
|
|
233
|
-
columns: 1,
|
|
234
|
-
fields: [
|
|
235
|
-
{
|
|
236
|
-
key: "minScore",
|
|
237
|
-
label: "Minimum Score",
|
|
238
|
-
type: "slider",
|
|
239
|
-
min: 0.05,
|
|
240
|
-
max: 1,
|
|
241
|
-
step: 0.05,
|
|
242
|
-
default: 0.3
|
|
243
|
-
}
|
|
244
|
-
]
|
|
245
|
-
}
|
|
246
|
-
]
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
getClassMap() {
|
|
250
|
-
return AUDIO_CLASS_MAP;
|
|
251
|
-
}
|
|
252
|
-
getModelCatalog() {
|
|
253
|
-
return [...AUDIO_CLASSIFICATION_MODELS];
|
|
254
|
-
}
|
|
255
|
-
getAvailableModels() {
|
|
256
|
-
return [];
|
|
257
|
-
}
|
|
258
|
-
getActiveLabels() {
|
|
259
|
-
return AUDIO_LABELS2;
|
|
260
|
-
}
|
|
261
|
-
async probe() {
|
|
262
|
-
return {
|
|
263
|
-
available: true,
|
|
264
|
-
runtime: this.engine?.runtime ?? "onnx",
|
|
265
|
-
device: this.engine?.device ?? "cpu",
|
|
266
|
-
capabilities: ["fp32"]
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
export {
|
|
272
|
-
yamnetPostprocess,
|
|
273
|
-
AUDIO_CLASSIFICATION_MODELS,
|
|
274
|
-
AudioClassificationAddon
|
|
275
|
-
};
|
|
276
|
-
//# sourceMappingURL=chunk-H7LMBTS5.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/catalogs/audio-classification-models.ts","../src/shared/postprocess/yamnet.ts","../src/addons/audio-classification/index.ts"],"sourcesContent":["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","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 ModelRequirement,\n ResolvedInferenceConfig,\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\n description: 'YAMNet-based audio event classification from audio waveform',\n\n slot: 'classifier',\n labelOutputType: 'classification',\n inputClasses: undefined,\n outputClasses: ['audio-event:*'],\n supportsCustomModels: false,\n mayRequirePython: false,\n defaultConfig: {\n modelId: 'yamnet',\n runtime: 'node',\n backend: 'cpu',\n minScore: 0.3,\n },\n }\n\n private engine: IInferenceEngine | null = null\n private modelEntry!: ModelCatalogEntry\n private minScore = 0.3\n private resolvedConfig: ResolvedInferenceConfig | null = null\n private ctx: AddonContext | null = null\n\n getModelRequirements(): ModelRequirement[] {\n return AUDIO_CLASSIFICATION_MODELS.map((m) => ({\n modelId: m.id,\n name: m.name,\n minRAM_MB: 100,\n accuracyScore: 80,\n formats: Object.keys(m.formats) as readonly string[],\n }))\n }\n\n configure(config: ResolvedInferenceConfig): void {\n this.resolvedConfig = config\n }\n\n async initialize(ctx: AddonContext): Promise<void> {\n this.ctx = ctx\n const cfg = ctx.addonConfig\n const modelId = (cfg['modelId'] as string | undefined) ?? this.resolvedConfig?.modelId ?? 'yamnet'\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\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 if (!this.engine) await this.ensureEngine()\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 private async ensureEngine(): Promise<void> {\n const config = this.resolvedConfig\n const modelId = config?.modelId ?? this.modelEntry.id\n const runtime = config?.runtime === 'python' ? 'coreml' : (config?.runtime === 'node' ? 'onnx' : 'auto')\n const backend = config?.backend ?? 'cpu'\n const format = config?.format ?? 'onnx'\n\n const entry = AUDIO_CLASSIFICATION_MODELS.find((m) => m.id === modelId) ?? this.modelEntry\n this.modelEntry = entry\n\n const modelsDir = this.ctx!.models?.getModelsDir() ?? this.ctx!.locationPaths.models\n\n if (this.ctx!.models) {\n await this.ctx!.models.ensure(modelId, format as any)\n }\n\n const resolved = await resolveEngine({\n runtime: runtime as 'auto',\n backend,\n modelEntry: entry,\n modelsDir,\n models: this.ctx!.models,\n })\n this.engine = resolved.engine\n }\n\n async shutdown(): Promise<void> {\n await this.engine?.dispose()\n }\n\n getConfigSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'model',\n title: 'Model',\n columns: 1,\n fields: [\n {\n key: 'modelId',\n label: 'Model',\n type: 'model-selector',\n catalog: [...AUDIO_CLASSIFICATION_MODELS],\n allowCustom: false,\n allowConversion: false,\n acceptFormats: ['onnx', 'openvino'],\n requiredMetadata: ['inputSize', 'labels', 'outputFormat'],\n outputFormatHint: 'classification',\n },\n ],\n },\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' },\n { value: 'onnx', label: 'ONNX Runtime' },\n { value: 'coreml', label: 'CoreML (Apple)' },\n { value: 'openvino', label: 'OpenVINO (Intel)' },\n ],\n },\n {\n key: 'backend',\n label: 'Backend',\n type: 'select',\n showWhen: { field: 'runtime', equals: 'onnx' },\n options: [\n { value: 'auto', label: 'Auto' },\n { value: 'cpu', label: 'CPU' },\n { value: 'coreml', label: 'CoreML' },\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"],"mappings":"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","names":["AUDIO_LABELS"]}
|
package/dist/chunk-IK4XIQPC.mjs
DELETED
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ANIMAL_TYPE_MODELS
|
|
3
|
-
} from "./chunk-DUN6XU3N.mjs";
|
|
4
|
-
import {
|
|
5
|
-
cropRegion,
|
|
6
|
-
resizeAndNormalize
|
|
7
|
-
} from "./chunk-22BHCDT5.mjs";
|
|
8
|
-
import {
|
|
9
|
-
resolveEngine
|
|
10
|
-
} from "./chunk-6DJZZR64.mjs";
|
|
11
|
-
|
|
12
|
-
// src/addons/animal-classifier/index.ts
|
|
13
|
-
var ANIMAL_TYPE_LABEL = { id: "animal-type", name: "Animal Type" };
|
|
14
|
-
var ANIMAL_TYPE_LABELS = [ANIMAL_TYPE_LABEL];
|
|
15
|
-
var ANIMAL_CLASS_MAP = { mapping: {}, preserveOriginal: true };
|
|
16
|
-
var ANIMAL_10_CLASSES = [
|
|
17
|
-
"cat",
|
|
18
|
-
"cow",
|
|
19
|
-
"dog",
|
|
20
|
-
"dolphin",
|
|
21
|
-
"eagle",
|
|
22
|
-
"giant panda",
|
|
23
|
-
"horse",
|
|
24
|
-
"monkey",
|
|
25
|
-
"sheep",
|
|
26
|
-
"spider"
|
|
27
|
-
];
|
|
28
|
-
function softmax(logits) {
|
|
29
|
-
const max = logits.reduce((a, b) => Math.max(a, b), -Infinity);
|
|
30
|
-
const exps = logits.map((v) => Math.exp(v - max));
|
|
31
|
-
const sum = exps.reduce((a, b) => a + b, 0);
|
|
32
|
-
return exps.map((v) => v / sum);
|
|
33
|
-
}
|
|
34
|
-
var AnimalClassifierAddon = class {
|
|
35
|
-
id = "animal-classifier";
|
|
36
|
-
slot = "classifier";
|
|
37
|
-
inputClasses = ["animal"];
|
|
38
|
-
outputClasses = ["animal-type:*"];
|
|
39
|
-
slotPriority = 0;
|
|
40
|
-
requiredSteps = [];
|
|
41
|
-
manifest = {
|
|
42
|
-
id: "animal-classifier",
|
|
43
|
-
name: "Animal Classifier",
|
|
44
|
-
version: "0.1.0",
|
|
45
|
-
description: "ViT-based animal type classifier \u2014 10 common species",
|
|
46
|
-
slot: "classifier",
|
|
47
|
-
labelOutputType: "classification",
|
|
48
|
-
inputClasses: ["animal"],
|
|
49
|
-
outputClasses: ["animal-type:*"],
|
|
50
|
-
supportsCustomModels: false,
|
|
51
|
-
mayRequirePython: false,
|
|
52
|
-
defaultConfig: {
|
|
53
|
-
modelId: "animals-10",
|
|
54
|
-
runtime: "node",
|
|
55
|
-
backend: "cpu",
|
|
56
|
-
minConfidence: 0.3
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
engine = null;
|
|
60
|
-
modelEntry;
|
|
61
|
-
minConfidence = 0.3;
|
|
62
|
-
resolvedConfig = null;
|
|
63
|
-
ctx = null;
|
|
64
|
-
getModelRequirements() {
|
|
65
|
-
return ANIMAL_TYPE_MODELS.map((m) => ({
|
|
66
|
-
modelId: m.id,
|
|
67
|
-
name: m.name,
|
|
68
|
-
minRAM_MB: 800,
|
|
69
|
-
accuracyScore: 75,
|
|
70
|
-
formats: Object.keys(m.formats)
|
|
71
|
-
}));
|
|
72
|
-
}
|
|
73
|
-
configure(config) {
|
|
74
|
-
this.resolvedConfig = config;
|
|
75
|
-
}
|
|
76
|
-
async initialize(ctx) {
|
|
77
|
-
this.ctx = ctx;
|
|
78
|
-
const cfg = ctx.addonConfig;
|
|
79
|
-
const modelId = cfg["modelId"] ?? this.resolvedConfig?.modelId ?? "animals-10";
|
|
80
|
-
this.minConfidence = cfg["minConfidence"] ?? 0.3;
|
|
81
|
-
const entry = ANIMAL_TYPE_MODELS.find((m) => m.id === modelId);
|
|
82
|
-
if (!entry) {
|
|
83
|
-
throw new Error(`AnimalClassifierAddon: unknown modelId "${modelId}"`);
|
|
84
|
-
}
|
|
85
|
-
this.modelEntry = entry;
|
|
86
|
-
}
|
|
87
|
-
async classify(input) {
|
|
88
|
-
if (!this.engine) await this.ensureEngine();
|
|
89
|
-
const start = Date.now();
|
|
90
|
-
const { width: inputW, height: inputH } = this.modelEntry.inputSize;
|
|
91
|
-
const animalCrop = await cropRegion(input.frame.data, input.roi);
|
|
92
|
-
const normalized = await resizeAndNormalize(animalCrop, inputW, inputH, "imagenet", "nchw");
|
|
93
|
-
const rawOutput = await this.engine.run(normalized, [1, 3, inputH, inputW]);
|
|
94
|
-
const probs = softmax(rawOutput);
|
|
95
|
-
let maxIdx = 0;
|
|
96
|
-
let maxScore = probs[0] ?? 0;
|
|
97
|
-
for (let i = 1; i < probs.length; i++) {
|
|
98
|
-
const score = probs[i] ?? 0;
|
|
99
|
-
if (score > maxScore) {
|
|
100
|
-
maxScore = score;
|
|
101
|
-
maxIdx = i;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
if (maxScore < this.minConfidence) {
|
|
105
|
-
return {
|
|
106
|
-
classifications: [],
|
|
107
|
-
inferenceMs: Date.now() - start,
|
|
108
|
-
modelId: this.modelEntry.id
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
const label = ANIMAL_10_CLASSES[maxIdx] ?? `animal_${maxIdx}`;
|
|
112
|
-
return {
|
|
113
|
-
classifications: [
|
|
114
|
-
{
|
|
115
|
-
class: label,
|
|
116
|
-
score: maxScore
|
|
117
|
-
}
|
|
118
|
-
],
|
|
119
|
-
inferenceMs: Date.now() - start,
|
|
120
|
-
modelId: this.modelEntry.id
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
async ensureEngine() {
|
|
124
|
-
const config = this.resolvedConfig;
|
|
125
|
-
const modelId = config?.modelId ?? this.modelEntry.id;
|
|
126
|
-
const runtime = config?.runtime === "python" ? "coreml" : config?.runtime === "node" ? "onnx" : "auto";
|
|
127
|
-
const backend = config?.backend ?? "cpu";
|
|
128
|
-
const format = config?.format ?? "onnx";
|
|
129
|
-
const entry = ANIMAL_TYPE_MODELS.find((m) => m.id === modelId) ?? this.modelEntry;
|
|
130
|
-
this.modelEntry = entry;
|
|
131
|
-
const modelsDir = this.ctx.models?.getModelsDir() ?? this.ctx.locationPaths.models;
|
|
132
|
-
if (this.ctx.models) {
|
|
133
|
-
await this.ctx.models.ensure(modelId, format);
|
|
134
|
-
}
|
|
135
|
-
const resolved = await resolveEngine({
|
|
136
|
-
runtime,
|
|
137
|
-
backend,
|
|
138
|
-
modelEntry: entry,
|
|
139
|
-
modelsDir,
|
|
140
|
-
models: this.ctx.models
|
|
141
|
-
});
|
|
142
|
-
this.engine = resolved.engine;
|
|
143
|
-
}
|
|
144
|
-
async shutdown() {
|
|
145
|
-
await this.engine?.dispose();
|
|
146
|
-
}
|
|
147
|
-
getConfigSchema() {
|
|
148
|
-
return {
|
|
149
|
-
sections: [
|
|
150
|
-
{
|
|
151
|
-
id: "model",
|
|
152
|
-
title: "Model",
|
|
153
|
-
columns: 1,
|
|
154
|
-
fields: [
|
|
155
|
-
{
|
|
156
|
-
key: "modelId",
|
|
157
|
-
label: "Model",
|
|
158
|
-
type: "model-selector",
|
|
159
|
-
catalog: [...ANIMAL_TYPE_MODELS],
|
|
160
|
-
allowCustom: false,
|
|
161
|
-
allowConversion: false,
|
|
162
|
-
acceptFormats: ["onnx", "coreml", "openvino"],
|
|
163
|
-
requiredMetadata: ["inputSize", "labels"],
|
|
164
|
-
outputFormatHint: "classification"
|
|
165
|
-
}
|
|
166
|
-
]
|
|
167
|
-
},
|
|
168
|
-
{
|
|
169
|
-
id: "runtime",
|
|
170
|
-
title: "Runtime",
|
|
171
|
-
columns: 2,
|
|
172
|
-
fields: [
|
|
173
|
-
{
|
|
174
|
-
key: "runtime",
|
|
175
|
-
label: "Runtime",
|
|
176
|
-
type: "select",
|
|
177
|
-
options: [
|
|
178
|
-
{ value: "auto", label: "Auto" },
|
|
179
|
-
{ value: "onnx", label: "ONNX Runtime" },
|
|
180
|
-
{ value: "coreml", label: "CoreML (Apple)" },
|
|
181
|
-
{ value: "openvino", label: "OpenVINO (Intel)" }
|
|
182
|
-
]
|
|
183
|
-
},
|
|
184
|
-
{
|
|
185
|
-
key: "backend",
|
|
186
|
-
label: "Backend",
|
|
187
|
-
type: "select",
|
|
188
|
-
showWhen: { field: "runtime", equals: "onnx" },
|
|
189
|
-
options: [
|
|
190
|
-
{ value: "auto", label: "Auto" },
|
|
191
|
-
{ value: "cpu", label: "CPU" },
|
|
192
|
-
{ value: "coreml", label: "CoreML" },
|
|
193
|
-
{ value: "cuda", label: "CUDA (NVIDIA)" }
|
|
194
|
-
]
|
|
195
|
-
}
|
|
196
|
-
]
|
|
197
|
-
},
|
|
198
|
-
{
|
|
199
|
-
id: "thresholds",
|
|
200
|
-
title: "Classification Settings",
|
|
201
|
-
columns: 1,
|
|
202
|
-
fields: [
|
|
203
|
-
{
|
|
204
|
-
key: "minConfidence",
|
|
205
|
-
label: "Minimum Confidence",
|
|
206
|
-
type: "slider",
|
|
207
|
-
min: 0.05,
|
|
208
|
-
max: 1,
|
|
209
|
-
step: 0.05,
|
|
210
|
-
default: 0.3
|
|
211
|
-
}
|
|
212
|
-
]
|
|
213
|
-
}
|
|
214
|
-
]
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
getClassMap() {
|
|
218
|
-
return ANIMAL_CLASS_MAP;
|
|
219
|
-
}
|
|
220
|
-
getModelCatalog() {
|
|
221
|
-
return [...ANIMAL_TYPE_MODELS];
|
|
222
|
-
}
|
|
223
|
-
getAvailableModels() {
|
|
224
|
-
return [];
|
|
225
|
-
}
|
|
226
|
-
getActiveLabels() {
|
|
227
|
-
return ANIMAL_TYPE_LABELS;
|
|
228
|
-
}
|
|
229
|
-
async probe() {
|
|
230
|
-
return {
|
|
231
|
-
available: true,
|
|
232
|
-
runtime: this.engine?.runtime ?? "onnx",
|
|
233
|
-
device: this.engine?.device ?? "cpu",
|
|
234
|
-
capabilities: ["fp32"]
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
export {
|
|
240
|
-
AnimalClassifierAddon
|
|
241
|
-
};
|
|
242
|
-
//# sourceMappingURL=chunk-IK4XIQPC.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/addons/animal-classifier/index.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 ModelRequirement,\n ResolvedInferenceConfig,\n} from '@camstack/types'\nimport { ANIMAL_TYPE_MODELS } from '../../catalogs/animal-classification-models.js'\nimport { cropRegion, resizeAndNormalize } from '../../shared/image-utils.js'\nimport { resolveEngine } from '../../shared/engine-resolver.js'\n\nconst ANIMAL_TYPE_LABEL: LabelDefinition = { id: 'animal-type', name: 'Animal Type' }\nconst ANIMAL_TYPE_LABELS: readonly LabelDefinition[] = [ANIMAL_TYPE_LABEL]\nconst ANIMAL_CLASS_MAP: ClassMapDefinition = { mapping: {}, preserveOriginal: true }\n\nconst ANIMAL_10_CLASSES = [\n 'cat',\n 'cow',\n 'dog',\n 'dolphin',\n 'eagle',\n 'giant panda',\n 'horse',\n 'monkey',\n 'sheep',\n 'spider',\n] as const\n\nfunction softmax(logits: Float32Array): Float32Array {\n const max = logits.reduce((a, b) => Math.max(a, b), -Infinity)\n const exps = logits.map((v) => Math.exp(v - max))\n const sum = exps.reduce((a, b) => a + b, 0)\n return exps.map((v) => v / sum) as unknown as Float32Array\n}\n\nexport default class AnimalClassifierAddon implements IClassifierProvider, IDetectionAddon {\n readonly id = 'animal-classifier'\n readonly slot = 'classifier' as const\n readonly inputClasses = ['animal'] as const\n readonly outputClasses = ['animal-type:*'] as const\n readonly slotPriority = 0\n readonly requiredSteps = [] as const\n readonly manifest: AddonManifest = {\n id: 'animal-classifier',\n name: 'Animal Classifier',\n version: '0.1.0',\n\n description: 'ViT-based animal type classifier — 10 common species',\n\n slot: 'classifier',\n labelOutputType: 'classification',\n inputClasses: ['animal'],\n outputClasses: ['animal-type:*'],\n supportsCustomModels: false,\n mayRequirePython: false,\n defaultConfig: {\n modelId: 'animals-10',\n runtime: 'node',\n backend: 'cpu',\n minConfidence: 0.3,\n },\n }\n\n private engine: IInferenceEngine | null = null\n private modelEntry!: ModelCatalogEntry\n private minConfidence = 0.3\n private resolvedConfig: ResolvedInferenceConfig | null = null\n private ctx: AddonContext | null = null\n\n getModelRequirements(): ModelRequirement[] {\n return ANIMAL_TYPE_MODELS.map((m) => ({\n modelId: m.id,\n name: m.name,\n minRAM_MB: 800,\n accuracyScore: 75,\n formats: Object.keys(m.formats) as readonly string[],\n }))\n }\n\n configure(config: ResolvedInferenceConfig): void {\n this.resolvedConfig = config\n }\n\n async initialize(ctx: AddonContext): Promise<void> {\n this.ctx = ctx\n const cfg = ctx.addonConfig\n const modelId = (cfg['modelId'] as string | undefined) ?? this.resolvedConfig?.modelId ?? 'animals-10'\n this.minConfidence = (cfg['minConfidence'] as number | undefined) ?? 0.3\n\n const entry = ANIMAL_TYPE_MODELS.find((m) => m.id === modelId)\n if (!entry) {\n throw new Error(`AnimalClassifierAddon: unknown modelId \"${modelId}\"`)\n }\n this.modelEntry = entry\n }\n\n async classify(input: CropInput): Promise<ClassifierOutput> {\n if (!this.engine) await this.ensureEngine()\n const start = Date.now()\n const { width: inputW, height: inputH } = this.modelEntry.inputSize\n\n // Crop the animal region\n const animalCrop = await cropRegion(input.frame.data, input.roi)\n\n // Resize to 224x224, ImageNet normalization, NCHW\n const normalized = await resizeAndNormalize(animalCrop, inputW, inputH, 'imagenet', 'nchw')\n\n // Run inference — output shape: [1, 10]\n const rawOutput = await this.engine!.run(normalized, [1, 3, inputH, inputW])\n\n // Softmax to get probabilities\n const probs = softmax(rawOutput)\n\n // Find argmax\n let maxIdx = 0\n let maxScore = probs[0] ?? 0\n for (let i = 1; i < probs.length; i++) {\n const score = probs[i] ?? 0\n if (score > maxScore) {\n maxScore = score\n maxIdx = i\n }\n }\n\n if (maxScore < this.minConfidence) {\n return {\n classifications: [],\n inferenceMs: Date.now() - start,\n modelId: this.modelEntry.id,\n }\n }\n\n const label = ANIMAL_10_CLASSES[maxIdx] ?? `animal_${maxIdx}`\n\n return {\n classifications: [\n {\n class: label,\n score: maxScore,\n },\n ],\n inferenceMs: Date.now() - start,\n modelId: this.modelEntry.id,\n }\n }\n\n private async ensureEngine(): Promise<void> {\n const config = this.resolvedConfig\n const modelId = config?.modelId ?? this.modelEntry.id\n const runtime = config?.runtime === 'python' ? 'coreml' : (config?.runtime === 'node' ? 'onnx' : 'auto')\n const backend = config?.backend ?? 'cpu'\n const format = config?.format ?? 'onnx'\n\n const entry = ANIMAL_TYPE_MODELS.find((m) => m.id === modelId) ?? this.modelEntry\n this.modelEntry = entry\n\n const modelsDir = this.ctx!.models?.getModelsDir() ?? this.ctx!.locationPaths.models\n\n if (this.ctx!.models) {\n await this.ctx!.models.ensure(modelId, format as any)\n }\n\n const resolved = await resolveEngine({\n runtime: runtime as 'auto',\n backend,\n modelEntry: entry,\n modelsDir,\n models: this.ctx!.models,\n })\n this.engine = resolved.engine\n }\n\n async shutdown(): Promise<void> {\n await this.engine?.dispose()\n }\n\n getConfigSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'model',\n title: 'Model',\n columns: 1,\n fields: [\n {\n key: 'modelId',\n label: 'Model',\n type: 'model-selector',\n catalog: [...ANIMAL_TYPE_MODELS],\n allowCustom: false,\n allowConversion: false,\n acceptFormats: ['onnx', 'coreml', 'openvino'],\n requiredMetadata: ['inputSize', 'labels'],\n outputFormatHint: 'classification',\n },\n ],\n },\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' },\n { value: 'onnx', label: 'ONNX Runtime' },\n { value: 'coreml', label: 'CoreML (Apple)' },\n { value: 'openvino', label: 'OpenVINO (Intel)' },\n ],\n },\n {\n key: 'backend',\n label: 'Backend',\n type: 'select',\n showWhen: { field: 'runtime', equals: 'onnx' },\n options: [\n { value: 'auto', label: 'Auto' },\n { value: 'cpu', label: 'CPU' },\n { value: 'coreml', label: 'CoreML' },\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: 'minConfidence',\n label: 'Minimum Confidence',\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 ANIMAL_CLASS_MAP\n }\n\n getModelCatalog(): ModelCatalogEntry[] {\n return [...ANIMAL_TYPE_MODELS]\n }\n\n getAvailableModels(): DetectionModel[] {\n return []\n }\n\n getActiveLabels(): readonly LabelDefinition[] {\n return ANIMAL_TYPE_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"],"mappings":"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","names":[]}
|