@camstack/addon-vision 0.1.0 → 0.1.1
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.d.mts +6 -1
- package/dist/addons/animal-classifier/index.d.ts +6 -1
- package/dist/addons/animal-classifier/index.js +514 -49
- package/dist/addons/animal-classifier/index.js.map +1 -1
- package/dist/addons/animal-classifier/index.mjs +6 -4
- package/dist/addons/audio-classification/index.d.mts +6 -1
- package/dist/addons/audio-classification/index.d.ts +6 -1
- package/dist/addons/audio-classification/index.js +87 -26
- package/dist/addons/audio-classification/index.js.map +1 -1
- package/dist/addons/audio-classification/index.mjs +3 -2
- package/dist/addons/bird-global-classifier/index.d.mts +6 -1
- package/dist/addons/bird-global-classifier/index.d.ts +6 -1
- package/dist/addons/bird-global-classifier/index.js +515 -50
- package/dist/addons/bird-global-classifier/index.js.map +1 -1
- package/dist/addons/bird-global-classifier/index.mjs +6 -4
- package/dist/addons/bird-nabirds-classifier/index.d.mts +6 -1
- package/dist/addons/bird-nabirds-classifier/index.d.ts +6 -1
- package/dist/addons/bird-nabirds-classifier/index.js +524 -60
- package/dist/addons/bird-nabirds-classifier/index.js.map +1 -1
- package/dist/addons/bird-nabirds-classifier/index.mjs +6 -4
- package/dist/addons/face-detection/index.d.mts +6 -1
- package/dist/addons/face-detection/index.d.ts +6 -1
- package/dist/addons/face-detection/index.js +539 -39
- package/dist/addons/face-detection/index.js.map +1 -1
- package/dist/addons/face-detection/index.mjs +5 -3
- package/dist/addons/face-recognition/index.d.mts +6 -1
- package/dist/addons/face-recognition/index.d.ts +6 -1
- package/dist/addons/face-recognition/index.js +488 -33
- package/dist/addons/face-recognition/index.js.map +1 -1
- package/dist/addons/face-recognition/index.mjs +5 -3
- package/dist/addons/motion-detection/index.d.mts +3 -1
- package/dist/addons/motion-detection/index.d.ts +3 -1
- package/dist/addons/motion-detection/index.js +11 -3
- package/dist/addons/motion-detection/index.js.map +1 -1
- package/dist/addons/motion-detection/index.mjs +140 -3
- package/dist/addons/motion-detection/index.mjs.map +1 -1
- package/dist/addons/object-detection/index.d.mts +6 -1
- package/dist/addons/object-detection/index.d.ts +6 -1
- package/dist/addons/object-detection/index.js +370 -72
- package/dist/addons/object-detection/index.js.map +1 -1
- package/dist/addons/object-detection/index.mjs +5 -3
- package/dist/addons/plate-detection/index.d.mts +6 -1
- package/dist/addons/plate-detection/index.d.ts +6 -1
- package/dist/addons/plate-detection/index.js +532 -31
- package/dist/addons/plate-detection/index.js.map +1 -1
- package/dist/addons/plate-detection/index.mjs +5 -3
- package/dist/addons/plate-recognition/index.d.mts +7 -1
- package/dist/addons/plate-recognition/index.d.ts +7 -1
- package/dist/addons/plate-recognition/index.js +177 -44
- package/dist/addons/plate-recognition/index.js.map +1 -1
- package/dist/addons/plate-recognition/index.mjs +4 -3
- package/dist/addons/segmentation-refiner/index.d.mts +30 -0
- package/dist/addons/segmentation-refiner/index.d.ts +30 -0
- package/dist/addons/segmentation-refiner/index.js +1049 -0
- package/dist/addons/segmentation-refiner/index.js.map +1 -0
- package/dist/addons/segmentation-refiner/index.mjs +209 -0
- package/dist/addons/segmentation-refiner/index.mjs.map +1 -0
- package/dist/addons/vehicle-classifier/index.d.mts +31 -0
- package/dist/addons/vehicle-classifier/index.d.ts +31 -0
- package/dist/addons/vehicle-classifier/index.js +689 -0
- package/dist/addons/vehicle-classifier/index.js.map +1 -0
- package/dist/addons/vehicle-classifier/index.mjs +250 -0
- package/dist/addons/vehicle-classifier/index.mjs.map +1 -0
- package/dist/{chunk-6OR5TE7A.mjs → chunk-22BHCDT5.mjs} +2 -2
- package/dist/chunk-22BHCDT5.mjs.map +1 -0
- package/dist/{chunk-LPI42WL6.mjs → chunk-6DJZZR64.mjs} +24 -12
- package/dist/chunk-6DJZZR64.mjs.map +1 -0
- package/dist/chunk-7DYHXUPZ.mjs +36 -0
- package/dist/chunk-7DYHXUPZ.mjs.map +1 -0
- package/dist/chunk-BJTO5JO5.mjs +11 -0
- package/dist/chunk-BP7H4NFS.mjs +412 -0
- package/dist/chunk-BP7H4NFS.mjs.map +1 -0
- package/dist/chunk-BR2FPGOX.mjs +98 -0
- package/dist/chunk-BR2FPGOX.mjs.map +1 -0
- package/dist/{chunk-B3R66MPF.mjs → chunk-DNQNGDR4.mjs} +58 -21
- package/dist/chunk-DNQNGDR4.mjs.map +1 -0
- package/dist/{chunk-ISOIDU4U.mjs → chunk-DUN6XU3N.mjs} +23 -5
- package/dist/chunk-DUN6XU3N.mjs.map +1 -0
- package/dist/{chunk-MEVASN3P.mjs → chunk-EPNWLSCG.mjs} +104 -22
- package/dist/chunk-EPNWLSCG.mjs.map +1 -0
- package/dist/{chunk-AYBFB7ID.mjs → chunk-G32RCIUI.mjs} +200 -318
- package/dist/chunk-G32RCIUI.mjs.map +1 -0
- package/dist/{chunk-3MQFUDRU.mjs → chunk-GR65KM6X.mjs} +76 -47
- package/dist/chunk-GR65KM6X.mjs.map +1 -0
- package/dist/{chunk-5AIQSN32.mjs → chunk-H7LMBTS5.mjs} +66 -17
- package/dist/chunk-H7LMBTS5.mjs.map +1 -0
- package/dist/{chunk-J4WRYHHY.mjs → chunk-IK4XIQPC.mjs} +66 -36
- package/dist/chunk-IK4XIQPC.mjs.map +1 -0
- package/dist/{chunk-5JJZGKL7.mjs → chunk-J6VNIIYX.mjs} +102 -19
- package/dist/chunk-J6VNIIYX.mjs.map +1 -0
- package/dist/{chunk-Q3SQOYG6.mjs → chunk-ML2JX43J.mjs} +67 -37
- package/dist/chunk-ML2JX43J.mjs.map +1 -0
- package/dist/{chunk-PDSHDDPV.mjs → chunk-WUMV524J.mjs} +159 -35
- package/dist/chunk-WUMV524J.mjs.map +1 -0
- package/dist/chunk-XZ6ZMXXU.mjs +39 -0
- package/dist/chunk-XZ6ZMXXU.mjs.map +1 -0
- package/dist/index.d.mts +17 -5
- package/dist/index.d.ts +17 -5
- package/dist/index.js +1344 -550
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +191 -20
- package/dist/index.mjs.map +1 -1
- package/package.json +95 -18
- package/python/coreml_inference.py +61 -18
- package/python/openvino_inference.py +12 -4
- package/python/pytorch_inference.py +12 -4
- package/dist/addons/camera-native-detection/index.d.mts +0 -32
- package/dist/addons/camera-native-detection/index.d.ts +0 -32
- package/dist/addons/camera-native-detection/index.js +0 -99
- package/dist/addons/camera-native-detection/index.js.map +0 -1
- package/dist/addons/camera-native-detection/index.mjs +0 -7
- package/dist/chunk-3MQFUDRU.mjs.map +0 -1
- package/dist/chunk-5AIQSN32.mjs.map +0 -1
- package/dist/chunk-5JJZGKL7.mjs.map +0 -1
- package/dist/chunk-6OR5TE7A.mjs.map +0 -1
- package/dist/chunk-AYBFB7ID.mjs.map +0 -1
- package/dist/chunk-B3R66MPF.mjs.map +0 -1
- package/dist/chunk-DTOAB2CE.mjs +0 -79
- package/dist/chunk-DTOAB2CE.mjs.map +0 -1
- package/dist/chunk-ISOIDU4U.mjs.map +0 -1
- package/dist/chunk-J4WRYHHY.mjs.map +0 -1
- package/dist/chunk-LPI42WL6.mjs.map +0 -1
- package/dist/chunk-MEVASN3P.mjs.map +0 -1
- package/dist/chunk-PDSHDDPV.mjs.map +0 -1
- package/dist/chunk-Q3SQOYG6.mjs.map +0 -1
- package/dist/chunk-QIMDG34B.mjs +0 -229
- package/dist/chunk-QIMDG34B.mjs.map +0 -1
- package/python/__pycache__/coreml_inference.cpython-313.pyc +0 -0
- package/python/__pycache__/openvino_inference.cpython-313.pyc +0 -0
- package/python/__pycache__/pytorch_inference.cpython-313.pyc +0 -0
- /package/dist/{addons/camera-native-detection/index.mjs.map → chunk-BJTO5JO5.mjs.map} +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ANIMAL_TYPE_MODELS
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-DUN6XU3N.mjs";
|
|
4
4
|
import {
|
|
5
5
|
cropRegion,
|
|
6
6
|
resizeAndNormalize
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-22BHCDT5.mjs";
|
|
8
8
|
import {
|
|
9
9
|
resolveEngine
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-6DJZZR64.mjs";
|
|
11
11
|
|
|
12
12
|
// src/addons/animal-classifier/index.ts
|
|
13
13
|
var ANIMAL_TYPE_LABEL = { id: "animal-type", name: "Animal Type" };
|
|
@@ -43,42 +43,49 @@ var AnimalClassifierAddon = class {
|
|
|
43
43
|
name: "Animal Classifier",
|
|
44
44
|
version: "0.1.0",
|
|
45
45
|
description: "ViT-based animal type classifier \u2014 10 common species",
|
|
46
|
-
packageName: "@camstack/addon-vision",
|
|
47
46
|
slot: "classifier",
|
|
47
|
+
labelOutputType: "classification",
|
|
48
48
|
inputClasses: ["animal"],
|
|
49
49
|
outputClasses: ["animal-type:*"],
|
|
50
50
|
supportsCustomModels: false,
|
|
51
51
|
mayRequirePython: false,
|
|
52
52
|
defaultConfig: {
|
|
53
53
|
modelId: "animals-10",
|
|
54
|
-
runtime: "
|
|
54
|
+
runtime: "node",
|
|
55
55
|
backend: "cpu",
|
|
56
56
|
minConfidence: 0.3
|
|
57
57
|
}
|
|
58
58
|
};
|
|
59
|
-
engine;
|
|
59
|
+
engine = null;
|
|
60
60
|
modelEntry;
|
|
61
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
|
+
}
|
|
62
76
|
async initialize(ctx) {
|
|
77
|
+
this.ctx = ctx;
|
|
63
78
|
const cfg = ctx.addonConfig;
|
|
64
|
-
const modelId = cfg["modelId"] ?? "animals-10";
|
|
65
|
-
const runtime = cfg["runtime"] ?? "auto";
|
|
66
|
-
const backend = cfg["backend"] ?? "cpu";
|
|
79
|
+
const modelId = cfg["modelId"] ?? this.resolvedConfig?.modelId ?? "animals-10";
|
|
67
80
|
this.minConfidence = cfg["minConfidence"] ?? 0.3;
|
|
68
81
|
const entry = ANIMAL_TYPE_MODELS.find((m) => m.id === modelId);
|
|
69
82
|
if (!entry) {
|
|
70
83
|
throw new Error(`AnimalClassifierAddon: unknown modelId "${modelId}"`);
|
|
71
84
|
}
|
|
72
85
|
this.modelEntry = entry;
|
|
73
|
-
const resolved = await resolveEngine({
|
|
74
|
-
runtime,
|
|
75
|
-
backend,
|
|
76
|
-
modelEntry: entry,
|
|
77
|
-
modelsDir: ctx.locationPaths.models
|
|
78
|
-
});
|
|
79
|
-
this.engine = resolved.engine;
|
|
80
86
|
}
|
|
81
87
|
async classify(input) {
|
|
88
|
+
if (!this.engine) await this.ensureEngine();
|
|
82
89
|
const start = Date.now();
|
|
83
90
|
const { width: inputW, height: inputH } = this.modelEntry.inputSize;
|
|
84
91
|
const animalCrop = await cropRegion(input.frame.data, input.roi);
|
|
@@ -113,6 +120,27 @@ var AnimalClassifierAddon = class {
|
|
|
113
120
|
modelId: this.modelEntry.id
|
|
114
121
|
};
|
|
115
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
|
+
}
|
|
116
144
|
async shutdown() {
|
|
117
145
|
await this.engine?.dispose();
|
|
118
146
|
}
|
|
@@ -137,22 +165,6 @@ var AnimalClassifierAddon = class {
|
|
|
137
165
|
}
|
|
138
166
|
]
|
|
139
167
|
},
|
|
140
|
-
{
|
|
141
|
-
id: "thresholds",
|
|
142
|
-
title: "Classification Settings",
|
|
143
|
-
columns: 1,
|
|
144
|
-
fields: [
|
|
145
|
-
{
|
|
146
|
-
key: "minConfidence",
|
|
147
|
-
label: "Minimum Confidence",
|
|
148
|
-
type: "slider",
|
|
149
|
-
min: 0.05,
|
|
150
|
-
max: 1,
|
|
151
|
-
step: 0.05,
|
|
152
|
-
default: 0.3
|
|
153
|
-
}
|
|
154
|
-
]
|
|
155
|
-
},
|
|
156
168
|
{
|
|
157
169
|
id: "runtime",
|
|
158
170
|
title: "Runtime",
|
|
@@ -163,23 +175,41 @@ var AnimalClassifierAddon = class {
|
|
|
163
175
|
label: "Runtime",
|
|
164
176
|
type: "select",
|
|
165
177
|
options: [
|
|
166
|
-
{ value: "auto", label: "Auto
|
|
178
|
+
{ value: "auto", label: "Auto" },
|
|
167
179
|
{ value: "onnx", label: "ONNX Runtime" },
|
|
168
|
-
{ value: "coreml", label: "CoreML (Apple)" }
|
|
180
|
+
{ value: "coreml", label: "CoreML (Apple)" },
|
|
181
|
+
{ value: "openvino", label: "OpenVINO (Intel)" }
|
|
169
182
|
]
|
|
170
183
|
},
|
|
171
184
|
{
|
|
172
185
|
key: "backend",
|
|
173
186
|
label: "Backend",
|
|
174
187
|
type: "select",
|
|
175
|
-
|
|
188
|
+
showWhen: { field: "runtime", equals: "onnx" },
|
|
176
189
|
options: [
|
|
190
|
+
{ value: "auto", label: "Auto" },
|
|
177
191
|
{ value: "cpu", label: "CPU" },
|
|
178
192
|
{ value: "coreml", label: "CoreML" },
|
|
179
193
|
{ value: "cuda", label: "CUDA (NVIDIA)" }
|
|
180
194
|
]
|
|
181
195
|
}
|
|
182
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
|
+
]
|
|
183
213
|
}
|
|
184
214
|
]
|
|
185
215
|
};
|
|
@@ -209,4 +239,4 @@ var AnimalClassifierAddon = class {
|
|
|
209
239
|
export {
|
|
210
240
|
AnimalClassifierAddon
|
|
211
241
|
};
|
|
212
|
-
//# sourceMappingURL=chunk-
|
|
242
|
+
//# sourceMappingURL=chunk-IK4XIQPC.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
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":";;;;;;;;;;;;AAqBA,IAAM,oBAAqC,EAAE,IAAI,eAAe,MAAM,cAAc;AACpF,IAAM,qBAAiD,CAAC,iBAAiB;AACzE,IAAM,mBAAuC,EAAE,SAAS,CAAC,GAAG,kBAAkB,KAAK;AAEnF,IAAM,oBAAoB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,QAAQ,QAAoC;AACnD,QAAM,MAAM,OAAO,OAAO,CAAC,GAAG,MAAM,KAAK,IAAI,GAAG,CAAC,GAAG,SAAS;AAC7D,QAAM,OAAO,OAAO,IAAI,CAAC,MAAM,KAAK,IAAI,IAAI,GAAG,CAAC;AAChD,QAAM,MAAM,KAAK,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC;AAC1C,SAAO,KAAK,IAAI,CAAC,MAAM,IAAI,GAAG;AAChC;AAEA,IAAqB,wBAArB,MAA2F;AAAA,EAChF,KAAK;AAAA,EACL,OAAO;AAAA,EACP,eAAe,CAAC,QAAQ;AAAA,EACxB,gBAAgB,CAAC,eAAe;AAAA,EAChC,eAAe;AAAA,EACf,gBAAgB,CAAC;AAAA,EACjB,WAA0B;AAAA,IACjC,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,SAAS;AAAA,IAET,aAAa;AAAA,IAEb,MAAM;AAAA,IACN,iBAAiB;AAAA,IACjB,cAAc,CAAC,QAAQ;AAAA,IACvB,eAAe,CAAC,eAAe;AAAA,IAC/B,sBAAsB;AAAA,IACtB,kBAAkB;AAAA,IAClB,eAAe;AAAA,MACb,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,eAAe;AAAA,IACjB;AAAA,EACF;AAAA,EAEQ,SAAkC;AAAA,EAClC;AAAA,EACA,gBAAgB;AAAA,EAChB,iBAAiD;AAAA,EACjD,MAA2B;AAAA,EAEnC,uBAA2C;AACzC,WAAO,mBAAmB,IAAI,CAAC,OAAO;AAAA,MACpC,SAAS,EAAE;AAAA,MACX,MAAM,EAAE;AAAA,MACR,WAAW;AAAA,MACX,eAAe;AAAA,MACf,SAAS,OAAO,KAAK,EAAE,OAAO;AAAA,IAChC,EAAE;AAAA,EACJ;AAAA,EAEA,UAAU,QAAuC;AAC/C,SAAK,iBAAiB;AAAA,EACxB;AAAA,EAEA,MAAM,WAAW,KAAkC;AACjD,SAAK,MAAM;AACX,UAAM,MAAM,IAAI;AAChB,UAAM,UAAW,IAAI,SAAS,KAA4B,KAAK,gBAAgB,WAAW;AAC1F,SAAK,gBAAiB,IAAI,eAAe,KAA4B;AAErE,UAAM,QAAQ,mBAAmB,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO;AAC7D,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,MAAM,2CAA2C,OAAO,GAAG;AAAA,IACvE;AACA,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,MAAM,SAAS,OAA6C;AAC1D,QAAI,CAAC,KAAK,OAAQ,OAAM,KAAK,aAAa;AAC1C,UAAM,QAAQ,KAAK,IAAI;AACvB,UAAM,EAAE,OAAO,QAAQ,QAAQ,OAAO,IAAI,KAAK,WAAW;AAG1D,UAAM,aAAa,MAAM,WAAW,MAAM,MAAM,MAAM,MAAM,GAAG;AAG/D,UAAM,aAAa,MAAM,mBAAmB,YAAY,QAAQ,QAAQ,YAAY,MAAM;AAG1F,UAAM,YAAY,MAAM,KAAK,OAAQ,IAAI,YAAY,CAAC,GAAG,GAAG,QAAQ,MAAM,CAAC;AAG3E,UAAM,QAAQ,QAAQ,SAAS;AAG/B,QAAI,SAAS;AACb,QAAI,WAAW,MAAM,CAAC,KAAK;AAC3B,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAM,QAAQ,MAAM,CAAC,KAAK;AAC1B,UAAI,QAAQ,UAAU;AACpB,mBAAW;AACX,iBAAS;AAAA,MACX;AAAA,IACF;AAEA,QAAI,WAAW,KAAK,eAAe;AACjC,aAAO;AAAA,QACL,iBAAiB,CAAC;AAAA,QAClB,aAAa,KAAK,IAAI,IAAI;AAAA,QAC1B,SAAS,KAAK,WAAW;AAAA,MAC3B;AAAA,IACF;AAEA,UAAM,QAAQ,kBAAkB,MAAM,KAAK,UAAU,MAAM;AAE3D,WAAO;AAAA,MACL,iBAAiB;AAAA,QACf;AAAA,UACE,OAAO;AAAA,UACP,OAAO;AAAA,QACT;AAAA,MACF;AAAA,MACA,aAAa,KAAK,IAAI,IAAI;AAAA,MAC1B,SAAS,KAAK,WAAW;AAAA,IAC3B;AAAA,EACF;AAAA,EAEA,MAAc,eAA8B;AAC1C,UAAM,SAAS,KAAK;AACpB,UAAM,UAAU,QAAQ,WAAW,KAAK,WAAW;AACnD,UAAM,UAAU,QAAQ,YAAY,WAAW,WAAY,QAAQ,YAAY,SAAS,SAAS;AACjG,UAAM,UAAU,QAAQ,WAAW;AACnC,UAAM,SAAS,QAAQ,UAAU;AAEjC,UAAM,QAAQ,mBAAmB,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO,KAAK,KAAK;AACvE,SAAK,aAAa;AAElB,UAAM,YAAY,KAAK,IAAK,QAAQ,aAAa,KAAK,KAAK,IAAK,cAAc;AAE9E,QAAI,KAAK,IAAK,QAAQ;AACpB,YAAM,KAAK,IAAK,OAAO,OAAO,SAAS,MAAa;AAAA,IACtD;AAEA,UAAM,WAAW,MAAM,cAAc;AAAA,MACnC;AAAA,MACA;AAAA,MACA,YAAY;AAAA,MACZ;AAAA,MACA,QAAQ,KAAK,IAAK;AAAA,IACpB,CAAC;AACD,SAAK,SAAS,SAAS;AAAA,EACzB;AAAA,EAEA,MAAM,WAA0B;AAC9B,UAAM,KAAK,QAAQ,QAAQ;AAAA,EAC7B;AAAA,EAEA,kBAAkC;AAChC,WAAO;AAAA,MACL,UAAU;AAAA,QACR;AAAA,UACE,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,SAAS;AAAA,UACT,QAAQ;AAAA,YACN;AAAA,cACE,KAAK;AAAA,cACL,OAAO;AAAA,cACP,MAAM;AAAA,cACN,SAAS,CAAC,GAAG,kBAAkB;AAAA,cAC/B,aAAa;AAAA,cACb,iBAAiB;AAAA,cACjB,eAAe,CAAC,QAAQ,UAAU,UAAU;AAAA,cAC5C,kBAAkB,CAAC,aAAa,QAAQ;AAAA,cACxC,kBAAkB;AAAA,YACpB;AAAA,UACF;AAAA,QACF;AAAA,QACA;AAAA,UACE,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,SAAS;AAAA,UACT,QAAQ;AAAA,YACN;AAAA,cACE,KAAK;AAAA,cACL,OAAO;AAAA,cACP,MAAM;AAAA,cACN,SAAS;AAAA,gBACP,EAAE,OAAO,QAAQ,OAAO,OAAO;AAAA,gBAC/B,EAAE,OAAO,QAAQ,OAAO,eAAe;AAAA,gBACvC,EAAE,OAAO,UAAU,OAAO,iBAAiB;AAAA,gBAC3C,EAAE,OAAO,YAAY,OAAO,mBAAmB;AAAA,cACjD;AAAA,YACF;AAAA,YACA;AAAA,cACE,KAAK;AAAA,cACL,OAAO;AAAA,cACP,MAAM;AAAA,cACN,UAAU,EAAE,OAAO,WAAW,QAAQ,OAAO;AAAA,cAC7C,SAAS;AAAA,gBACP,EAAE,OAAO,QAAQ,OAAO,OAAO;AAAA,gBAC/B,EAAE,OAAO,OAAO,OAAO,MAAM;AAAA,gBAC7B,EAAE,OAAO,UAAU,OAAO,SAAS;AAAA,gBACnC,EAAE,OAAO,QAAQ,OAAO,gBAAgB;AAAA,cAC1C;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,QACA;AAAA,UACE,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,SAAS;AAAA,UACT,QAAQ;AAAA,YACN;AAAA,cACE,KAAK;AAAA,cACL,OAAO;AAAA,cACP,MAAM;AAAA,cACN,KAAK;AAAA,cACL,KAAK;AAAA,cACL,MAAM;AAAA,cACN,SAAS;AAAA,YACX;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,cAAkC;AAChC,WAAO;AAAA,EACT;AAAA,EAEA,kBAAuC;AACrC,WAAO,CAAC,GAAG,kBAAkB;AAAA,EAC/B;AAAA,EAEA,qBAAuC;AACrC,WAAO,CAAC;AAAA,EACV;AAAA,EAEA,kBAA8C;AAC5C,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,QAA8B;AAClC,WAAO;AAAA,MACL,WAAW;AAAA,MACX,SAAS,KAAK,QAAQ,WAAW;AAAA,MACjC,QAAQ,KAAK,QAAQ,UAAU;AAAA,MAC/B,cAAc,CAAC,MAAM;AAAA,IACvB;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
yoloPostprocess
|
|
3
3
|
} from "./chunk-KUO2BVFY.mjs";
|
|
4
|
+
import {
|
|
5
|
+
MLPACKAGE_FILES
|
|
6
|
+
} from "./chunk-BP7H4NFS.mjs";
|
|
4
7
|
import {
|
|
5
8
|
cropRegion,
|
|
6
9
|
letterbox
|
|
7
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-22BHCDT5.mjs";
|
|
8
11
|
import {
|
|
9
12
|
resolveEngine
|
|
10
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-6DJZZR64.mjs";
|
|
11
14
|
|
|
12
15
|
// src/catalogs/plate-detection-models.ts
|
|
13
16
|
import { hfModelUrl } from "@camstack/types";
|
|
@@ -29,15 +32,20 @@ var PLATE_DETECTION_MODELS = [
|
|
|
29
32
|
},
|
|
30
33
|
coreml: {
|
|
31
34
|
url: hfModelUrl(HF_REPO, "plateDetection/yolov8-plate/coreml/camstack-yolov8n-plate.mlpackage"),
|
|
32
|
-
sizeMB: 5.9
|
|
35
|
+
sizeMB: 5.9,
|
|
36
|
+
isDirectory: true,
|
|
37
|
+
files: MLPACKAGE_FILES,
|
|
38
|
+
runtimes: ["python"]
|
|
33
39
|
},
|
|
34
40
|
openvino: {
|
|
35
41
|
url: hfModelUrl(HF_REPO, "plateDetection/yolov8-plate/openvino/camstack-yolov8n-plate.xml"),
|
|
36
|
-
sizeMB: 6.1
|
|
42
|
+
sizeMB: 6.1,
|
|
43
|
+
runtimes: ["python"]
|
|
37
44
|
},
|
|
38
45
|
tflite: {
|
|
39
46
|
url: hfModelUrl(HF_REPO, "plateDetection/yolov8-plate/tflite/camstack-yolov8n-plate_float32.tflite"),
|
|
40
|
-
sizeMB: 12
|
|
47
|
+
sizeMB: 12,
|
|
48
|
+
runtimes: ["python"]
|
|
41
49
|
}
|
|
42
50
|
}
|
|
43
51
|
}
|
|
@@ -58,7 +66,6 @@ var PlateDetectionAddon = class {
|
|
|
58
66
|
name: "License Plate Detection",
|
|
59
67
|
version: "0.1.0",
|
|
60
68
|
description: "YOLO-based license plate detector \u2014 crops plate regions from vehicle detections",
|
|
61
|
-
packageName: "@camstack/addon-vision",
|
|
62
69
|
slot: "cropper",
|
|
63
70
|
inputClasses: ["vehicle"],
|
|
64
71
|
outputClasses: ["plate"],
|
|
@@ -66,21 +73,34 @@ var PlateDetectionAddon = class {
|
|
|
66
73
|
mayRequirePython: false,
|
|
67
74
|
defaultConfig: {
|
|
68
75
|
modelId: "yolov8n-plate",
|
|
69
|
-
runtime: "
|
|
76
|
+
runtime: "node",
|
|
70
77
|
backend: "cpu",
|
|
71
78
|
confidence: 0.5,
|
|
72
79
|
iouThreshold: 0.45
|
|
73
80
|
}
|
|
74
81
|
};
|
|
75
|
-
engine;
|
|
82
|
+
engine = null;
|
|
76
83
|
modelEntry;
|
|
77
84
|
confidence = 0.5;
|
|
78
85
|
iouThreshold = 0.45;
|
|
86
|
+
resolvedConfig = null;
|
|
87
|
+
ctx = null;
|
|
88
|
+
getModelRequirements() {
|
|
89
|
+
return PLATE_DETECTION_MODELS.map((m) => ({
|
|
90
|
+
modelId: m.id,
|
|
91
|
+
name: m.name,
|
|
92
|
+
minRAM_MB: 80,
|
|
93
|
+
accuracyScore: 60,
|
|
94
|
+
formats: Object.keys(m.formats)
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
configure(config) {
|
|
98
|
+
this.resolvedConfig = config;
|
|
99
|
+
}
|
|
79
100
|
async initialize(ctx) {
|
|
101
|
+
this.ctx = ctx;
|
|
80
102
|
const cfg = ctx.addonConfig;
|
|
81
|
-
const modelId = cfg["modelId"] ?? "yolov8n-plate";
|
|
82
|
-
const runtime = cfg["runtime"] ?? "auto";
|
|
83
|
-
const backend = cfg["backend"] ?? "cpu";
|
|
103
|
+
const modelId = cfg["modelId"] ?? this.resolvedConfig?.modelId ?? "yolov8n-plate";
|
|
84
104
|
this.confidence = cfg["confidence"] ?? 0.5;
|
|
85
105
|
this.iouThreshold = cfg["iouThreshold"] ?? 0.45;
|
|
86
106
|
const entry = PLATE_DETECTION_MODELS.find((m) => m.id === modelId);
|
|
@@ -88,15 +108,9 @@ var PlateDetectionAddon = class {
|
|
|
88
108
|
throw new Error(`PlateDetectionAddon: unknown modelId "${modelId}"`);
|
|
89
109
|
}
|
|
90
110
|
this.modelEntry = entry;
|
|
91
|
-
const resolved = await resolveEngine({
|
|
92
|
-
runtime,
|
|
93
|
-
backend,
|
|
94
|
-
modelEntry: entry,
|
|
95
|
-
modelsDir: ctx.locationPaths.models
|
|
96
|
-
});
|
|
97
|
-
this.engine = resolved.engine;
|
|
98
111
|
}
|
|
99
112
|
async crop(input) {
|
|
113
|
+
if (!this.engine) await this.ensureEngine();
|
|
100
114
|
const start = Date.now();
|
|
101
115
|
const { width: inputW, height: inputH } = this.modelEntry.inputSize;
|
|
102
116
|
const targetSize = Math.max(inputW, inputH);
|
|
@@ -123,12 +137,81 @@ var PlateDetectionAddon = class {
|
|
|
123
137
|
modelId: this.modelEntry.id
|
|
124
138
|
};
|
|
125
139
|
}
|
|
140
|
+
async ensureEngine() {
|
|
141
|
+
const config = this.resolvedConfig;
|
|
142
|
+
const modelId = config?.modelId ?? this.modelEntry.id;
|
|
143
|
+
const runtime = config?.runtime === "python" ? "coreml" : config?.runtime === "node" ? "onnx" : "auto";
|
|
144
|
+
const backend = config?.backend ?? "cpu";
|
|
145
|
+
const format = config?.format ?? "onnx";
|
|
146
|
+
const entry = PLATE_DETECTION_MODELS.find((m) => m.id === modelId) ?? this.modelEntry;
|
|
147
|
+
this.modelEntry = entry;
|
|
148
|
+
const modelsDir = this.ctx.models?.getModelsDir() ?? this.ctx.locationPaths.models;
|
|
149
|
+
if (this.ctx.models) {
|
|
150
|
+
await this.ctx.models.ensure(modelId, format);
|
|
151
|
+
}
|
|
152
|
+
const resolved = await resolveEngine({
|
|
153
|
+
runtime,
|
|
154
|
+
backend,
|
|
155
|
+
modelEntry: entry,
|
|
156
|
+
modelsDir,
|
|
157
|
+
models: this.ctx.models
|
|
158
|
+
});
|
|
159
|
+
this.engine = resolved.engine;
|
|
160
|
+
}
|
|
126
161
|
async shutdown() {
|
|
127
162
|
await this.engine?.dispose();
|
|
128
163
|
}
|
|
129
164
|
getConfigSchema() {
|
|
130
165
|
return {
|
|
131
166
|
sections: [
|
|
167
|
+
{
|
|
168
|
+
id: "model",
|
|
169
|
+
title: "Model",
|
|
170
|
+
columns: 1,
|
|
171
|
+
fields: [
|
|
172
|
+
{
|
|
173
|
+
key: "modelId",
|
|
174
|
+
label: "Model",
|
|
175
|
+
type: "model-selector",
|
|
176
|
+
catalog: [...PLATE_DETECTION_MODELS],
|
|
177
|
+
allowCustom: false,
|
|
178
|
+
allowConversion: false,
|
|
179
|
+
acceptFormats: ["onnx", "coreml", "openvino"],
|
|
180
|
+
requiredMetadata: ["inputSize", "labels", "outputFormat"],
|
|
181
|
+
outputFormatHint: "yolo"
|
|
182
|
+
}
|
|
183
|
+
]
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
id: "runtime",
|
|
187
|
+
title: "Runtime",
|
|
188
|
+
columns: 2,
|
|
189
|
+
fields: [
|
|
190
|
+
{
|
|
191
|
+
key: "runtime",
|
|
192
|
+
label: "Runtime",
|
|
193
|
+
type: "select",
|
|
194
|
+
options: [
|
|
195
|
+
{ value: "auto", label: "Auto" },
|
|
196
|
+
{ value: "onnx", label: "ONNX Runtime" },
|
|
197
|
+
{ value: "coreml", label: "CoreML (Apple)" },
|
|
198
|
+
{ value: "openvino", label: "OpenVINO (Intel)" }
|
|
199
|
+
]
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
key: "backend",
|
|
203
|
+
label: "Backend",
|
|
204
|
+
type: "select",
|
|
205
|
+
showWhen: { field: "runtime", equals: "onnx" },
|
|
206
|
+
options: [
|
|
207
|
+
{ value: "auto", label: "Auto" },
|
|
208
|
+
{ value: "cpu", label: "CPU" },
|
|
209
|
+
{ value: "coreml", label: "CoreML" },
|
|
210
|
+
{ value: "cuda", label: "CUDA (NVIDIA)" }
|
|
211
|
+
]
|
|
212
|
+
}
|
|
213
|
+
]
|
|
214
|
+
},
|
|
132
215
|
{
|
|
133
216
|
id: "thresholds",
|
|
134
217
|
title: "Detection Thresholds",
|
|
@@ -183,4 +266,4 @@ export {
|
|
|
183
266
|
PLATE_DETECTION_MODELS,
|
|
184
267
|
PlateDetectionAddon
|
|
185
268
|
};
|
|
186
|
-
//# sourceMappingURL=chunk-
|
|
269
|
+
//# sourceMappingURL=chunk-J6VNIIYX.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/catalogs/plate-detection-models.ts","../src/addons/plate-detection/index.ts"],"sourcesContent":["import type { ModelCatalogEntry, LabelDefinition } from '@camstack/types'\nimport { hfModelUrl } from '@camstack/types'\nimport { MLPACKAGE_FILES } from './object-detection-models.js'\n\nconst HF_REPO = 'camstack/camstack-models'\n\nconst PLATE_LABELS: readonly LabelDefinition[] = [\n { id: 'plate', name: 'License Plate' },\n] as const\n\nexport const PLATE_DETECTION_MODELS: readonly ModelCatalogEntry[] = [\n {\n id: 'yolov8n-plate',\n name: 'YOLOv8 Nano — License Plate',\n description: 'YOLOv8 Nano fine-tuned for license plate detection',\n inputSize: { width: 640, height: 640 },\n labels: PLATE_LABELS,\n formats: {\n onnx: {\n url: hfModelUrl(HF_REPO, 'plateDetection/yolov8-plate/onnx/camstack-yolov8n-plate.onnx'),\n sizeMB: 12,\n },\n coreml: {\n url: hfModelUrl(HF_REPO, 'plateDetection/yolov8-plate/coreml/camstack-yolov8n-plate.mlpackage'),\n sizeMB: 5.9,\n isDirectory: true,\n files: MLPACKAGE_FILES,\n runtimes: ['python'],\n },\n openvino: {\n url: hfModelUrl(HF_REPO, 'plateDetection/yolov8-plate/openvino/camstack-yolov8n-plate.xml'),\n sizeMB: 6.1,\n runtimes: ['python'],\n },\n tflite: {\n url: hfModelUrl(HF_REPO, 'plateDetection/yolov8-plate/tflite/camstack-yolov8n-plate_float32.tflite'),\n sizeMB: 12,\n runtimes: ['python'],\n },\n },\n },\n] as const\n","import type {\n ICropperProvider,\n IDetectionAddon,\n AddonManifest,\n AddonContext,\n CropInput,\n CropperOutput,\n ConfigUISchema,\n ClassMapDefinition,\n ProbeResult,\n ModelCatalogEntry,\n DetectionModel,\n LabelDefinition,\n IInferenceEngine,\n ModelRequirement,\n ResolvedInferenceConfig,\n} from '@camstack/types'\nimport { PLATE_DETECTION_MODELS } from '../../catalogs/plate-detection-models.js'\nimport { cropRegion, letterbox } from '../../shared/image-utils.js'\nimport { yoloPostprocess } from '../../shared/postprocess/yolo.js'\nimport { resolveEngine } from '../../shared/engine-resolver.js'\n\nconst PLATE_LABEL: LabelDefinition = { id: 'plate', name: 'License Plate' }\nconst PLATE_LABELS: readonly LabelDefinition[] = [PLATE_LABEL]\nconst PLATE_CLASS_MAP: ClassMapDefinition = { mapping: {}, preserveOriginal: true }\n\nexport default class PlateDetectionAddon implements ICropperProvider, IDetectionAddon {\n readonly id = 'plate-detection'\n readonly slot = 'cropper' as const\n readonly inputClasses = ['vehicle'] as const\n readonly outputClasses = ['plate'] as const\n readonly slotPriority = 0\n readonly manifest: AddonManifest = {\n id: 'plate-detection',\n name: 'License Plate Detection',\n version: '0.1.0',\n\n description: 'YOLO-based license plate detector — crops plate regions from vehicle detections',\n\n slot: 'cropper',\n inputClasses: ['vehicle'],\n outputClasses: ['plate'],\n supportsCustomModels: false,\n mayRequirePython: false,\n defaultConfig: {\n modelId: 'yolov8n-plate',\n runtime: 'node',\n backend: 'cpu',\n confidence: 0.5,\n iouThreshold: 0.45,\n },\n }\n\n private engine: IInferenceEngine | null = null\n private modelEntry!: ModelCatalogEntry\n private confidence = 0.5\n private iouThreshold = 0.45\n private resolvedConfig: ResolvedInferenceConfig | null = null\n private ctx: AddonContext | null = null\n\n getModelRequirements(): ModelRequirement[] {\n return PLATE_DETECTION_MODELS.map((m) => ({\n modelId: m.id,\n name: m.name,\n minRAM_MB: 80,\n accuracyScore: 60,\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 ?? 'yolov8n-plate'\n this.confidence = (cfg['confidence'] as number | undefined) ?? 0.5\n this.iouThreshold = (cfg['iouThreshold'] as number | undefined) ?? 0.45\n\n const entry = PLATE_DETECTION_MODELS.find((m) => m.id === modelId)\n if (!entry) {\n throw new Error(`PlateDetectionAddon: unknown modelId \"${modelId}\"`)\n }\n this.modelEntry = entry\n }\n\n async crop(input: CropInput): Promise<CropperOutput> {\n if (!this.engine) await this.ensureEngine()\n const start = Date.now()\n const { width: inputW, height: inputH } = this.modelEntry.inputSize\n const targetSize = Math.max(inputW, inputH)\n\n // Crop the vehicle region from the full frame\n const vehicleCrop = await cropRegion(input.frame.data, input.roi)\n\n const lb = await letterbox(vehicleCrop, targetSize)\n const output = await this.engine!.run(lb.data, [1, 3, targetSize, targetSize])\n\n const numClasses = this.modelEntry.labels.length\n const numBoxes = output.length / (4 + numClasses)\n const labels = this.modelEntry.labels.map((l) => l.id)\n\n const plates = yoloPostprocess(output, numClasses, numBoxes, {\n confidence: this.confidence,\n iouThreshold: this.iouThreshold,\n labels,\n scale: lb.scale,\n padX: lb.padX,\n padY: lb.padY,\n originalWidth: lb.originalWidth,\n originalHeight: lb.originalHeight,\n })\n\n // Override class to 'plate'\n const crops = plates.map((p) => ({ ...p, class: 'plate', originalClass: p.originalClass }))\n\n return {\n crops,\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 = PLATE_DETECTION_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: [...PLATE_DETECTION_MODELS],\n allowCustom: false,\n allowConversion: false,\n acceptFormats: ['onnx', 'coreml', 'openvino'],\n requiredMetadata: ['inputSize', 'labels', 'outputFormat'],\n outputFormatHint: 'yolo',\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: 'Detection Thresholds',\n columns: 2,\n fields: [\n {\n key: 'confidence',\n label: 'Confidence Threshold',\n type: 'slider',\n min: 0.1,\n max: 1.0,\n step: 0.05,\n default: 0.5,\n },\n {\n key: 'iouThreshold',\n label: 'IoU Threshold (NMS)',\n type: 'slider',\n min: 0.1,\n max: 1.0,\n step: 0.05,\n default: 0.45,\n },\n ],\n },\n ],\n }\n }\n\n getClassMap(): ClassMapDefinition {\n return PLATE_CLASS_MAP\n }\n\n getModelCatalog(): ModelCatalogEntry[] {\n return [...PLATE_DETECTION_MODELS]\n }\n\n getAvailableModels(): DetectionModel[] {\n return []\n }\n\n getActiveLabels(): readonly LabelDefinition[] {\n return PLATE_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":["PLATE_LABELS"]}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BIRD_SPECIES_MODELS
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-DUN6XU3N.mjs";
|
|
4
4
|
import {
|
|
5
5
|
cropRegion,
|
|
6
6
|
resizeAndNormalize
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-22BHCDT5.mjs";
|
|
8
8
|
import {
|
|
9
9
|
resolveEngine
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-6DJZZR64.mjs";
|
|
11
11
|
|
|
12
12
|
// src/addons/bird-global-classifier/index.ts
|
|
13
13
|
import * as fs from "fs";
|
|
@@ -47,44 +47,50 @@ var BirdGlobalClassifierAddon = class {
|
|
|
47
47
|
name: "Bird Classifier (Global, 525 species)",
|
|
48
48
|
version: "0.1.0",
|
|
49
49
|
description: "EfficientNet \u2014 525 worldwide bird species (MIT license, ONNX only)",
|
|
50
|
-
packageName: "@camstack/addon-vision",
|
|
51
50
|
slot: "classifier",
|
|
51
|
+
labelOutputType: "classification",
|
|
52
52
|
inputClasses: ["animal"],
|
|
53
53
|
outputClasses: ["species:*"],
|
|
54
54
|
supportsCustomModels: false,
|
|
55
55
|
mayRequirePython: false,
|
|
56
56
|
defaultConfig: {
|
|
57
57
|
modelId: "bird-species-525",
|
|
58
|
-
runtime: "
|
|
58
|
+
runtime: "node",
|
|
59
59
|
backend: "cpu",
|
|
60
60
|
minConfidence: 0.3
|
|
61
61
|
}
|
|
62
62
|
};
|
|
63
|
-
engine;
|
|
63
|
+
engine = null;
|
|
64
64
|
modelEntry;
|
|
65
65
|
labels = [];
|
|
66
66
|
minConfidence = 0.3;
|
|
67
|
+
resolvedConfig = null;
|
|
68
|
+
ctx = null;
|
|
69
|
+
getModelRequirements() {
|
|
70
|
+
return BIRD_SPECIES_MODELS.map((m) => ({
|
|
71
|
+
modelId: m.id,
|
|
72
|
+
name: m.name,
|
|
73
|
+
minRAM_MB: 120,
|
|
74
|
+
accuracyScore: 80,
|
|
75
|
+
formats: Object.keys(m.formats)
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
configure(config) {
|
|
79
|
+
this.resolvedConfig = config;
|
|
80
|
+
}
|
|
67
81
|
async initialize(ctx) {
|
|
82
|
+
this.ctx = ctx;
|
|
68
83
|
const cfg = ctx.addonConfig;
|
|
69
|
-
const modelId = cfg["modelId"] ?? "bird-species-525";
|
|
70
|
-
const runtime = cfg["runtime"] ?? "auto";
|
|
71
|
-
const backend = cfg["backend"] ?? "cpu";
|
|
84
|
+
const modelId = cfg["modelId"] ?? this.resolvedConfig?.modelId ?? "bird-species-525";
|
|
72
85
|
this.minConfidence = cfg["minConfidence"] ?? 0.3;
|
|
73
86
|
const entry = BIRD_SPECIES_MODELS.find((m) => m.id === modelId);
|
|
74
87
|
if (!entry) {
|
|
75
88
|
throw new Error(`BirdGlobalClassifierAddon: unknown modelId "${modelId}"`);
|
|
76
89
|
}
|
|
77
90
|
this.modelEntry = entry;
|
|
78
|
-
this.labels = loadLabels(ctx.locationPaths.models, modelId);
|
|
79
|
-
const resolved = await resolveEngine({
|
|
80
|
-
runtime,
|
|
81
|
-
backend,
|
|
82
|
-
modelEntry: entry,
|
|
83
|
-
modelsDir: ctx.locationPaths.models
|
|
84
|
-
});
|
|
85
|
-
this.engine = resolved.engine;
|
|
86
91
|
}
|
|
87
92
|
async classify(input) {
|
|
93
|
+
if (!this.engine) await this.ensureEngine();
|
|
88
94
|
const start = Date.now();
|
|
89
95
|
const { width: inputW, height: inputH } = this.modelEntry.inputSize;
|
|
90
96
|
const animalCrop = await cropRegion(input.frame.data, input.roi);
|
|
@@ -119,6 +125,28 @@ var BirdGlobalClassifierAddon = class {
|
|
|
119
125
|
modelId: this.modelEntry.id
|
|
120
126
|
};
|
|
121
127
|
}
|
|
128
|
+
async ensureEngine() {
|
|
129
|
+
const config = this.resolvedConfig;
|
|
130
|
+
const modelId = config?.modelId ?? this.modelEntry.id;
|
|
131
|
+
const runtime = config?.runtime === "python" ? "coreml" : config?.runtime === "node" ? "onnx" : "auto";
|
|
132
|
+
const backend = config?.backend ?? "cpu";
|
|
133
|
+
const format = config?.format ?? "onnx";
|
|
134
|
+
const entry = BIRD_SPECIES_MODELS.find((m) => m.id === modelId) ?? this.modelEntry;
|
|
135
|
+
this.modelEntry = entry;
|
|
136
|
+
const modelsDir = this.ctx.models?.getModelsDir() ?? this.ctx.locationPaths.models;
|
|
137
|
+
if (this.ctx.models) {
|
|
138
|
+
await this.ctx.models.ensure(modelId, format);
|
|
139
|
+
}
|
|
140
|
+
this.labels = loadLabels(modelsDir, modelId);
|
|
141
|
+
const resolved = await resolveEngine({
|
|
142
|
+
runtime,
|
|
143
|
+
backend,
|
|
144
|
+
modelEntry: entry,
|
|
145
|
+
modelsDir,
|
|
146
|
+
models: this.ctx.models
|
|
147
|
+
});
|
|
148
|
+
this.engine = resolved.engine;
|
|
149
|
+
}
|
|
122
150
|
async shutdown() {
|
|
123
151
|
await this.engine?.dispose();
|
|
124
152
|
}
|
|
@@ -143,22 +171,6 @@ var BirdGlobalClassifierAddon = class {
|
|
|
143
171
|
}
|
|
144
172
|
]
|
|
145
173
|
},
|
|
146
|
-
{
|
|
147
|
-
id: "thresholds",
|
|
148
|
-
title: "Classification Settings",
|
|
149
|
-
columns: 1,
|
|
150
|
-
fields: [
|
|
151
|
-
{
|
|
152
|
-
key: "minConfidence",
|
|
153
|
-
label: "Minimum Confidence",
|
|
154
|
-
type: "slider",
|
|
155
|
-
min: 0.05,
|
|
156
|
-
max: 1,
|
|
157
|
-
step: 0.05,
|
|
158
|
-
default: 0.3
|
|
159
|
-
}
|
|
160
|
-
]
|
|
161
|
-
},
|
|
162
174
|
{
|
|
163
175
|
id: "runtime",
|
|
164
176
|
title: "Runtime",
|
|
@@ -169,23 +181,41 @@ var BirdGlobalClassifierAddon = class {
|
|
|
169
181
|
label: "Runtime",
|
|
170
182
|
type: "select",
|
|
171
183
|
options: [
|
|
172
|
-
{ value: "auto", label: "Auto
|
|
184
|
+
{ value: "auto", label: "Auto" },
|
|
173
185
|
{ value: "onnx", label: "ONNX Runtime" },
|
|
174
|
-
{ value: "coreml", label: "CoreML (Apple)" }
|
|
186
|
+
{ value: "coreml", label: "CoreML (Apple)" },
|
|
187
|
+
{ value: "openvino", label: "OpenVINO (Intel)" }
|
|
175
188
|
]
|
|
176
189
|
},
|
|
177
190
|
{
|
|
178
191
|
key: "backend",
|
|
179
192
|
label: "Backend",
|
|
180
193
|
type: "select",
|
|
181
|
-
|
|
194
|
+
showWhen: { field: "runtime", equals: "onnx" },
|
|
182
195
|
options: [
|
|
196
|
+
{ value: "auto", label: "Auto" },
|
|
183
197
|
{ value: "cpu", label: "CPU" },
|
|
184
198
|
{ value: "coreml", label: "CoreML" },
|
|
185
199
|
{ value: "cuda", label: "CUDA (NVIDIA)" }
|
|
186
200
|
]
|
|
187
201
|
}
|
|
188
202
|
]
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
id: "thresholds",
|
|
206
|
+
title: "Classification Settings",
|
|
207
|
+
columns: 1,
|
|
208
|
+
fields: [
|
|
209
|
+
{
|
|
210
|
+
key: "minConfidence",
|
|
211
|
+
label: "Minimum Confidence",
|
|
212
|
+
type: "slider",
|
|
213
|
+
min: 0.05,
|
|
214
|
+
max: 1,
|
|
215
|
+
step: 0.05,
|
|
216
|
+
default: 0.3
|
|
217
|
+
}
|
|
218
|
+
]
|
|
189
219
|
}
|
|
190
220
|
]
|
|
191
221
|
};
|
|
@@ -215,4 +245,4 @@ var BirdGlobalClassifierAddon = class {
|
|
|
215
245
|
export {
|
|
216
246
|
BirdGlobalClassifierAddon
|
|
217
247
|
};
|
|
218
|
-
//# sourceMappingURL=chunk-
|
|
248
|
+
//# sourceMappingURL=chunk-ML2JX43J.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/addons/bird-global-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 { BIRD_SPECIES_MODELS } from '../../catalogs/animal-classification-models.js'\nimport { cropRegion, resizeAndNormalize } from '../../shared/image-utils.js'\nimport { resolveEngine } from '../../shared/engine-resolver.js'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\n\nconst SPECIES_LABEL: LabelDefinition = { id: 'species', name: 'Bird Species' }\nconst SPECIES_LABELS: readonly LabelDefinition[] = [SPECIES_LABEL]\nconst BIRD_CLASS_MAP: ClassMapDefinition = { mapping: {}, preserveOriginal: true }\n\n/** Load bird species labels from JSON file in modelsDir */\nfunction loadLabels(modelsDir: string, modelId: string): readonly string[] {\n const labelNames = [\n `camstack-${modelId}-labels.json`,\n `camstack-bird-species-525-labels.json`,\n ]\n for (const name of labelNames) {\n const labelPath = path.join(modelsDir, name)\n if (fs.existsSync(labelPath)) {\n const raw = fs.readFileSync(labelPath, 'utf-8')\n return JSON.parse(raw) as string[]\n }\n }\n throw new Error(`BirdGlobalClassifierAddon: labels JSON not found in ${modelsDir}`)\n}\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 BirdGlobalClassifierAddon implements IClassifierProvider, IDetectionAddon {\n readonly id = 'bird-global-classifier'\n readonly slot = 'classifier' as const\n readonly inputClasses = ['animal'] as const\n readonly outputClasses = ['species:*'] as const\n readonly slotPriority = 0\n readonly requiredSteps = [] as const\n readonly manifest: AddonManifest = {\n id: 'bird-global-classifier',\n name: 'Bird Classifier (Global, 525 species)',\n version: '0.1.0',\n\n description: 'EfficientNet — 525 worldwide bird species (MIT license, ONNX only)',\n\n slot: 'classifier',\n labelOutputType: 'classification',\n inputClasses: ['animal'],\n outputClasses: ['species:*'],\n supportsCustomModels: false,\n mayRequirePython: false,\n defaultConfig: {\n modelId: 'bird-species-525',\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 labels: readonly string[] = []\n private minConfidence = 0.3\n private resolvedConfig: ResolvedInferenceConfig | null = null\n private ctx: AddonContext | null = null\n\n getModelRequirements(): ModelRequirement[] {\n return BIRD_SPECIES_MODELS.map((m) => ({\n modelId: m.id,\n name: m.name,\n minRAM_MB: 120,\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 ?? 'bird-species-525'\n this.minConfidence = (cfg['minConfidence'] as number | undefined) ?? 0.3\n\n const entry = BIRD_SPECIES_MODELS.find((m) => m.id === modelId)\n if (!entry) {\n throw new Error(`BirdGlobalClassifierAddon: 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, 525]\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 = this.labels[maxIdx] ?? `species_${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 = BIRD_SPECIES_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 // Ensure model + extra files (labels JSON) are downloaded via unified service\n if (this.ctx!.models) {\n await this.ctx!.models.ensure(modelId, format as any)\n }\n\n // Load labels from JSON file (lazy — only on first use)\n this.labels = loadLabels(modelsDir, modelId)\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: [...BIRD_SPECIES_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 BIRD_CLASS_MAP\n }\n\n getModelCatalog(): ModelCatalogEntry[] {\n return [...BIRD_SPECIES_MODELS]\n }\n\n getAvailableModels(): DetectionModel[] {\n return []\n }\n\n getActiveLabels(): readonly LabelDefinition[] {\n return SPECIES_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":[]}
|