@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.
Files changed (111) hide show
  1. package/dist/addons/animal-classifier/index.js +999 -823
  2. package/dist/addons/animal-classifier/index.js.map +1 -1
  3. package/dist/addons/animal-classifier/index.mjs +242 -7
  4. package/dist/addons/animal-classifier/index.mjs.map +1 -1
  5. package/dist/addons/audio-classification/index.js +501 -379
  6. package/dist/addons/audio-classification/index.js.map +1 -1
  7. package/dist/addons/audio-classification/index.mjs +224 -4
  8. package/dist/addons/audio-classification/index.mjs.map +1 -1
  9. package/dist/addons/bird-global-classifier/index.js +1002 -826
  10. package/dist/addons/bird-global-classifier/index.js.map +1 -1
  11. package/dist/addons/bird-global-classifier/index.mjs +248 -7
  12. package/dist/addons/bird-global-classifier/index.mjs.map +1 -1
  13. package/dist/addons/bird-nabirds-classifier/index.js +1002 -826
  14. package/dist/addons/bird-nabirds-classifier/index.js.map +1 -1
  15. package/dist/addons/bird-nabirds-classifier/index.mjs +289 -7
  16. package/dist/addons/bird-nabirds-classifier/index.mjs.map +1 -1
  17. package/dist/addons/face-detection/index.js +1196 -935
  18. package/dist/addons/face-detection/index.js.map +1 -1
  19. package/dist/addons/face-detection/index.mjs +227 -7
  20. package/dist/addons/face-detection/index.mjs.map +1 -1
  21. package/dist/addons/face-recognition/index.js +1003 -808
  22. package/dist/addons/face-recognition/index.js.map +1 -1
  23. package/dist/addons/face-recognition/index.mjs +197 -6
  24. package/dist/addons/face-recognition/index.mjs.map +1 -1
  25. package/dist/addons/motion-detection/index.js +214 -111
  26. package/dist/addons/motion-detection/index.js.map +1 -1
  27. package/dist/addons/motion-detection/index.mjs +12 -9
  28. package/dist/addons/motion-detection/index.mjs.map +1 -1
  29. package/dist/addons/object-detection/index.js +1287 -1083
  30. package/dist/addons/object-detection/index.js.map +1 -1
  31. package/dist/addons/object-detection/index.mjs +373 -7
  32. package/dist/addons/object-detection/index.mjs.map +1 -1
  33. package/dist/addons/plate-detection/index.js +1075 -869
  34. package/dist/addons/plate-detection/index.js.map +1 -1
  35. package/dist/addons/plate-detection/index.mjs +230 -7
  36. package/dist/addons/plate-detection/index.mjs.map +1 -1
  37. package/dist/addons/plate-recognition/index.js +684 -506
  38. package/dist/addons/plate-recognition/index.js.map +1 -1
  39. package/dist/addons/plate-recognition/index.mjs +244 -5
  40. package/dist/addons/plate-recognition/index.mjs.map +1 -1
  41. package/dist/addons/segmentation-refiner/index.js +967 -791
  42. package/dist/addons/segmentation-refiner/index.js.map +1 -1
  43. package/dist/addons/segmentation-refiner/index.mjs +21 -17
  44. package/dist/addons/segmentation-refiner/index.mjs.map +1 -1
  45. package/dist/addons/vehicle-classifier/index.js +581 -411
  46. package/dist/addons/vehicle-classifier/index.js.map +1 -1
  47. package/dist/addons/vehicle-classifier/index.mjs +20 -16
  48. package/dist/addons/vehicle-classifier/index.mjs.map +1 -1
  49. package/dist/chunk-2YMA6QOV.mjs +193 -0
  50. package/dist/chunk-2YMA6QOV.mjs.map +1 -0
  51. package/dist/chunk-3IIFBJCD.mjs +45 -0
  52. package/dist/chunk-BS4DKYGN.mjs +48 -0
  53. package/dist/{chunk-7DYHXUPZ.mjs.map → chunk-BS4DKYGN.mjs.map} +1 -1
  54. package/dist/chunk-DE7I3VHO.mjs +106 -0
  55. package/dist/{chunk-KUO2BVFY.mjs.map → chunk-DE7I3VHO.mjs.map} +1 -1
  56. package/dist/chunk-F6D2OZ36.mjs +89 -0
  57. package/dist/chunk-F6D2OZ36.mjs.map +1 -0
  58. package/dist/chunk-GAOIFQDX.mjs +59 -0
  59. package/dist/chunk-GAOIFQDX.mjs.map +1 -0
  60. package/dist/chunk-HUIX2XVR.mjs +159 -0
  61. package/dist/chunk-HUIX2XVR.mjs.map +1 -0
  62. package/dist/chunk-K36R6HWY.mjs +51 -0
  63. package/dist/{chunk-XZ6ZMXXU.mjs.map → chunk-K36R6HWY.mjs.map} +1 -1
  64. package/dist/chunk-MBTAI3WE.mjs +78 -0
  65. package/dist/chunk-MBTAI3WE.mjs.map +1 -0
  66. package/dist/chunk-MGT6RUVX.mjs +423 -0
  67. package/dist/{chunk-BP7H4NFS.mjs.map → chunk-MGT6RUVX.mjs.map} +1 -1
  68. package/dist/chunk-PIFS7AIT.mjs +446 -0
  69. package/dist/chunk-PIFS7AIT.mjs.map +1 -0
  70. package/dist/chunk-WG66JYYW.mjs +116 -0
  71. package/dist/{chunk-22BHCDT5.mjs.map → chunk-WG66JYYW.mjs.map} +1 -1
  72. package/dist/chunk-XD7WGXHZ.mjs +82 -0
  73. package/dist/{chunk-DUN6XU3N.mjs.map → chunk-XD7WGXHZ.mjs.map} +1 -1
  74. package/dist/chunk-YYDM6V2F.mjs +113 -0
  75. package/dist/{chunk-BR2FPGOX.mjs.map → chunk-YYDM6V2F.mjs.map} +1 -1
  76. package/dist/chunk-ZK7P3TZN.mjs +286 -0
  77. package/dist/chunk-ZK7P3TZN.mjs.map +1 -0
  78. package/dist/index.js +4443 -3925
  79. package/dist/index.js.map +1 -1
  80. package/dist/index.mjs +2698 -250
  81. package/dist/index.mjs.map +1 -1
  82. package/package.json +2 -3
  83. package/dist/chunk-22BHCDT5.mjs +0 -101
  84. package/dist/chunk-6DJZZR64.mjs +0 -336
  85. package/dist/chunk-6DJZZR64.mjs.map +0 -1
  86. package/dist/chunk-7DYHXUPZ.mjs +0 -36
  87. package/dist/chunk-BJTO5JO5.mjs +0 -11
  88. package/dist/chunk-BP7H4NFS.mjs +0 -412
  89. package/dist/chunk-BR2FPGOX.mjs +0 -98
  90. package/dist/chunk-DNQNGDR4.mjs +0 -256
  91. package/dist/chunk-DNQNGDR4.mjs.map +0 -1
  92. package/dist/chunk-DUN6XU3N.mjs +0 -72
  93. package/dist/chunk-EPNWLSCG.mjs +0 -387
  94. package/dist/chunk-EPNWLSCG.mjs.map +0 -1
  95. package/dist/chunk-G32RCIUI.mjs +0 -645
  96. package/dist/chunk-G32RCIUI.mjs.map +0 -1
  97. package/dist/chunk-GR65KM6X.mjs +0 -289
  98. package/dist/chunk-GR65KM6X.mjs.map +0 -1
  99. package/dist/chunk-H7LMBTS5.mjs +0 -276
  100. package/dist/chunk-H7LMBTS5.mjs.map +0 -1
  101. package/dist/chunk-IK4XIQPC.mjs +0 -242
  102. package/dist/chunk-IK4XIQPC.mjs.map +0 -1
  103. package/dist/chunk-J6VNIIYX.mjs +0 -269
  104. package/dist/chunk-J6VNIIYX.mjs.map +0 -1
  105. package/dist/chunk-KUO2BVFY.mjs +0 -90
  106. package/dist/chunk-ML2JX43J.mjs +0 -248
  107. package/dist/chunk-ML2JX43J.mjs.map +0 -1
  108. package/dist/chunk-WUMV524J.mjs +0 -379
  109. package/dist/chunk-WUMV524J.mjs.map +0 -1
  110. package/dist/chunk-XZ6ZMXXU.mjs +0 -39
  111. /package/dist/{chunk-BJTO5JO5.mjs.map → chunk-3IIFBJCD.mjs.map} +0 -0
