@camstack/addon-vision 0.1.2 → 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 (110) hide show
  1. package/dist/addons/animal-classifier/index.js +999 -822
  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 -378
  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 -825
  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 -825
  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 -934
  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 -807
  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 -1082
  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 -868
  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 -505
  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 -790
  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 -410
  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-2IOKI4ES.mjs.map → chunk-PIFS7AIT.mjs.map} +1 -1
  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 -3924
  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 +1 -1
  83. package/dist/chunk-22BHCDT5.mjs +0 -101
  84. package/dist/chunk-2IOKI4ES.mjs +0 -335
  85. package/dist/chunk-7DYHXUPZ.mjs +0 -36
  86. package/dist/chunk-BJTO5JO5.mjs +0 -11
  87. package/dist/chunk-BP7H4NFS.mjs +0 -412
  88. package/dist/chunk-BR2FPGOX.mjs +0 -98
  89. package/dist/chunk-D6WEHN33.mjs +0 -276
  90. package/dist/chunk-D6WEHN33.mjs.map +0 -1
  91. package/dist/chunk-DRYFGARD.mjs +0 -289
  92. package/dist/chunk-DRYFGARD.mjs.map +0 -1
  93. package/dist/chunk-DUN6XU3N.mjs +0 -72
  94. package/dist/chunk-ESLHNWWE.mjs +0 -387
  95. package/dist/chunk-ESLHNWWE.mjs.map +0 -1
  96. package/dist/chunk-JUQEW6ON.mjs +0 -256
  97. package/dist/chunk-JUQEW6ON.mjs.map +0 -1
  98. package/dist/chunk-KUO2BVFY.mjs +0 -90
  99. package/dist/chunk-R5J3WAUI.mjs +0 -645
  100. package/dist/chunk-R5J3WAUI.mjs.map +0 -1
  101. package/dist/chunk-XZ6ZMXXU.mjs +0 -39
  102. package/dist/chunk-YPU4WTXZ.mjs +0 -269
  103. package/dist/chunk-YPU4WTXZ.mjs.map +0 -1
  104. package/dist/chunk-YUCD2TFH.mjs +0 -242
  105. package/dist/chunk-YUCD2TFH.mjs.map +0 -1
  106. package/dist/chunk-ZTJENCFC.mjs +0 -379
  107. package/dist/chunk-ZTJENCFC.mjs.map +0 -1
  108. package/dist/chunk-ZWYXXCXP.mjs +0 -248
  109. package/dist/chunk-ZWYXXCXP.mjs.map +0 -1
  110. /package/dist/{chunk-BJTO5JO5.mjs.map → chunk-3IIFBJCD.mjs.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/catalogs/plate-recognition-models.ts","../src/shared/postprocess/paddleocr.ts","../src/addons/plate-recognition/index.ts"],"sourcesContent":["import type { ModelCatalogEntry, LabelDefinition } from '@camstack/types'\nimport { hfModelUrl } from '@camstack/types'\n\nconst HF_REPO = 'camstack/camstack-models'\n\nconst PLATE_TEXT_LABELS: readonly LabelDefinition[] = [\n { id: 'text', name: 'Plate Text' },\n] as const\n\nexport const PLATE_RECOGNITION_MODELS: readonly ModelCatalogEntry[] = [\n // ── PaddleOCR PP-OCRv5 ────────────────────────────────────────\n {\n id: 'paddleocr-latin',\n name: 'PaddleOCR Latin',\n description: 'PaddleOCR PP-OCRv5 recognition model for Latin-script license plates',\n inputSize: { width: 320, height: 48 },\n labels: PLATE_TEXT_LABELS,\n formats: {\n // ONNX only — PaddleOCR has dynamic dimensions incompatible with CoreML native conversion.\n // On Apple Silicon, ONNX Runtime uses CoreML EP automatically for acceleration.\n onnx: {\n url: hfModelUrl(HF_REPO, 'plateRecognition/paddleocr/onnx/camstack-paddleocr-latin-rec.onnx'),\n sizeMB: 7.5,\n },\n openvino: {\n url: hfModelUrl(HF_REPO, 'plateRecognition/paddleocr/openvino/camstack-paddleocr-latin.xml'),\n sizeMB: 4,\n runtimes: ['python'],\n },\n },\n extraFiles: [\n {\n url: hfModelUrl(HF_REPO, 'plateRecognition/paddleocr/onnx/camstack-paddleocr-latin-dict.txt'),\n filename: 'camstack-paddleocr-latin-dict.txt',\n sizeMB: 0.01,\n },\n ],\n },\n {\n id: 'paddleocr-en',\n name: 'PaddleOCR English',\n description: 'PaddleOCR PP-OCRv5 recognition model optimized for English license plates',\n inputSize: { width: 320, height: 48 },\n labels: PLATE_TEXT_LABELS,\n formats: {\n onnx: {\n url: hfModelUrl(HF_REPO, 'plateRecognition/paddleocr/onnx/camstack-paddleocr-en-rec.onnx'),\n sizeMB: 7.5,\n },\n openvino: {\n url: hfModelUrl(HF_REPO, 'plateRecognition/paddleocr/openvino/camstack-paddleocr-en.xml'),\n sizeMB: 4,\n runtimes: ['python'],\n },\n },\n extraFiles: [\n {\n url: hfModelUrl(HF_REPO, 'plateRecognition/paddleocr/onnx/camstack-paddleocr-en-dict.txt'),\n filename: 'camstack-paddleocr-en-dict.txt',\n sizeMB: 0.01,\n },\n ],\n },\n\n // ── CRNN-MobileNetV3 (via OnnxTR/docTR) ─────────────────────────\n // Simple CNN+LSTM+CTC architecture — good CoreML compatibility (no dynamic ops)\n {\n id: 'crnn-mobilenet-v3-small',\n name: 'CRNN MobileNet V3 Small',\n description: 'CRNN MobileNetV3-Small — lightweight text recognition, CoreML compatible via OnnxTR',\n inputSize: { width: 128, height: 32 },\n labels: PLATE_TEXT_LABELS,\n formats: {\n onnx: {\n url: hfModelUrl(HF_REPO, 'plateRecognition/crnn-mobilenet/onnx/camstack-crnn-mobilenet-v3-small.onnx'),\n sizeMB: 8,\n },\n },\n extraFiles: [\n {\n url: hfModelUrl(HF_REPO, 'plateRecognition/crnn-mobilenet/camstack-crnn-mobilenet-charset.txt'),\n filename: 'camstack-crnn-mobilenet-charset.txt',\n sizeMB: 0.01,\n },\n ],\n },\n {\n id: 'crnn-mobilenet-v3-large',\n name: 'CRNN MobileNet V3 Large',\n description: 'CRNN MobileNetV3-Large — higher accuracy text recognition, CoreML compatible',\n inputSize: { width: 128, height: 32 },\n labels: PLATE_TEXT_LABELS,\n formats: {\n onnx: {\n url: hfModelUrl(HF_REPO, 'plateRecognition/crnn-mobilenet/onnx/camstack-crnn-mobilenet-v3-large.onnx'),\n sizeMB: 17,\n },\n },\n extraFiles: [\n {\n url: hfModelUrl(HF_REPO, 'plateRecognition/crnn-mobilenet/camstack-crnn-mobilenet-charset.txt'),\n filename: 'camstack-crnn-mobilenet-charset.txt',\n sizeMB: 0.01,\n },\n ],\n },\n] as const\n","/** Decode CTC output to text.\n *\n * Output shape: [1, seqLen, numChars]\n * Algorithm: argmax per timestep → collapse consecutive duplicates → remove blank (index 0) → join\n */\nexport function ctcDecode(\n output: Float32Array,\n seqLen: number,\n numChars: number,\n charset: readonly string[], // index 0 = blank token\n): { text: string; confidence: number } {\n // Step 1: argmax per timestep + track confidence as mean of selected scores\n let totalLogScore = 0\n const rawIndices: number[] = []\n\n for (let t = 0; t < seqLen; t++) {\n const offset = t * numChars\n let bestIdx = 0\n let bestVal = output[offset]!\n\n for (let c = 1; c < numChars; c++) {\n const val = output[offset + c]!\n if (val > bestVal) {\n bestVal = val\n bestIdx = c\n }\n }\n\n rawIndices.push(bestIdx)\n totalLogScore += bestVal\n }\n\n // Step 2: collapse consecutive duplicates\n const collapsed: number[] = []\n for (let i = 0; i < rawIndices.length; i++) {\n const cur = rawIndices[i]!\n if (i === 0 || cur !== rawIndices[i - 1]) {\n collapsed.push(cur)\n }\n }\n\n // Step 3: remove blank (index 0)\n const filtered = collapsed.filter((idx) => idx !== 0)\n\n // Step 4: join characters\n const text = filtered.map((idx) => charset[idx] ?? '').join('')\n\n const confidence = seqLen > 0 ? totalLogScore / seqLen : 0\n\n return { text, confidence }\n}\n","import type {\n IClassifierProvider,\n IDetectionAddon,\n AddonManifest,\n AddonContext,\n CropInput,\n ClassifierOutput,\n ConfigUISchema,\n ClassMapDefinition,\n ProbeResult,\n ModelCatalogEntry,\n DetectionModel,\n LabelDefinition,\n IInferenceEngine,\n RequiredStep,\n ModelRequirement,\n ResolvedInferenceConfig,\n} from '@camstack/types'\nimport { PLATE_RECOGNITION_MODELS } from '../../catalogs/plate-recognition-models.js'\nimport { cropRegion, resizeAndNormalize } from '../../shared/image-utils.js'\nimport { ctcDecode } from '../../shared/postprocess/paddleocr.js'\nimport { resolveEngine } from '../../shared/engine-resolver.js'\n\nconst PLATE_TEXT_LABEL: LabelDefinition = { id: 'plate-text', name: 'Plate Text' }\nconst PLATE_TEXT_LABELS: readonly LabelDefinition[] = [PLATE_TEXT_LABEL]\nconst PLATE_REC_CLASS_MAP: ClassMapDefinition = { mapping: {}, preserveOriginal: true }\n\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\n\n/** Load charset from dict.txt file — index 0 is the CTC blank token */\nfunction loadCharset(modelsDir: string, modelId: string): readonly string[] {\n // Try to find the dict file next to the model\n const dictNames = [\n `camstack-${modelId}-dict.txt`,\n `camstack-paddleocr-latin-dict.txt`,\n `camstack-paddleocr-en-dict.txt`,\n `camstack-crnn-mobilenet-charset.txt`,\n ]\n for (const name of dictNames) {\n const dictPath = path.join(modelsDir, name)\n if (fs.existsSync(dictPath)) {\n const lines = fs.readFileSync(dictPath, 'utf-8').split('\\n').filter((l) => l.length > 0)\n // PaddleOCR convention: blank token at index 0, then dict chars, then a space token at end\n return ['', ...lines, ' ']\n }\n }\n throw new Error(`PlateRecognitionAddon: dict.txt not found in ${modelsDir}`)\n}\n\nconst REQUIRED_STEPS: readonly RequiredStep[] = [\n { slot: 'cropper', outputClasses: ['plate'], description: 'Requires a plate detector' },\n]\n\nexport default class PlateRecognitionAddon implements IClassifierProvider, IDetectionAddon {\n readonly id = 'plate-recognition'\n readonly slot = 'classifier' as const\n readonly inputClasses = ['plate'] as const\n readonly outputClasses = ['plate-text:*'] as const\n readonly slotPriority = 0\n readonly requiredSteps = REQUIRED_STEPS\n readonly manifest: AddonManifest = {\n id: 'plate-recognition',\n name: 'License Plate Recognition (OCR)',\n version: '0.1.0',\n\n description: 'PaddleOCR-based license plate text recognition',\n\n slot: 'classifier',\n labelOutputType: 'plate',\n inputClasses: ['plate'],\n outputClasses: ['plate-text:*'],\n requiredSteps: REQUIRED_STEPS,\n supportsCustomModels: false,\n mayRequirePython: false,\n defaultConfig: {\n modelId: 'paddleocr-latin',\n runtime: 'node',\n backend: 'cpu',\n minConfidence: 0.5,\n },\n }\n\n private engine: IInferenceEngine | null = null\n private modelEntry!: ModelCatalogEntry\n private minConfidence = 0.5\n private charset: readonly string[] = []\n private resolvedConfig: ResolvedInferenceConfig | null = null\n private ctx: AddonContext | null = null\n\n getModelRequirements(): ModelRequirement[] {\n const scores: Record<string, { ram: number; accuracy: number }> = {\n 'paddleocr-latin': { ram: 100, accuracy: 80 },\n 'paddleocr-en': { ram: 100, accuracy: 80 },\n }\n return PLATE_RECOGNITION_MODELS.map((m) => ({\n modelId: m.id,\n name: m.name,\n minRAM_MB: scores[m.id]?.ram ?? 100,\n accuracyScore: scores[m.id]?.accuracy ?? 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 ?? 'paddleocr-latin'\n this.minConfidence = (cfg['minConfidence'] as number | undefined) ?? 0.5\n\n const entry = PLATE_RECOGNITION_MODELS.find((m) => m.id === modelId)\n if (!entry) {\n throw new Error(`PlateRecognitionAddon: 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 plate region\n console.log(`[plate-recognition] ROI: x=${input.roi?.x}, y=${input.roi?.y}, w=${input.roi?.w}, h=${input.roi?.h}, frameSize=${input.frame?.data?.length}`)\n const plateCrop = await cropRegion(input.frame.data, input.roi)\n console.log(`[plate-recognition] Crop size: ${plateCrop.length} bytes`)\n // DEBUG: save crop to /tmp for inspection\n try { require('fs').writeFileSync('/tmp/plate-recognition-crop.jpg', plateCrop) } catch {}\n\n // Resize to 320x48, normalize to [0,1], NCHW\n const normalized = await resizeAndNormalize(plateCrop, inputW, inputH, 'zero-one', 'nchw')\n\n const output = await this.engine!.run(normalized, [1, 3, inputH, inputW])\n\n // PaddleOCR CTC output shape: [1, seqLen, numChars]\n const numChars = this.charset.length\n const seqLen = output.length / numChars\n const { text, confidence } = ctcDecode(output, seqLen, numChars, this.charset)\n\n // Always return the result with confidence — let the pipeline/UI decide on thresholds\n return {\n classifications: [\n {\n class: 'plate-text',\n score: confidence,\n text: text.trim() || '(unreadable)',\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 = PLATE_RECOGNITION_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 (dict.txt) are downloaded via unified service\n if (this.ctx!.models) {\n await this.ctx!.models.ensure(modelId, format as any)\n }\n\n // Load charset from dict.txt (lazy — only on first use)\n this.charset = loadCharset(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: [...PLATE_RECOGNITION_MODELS],\n allowCustom: false,\n allowConversion: false,\n acceptFormats: ['onnx', 'openvino'],\n requiredMetadata: ['inputSize', 'labels', 'outputFormat'],\n outputFormatHint: 'ocr',\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: '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: 'cuda', label: 'CUDA (NVIDIA)' },\n ],\n },\n ],\n },\n {\n id: 'thresholds',\n title: 'Recognition Settings',\n columns: 1,\n fields: [\n {\n key: 'minConfidence',\n label: 'Minimum Confidence',\n type: 'slider',\n min: 0.1,\n max: 1.0,\n step: 0.05,\n default: 0.5,\n },\n ],\n },\n ],\n }\n }\n\n getClassMap(): ClassMapDefinition {\n return PLATE_REC_CLASS_MAP\n }\n\n getModelCatalog(): ModelCatalogEntry[] {\n return [...PLATE_RECOGNITION_MODELS]\n }\n\n getAvailableModels(): DetectionModel[] {\n return []\n }\n\n getActiveLabels(): readonly LabelDefinition[] {\n return PLATE_TEXT_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_TEXT_LABELS"]}
@@ -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-2IOKI4ES.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-ZWYXXCXP.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":[]}