@@ -1,269 +0,0 @@
1
- import {
2
- yoloPostprocess
3
- } from "./chunk-KUO2BVFY.mjs";
4
- import {
5
- MLPACKAGE_FILES
6
- } from "./chunk-BP7H4NFS.mjs";
7
- import {
8
- cropRegion,
9
- letterbox
10
- } from "./chunk-22BHCDT5.mjs";
11
- import {
12
- resolveEngine
13
- } from "./chunk-6DJZZR64.mjs";
14
-
15
- // src/catalogs/plate-detection-models.ts
16
- import { hfModelUrl } from "@camstack/types";
17
- var HF_REPO = "camstack/camstack-models";
18
- var PLATE_LABELS = [
19
- { id: "plate", name: "License Plate" }
20
- ];
21
- var PLATE_DETECTION_MODELS = [
22
- {
23
- id: "yolov8n-plate",
24
- name: "YOLOv8 Nano \u2014 License Plate",
25
- description: "YOLOv8 Nano fine-tuned for license plate detection",
26
- inputSize: { width: 640, height: 640 },
27
- labels: PLATE_LABELS,
28
- formats: {
29
- onnx: {
30
- url: hfModelUrl(HF_REPO, "plateDetection/yolov8-plate/onnx/camstack-yolov8n-plate.onnx"),
31
- sizeMB: 12
32
- },
33
- coreml: {
34
- url: hfModelUrl(HF_REPO, "plateDetection/yolov8-plate/coreml/camstack-yolov8n-plate.mlpackage"),
35
- sizeMB: 5.9,
36
- isDirectory: true,
37
- files: MLPACKAGE_FILES,
38
- runtimes: ["python"]
39
- },
40
- openvino: {
41
- url: hfModelUrl(HF_REPO, "plateDetection/yolov8-plate/openvino/camstack-yolov8n-plate.xml"),
42
- sizeMB: 6.1,
43
- runtimes: ["python"]
44
- },
45
- tflite: {
46
- url: hfModelUrl(HF_REPO, "plateDetection/yolov8-plate/tflite/camstack-yolov8n-plate_float32.tflite"),
47
- sizeMB: 12,
48
- runtimes: ["python"]
49
- }
50
- }
51
- }
52
- ];
53
-
54
- // src/addons/plate-detection/index.ts
55
- var PLATE_LABEL = { id: "plate", name: "License Plate" };
56
- var PLATE_LABELS2 = [PLATE_LABEL];
57
- var PLATE_CLASS_MAP = { mapping: {}, preserveOriginal: true };
58
- var PlateDetectionAddon = class {
59
- id = "plate-detection";
60
- slot = "cropper";
61
- inputClasses = ["vehicle"];
62
- outputClasses = ["plate"];
63
- slotPriority = 0;
64
- manifest = {
65
- id: "plate-detection",
66
- name: "License Plate Detection",
67
- version: "0.1.0",
68
- description: "YOLO-based license plate detector \u2014 crops plate regions from vehicle detections",
69
- slot: "cropper",
70
- inputClasses: ["vehicle"],
71
- outputClasses: ["plate"],
72
- supportsCustomModels: false,
73
- mayRequirePython: false,
74
- defaultConfig: {
75
- modelId: "yolov8n-plate",
76
- runtime: "node",
77
- backend: "cpu",
78
- confidence: 0.5,
79
- iouThreshold: 0.45
80
- }
81
- };
82
- engine = null;
83
- modelEntry;
84
- confidence = 0.5;
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
- }
100
- async initialize(ctx) {
101
- this.ctx = ctx;
102
- const cfg = ctx.addonConfig;
103
- const modelId = cfg["modelId"] ?? this.resolvedConfig?.modelId ?? "yolov8n-plate";
104
- this.confidence = cfg["confidence"] ?? 0.5;
105
- this.iouThreshold = cfg["iouThreshold"] ?? 0.45;
106
- const entry = PLATE_DETECTION_MODELS.find((m) => m.id === modelId);
107
- if (!entry) {
108
- throw new Error(`PlateDetectionAddon: unknown modelId "${modelId}"`);
109
- }
110
- this.modelEntry = entry;
111
- }
112
- async crop(input) {
113
- if (!this.engine) await this.ensureEngine();
114
- const start = Date.now();
115
- const { width: inputW, height: inputH } = this.modelEntry.inputSize;
116
- const targetSize = Math.max(inputW, inputH);
117
- const vehicleCrop = await cropRegion(input.frame.data, input.roi);
118
- const lb = await letterbox(vehicleCrop, targetSize);
119
- const output = await this.engine.run(lb.data, [1, 3, targetSize, targetSize]);
120
- const numClasses = this.modelEntry.labels.length;
121
- const numBoxes = output.length / (4 + numClasses);
122
- const labels = this.modelEntry.labels.map((l) => l.id);
123
- const plates = yoloPostprocess(output, numClasses, numBoxes, {
124
- confidence: this.confidence,
125
- iouThreshold: this.iouThreshold,
126
- labels,
127
- scale: lb.scale,
128
- padX: lb.padX,
129
- padY: lb.padY,
130
- originalWidth: lb.originalWidth,
131
- originalHeight: lb.originalHeight
132
- });
133
- const crops = plates.map((p) => ({ ...p, class: "plate", originalClass: p.originalClass }));
134
- return {
135
- crops,
136
- inferenceMs: Date.now() - start,
137
- modelId: this.modelEntry.id
138
- };
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
- }
161
- async shutdown() {
162
- await this.engine?.dispose();
163
- }
164
- getConfigSchema() {
165
- return {
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
- },
215
- {
216
- id: "thresholds",
217
- title: "Detection Thresholds",
218
- columns: 2,
219
- fields: [
220
- {
221
- key: "confidence",
222
- label: "Confidence Threshold",
223
- type: "slider",
224
- min: 0.1,
225
- max: 1,
226
- step: 0.05,
227
- default: 0.5
228
- },
229
- {
230
- key: "iouThreshold",
231
- label: "IoU Threshold (NMS)",
232
- type: "slider",
233
- min: 0.1,
234
- max: 1,
235
- step: 0.05,
236
- default: 0.45
237
- }
238
- ]
239
- }
240
- ]
241
- };
242
- }
243
- getClassMap() {
244
- return PLATE_CLASS_MAP;
245
- }
246
- getModelCatalog() {
247
- return [...PLATE_DETECTION_MODELS];
248
- }
249
- getAvailableModels() {
250
- return [];
251
- }
252
- getActiveLabels() {
253
- return PLATE_LABELS2;
254
- }
255
- async probe() {
256
- return {
257
- available: true,
258
- runtime: this.engine?.runtime ?? "onnx",
259
- device: this.engine?.device ?? "cpu",
260
- capabilities: ["fp32"]
261
- };
262
- }
263
- };
264
-
265
- export {
266
- PLATE_DETECTION_MODELS,
267
- PlateDetectionAddon
268
- };
269
- //# sourceMappingURL=chunk-J6VNIIYX.mjs.map
@@ -1 +0,0 @@
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,90 +0,0 @@
1
- // src/shared/postprocess/yolo.ts
2
- function iou(a, b) {
3
- const ax1 = a.x;
4
- const ay1 = a.y;
5
- const ax2 = a.x + a.w;
6
- const ay2 = a.y + a.h;
7
- const bx1 = b.x;
8
- const by1 = b.y;
9
- const bx2 = b.x + b.w;
10
- const by2 = b.y + b.h;
11
- const interX1 = Math.max(ax1, bx1);
12
- const interY1 = Math.max(ay1, by1);
13
- const interX2 = Math.min(ax2, bx2);
14
- const interY2 = Math.min(ay2, by2);
15
- const interW = Math.max(0, interX2 - interX1);
16
- const interH = Math.max(0, interY2 - interY1);
17
- const interArea = interW * interH;
18
- if (interArea === 0) return 0;
19
- const areaA = a.w * a.h;
20
- const areaB = b.w * b.h;
21
- const unionArea = areaA + areaB - interArea;
22
- return unionArea === 0 ? 0 : interArea / unionArea;
23
- }
24
- function nms(boxes, iouThreshold) {
25
- const indices = boxes.map((_, i) => i).sort((a, b) => boxes[b].score - boxes[a].score);
26
- const kept = [];
27
- const suppressed = /* @__PURE__ */ new Set();
28
- for (const idx of indices) {
29
- if (suppressed.has(idx)) continue;
30
- kept.push(idx);
31
- for (const other of indices) {
32
- if (other === idx || suppressed.has(other)) continue;
33
- if (iou(boxes[idx].bbox, boxes[other].bbox) > iouThreshold) {
34
- suppressed.add(other);
35
- }
36
- }
37
- }
38
- return kept;
39
- }
40
- function yoloPostprocess(output, numClasses, numBoxes, options) {
41
- const { confidence, iouThreshold, labels, scale, padX, padY, originalWidth, originalHeight } = options;
42
- const candidates = [];
43
- for (let i = 0; i < numBoxes; i++) {
44
- const cx = output[0 * numBoxes + i];
45
- const cy = output[1 * numBoxes + i];
46
- const w = output[2 * numBoxes + i];
47
- const h = output[3 * numBoxes + i];
48
- let bestScore = -Infinity;
49
- let bestClass = 0;
50
- for (let j = 0; j < numClasses; j++) {
51
- const score = output[(4 + j) * numBoxes + i];
52
- if (score > bestScore) {
53
- bestScore = score;
54
- bestClass = j;
55
- }
56
- }
57
- if (bestScore < confidence) continue;
58
- const bbox = {
59
- x: cx - w / 2,
60
- y: cy - h / 2,
61
- w,
62
- h
63
- };
64
- candidates.push({ bbox, score: bestScore, classIdx: bestClass });
65
- }
66
- if (candidates.length === 0) return [];
67
- const keptIndices = nms(candidates, iouThreshold);
68
- return keptIndices.map((idx) => {
69
- const { bbox, score, classIdx } = candidates[idx];
70
- const label = labels[classIdx] ?? String(classIdx);
71
- const x = Math.max(0, Math.min(originalWidth, (bbox.x - padX) / scale));
72
- const y = Math.max(0, Math.min(originalHeight, (bbox.y - padY) / scale));
73
- const x2 = Math.max(0, Math.min(originalWidth, (bbox.x + bbox.w - padX) / scale));
74
- const y2 = Math.max(0, Math.min(originalHeight, (bbox.y + bbox.h - padY) / scale));
75
- const finalBbox = { x, y, w: x2 - x, h: y2 - y };
76
- return {
77
- class: label,
78
- originalClass: label,
79
- score,
80
- bbox: finalBbox
81
- };
82
- });
83
- }
84
-
85
- export {
86
- iou,
87
- nms,
88
- yoloPostprocess
89
- };
90
- //# sourceMappingURL=chunk-KUO2BVFY.mjs.map
@@ -1,248 +0,0 @@
1
- import {
2
- BIRD_SPECIES_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/bird-global-classifier/index.ts
13
- import * as fs from "fs";
14
- import * as path from "path";
15
- var SPECIES_LABEL = { id: "species", name: "Bird Species" };
16
- var SPECIES_LABELS = [SPECIES_LABEL];
17
- var BIRD_CLASS_MAP = { mapping: {}, preserveOriginal: true };
18
- function loadLabels(modelsDir, modelId) {
19
- const labelNames = [
20
- `camstack-${modelId}-labels.json`,
21
- `camstack-bird-species-525-labels.json`
22
- ];
23
- for (const name of labelNames) {
24
- const labelPath = path.join(modelsDir, name);
25
- if (fs.existsSync(labelPath)) {
26
- const raw = fs.readFileSync(labelPath, "utf-8");
27
- return JSON.parse(raw);
28
- }
29
- }
30
- throw new Error(`BirdGlobalClassifierAddon: labels JSON not found in ${modelsDir}`);
31
- }
32
- function softmax(logits) {
33
- const max = logits.reduce((a, b) => Math.max(a, b), -Infinity);
34
- const exps = logits.map((v) => Math.exp(v - max));
35
- const sum = exps.reduce((a, b) => a + b, 0);
36
- return exps.map((v) => v / sum);
37
- }
38
- var BirdGlobalClassifierAddon = class {
39
- id = "bird-global-classifier";
40
- slot = "classifier";
41
- inputClasses = ["animal"];
42
- outputClasses = ["species:*"];
43
- slotPriority = 0;
44
- requiredSteps = [];
45
- manifest = {
46
- id: "bird-global-classifier",
47
- name: "Bird Classifier (Global, 525 species)",
48
- version: "0.1.0",
49
- description: "EfficientNet \u2014 525 worldwide bird species (MIT license, ONNX only)",
50
- slot: "classifier",
51
- labelOutputType: "classification",
52
- inputClasses: ["animal"],
53
- outputClasses: ["species:*"],
54
- supportsCustomModels: false,
55
- mayRequirePython: false,
56
- defaultConfig: {
57
- modelId: "bird-species-525",
58
- runtime: "node",
59
- backend: "cpu",
60
- minConfidence: 0.3
61
- }
62
- };
63
- engine = null;
64
- modelEntry;
65
- labels = [];
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
- }
81
- async initialize(ctx) {
82
- this.ctx = ctx;
83
- const cfg = ctx.addonConfig;
84
- const modelId = cfg["modelId"] ?? this.resolvedConfig?.modelId ?? "bird-species-525";
85
- this.minConfidence = cfg["minConfidence"] ?? 0.3;
86
- const entry = BIRD_SPECIES_MODELS.find((m) => m.id === modelId);
87
- if (!entry) {
88
- throw new Error(`BirdGlobalClassifierAddon: unknown modelId "${modelId}"`);
89
- }
90
- this.modelEntry = entry;
91
- }
92
- async classify(input) {
93
- if (!this.engine) await this.ensureEngine();
94
- const start = Date.now();
95
- const { width: inputW, height: inputH } = this.modelEntry.inputSize;
96
- const animalCrop = await cropRegion(input.frame.data, input.roi);
97
- const normalized = await resizeAndNormalize(animalCrop, inputW, inputH, "imagenet", "nchw");
98
- const rawOutput = await this.engine.run(normalized, [1, 3, inputH, inputW]);
99
- const probs = softmax(rawOutput);
100
- let maxIdx = 0;
101
- let maxScore = probs[0] ?? 0;
102
- for (let i = 1; i < probs.length; i++) {
103
- const score = probs[i] ?? 0;
104
- if (score > maxScore) {
105
- maxScore = score;
106
- maxIdx = i;
107
- }
108
- }
109
- if (maxScore < this.minConfidence) {
110
- return {
111
- classifications: [],
112
- inferenceMs: Date.now() - start,
113
- modelId: this.modelEntry.id
114
- };
115
- }
116
- const label = this.labels[maxIdx] ?? `species_${maxIdx}`;
117
- return {
118
- classifications: [
119
- {
120
- class: label,
121
- score: maxScore
122
- }
123
- ],
124
- inferenceMs: Date.now() - start,
125
- modelId: this.modelEntry.id
126
- };
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
- }
150
- async shutdown() {
151
- await this.engine?.dispose();
152
- }
153
- getConfigSchema() {
154
- return {
155
- sections: [
156
- {
157
- id: "model",
158
- title: "Model",
159
- columns: 1,
160
- fields: [
161
- {
162
- key: "modelId",
163
- label: "Model",
164
- type: "model-selector",
165
- catalog: [...BIRD_SPECIES_MODELS],
166
- allowCustom: false,
167
- allowConversion: false,
168
- acceptFormats: ["onnx", "coreml", "openvino"],
169
- requiredMetadata: ["inputSize", "labels"],
170
- outputFormatHint: "classification"
171
- }
172
- ]
173
- },
174
- {
175
- id: "runtime",
176
- title: "Runtime",
177
- columns: 2,
178
- fields: [
179
- {
180
- key: "runtime",
181
- label: "Runtime",
182
- type: "select",
183
- options: [
184
- { value: "auto", label: "Auto" },
185
- { value: "onnx", label: "ONNX Runtime" },
186
- { value: "coreml", label: "CoreML (Apple)" },
187
- { value: "openvino", label: "OpenVINO (Intel)" }
188
- ]
189
- },
190
- {
191
- key: "backend",
192
- label: "Backend",
193
- type: "select",
194
- showWhen: { field: "runtime", equals: "onnx" },
195
- options: [
196
- { value: "auto", label: "Auto" },
197
- { value: "cpu", label: "CPU" },
198
- { value: "coreml", label: "CoreML" },
199
- { value: "cuda", label: "CUDA (NVIDIA)" }
200
- ]
201
- }
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
- ]
219
- }
220
- ]
221
- };
222
- }
223
- getClassMap() {
224
- return BIRD_CLASS_MAP;
225
- }
226
- getModelCatalog() {
227
- return [...BIRD_SPECIES_MODELS];
228
- }
229
- getAvailableModels() {
230
- return [];
231
- }
232
- getActiveLabels() {
233
- return SPECIES_LABELS;
234
- }
235
- async probe() {
236
- return {
237
- available: true,
238
- runtime: this.engine?.runtime ?? "onnx",
239
- device: this.engine?.device ?? "cpu",
240
- capabilities: ["fp32"]
241
- };
242
- }
243
- };
244
-
245
- export {
246
- BirdGlobalClassifierAddon
247
- };
248
- //# sourceMappingURL=chunk-ML2JX43J.mjs.map
@@ -1 +0,0 @@
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":[]}