@camstack/addon-vision 0.1.2 → 0.1.4

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 +3 -2
  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 +1 @@
1
- {"version":3,"sources":["../../../src/addons/vehicle-classifier/index.ts","../../../src/catalogs/vehicle-classification-models.ts","../../../src/shared/image-utils.ts","../../../src/shared/engine-resolver.ts","../../../src/shared/node-engine.ts","../../../src/shared/python-engine.ts"],"sourcesContent":["import type {\n IClassifierProvider,\n IDetectionAddon,\n AddonManifest,\n AddonContext,\n CropInput,\n ClassifierOutput,\n ConfigUISchema,\n ClassMapDefinition,\n ProbeResult,\n ModelCatalogEntry,\n DetectionModel,\n LabelDefinition,\n IInferenceEngine,\n ModelRequirement,\n ResolvedInferenceConfig,\n} from '@camstack/types'\nimport { VEHICLE_TYPE_MODELS } from '../../catalogs/vehicle-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 VEHICLE_TYPE_LABEL: LabelDefinition = { id: 'vehicle-type', name: 'Vehicle Type' }\nconst VEHICLE_TYPE_LABELS: readonly LabelDefinition[] = [VEHICLE_TYPE_LABEL]\nconst VEHICLE_CLASS_MAP: ClassMapDefinition = { mapping: {}, preserveOriginal: true }\n\n/** Load vehicle type labels from JSON file in modelsDir */\nfunction loadLabels(modelsDir: string, modelId: string): readonly string[] {\n const labelNames = [\n `camstack-${modelId}-labels.json`,\n `camstack-vehicle-type-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(`VehicleClassifierAddon: 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 VehicleClassifierAddon implements IClassifierProvider, IDetectionAddon {\n readonly id = 'vehicle-classifier'\n readonly slot = 'classifier' as const\n readonly inputClasses = ['vehicle'] as const\n readonly outputClasses = ['vehicle-type:*'] as const\n readonly slotPriority = 0\n readonly requiredSteps = [] as const\n readonly manifest: AddonManifest = {\n id: 'vehicle-classifier',\n name: 'Vehicle Classifier',\n version: '0.1.0',\n\n description: 'EfficientNet-B4 — 8,949 vehicle make/model/year classes from VMMRdb',\n\n slot: 'classifier',\n labelOutputType: 'classification',\n inputClasses: ['vehicle'],\n outputClasses: ['vehicle-type:*'],\n supportsCustomModels: false,\n mayRequirePython: false,\n defaultConfig: {\n modelId: 'vehicle-type-efficientnet',\n runtime: 'node',\n backend: 'cpu',\n confidence: 0.05, // Low pipeline threshold for 8949-class model\n minConfidence: 0.05,\n },\n }\n\n private engine: IInferenceEngine | null = null\n private modelEntry!: ModelCatalogEntry\n private labels: readonly string[] = []\n private minConfidence = 0.05\n private resolvedConfig: ResolvedInferenceConfig | null = null\n private ctx: AddonContext | null = null\n\n getModelRequirements(): ModelRequirement[] {\n return VEHICLE_TYPE_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 ?? 'vehicle-type-efficientnet'\n this.minConfidence = (cfg['minConfidence'] as number | undefined) ?? 0.05\n\n const entry = VEHICLE_TYPE_MODELS.find((m) => m.id === modelId)\n if (!entry) {\n throw new Error(`VehicleClassifierAddon: 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 vehicle region\n const vehicleCrop = await cropRegion(input.frame.data, input.roi)\n\n // Resize to 224x224, ImageNet normalization, NCHW\n const normalized = await resizeAndNormalize(vehicleCrop, inputW, inputH, 'imagenet', 'nchw')\n\n // Run inference\n const rawOutput = await this.engine!.run(normalized, [1, 3, inputH, inputW])\n\n console.log(`[VehicleClassifier] Output length: ${rawOutput.length}, labels: ${this.labels.length}, first 5 raw: [${Array.from(rawOutput.slice(0, 5)).map(v => v.toFixed(3)).join(', ')}]`)\n\n // Softmax to get probabilities\n const probs = softmax(rawOutput)\n\n // Find top-3 for debugging\n const indexed = Array.from(probs).map((v, i) => ({ score: v, idx: i }))\n indexed.sort((a, b) => b.score - a.score)\n const top3 = indexed.slice(0, 3)\n console.log(`[VehicleClassifier] Top-3: ${top3.map(t => `${this.labels[t.idx] ?? t.idx}=${(t.score * 100).toFixed(1)}%`).join(', ')}`)\n\n // Find argmax\n const maxIdx = top3[0]?.idx ?? 0\n const maxScore = top3[0]?.score ?? 0\n\n if (maxScore < this.minConfidence) {\n console.log(`[VehicleClassifier] Below confidence threshold: ${(maxScore * 100).toFixed(1)}% < ${(this.minConfidence * 100).toFixed(0)}%`)\n return {\n classifications: [],\n inferenceMs: Date.now() - start,\n modelId: this.modelEntry.id,\n }\n }\n\n const label = this.labels[maxIdx] ?? `vehicle_type_${maxIdx}`\n console.log(`[VehicleClassifier] Result: ${label} (${(maxScore * 100).toFixed(1)}%)`)\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 = VEHICLE_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 // 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: [...VEHICLE_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 VEHICLE_CLASS_MAP\n }\n\n getModelCatalog(): ModelCatalogEntry[] {\n return [...VEHICLE_TYPE_MODELS]\n }\n\n getAvailableModels(): DetectionModel[] {\n return []\n }\n\n getActiveLabels(): readonly LabelDefinition[] {\n return VEHICLE_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","import type { ModelCatalogEntry, LabelDefinition } from '@camstack/types'\nimport { hfModelUrl } from '@camstack/types'\n\nconst HF_REPO = 'camstack/camstack-models'\n\nconst hf = (path: string) => hfModelUrl(HF_REPO, path)\n\nconst VEHICLE_LABELS: readonly LabelDefinition[] = [\n { id: 'vehicle-type', name: 'Vehicle Type' },\n]\n\nexport const VEHICLE_TYPE_MODELS: readonly ModelCatalogEntry[] = [\n {\n id: 'vehicle-type-efficientnet',\n name: 'Vehicle Type (EfficientNet)',\n description: 'EfficientNet-B4 vehicle make/model/year classifier — 8,949 classes from VMMRdb',\n inputSize: { width: 380, height: 380 },\n inputNormalization: 'imagenet',\n labels: VEHICLE_LABELS,\n formats: {\n onnx: { url: hf('vehicleClassification/efficientnet/onnx/camstack-vehicle-type-efficientnet.onnx'), sizeMB: 135 },\n coreml: {\n url: hf('vehicleClassification/efficientnet/coreml/camstack-vehicle-type-efficientnet.mlpackage'),\n sizeMB: 10,\n isDirectory: true,\n files: ['Manifest.json', 'Data/com.apple.CoreML/model.mlmodel', 'Data/com.apple.CoreML/weights/weight.bin'],\n runtimes: ['python'],\n },\n },\n extraFiles: [\n {\n url: hf('vehicleClassification/efficientnet/camstack-vehicle-type-labels.json'),\n filename: 'camstack-vehicle-type-labels.json',\n sizeMB: 0.2,\n },\n ],\n },\n] as const\n","import sharp from 'sharp'\nimport type { BoundingBox } from '@camstack/types'\n\n/** Decode JPEG to raw RGB pixels */\nexport async function jpegToRgb(\n jpeg: Buffer,\n): Promise<{ data: Buffer; width: number; height: number }> {\n const { data, info } = await sharp(jpeg)\n .removeAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true })\n return { data, width: info.width, height: info.height }\n}\n\n/** Crop a region from a JPEG buffer */\nexport async function cropRegion(jpeg: Buffer, roi: BoundingBox): Promise<Buffer> {\n return sharp(jpeg)\n .extract({\n left: Math.round(roi.x),\n top: Math.round(roi.y),\n width: Math.round(roi.w),\n height: Math.round(roi.h),\n })\n .jpeg()\n .toBuffer()\n}\n\n/** Letterbox resize for YOLO: resize preserving aspect ratio, pad to square */\nexport async function letterbox(\n jpeg: Buffer,\n targetSize: number,\n): Promise<{\n data: Float32Array\n scale: number\n padX: number\n padY: number\n originalWidth: number\n originalHeight: number\n}> {\n const meta = await sharp(jpeg).metadata()\n const originalWidth = meta.width ?? 0\n const originalHeight = meta.height ?? 0\n\n const scale = Math.min(targetSize / originalWidth, targetSize / originalHeight)\n const scaledWidth = Math.round(originalWidth * scale)\n const scaledHeight = Math.round(originalHeight * scale)\n\n const padX = Math.floor((targetSize - scaledWidth) / 2)\n const padY = Math.floor((targetSize - scaledHeight) / 2)\n\n const { data } = await sharp(jpeg)\n .resize(scaledWidth, scaledHeight)\n .extend({\n top: padY,\n bottom: targetSize - scaledHeight - padY,\n left: padX,\n right: targetSize - scaledWidth - padX,\n background: { r: 114, g: 114, b: 114 },\n })\n .removeAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true })\n\n // Convert HWC uint8 to CHW float [0,1]\n const numPixels = targetSize * targetSize\n const float32 = new Float32Array(3 * numPixels)\n for (let i = 0; i < numPixels; i++) {\n const srcBase = i * 3\n float32[0 * numPixels + i] = (data[srcBase]! / 255)\n float32[1 * numPixels + i] = (data[srcBase + 1]! / 255)\n float32[2 * numPixels + i] = (data[srcBase + 2]! / 255)\n }\n\n return { data: float32, scale, padX, padY, originalWidth, originalHeight }\n}\n\n/** Resize and normalize to Float32Array */\nexport async function resizeAndNormalize(\n jpeg: Buffer,\n targetWidth: number,\n targetHeight: number,\n normalization: 'zero-one' | 'imagenet' | 'none',\n layout: 'nchw' | 'nhwc',\n): Promise<Float32Array> {\n const { data } = await sharp(jpeg)\n .resize(targetWidth, targetHeight, { fit: 'fill' })\n .removeAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true })\n\n const numPixels = targetWidth * targetHeight\n const float32 = new Float32Array(3 * numPixels)\n\n // ImageNet mean and std per channel\n const mean = [0.485, 0.456, 0.406]\n const std = [0.229, 0.224, 0.225]\n\n if (layout === 'nchw') {\n for (let i = 0; i < numPixels; i++) {\n const srcBase = i * 3\n for (let c = 0; c < 3; c++) {\n const raw = data[srcBase + c]! / 255\n let val: number\n if (normalization === 'zero-one') {\n val = raw\n } else if (normalization === 'imagenet') {\n val = (raw - mean[c]!) / std[c]!\n } else {\n val = data[srcBase + c]!\n }\n float32[c * numPixels + i] = val\n }\n }\n } else {\n // nhwc\n for (let i = 0; i < numPixels; i++) {\n const srcBase = i * 3\n for (let c = 0; c < 3; c++) {\n const raw = data[srcBase + c]! / 255\n let val: number\n if (normalization === 'zero-one') {\n val = raw\n } else if (normalization === 'imagenet') {\n val = (raw - mean[c]!) / std[c]!\n } else {\n val = data[srcBase + c]!\n }\n float32[i * 3 + c] = val\n }\n }\n }\n\n return float32\n}\n\n/** Convert raw RGB to grayscale Uint8Array */\nexport function rgbToGrayscale(rgb: Buffer, width: number, height: number): Uint8Array {\n const numPixels = width * height\n const gray = new Uint8Array(numPixels)\n for (let i = 0; i < numPixels; i++) {\n const r = rgb[i * 3]!\n const g = rgb[i * 3 + 1]!\n const b = rgb[i * 3 + 2]!\n // BT.601 luma\n gray[i] = Math.round(0.299 * r + 0.587 * g + 0.114 * b)\n }\n return gray\n}\n","// TODO: Wire PythonInferenceEngine for PyTorch/OpenVINO/TFLite runtimes\n// Currently falls back to ONNX CPU when non-ONNX runtime is requested.\n// See: packages/addon-vision/python/ for stub implementations.\n//\n// WHY THIS FILE USES RAW FILESYSTEM PATHS (modelsDir: string):\n//\n// Model files must be loaded via absolute filesystem paths because inference\n// engines (ONNX Runtime, CoreML, etc.) require direct file access -- they do\n// not accept Buffer or stream inputs. This is fundamentally different from\n// user data (recordings, media) where IAddonFileStorage abstraction makes\n// sense. Models are closer to binaries or compiled artifacts that must live\n// on the local filesystem at runtime.\n//\n// IAddonFileStorage.readFile() returns a Buffer, but onnxruntime-node's\n// InferenceSession.create() only accepts a file path string or a Uint8Array\n// loaded into memory. For large models (hundreds of MB), loading into a\n// Uint8Array is impractical. Therefore, modelsDir stays as a raw string path\n// and is intentionally NOT replaced with IAddonFileStorage here.\n\nimport type {\n IInferenceEngine,\n DetectionRuntime,\n ModelCatalogEntry,\n ModelFormat,\n IAddonModelManager,\n} from '@camstack/types'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { NodeInferenceEngine } from './node-engine.js'\nimport { PythonInferenceEngine } from './python-engine.js'\n\nexport interface EngineResolverOptions {\n readonly runtime: DetectionRuntime | 'auto'\n readonly backend: string\n readonly modelEntry: ModelCatalogEntry\n readonly modelsDir: string\n readonly pythonPath?: string\n /** Model service for downloading models. When provided, used instead of raw filesystem probing. */\n readonly models?: IAddonModelManager\n}\n\nexport interface ResolvedEngine {\n readonly engine: IInferenceEngine\n readonly format: ModelFormat\n readonly modelPath: string\n}\n\n/** Priority order for auto-selection of ONNX backends */\nconst AUTO_BACKEND_PRIORITY = ['coreml', 'cuda', 'tensorrt', 'cpu'] as const\n\n/** Map backend names to the model format they require.\n * CoreML backend uses ONNX format -- onnxruntime-node loads the .onnx file\n * and accelerates it via the CoreML execution provider internally.\n * Native .mlpackage is only for PythonInferenceEngine. */\nconst BACKEND_TO_FORMAT: Readonly<Record<string, ModelFormat>> = {\n cpu: 'onnx',\n coreml: 'onnx',\n cuda: 'onnx',\n tensorrt: 'onnx',\n} as const\n\n/** Map DetectionRuntime to ModelFormat */\nconst RUNTIME_TO_FORMAT: Readonly<Partial<Record<DetectionRuntime, ModelFormat>>> = {\n onnx: 'onnx',\n coreml: 'coreml',\n openvino: 'openvino',\n tflite: 'tflite',\n pytorch: 'pt',\n} as const\n\nfunction modelFilePath(modelsDir: string, modelEntry: ModelCatalogEntry, format: ModelFormat): string {\n const formatEntry = modelEntry.formats[format]\n if (!formatEntry) {\n throw new Error(`Model ${modelEntry.id} has no ${format} format`)\n }\n // Derive filename from URL\n const urlParts = formatEntry.url.split('/')\n const filename = urlParts[urlParts.length - 1] ?? `${modelEntry.id}.${format}`\n return path.join(modelsDir, filename)\n}\n\nfunction modelExists(filePath: string): boolean {\n try {\n return fs.existsSync(filePath)\n } catch {\n return false\n }\n}\n\nexport async function resolveEngine(options: EngineResolverOptions): Promise<ResolvedEngine> {\n const { runtime, backend, modelEntry, modelsDir, models } = options\n\n let selectedFormat: ModelFormat\n let selectedBackend: string\n\n if (runtime === 'auto') {\n // Probe available ONNX backends and pick best\n const available = await probeOnnxBackends()\n\n // Pick first priority backend that has a corresponding model format available\n let chosen: { backend: string; format: ModelFormat } | null = null\n\n for (const b of AUTO_BACKEND_PRIORITY) {\n if (!available.includes(b)) continue\n const fmt = BACKEND_TO_FORMAT[b]\n if (!fmt) continue\n if (!modelEntry.formats[fmt]) continue\n chosen = { backend: b, format: fmt }\n break\n }\n\n if (!chosen) {\n throw new Error(\n `resolveEngine: no compatible backend found for model ${modelEntry.id}. Available backends: ${available.join(', ')}`,\n )\n }\n\n selectedFormat = chosen.format\n selectedBackend = chosen.backend\n } else {\n // Explicit runtime requested\n const fmt = RUNTIME_TO_FORMAT[runtime]\n if (!fmt) {\n throw new Error(`resolveEngine: unsupported runtime \"${runtime}\"`)\n }\n if (!modelEntry.formats[fmt]) {\n throw new Error(\n `resolveEngine: model ${modelEntry.id} has no ${fmt} format for runtime ${runtime}`,\n )\n }\n selectedFormat = fmt\n // For onnx runtime, use the provided backend; otherwise use the runtime name\n selectedBackend = runtime === 'onnx' ? (backend || 'cpu') : runtime\n }\n\n // Download model and extra files via the unified service\n let modelPath: string\n\n if (models) {\n // Use the unified ModelDownloadService\n modelPath = await models.ensure(modelEntry.id, selectedFormat)\n } else {\n // Fallback: direct filesystem check (no download capability)\n modelPath = modelFilePath(modelsDir, modelEntry, selectedFormat)\n if (!modelExists(modelPath)) {\n throw new Error(\n `resolveEngine: model file not found at ${modelPath} and no model service provided`,\n )\n }\n }\n\n // NodeInferenceEngine handles ONNX format only (with any ONNX backend including CoreML provider).\n // Native .mlpackage/.mlmodel files go to PythonInferenceEngine below.\n if (selectedFormat === 'onnx') {\n const engine = new NodeInferenceEngine(modelPath, selectedBackend)\n await engine.initialize()\n return { engine, format: selectedFormat, modelPath }\n }\n\n // For non-ONNX/CoreML formats, try PythonInferenceEngine when a python binary is available\n const { pythonPath } = options\n const PYTHON_SCRIPT_MAP: Readonly<Record<string, string>> = {\n coreml: 'coreml_inference.py',\n pytorch: 'pytorch_inference.py',\n openvino: 'openvino_inference.py',\n } as const\n\n const effectiveRuntime = runtime === 'auto' ? selectedBackend : runtime\n const scriptName = PYTHON_SCRIPT_MAP[effectiveRuntime]\n\n if (scriptName && pythonPath) {\n // Resolve python script path -- search multiple locations:\n // - src/shared/ -> ../../python/ (source development)\n // - dist/shared/ -> ../../python/ (installed addon with python/ at package root)\n // - dist/ -> ../python/ (flat dist)\n const candidates = [\n path.join(__dirname, '../../python', scriptName),\n path.join(__dirname, '../python', scriptName),\n path.join(__dirname, '../../../python', scriptName),\n ]\n const scriptPath = candidates.find(p => fs.existsSync(p))\n if (!scriptPath) {\n throw new Error(\n `resolveEngine: Python script \"${scriptName}\" not found. Searched:\\n${candidates.join('\\n')}`,\n )\n }\n const inputSize = Math.max(modelEntry.inputSize.width, modelEntry.inputSize.height)\n const engine = new PythonInferenceEngine(pythonPath, scriptPath, effectiveRuntime as DetectionRuntime, modelPath, [\n `--input-size=${inputSize}`,\n `--confidence=0.25`,\n ])\n await engine.initialize()\n return { engine, format: selectedFormat, modelPath }\n }\n\n // Final fallback: use ONNX CPU if available\n const fallbackPath = modelFilePath(modelsDir, modelEntry, 'onnx')\n if (modelEntry.formats['onnx'] && modelExists(fallbackPath)) {\n const engine = new NodeInferenceEngine(fallbackPath, 'cpu')\n await engine.initialize()\n return { engine, format: 'onnx', modelPath: fallbackPath }\n }\n\n throw new Error(\n `resolveEngine: format ${selectedFormat} is not yet supported by NodeInferenceEngine, ` +\n `no Python runtime is available, and no ONNX fallback exists`,\n )\n}\n\n/** Probe which ONNX execution providers are available on this system */\nexport async function probeOnnxBackends(): Promise<string[]> {\n const available: string[] = ['cpu'] // CPU is always available\n\n try {\n const ort = await import('onnxruntime-node')\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const providers: string[] = (ort as any).env?.webgl?.disabled !== undefined\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ? ((ort as any).InferenceSession?.getAvailableProviders?.() ?? [])\n : []\n\n for (const p of providers) {\n const normalized = p.toLowerCase().replace('executionprovider', '')\n if (normalized === 'coreml') available.push('coreml')\n else if (normalized === 'cuda') available.push('cuda')\n else if (normalized === 'tensorrt') available.push('tensorrt')\n }\n } catch {\n // onnxruntime-node may not be installed; CPU only\n }\n\n // Platform-specific hints when getAvailableProviders isn't exposed\n if (process.platform === 'darwin' && !available.includes('coreml')) {\n available.push('coreml')\n }\n\n return [...new Set(available)]\n}\n","import type { IInferenceEngine, DetectionRuntime, DetectionDevice } from '@camstack/types'\nimport * as path from 'node:path'\n\nconst BACKEND_TO_PROVIDER: Readonly<Record<string, string>> = {\n cpu: 'cpu',\n coreml: 'coreml',\n cuda: 'cuda',\n tensorrt: 'tensorrt',\n dml: 'dml',\n} as const\n\nconst BACKEND_TO_DEVICE: Readonly<Record<string, DetectionDevice>> = {\n cpu: 'cpu',\n coreml: 'gpu-mps',\n cuda: 'gpu-cuda',\n tensorrt: 'tensorrt',\n} as const\n\nexport class NodeInferenceEngine implements IInferenceEngine {\n readonly runtime: DetectionRuntime = 'onnx'\n readonly device: DetectionDevice\n private session: unknown = null\n\n constructor(\n private readonly modelPath: string,\n private readonly backend: string,\n ) {\n this.device = (BACKEND_TO_DEVICE[backend] ?? 'cpu') as DetectionDevice\n }\n\n async initialize(): Promise<void> {\n const ort = await import('onnxruntime-node')\n const provider = BACKEND_TO_PROVIDER[this.backend] ?? 'cpu'\n\n // Resolve absolute path\n const absModelPath = path.isAbsolute(this.modelPath)\n ? this.modelPath\n : path.resolve(process.cwd(), this.modelPath)\n\n const sessionOptions: Record<string, unknown> = {\n executionProviders: [provider],\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this.session = await (ort as any).InferenceSession.create(absModelPath, sessionOptions)\n }\n\n async run(input: Float32Array, inputShape: readonly number[]): Promise<Float32Array> {\n if (!this.session) {\n throw new Error('NodeInferenceEngine: not initialized — call initialize() first')\n }\n\n const ort = await import('onnxruntime-node')\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const sess = this.session as any\n\n // Get the first input name\n const inputName: string = sess.inputNames[0]\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const tensor = new (ort as any).Tensor('float32', input, [...inputShape])\n const feeds: Record<string, unknown> = { [inputName]: tensor }\n\n const results = await sess.run(feeds)\n const outputName: string = sess.outputNames[0]\n const outputTensor = results[outputName]\n\n return outputTensor.data as Float32Array\n }\n\n async runMultiOutput(\n input: Float32Array,\n inputShape: readonly number[],\n ): Promise<Record<string, Float32Array>> {\n if (!this.session) {\n throw new Error('NodeInferenceEngine: not initialized — call initialize() first')\n }\n\n const ort = await import('onnxruntime-node')\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const sess = this.session as any\n\n const inputName: string = sess.inputNames[0]\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const tensor = new (ort as any).Tensor('float32', input, [...inputShape])\n const feeds: Record<string, unknown> = { [inputName]: tensor }\n\n const results = await sess.run(feeds)\n\n const out: Record<string, Float32Array> = {}\n for (const name of sess.outputNames as string[]) {\n out[name] = results[name].data as Float32Array\n }\n return out\n }\n\n async dispose(): Promise<void> {\n // onnxruntime-node sessions don't have explicit close in all versions\n // but we clear the reference\n this.session = null\n }\n}\n","import type { IInferenceEngine, DetectionRuntime, DetectionDevice, IAddonDepsManager } from '@camstack/types'\nimport { spawn, type ChildProcess } from 'node:child_process'\n\nexport class PythonInferenceEngine implements IInferenceEngine {\n readonly runtime: DetectionRuntime\n readonly device: DetectionDevice\n private process: ChildProcess | null = null\n private receiveBuffer: Buffer = Buffer.alloc(0)\n private pendingResolve: ((value: Record<string, unknown>) => void) | null = null\n private pendingReject: ((reason: Error) => void) | null = null\n\n constructor(\n private readonly pythonPath: string,\n private readonly scriptPath: string,\n runtime: DetectionRuntime,\n private readonly modelPath: string,\n private readonly extraArgs: readonly string[] = [],\n ) {\n this.runtime = runtime\n // Determine device from runtime\n const runtimeDeviceMap: Readonly<Record<DetectionRuntime, DetectionDevice>> = {\n onnx: 'cpu',\n coreml: 'gpu-mps',\n pytorch: 'cpu',\n openvino: 'cpu',\n tflite: 'cpu',\n }\n this.device = runtimeDeviceMap[runtime]\n }\n\n async initialize(): Promise<void> {\n const args = [this.scriptPath, this.modelPath, ...this.extraArgs]\n this.process = spawn(this.pythonPath, args, {\n stdio: ['pipe', 'pipe', 'pipe'],\n })\n\n if (!this.process.stdout || !this.process.stdin) {\n throw new Error('PythonInferenceEngine: failed to create process pipes')\n }\n\n this.process.stderr?.on('data', (chunk: Buffer) => {\n // Log stderr from python process for debugging\n process.stderr.write(`[python-engine] ${chunk.toString()}`)\n })\n\n this.process.on('error', (err) => {\n this.pendingReject?.(err)\n this.pendingReject = null\n this.pendingResolve = null\n })\n\n this.process.on('exit', (code) => {\n if (code !== 0) {\n const err = new Error(`PythonInferenceEngine: process exited with code ${code}`)\n this.pendingReject?.(err)\n this.pendingReject = null\n this.pendingResolve = null\n }\n })\n\n this.process.stdout.on('data', (chunk: Buffer) => {\n this.receiveBuffer = Buffer.concat([this.receiveBuffer, chunk])\n this._tryReceive()\n })\n\n // Give the process a moment to start up and load the model\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => resolve(), 2000)\n this.process?.on('error', (err) => {\n clearTimeout(timeout)\n reject(err)\n })\n this.process?.on('exit', (code) => {\n clearTimeout(timeout)\n if (code !== 0) {\n reject(new Error(`PythonInferenceEngine: process exited early with code ${code}`))\n }\n })\n })\n }\n\n private _tryReceive(): void {\n // Binary IPC: [4 bytes LE uint32 length][JSON bytes]\n if (this.receiveBuffer.length < 4) return\n\n const length = this.receiveBuffer.readUInt32LE(0)\n if (this.receiveBuffer.length < 4 + length) return\n\n const jsonBytes = this.receiveBuffer.subarray(4, 4 + length)\n this.receiveBuffer = this.receiveBuffer.subarray(4 + length)\n\n const resolve = this.pendingResolve\n const reject = this.pendingReject\n this.pendingResolve = null\n this.pendingReject = null\n\n if (!resolve) return\n\n try {\n const parsed = JSON.parse(jsonBytes.toString('utf8')) as Record<string, unknown>\n resolve(parsed)\n } catch (err) {\n reject?.(err instanceof Error ? err : new Error(String(err)))\n }\n }\n\n /** Send JPEG buffer, receive JSON detection results */\n async runJpeg(jpeg: Buffer): Promise<Record<string, unknown>> {\n if (!this.process?.stdin) {\n throw new Error('PythonInferenceEngine: process not initialized')\n }\n\n return new Promise<Record<string, unknown>>((resolve, reject) => {\n this.pendingResolve = resolve\n this.pendingReject = reject\n\n // Binary IPC: [4 bytes LE uint32 length][JPEG bytes]\n const lengthBuf = Buffer.allocUnsafe(4)\n lengthBuf.writeUInt32LE(jpeg.length, 0)\n this.process!.stdin!.write(Buffer.concat([lengthBuf, jpeg]))\n })\n }\n\n /** IInferenceEngine.run — wraps runJpeg for compatibility */\n async run(_input: Float32Array, _inputShape: readonly number[]): Promise<Float32Array> {\n throw new Error(\n 'PythonInferenceEngine: use runJpeg() directly — this engine operates on JPEG input',\n )\n }\n\n /** IInferenceEngine.runMultiOutput — not supported by Python engine (operates on JPEG input) */\n async runMultiOutput(\n _input: Float32Array,\n _inputShape: readonly number[],\n ): Promise<Record<string, Float32Array>> {\n throw new Error(\n 'PythonInferenceEngine: runMultiOutput() is not supported — this engine operates on JPEG input',\n )\n }\n\n async dispose(): Promise<void> {\n if (this.process) {\n this.process.stdin?.end()\n this.process.kill('SIGTERM')\n this.process = null\n }\n }\n}\n\n/**\n * Resolve Python binary for ML inference. Priority:\n * 1. Explicit config pythonPath\n * 2. Embedded (data/deps/python/bin/python3)\n * 3. System PATH (python3, python)\n * 4. Download standalone Python\n *\n * Returns null if Python is not available.\n */\nexport async function resolvePythonBinary(\n configPath: string | undefined,\n deps: IAddonDepsManager,\n): Promise<string | null> {\n if (configPath) return configPath\n return deps.ensurePython()\n}\n"],"mappings":"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","names":["path","sharp","sharp","path","resolve","fs","path"]}
1
+ {"version":3,"sources":["../../../src/catalogs/vehicle-classification-models.ts","../../../src/shared/image-utils.ts","../../../src/shared/node-engine.ts","../../../src/shared/python-engine.ts","../../../src/shared/engine-resolver.ts","../../../src/addons/vehicle-classifier/index.ts"],"sourcesContent":["import type { ModelCatalogEntry, LabelDefinition } from '@camstack/types'\nimport { hfModelUrl } from '@camstack/types'\n\nconst HF_REPO = 'camstack/camstack-models'\n\nconst hf = (path: string) => hfModelUrl(HF_REPO, path)\n\nconst VEHICLE_LABELS: readonly LabelDefinition[] = [\n { id: 'vehicle-type', name: 'Vehicle Type' },\n]\n\nexport const VEHICLE_TYPE_MODELS: readonly ModelCatalogEntry[] = [\n {\n id: 'vehicle-type-efficientnet',\n name: 'Vehicle Type (EfficientNet)',\n description: 'EfficientNet-B4 vehicle make/model/year classifier — 8,949 classes from VMMRdb',\n inputSize: { width: 380, height: 380 },\n inputNormalization: 'imagenet',\n labels: VEHICLE_LABELS,\n formats: {\n onnx: { url: hf('vehicleClassification/efficientnet/onnx/camstack-vehicle-type-efficientnet.onnx'), sizeMB: 135 },\n coreml: {\n url: hf('vehicleClassification/efficientnet/coreml/camstack-vehicle-type-efficientnet.mlpackage'),\n sizeMB: 10,\n isDirectory: true,\n files: ['Manifest.json', 'Data/com.apple.CoreML/model.mlmodel', 'Data/com.apple.CoreML/weights/weight.bin'],\n runtimes: ['python'],\n },\n },\n extraFiles: [\n {\n url: hf('vehicleClassification/efficientnet/camstack-vehicle-type-labels.json'),\n filename: 'camstack-vehicle-type-labels.json',\n sizeMB: 0.2,\n },\n ],\n },\n] as const\n","import sharp from 'sharp'\nimport type { BoundingBox } from '@camstack/types'\n\n/** Decode JPEG to raw RGB pixels */\nexport async function jpegToRgb(\n jpeg: Buffer,\n): Promise<{ data: Buffer; width: number; height: number }> {\n const { data, info } = await sharp(jpeg)\n .removeAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true })\n return { data, width: info.width, height: info.height }\n}\n\n/** Crop a region from a JPEG buffer */\nexport async function cropRegion(jpeg: Buffer, roi: BoundingBox): Promise<Buffer> {\n return sharp(jpeg)\n .extract({\n left: Math.round(roi.x),\n top: Math.round(roi.y),\n width: Math.round(roi.w),\n height: Math.round(roi.h),\n })\n .jpeg()\n .toBuffer()\n}\n\n/** Letterbox resize for YOLO: resize preserving aspect ratio, pad to square */\nexport async function letterbox(\n jpeg: Buffer,\n targetSize: number,\n): Promise<{\n data: Float32Array\n scale: number\n padX: number\n padY: number\n originalWidth: number\n originalHeight: number\n}> {\n const meta = await sharp(jpeg).metadata()\n const originalWidth = meta.width ?? 0\n const originalHeight = meta.height ?? 0\n\n const scale = Math.min(targetSize / originalWidth, targetSize / originalHeight)\n const scaledWidth = Math.round(originalWidth * scale)\n const scaledHeight = Math.round(originalHeight * scale)\n\n const padX = Math.floor((targetSize - scaledWidth) / 2)\n const padY = Math.floor((targetSize - scaledHeight) / 2)\n\n const { data } = await sharp(jpeg)\n .resize(scaledWidth, scaledHeight)\n .extend({\n top: padY,\n bottom: targetSize - scaledHeight - padY,\n left: padX,\n right: targetSize - scaledWidth - padX,\n background: { r: 114, g: 114, b: 114 },\n })\n .removeAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true })\n\n // Convert HWC uint8 to CHW float [0,1]\n const numPixels = targetSize * targetSize\n const float32 = new Float32Array(3 * numPixels)\n for (let i = 0; i < numPixels; i++) {\n const srcBase = i * 3\n float32[0 * numPixels + i] = (data[srcBase]! / 255)\n float32[1 * numPixels + i] = (data[srcBase + 1]! / 255)\n float32[2 * numPixels + i] = (data[srcBase + 2]! / 255)\n }\n\n return { data: float32, scale, padX, padY, originalWidth, originalHeight }\n}\n\n/** Resize and normalize to Float32Array */\nexport async function resizeAndNormalize(\n jpeg: Buffer,\n targetWidth: number,\n targetHeight: number,\n normalization: 'zero-one' | 'imagenet' | 'none',\n layout: 'nchw' | 'nhwc',\n): Promise<Float32Array> {\n const { data } = await sharp(jpeg)\n .resize(targetWidth, targetHeight, { fit: 'fill' })\n .removeAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true })\n\n const numPixels = targetWidth * targetHeight\n const float32 = new Float32Array(3 * numPixels)\n\n // ImageNet mean and std per channel\n const mean = [0.485, 0.456, 0.406]\n const std = [0.229, 0.224, 0.225]\n\n if (layout === 'nchw') {\n for (let i = 0; i < numPixels; i++) {\n const srcBase = i * 3\n for (let c = 0; c < 3; c++) {\n const raw = data[srcBase + c]! / 255\n let val: number\n if (normalization === 'zero-one') {\n val = raw\n } else if (normalization === 'imagenet') {\n val = (raw - mean[c]!) / std[c]!\n } else {\n val = data[srcBase + c]!\n }\n float32[c * numPixels + i] = val\n }\n }\n } else {\n // nhwc\n for (let i = 0; i < numPixels; i++) {\n const srcBase = i * 3\n for (let c = 0; c < 3; c++) {\n const raw = data[srcBase + c]! / 255\n let val: number\n if (normalization === 'zero-one') {\n val = raw\n } else if (normalization === 'imagenet') {\n val = (raw - mean[c]!) / std[c]!\n } else {\n val = data[srcBase + c]!\n }\n float32[i * 3 + c] = val\n }\n }\n }\n\n return float32\n}\n\n/** Convert raw RGB to grayscale Uint8Array */\nexport function rgbToGrayscale(rgb: Buffer, width: number, height: number): Uint8Array {\n const numPixels = width * height\n const gray = new Uint8Array(numPixels)\n for (let i = 0; i < numPixels; i++) {\n const r = rgb[i * 3]!\n const g = rgb[i * 3 + 1]!\n const b = rgb[i * 3 + 2]!\n // BT.601 luma\n gray[i] = Math.round(0.299 * r + 0.587 * g + 0.114 * b)\n }\n return gray\n}\n","import type { IInferenceEngine, DetectionRuntime, DetectionDevice } from '@camstack/types'\nimport * as path from 'node:path'\n\nconst BACKEND_TO_PROVIDER: Readonly<Record<string, string>> = {\n cpu: 'cpu',\n coreml: 'coreml',\n cuda: 'cuda',\n tensorrt: 'tensorrt',\n dml: 'dml',\n} as const\n\nconst BACKEND_TO_DEVICE: Readonly<Record<string, DetectionDevice>> = {\n cpu: 'cpu',\n coreml: 'gpu-mps',\n cuda: 'gpu-cuda',\n tensorrt: 'tensorrt',\n} as const\n\nexport class NodeInferenceEngine implements IInferenceEngine {\n readonly runtime: DetectionRuntime = 'onnx'\n readonly device: DetectionDevice\n private session: unknown = null\n\n constructor(\n private readonly modelPath: string,\n private readonly backend: string,\n ) {\n this.device = (BACKEND_TO_DEVICE[backend] ?? 'cpu') as DetectionDevice\n }\n\n async initialize(): Promise<void> {\n const ort = await import('onnxruntime-node')\n const provider = BACKEND_TO_PROVIDER[this.backend] ?? 'cpu'\n\n // Resolve absolute path\n const absModelPath = path.isAbsolute(this.modelPath)\n ? this.modelPath\n : path.resolve(process.cwd(), this.modelPath)\n\n const sessionOptions: Record<string, unknown> = {\n executionProviders: [provider],\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this.session = await (ort as any).InferenceSession.create(absModelPath, sessionOptions)\n }\n\n async run(input: Float32Array, inputShape: readonly number[]): Promise<Float32Array> {\n if (!this.session) {\n throw new Error('NodeInferenceEngine: not initialized — call initialize() first')\n }\n\n const ort = await import('onnxruntime-node')\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const sess = this.session as any\n\n // Get the first input name\n const inputName: string = sess.inputNames[0]\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const tensor = new (ort as any).Tensor('float32', input, [...inputShape])\n const feeds: Record<string, unknown> = { [inputName]: tensor }\n\n const results = await sess.run(feeds)\n const outputName: string = sess.outputNames[0]\n const outputTensor = results[outputName]\n\n return outputTensor.data as Float32Array\n }\n\n async runMultiOutput(\n input: Float32Array,\n inputShape: readonly number[],\n ): Promise<Record<string, Float32Array>> {\n if (!this.session) {\n throw new Error('NodeInferenceEngine: not initialized — call initialize() first')\n }\n\n const ort = await import('onnxruntime-node')\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const sess = this.session as any\n\n const inputName: string = sess.inputNames[0]\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const tensor = new (ort as any).Tensor('float32', input, [...inputShape])\n const feeds: Record<string, unknown> = { [inputName]: tensor }\n\n const results = await sess.run(feeds)\n\n const out: Record<string, Float32Array> = {}\n for (const name of sess.outputNames as string[]) {\n out[name] = results[name].data as Float32Array\n }\n return out\n }\n\n async dispose(): Promise<void> {\n // onnxruntime-node sessions don't have explicit close in all versions\n // but we clear the reference\n this.session = null\n }\n}\n","import type { IInferenceEngine, DetectionRuntime, DetectionDevice, IAddonDepsManager } from '@camstack/types'\nimport { spawn, type ChildProcess } from 'node:child_process'\n\nexport class PythonInferenceEngine implements IInferenceEngine {\n readonly runtime: DetectionRuntime\n readonly device: DetectionDevice\n private process: ChildProcess | null = null\n private receiveBuffer: Buffer = Buffer.alloc(0)\n private pendingResolve: ((value: Record<string, unknown>) => void) | null = null\n private pendingReject: ((reason: Error) => void) | null = null\n\n constructor(\n private readonly pythonPath: string,\n private readonly scriptPath: string,\n runtime: DetectionRuntime,\n private readonly modelPath: string,\n private readonly extraArgs: readonly string[] = [],\n ) {\n this.runtime = runtime\n // Determine device from runtime\n const runtimeDeviceMap: Readonly<Record<DetectionRuntime, DetectionDevice>> = {\n onnx: 'cpu',\n coreml: 'gpu-mps',\n pytorch: 'cpu',\n openvino: 'cpu',\n tflite: 'cpu',\n }\n this.device = runtimeDeviceMap[runtime]\n }\n\n async initialize(): Promise<void> {\n const args = [this.scriptPath, this.modelPath, ...this.extraArgs]\n this.process = spawn(this.pythonPath, args, {\n stdio: ['pipe', 'pipe', 'pipe'],\n })\n\n if (!this.process.stdout || !this.process.stdin) {\n throw new Error('PythonInferenceEngine: failed to create process pipes')\n }\n\n this.process.stderr?.on('data', (chunk: Buffer) => {\n // Log stderr from python process for debugging\n process.stderr.write(`[python-engine] ${chunk.toString()}`)\n })\n\n this.process.on('error', (err) => {\n this.pendingReject?.(err)\n this.pendingReject = null\n this.pendingResolve = null\n })\n\n this.process.on('exit', (code) => {\n if (code !== 0) {\n const err = new Error(`PythonInferenceEngine: process exited with code ${code}`)\n this.pendingReject?.(err)\n this.pendingReject = null\n this.pendingResolve = null\n }\n })\n\n this.process.stdout.on('data', (chunk: Buffer) => {\n this.receiveBuffer = Buffer.concat([this.receiveBuffer, chunk])\n this._tryReceive()\n })\n\n // Give the process a moment to start up and load the model\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => resolve(), 2000)\n this.process?.on('error', (err) => {\n clearTimeout(timeout)\n reject(err)\n })\n this.process?.on('exit', (code) => {\n clearTimeout(timeout)\n if (code !== 0) {\n reject(new Error(`PythonInferenceEngine: process exited early with code ${code}`))\n }\n })\n })\n }\n\n private _tryReceive(): void {\n // Binary IPC: [4 bytes LE uint32 length][JSON bytes]\n if (this.receiveBuffer.length < 4) return\n\n const length = this.receiveBuffer.readUInt32LE(0)\n if (this.receiveBuffer.length < 4 + length) return\n\n const jsonBytes = this.receiveBuffer.subarray(4, 4 + length)\n this.receiveBuffer = this.receiveBuffer.subarray(4 + length)\n\n const resolve = this.pendingResolve\n const reject = this.pendingReject\n this.pendingResolve = null\n this.pendingReject = null\n\n if (!resolve) return\n\n try {\n const parsed = JSON.parse(jsonBytes.toString('utf8')) as Record<string, unknown>\n resolve(parsed)\n } catch (err) {\n reject?.(err instanceof Error ? err : new Error(String(err)))\n }\n }\n\n /** Send JPEG buffer, receive JSON detection results */\n async runJpeg(jpeg: Buffer): Promise<Record<string, unknown>> {\n if (!this.process?.stdin) {\n throw new Error('PythonInferenceEngine: process not initialized')\n }\n\n return new Promise<Record<string, unknown>>((resolve, reject) => {\n this.pendingResolve = resolve\n this.pendingReject = reject\n\n // Binary IPC: [4 bytes LE uint32 length][JPEG bytes]\n const lengthBuf = Buffer.allocUnsafe(4)\n lengthBuf.writeUInt32LE(jpeg.length, 0)\n this.process!.stdin!.write(Buffer.concat([lengthBuf, jpeg]))\n })\n }\n\n /** IInferenceEngine.run — wraps runJpeg for compatibility */\n async run(_input: Float32Array, _inputShape: readonly number[]): Promise<Float32Array> {\n throw new Error(\n 'PythonInferenceEngine: use runJpeg() directly — this engine operates on JPEG input',\n )\n }\n\n /** IInferenceEngine.runMultiOutput — not supported by Python engine (operates on JPEG input) */\n async runMultiOutput(\n _input: Float32Array,\n _inputShape: readonly number[],\n ): Promise<Record<string, Float32Array>> {\n throw new Error(\n 'PythonInferenceEngine: runMultiOutput() is not supported — this engine operates on JPEG input',\n )\n }\n\n async dispose(): Promise<void> {\n if (this.process) {\n this.process.stdin?.end()\n this.process.kill('SIGTERM')\n this.process = null\n }\n }\n}\n\n/**\n * Resolve Python binary for ML inference. Priority:\n * 1. Explicit config pythonPath\n * 2. Embedded (data/deps/python/bin/python3)\n * 3. System PATH (python3, python)\n * 4. Download standalone Python\n *\n * Returns null if Python is not available.\n */\nexport async function resolvePythonBinary(\n configPath: string | undefined,\n deps: IAddonDepsManager,\n): Promise<string | null> {\n if (configPath) return configPath\n return deps.ensurePython()\n}\n","// TODO: Wire PythonInferenceEngine for PyTorch/OpenVINO/TFLite runtimes\n// Currently falls back to ONNX CPU when non-ONNX runtime is requested.\n// See: packages/addon-vision/python/ for stub implementations.\n//\n// WHY THIS FILE USES RAW FILESYSTEM PATHS (modelsDir: string):\n//\n// Model files must be loaded via absolute filesystem paths because inference\n// engines (ONNX Runtime, CoreML, etc.) require direct file access -- they do\n// not accept Buffer or stream inputs. This is fundamentally different from\n// user data (recordings, media) where IAddonFileStorage abstraction makes\n// sense. Models are closer to binaries or compiled artifacts that must live\n// on the local filesystem at runtime.\n//\n// IAddonFileStorage.readFile() returns a Buffer, but onnxruntime-node's\n// InferenceSession.create() only accepts a file path string or a Uint8Array\n// loaded into memory. For large models (hundreds of MB), loading into a\n// Uint8Array is impractical. Therefore, modelsDir stays as a raw string path\n// and is intentionally NOT replaced with IAddonFileStorage here.\n\nimport type {\n IInferenceEngine,\n DetectionRuntime,\n ModelCatalogEntry,\n ModelFormat,\n IAddonModelManager,\n} from '@camstack/types'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { NodeInferenceEngine } from './node-engine.js'\nimport { PythonInferenceEngine } from './python-engine.js'\n\nexport interface EngineResolverOptions {\n readonly runtime: DetectionRuntime | 'auto'\n readonly backend: string\n readonly modelEntry: ModelCatalogEntry\n readonly modelsDir: string\n readonly pythonPath?: string\n /** Model service for downloading models. When provided, used instead of raw filesystem probing. */\n readonly models?: IAddonModelManager\n}\n\nexport interface ResolvedEngine {\n readonly engine: IInferenceEngine\n readonly format: ModelFormat\n readonly modelPath: string\n}\n\n/** Priority order for auto-selection of ONNX backends */\nconst AUTO_BACKEND_PRIORITY = ['coreml', 'cuda', 'tensorrt', 'cpu'] as const\n\n/** Map backend names to the model format they require.\n * CoreML backend uses ONNX format -- onnxruntime-node loads the .onnx file\n * and accelerates it via the CoreML execution provider internally.\n * Native .mlpackage is only for PythonInferenceEngine. */\nconst BACKEND_TO_FORMAT: Readonly<Record<string, ModelFormat>> = {\n cpu: 'onnx',\n coreml: 'onnx',\n cuda: 'onnx',\n tensorrt: 'onnx',\n} as const\n\n/** Map DetectionRuntime to ModelFormat */\nconst RUNTIME_TO_FORMAT: Readonly<Partial<Record<DetectionRuntime, ModelFormat>>> = {\n onnx: 'onnx',\n coreml: 'coreml',\n openvino: 'openvino',\n tflite: 'tflite',\n pytorch: 'pt',\n} as const\n\nfunction modelFilePath(modelsDir: string, modelEntry: ModelCatalogEntry, format: ModelFormat): string {\n const formatEntry = modelEntry.formats[format]\n if (!formatEntry) {\n throw new Error(`Model ${modelEntry.id} has no ${format} format`)\n }\n // Derive filename from URL\n const urlParts = formatEntry.url.split('/')\n const filename = urlParts[urlParts.length - 1] ?? `${modelEntry.id}.${format}`\n return path.join(modelsDir, filename)\n}\n\nfunction modelExists(filePath: string): boolean {\n try {\n return fs.existsSync(filePath)\n } catch {\n return false\n }\n}\n\nexport async function resolveEngine(options: EngineResolverOptions): Promise<ResolvedEngine> {\n const { runtime, backend, modelEntry, modelsDir, models } = options\n\n let selectedFormat: ModelFormat\n let selectedBackend: string\n\n if (runtime === 'auto') {\n // Probe available ONNX backends and pick best\n const available = await probeOnnxBackends()\n\n // Pick first priority backend that has a corresponding model format available\n let chosen: { backend: string; format: ModelFormat } | null = null\n\n for (const b of AUTO_BACKEND_PRIORITY) {\n if (!available.includes(b)) continue\n const fmt = BACKEND_TO_FORMAT[b]\n if (!fmt) continue\n if (!modelEntry.formats[fmt]) continue\n chosen = { backend: b, format: fmt }\n break\n }\n\n if (!chosen) {\n throw new Error(\n `resolveEngine: no compatible backend found for model ${modelEntry.id}. Available backends: ${available.join(', ')}`,\n )\n }\n\n selectedFormat = chosen.format\n selectedBackend = chosen.backend\n } else {\n // Explicit runtime requested\n const fmt = RUNTIME_TO_FORMAT[runtime]\n if (!fmt) {\n throw new Error(`resolveEngine: unsupported runtime \"${runtime}\"`)\n }\n if (!modelEntry.formats[fmt]) {\n throw new Error(\n `resolveEngine: model ${modelEntry.id} has no ${fmt} format for runtime ${runtime}`,\n )\n }\n selectedFormat = fmt\n // For onnx runtime, use the provided backend; otherwise use the runtime name\n selectedBackend = runtime === 'onnx' ? (backend || 'cpu') : runtime\n }\n\n // Download model and extra files via the unified service\n let modelPath: string\n\n if (models) {\n // Use the unified ModelDownloadService\n modelPath = await models.ensure(modelEntry.id, selectedFormat)\n } else {\n // Fallback: direct filesystem check (no download capability)\n modelPath = modelFilePath(modelsDir, modelEntry, selectedFormat)\n if (!modelExists(modelPath)) {\n throw new Error(\n `resolveEngine: model file not found at ${modelPath} and no model service provided`,\n )\n }\n }\n\n // NodeInferenceEngine handles ONNX format only (with any ONNX backend including CoreML provider).\n // Native .mlpackage/.mlmodel files go to PythonInferenceEngine below.\n if (selectedFormat === 'onnx') {\n const engine = new NodeInferenceEngine(modelPath, selectedBackend)\n await engine.initialize()\n return { engine, format: selectedFormat, modelPath }\n }\n\n // For non-ONNX/CoreML formats, try PythonInferenceEngine when a python binary is available\n const { pythonPath } = options\n const PYTHON_SCRIPT_MAP: Readonly<Record<string, string>> = {\n coreml: 'coreml_inference.py',\n pytorch: 'pytorch_inference.py',\n openvino: 'openvino_inference.py',\n } as const\n\n const effectiveRuntime = runtime === 'auto' ? selectedBackend : runtime\n const scriptName = PYTHON_SCRIPT_MAP[effectiveRuntime]\n\n if (scriptName && pythonPath) {\n // Resolve python script path -- search multiple locations:\n // - src/shared/ -> ../../python/ (source development)\n // - dist/shared/ -> ../../python/ (installed addon with python/ at package root)\n // - dist/ -> ../python/ (flat dist)\n const candidates = [\n path.join(__dirname, '../../python', scriptName),\n path.join(__dirname, '../python', scriptName),\n path.join(__dirname, '../../../python', scriptName),\n ]\n const scriptPath = candidates.find(p => fs.existsSync(p))\n if (!scriptPath) {\n throw new Error(\n `resolveEngine: Python script \"${scriptName}\" not found. Searched:\\n${candidates.join('\\n')}`,\n )\n }\n const inputSize = Math.max(modelEntry.inputSize.width, modelEntry.inputSize.height)\n const engine = new PythonInferenceEngine(pythonPath, scriptPath, effectiveRuntime as DetectionRuntime, modelPath, [\n `--input-size=${inputSize}`,\n `--confidence=0.25`,\n ])\n await engine.initialize()\n return { engine, format: selectedFormat, modelPath }\n }\n\n // Final fallback: use ONNX CPU if available\n const fallbackPath = modelFilePath(modelsDir, modelEntry, 'onnx')\n if (modelEntry.formats['onnx'] && modelExists(fallbackPath)) {\n const engine = new NodeInferenceEngine(fallbackPath, 'cpu')\n await engine.initialize()\n return { engine, format: 'onnx', modelPath: fallbackPath }\n }\n\n throw new Error(\n `resolveEngine: format ${selectedFormat} is not yet supported by NodeInferenceEngine, ` +\n `no Python runtime is available, and no ONNX fallback exists`,\n )\n}\n\n/** Probe which ONNX execution providers are available on this system */\nexport async function probeOnnxBackends(): Promise<string[]> {\n const available: string[] = ['cpu'] // CPU is always available\n\n try {\n const ort = await import('onnxruntime-node')\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const providers: string[] = (ort as any).env?.webgl?.disabled !== undefined\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ? ((ort as any).InferenceSession?.getAvailableProviders?.() ?? [])\n : []\n\n for (const p of providers) {\n const normalized = p.toLowerCase().replace('executionprovider', '')\n if (normalized === 'coreml') available.push('coreml')\n else if (normalized === 'cuda') available.push('cuda')\n else if (normalized === 'tensorrt') available.push('tensorrt')\n }\n } catch {\n // onnxruntime-node may not be installed; CPU only\n }\n\n // Platform-specific hints when getAvailableProviders isn't exposed\n if (process.platform === 'darwin' && !available.includes('coreml')) {\n available.push('coreml')\n }\n\n return [...new Set(available)]\n}\n","import type {\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 { VEHICLE_TYPE_MODELS } from '../../catalogs/vehicle-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 VEHICLE_TYPE_LABEL: LabelDefinition = { id: 'vehicle-type', name: 'Vehicle Type' }\nconst VEHICLE_TYPE_LABELS: readonly LabelDefinition[] = [VEHICLE_TYPE_LABEL]\nconst VEHICLE_CLASS_MAP: ClassMapDefinition = { mapping: {}, preserveOriginal: true }\n\n/** Load vehicle type labels from JSON file in modelsDir */\nfunction loadLabels(modelsDir: string, modelId: string): readonly string[] {\n const labelNames = [\n `camstack-${modelId}-labels.json`,\n `camstack-vehicle-type-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(`VehicleClassifierAddon: 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 VehicleClassifierAddon implements IClassifierProvider, IDetectionAddon {\n readonly id = 'vehicle-classifier'\n readonly slot = 'classifier' as const\n readonly inputClasses = ['vehicle'] as const\n readonly outputClasses = ['vehicle-type:*'] as const\n readonly slotPriority = 0\n readonly requiredSteps = [] as const\n readonly manifest: AddonManifest = {\n id: 'vehicle-classifier',\n name: 'Vehicle Classifier',\n version: '0.1.0',\n\n description: 'EfficientNet-B4 — 8,949 vehicle make/model/year classes from VMMRdb',\n\n slot: 'classifier',\n labelOutputType: 'classification',\n inputClasses: ['vehicle'],\n outputClasses: ['vehicle-type:*'],\n supportsCustomModels: false,\n mayRequirePython: false,\n defaultConfig: {\n modelId: 'vehicle-type-efficientnet',\n runtime: 'node',\n backend: 'cpu',\n confidence: 0.05, // Low pipeline threshold for 8949-class model\n minConfidence: 0.05,\n },\n }\n\n private engine: IInferenceEngine | null = null\n private modelEntry!: ModelCatalogEntry\n private labels: readonly string[] = []\n private minConfidence = 0.05\n private resolvedConfig: ResolvedInferenceConfig | null = null\n private ctx: AddonContext | null = null\n\n getModelRequirements(): ModelRequirement[] {\n return VEHICLE_TYPE_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 ?? 'vehicle-type-efficientnet'\n this.minConfidence = (cfg['minConfidence'] as number | undefined) ?? 0.05\n\n const entry = VEHICLE_TYPE_MODELS.find((m) => m.id === modelId)\n if (!entry) {\n throw new Error(`VehicleClassifierAddon: 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 vehicle region\n const vehicleCrop = await cropRegion(input.frame.data, input.roi)\n\n // Resize to 224x224, ImageNet normalization, NCHW\n const normalized = await resizeAndNormalize(vehicleCrop, inputW, inputH, 'imagenet', 'nchw')\n\n // Run inference\n const rawOutput = await this.engine!.run(normalized, [1, 3, inputH, inputW])\n\n console.log(`[VehicleClassifier] Output length: ${rawOutput.length}, labels: ${this.labels.length}, first 5 raw: [${Array.from(rawOutput.slice(0, 5)).map(v => v.toFixed(3)).join(', ')}]`)\n\n // Softmax to get probabilities\n const probs = softmax(rawOutput)\n\n // Find top-3 for debugging\n const indexed = Array.from(probs).map((v, i) => ({ score: v, idx: i }))\n indexed.sort((a, b) => b.score - a.score)\n const top3 = indexed.slice(0, 3)\n console.log(`[VehicleClassifier] Top-3: ${top3.map(t => `${this.labels[t.idx] ?? t.idx}=${(t.score * 100).toFixed(1)}%`).join(', ')}`)\n\n // Find argmax\n const maxIdx = top3[0]?.idx ?? 0\n const maxScore = top3[0]?.score ?? 0\n\n if (maxScore < this.minConfidence) {\n console.log(`[VehicleClassifier] Below confidence threshold: ${(maxScore * 100).toFixed(1)}% < ${(this.minConfidence * 100).toFixed(0)}%`)\n return {\n classifications: [],\n inferenceMs: Date.now() - start,\n modelId: this.modelEntry.id,\n }\n }\n\n const label = this.labels[maxIdx] ?? `vehicle_type_${maxIdx}`\n console.log(`[VehicleClassifier] Result: ${label} (${(maxScore * 100).toFixed(1)}%)`)\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 = VEHICLE_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 // 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: [...VEHICLE_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 VEHICLE_CLASS_MAP\n }\n\n getModelCatalog(): ModelCatalogEntry[] {\n return [...VEHICLE_TYPE_MODELS]\n }\n\n getAvailableModels(): DetectionModel[] {\n return []\n }\n\n getActiveLabels(): readonly LabelDefinition[] {\n return VEHICLE_TYPE_LABELS\n }\n\n async probe(): Promise<ProbeResult> {\n return {\n available: true,\n runtime: this.engine?.runtime ?? 'onnx',\n device: this.engine?.device ?? 'cpu',\n capabilities: ['fp32'],\n }\n }\n}\n"],"mappings":"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","names":["path","exports","exports","cropRegion","resizeAndNormalize","path","exports","exports","exports","resolveEngine","fs","path"]}
@@ -1,16 +1,20 @@
1
1
  import {
2
- VEHICLE_TYPE_MODELS
3
- } from "../../chunk-XZ6ZMXXU.mjs";
2
+ require_vehicle_classification_models
3
+ } from "../../chunk-K36R6HWY.mjs";
4
4
  import {
5
- cropRegion,
6
- resizeAndNormalize
7
- } from "../../chunk-22BHCDT5.mjs";
5
+ require_image_utils
6
+ } from "../../chunk-WG66JYYW.mjs";
8
7
  import {
9
- resolveEngine
10
- } from "../../chunk-2IOKI4ES.mjs";
11
- import "../../chunk-BJTO5JO5.mjs";
8
+ require_engine_resolver
9
+ } from "../../chunk-PIFS7AIT.mjs";
10
+ import {
11
+ __toESM
12
+ } from "../../chunk-3IIFBJCD.mjs";
12
13
 
13
14
  // src/addons/vehicle-classifier/index.ts
15
+ var import_vehicle_classification_models = __toESM(require_vehicle_classification_models());
16
+ var import_image_utils = __toESM(require_image_utils());
17
+ var import_engine_resolver = __toESM(require_engine_resolver());
14
18
  import * as fs from "fs";
15
19
  import * as path from "path";
16
20
  var VEHICLE_TYPE_LABEL = { id: "vehicle-type", name: "Vehicle Type" };
@@ -70,7 +74,7 @@ var VehicleClassifierAddon = class {
70
74
  resolvedConfig = null;
71
75
  ctx = null;
72
76
  getModelRequirements() {
73
- return VEHICLE_TYPE_MODELS.map((m) => ({
77
+ return import_vehicle_classification_models.VEHICLE_TYPE_MODELS.map((m) => ({
74
78
  modelId: m.id,
75
79
  name: m.name,
76
80
  minRAM_MB: 120,
@@ -86,7 +90,7 @@ var VehicleClassifierAddon = class {
86
90
  const cfg = ctx.addonConfig;
87
91
  const modelId = cfg["modelId"] ?? this.resolvedConfig?.modelId ?? "vehicle-type-efficientnet";
88
92
  this.minConfidence = cfg["minConfidence"] ?? 0.05;
89
- const entry = VEHICLE_TYPE_MODELS.find((m) => m.id === modelId);
93
+ const entry = import_vehicle_classification_models.VEHICLE_TYPE_MODELS.find((m) => m.id === modelId);
90
94
  if (!entry) {
91
95
  throw new Error(`VehicleClassifierAddon: unknown modelId "${modelId}"`);
92
96
  }
@@ -96,8 +100,8 @@ var VehicleClassifierAddon = class {
96
100
  if (!this.engine) await this.ensureEngine();
97
101
  const start = Date.now();
98
102
  const { width: inputW, height: inputH } = this.modelEntry.inputSize;
99
- const vehicleCrop = await cropRegion(input.frame.data, input.roi);
100
- const normalized = await resizeAndNormalize(vehicleCrop, inputW, inputH, "imagenet", "nchw");
103
+ const vehicleCrop = await (0, import_image_utils.cropRegion)(input.frame.data, input.roi);
104
+ const normalized = await (0, import_image_utils.resizeAndNormalize)(vehicleCrop, inputW, inputH, "imagenet", "nchw");
101
105
  const rawOutput = await this.engine.run(normalized, [1, 3, inputH, inputW]);
102
106
  console.log(`[VehicleClassifier] Output length: ${rawOutput.length}, labels: ${this.labels.length}, first 5 raw: [${Array.from(rawOutput.slice(0, 5)).map((v) => v.toFixed(3)).join(", ")}]`);
103
107
  const probs = softmax(rawOutput);
@@ -134,14 +138,14 @@ var VehicleClassifierAddon = class {
134
138
  const runtime = config?.runtime === "python" ? "coreml" : config?.runtime === "node" ? "onnx" : "auto";
135
139
  const backend = config?.backend ?? "cpu";
136
140
  const format = config?.format ?? "onnx";
137
- const entry = VEHICLE_TYPE_MODELS.find((m) => m.id === modelId) ?? this.modelEntry;
141
+ const entry = import_vehicle_classification_models.VEHICLE_TYPE_MODELS.find((m) => m.id === modelId) ?? this.modelEntry;
138
142
  this.modelEntry = entry;
139
143
  const modelsDir = this.ctx.models?.getModelsDir() ?? this.ctx.locationPaths.models;
140
144
  if (this.ctx.models) {
141
145
  await this.ctx.models.ensure(modelId, format);
142
146
  }
143
147
  this.labels = loadLabels(modelsDir, modelId);
144
- const resolved = await resolveEngine({
148
+ const resolved = await (0, import_engine_resolver.resolveEngine)({
145
149
  runtime,
146
150
  backend,
147
151
  modelEntry: entry,
@@ -165,7 +169,7 @@ var VehicleClassifierAddon = class {
165
169
  key: "modelId",
166
170
  label: "Model",
167
171
  type: "model-selector",
168
- catalog: [...VEHICLE_TYPE_MODELS],
172
+ catalog: [...import_vehicle_classification_models.VEHICLE_TYPE_MODELS],
169
173
  allowCustom: false,
170
174
  allowConversion: false,
171
175
  acceptFormats: ["onnx", "coreml", "openvino"],
@@ -227,7 +231,7 @@ var VehicleClassifierAddon = class {
227
231
  return VEHICLE_CLASS_MAP;
228
232
  }
229
233
  getModelCatalog() {
230
- return [...VEHICLE_TYPE_MODELS];
234
+ return [...import_vehicle_classification_models.VEHICLE_TYPE_MODELS];
231
235
  }
232
236
  getAvailableModels() {
233
237
  return [];
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/addons/vehicle-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 { VEHICLE_TYPE_MODELS } from '../../catalogs/vehicle-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 VEHICLE_TYPE_LABEL: LabelDefinition = { id: 'vehicle-type', name: 'Vehicle Type' }\nconst VEHICLE_TYPE_LABELS: readonly LabelDefinition[] = [VEHICLE_TYPE_LABEL]\nconst VEHICLE_CLASS_MAP: ClassMapDefinition = { mapping: {}, preserveOriginal: true }\n\n/** Load vehicle type labels from JSON file in modelsDir */\nfunction loadLabels(modelsDir: string, modelId: string): readonly string[] {\n const labelNames = [\n `camstack-${modelId}-labels.json`,\n `camstack-vehicle-type-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(`VehicleClassifierAddon: 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 VehicleClassifierAddon implements IClassifierProvider, IDetectionAddon {\n readonly id = 'vehicle-classifier'\n readonly slot = 'classifier' as const\n readonly inputClasses = ['vehicle'] as const\n readonly outputClasses = ['vehicle-type:*'] as const\n readonly slotPriority = 0\n readonly requiredSteps = [] as const\n readonly manifest: AddonManifest = {\n id: 'vehicle-classifier',\n name: 'Vehicle Classifier',\n version: '0.1.0',\n\n description: 'EfficientNet-B4 — 8,949 vehicle make/model/year classes from VMMRdb',\n\n slot: 'classifier',\n labelOutputType: 'classification',\n inputClasses: ['vehicle'],\n outputClasses: ['vehicle-type:*'],\n supportsCustomModels: false,\n mayRequirePython: false,\n defaultConfig: {\n modelId: 'vehicle-type-efficientnet',\n runtime: 'node',\n backend: 'cpu',\n confidence: 0.05, // Low pipeline threshold for 8949-class model\n minConfidence: 0.05,\n },\n }\n\n private engine: IInferenceEngine | null = null\n private modelEntry!: ModelCatalogEntry\n private labels: readonly string[] = []\n private minConfidence = 0.05\n private resolvedConfig: ResolvedInferenceConfig | null = null\n private ctx: AddonContext | null = null\n\n getModelRequirements(): ModelRequirement[] {\n return VEHICLE_TYPE_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 ?? 'vehicle-type-efficientnet'\n this.minConfidence = (cfg['minConfidence'] as number | undefined) ?? 0.05\n\n const entry = VEHICLE_TYPE_MODELS.find((m) => m.id === modelId)\n if (!entry) {\n throw new Error(`VehicleClassifierAddon: 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 vehicle region\n const vehicleCrop = await cropRegion(input.frame.data, input.roi)\n\n // Resize to 224x224, ImageNet normalization, NCHW\n const normalized = await resizeAndNormalize(vehicleCrop, inputW, inputH, 'imagenet', 'nchw')\n\n // Run inference\n const rawOutput = await this.engine!.run(normalized, [1, 3, inputH, inputW])\n\n console.log(`[VehicleClassifier] Output length: ${rawOutput.length}, labels: ${this.labels.length}, first 5 raw: [${Array.from(rawOutput.slice(0, 5)).map(v => v.toFixed(3)).join(', ')}]`)\n\n // Softmax to get probabilities\n const probs = softmax(rawOutput)\n\n // Find top-3 for debugging\n const indexed = Array.from(probs).map((v, i) => ({ score: v, idx: i }))\n indexed.sort((a, b) => b.score - a.score)\n const top3 = indexed.slice(0, 3)\n console.log(`[VehicleClassifier] Top-3: ${top3.map(t => `${this.labels[t.idx] ?? t.idx}=${(t.score * 100).toFixed(1)}%`).join(', ')}`)\n\n // Find argmax\n const maxIdx = top3[0]?.idx ?? 0\n const maxScore = top3[0]?.score ?? 0\n\n if (maxScore < this.minConfidence) {\n console.log(`[VehicleClassifier] Below confidence threshold: ${(maxScore * 100).toFixed(1)}% < ${(this.minConfidence * 100).toFixed(0)}%`)\n return {\n classifications: [],\n inferenceMs: Date.now() - start,\n modelId: this.modelEntry.id,\n }\n }\n\n const label = this.labels[maxIdx] ?? `vehicle_type_${maxIdx}`\n console.log(`[VehicleClassifier] Result: ${label} (${(maxScore * 100).toFixed(1)}%)`)\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 = VEHICLE_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 // 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: [...VEHICLE_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 VEHICLE_CLASS_MAP\n }\n\n getModelCatalog(): ModelCatalogEntry[] {\n return [...VEHICLE_TYPE_MODELS]\n }\n\n getAvailableModels(): DetectionModel[] {\n return []\n }\n\n getActiveLabels(): readonly LabelDefinition[] {\n return VEHICLE_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":";;;;;;;;;;;;;AAoBA,YAAY,QAAQ;AACpB,YAAY,UAAU;AAEtB,IAAM,qBAAsC,EAAE,IAAI,gBAAgB,MAAM,eAAe;AACvF,IAAM,sBAAkD,CAAC,kBAAkB;AAC3E,IAAM,oBAAwC,EAAE,SAAS,CAAC,GAAG,kBAAkB,KAAK;AAGpF,SAAS,WAAW,WAAmB,SAAoC;AACzE,QAAM,aAAa;AAAA,IACjB,YAAY,OAAO;AAAA,IACnB;AAAA,EACF;AACA,aAAW,QAAQ,YAAY;AAC7B,UAAM,YAAiB,UAAK,WAAW,IAAI;AAC3C,QAAO,cAAW,SAAS,GAAG;AAC5B,YAAM,MAAS,gBAAa,WAAW,OAAO;AAC9C,aAAO,KAAK,MAAM,GAAG;AAAA,IACvB;AAAA,EACF;AACA,QAAM,IAAI,MAAM,oDAAoD,SAAS,EAAE;AACjF;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,yBAArB,MAA4F;AAAA,EACjF,KAAK;AAAA,EACL,OAAO;AAAA,EACP,eAAe,CAAC,SAAS;AAAA,EACzB,gBAAgB,CAAC,gBAAgB;AAAA,EACjC,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,SAAS;AAAA,IACxB,eAAe,CAAC,gBAAgB;AAAA,IAChC,sBAAsB;AAAA,IACtB,kBAAkB;AAAA,IAClB,eAAe;AAAA,MACb,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,YAAY;AAAA;AAAA,MACZ,eAAe;AAAA,IACjB;AAAA,EACF;AAAA,EAEQ,SAAkC;AAAA,EAClC;AAAA,EACA,SAA4B,CAAC;AAAA,EAC7B,gBAAgB;AAAA,EAChB,iBAAiD;AAAA,EACjD,MAA2B;AAAA,EAEnC,uBAA2C;AACzC,WAAO,oBAAoB,IAAI,CAAC,OAAO;AAAA,MACrC,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,oBAAoB,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO;AAC9D,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,MAAM,4CAA4C,OAAO,GAAG;AAAA,IACxE;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,cAAc,MAAM,WAAW,MAAM,MAAM,MAAM,MAAM,GAAG;AAGhE,UAAM,aAAa,MAAM,mBAAmB,aAAa,QAAQ,QAAQ,YAAY,MAAM;AAG3F,UAAM,YAAY,MAAM,KAAK,OAAQ,IAAI,YAAY,CAAC,GAAG,GAAG,QAAQ,MAAM,CAAC;AAE3E,YAAQ,IAAI,sCAAsC,UAAU,MAAM,aAAa,KAAK,OAAO,MAAM,mBAAmB,MAAM,KAAK,UAAU,MAAM,GAAG,CAAC,CAAC,EAAE,IAAI,OAAK,EAAE,QAAQ,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,GAAG;AAG1L,UAAM,QAAQ,QAAQ,SAAS;AAG/B,UAAM,UAAU,MAAM,KAAK,KAAK,EAAE,IAAI,CAAC,GAAG,OAAO,EAAE,OAAO,GAAG,KAAK,EAAE,EAAE;AACtE,YAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AACxC,UAAM,OAAO,QAAQ,MAAM,GAAG,CAAC;AAC/B,YAAQ,IAAI,8BAA8B,KAAK,IAAI,OAAK,GAAG,KAAK,OAAO,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,QAAQ,KAAK,QAAQ,CAAC,CAAC,GAAG,EAAE,KAAK,IAAI,CAAC,EAAE;AAGrI,UAAM,SAAS,KAAK,CAAC,GAAG,OAAO;AAC/B,UAAM,WAAW,KAAK,CAAC,GAAG,SAAS;AAEnC,QAAI,WAAW,KAAK,eAAe;AACjC,cAAQ,IAAI,oDAAoD,WAAW,KAAK,QAAQ,CAAC,CAAC,QAAQ,KAAK,gBAAgB,KAAK,QAAQ,CAAC,CAAC,GAAG;AACzI,aAAO;AAAA,QACL,iBAAiB,CAAC;AAAA,QAClB,aAAa,KAAK,IAAI,IAAI;AAAA,QAC1B,SAAS,KAAK,WAAW;AAAA,MAC3B;AAAA,IACF;AAEA,UAAM,QAAQ,KAAK,OAAO,MAAM,KAAK,gBAAgB,MAAM;AAC3D,YAAQ,IAAI,+BAA+B,KAAK,MAAM,WAAW,KAAK,QAAQ,CAAC,CAAC,IAAI;AAEpF,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,oBAAoB,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO,KAAK,KAAK;AACxE,SAAK,aAAa;AAElB,UAAM,YAAY,KAAK,IAAK,QAAQ,aAAa,KAAK,KAAK,IAAK,cAAc;AAG9E,QAAI,KAAK,IAAK,QAAQ;AACpB,YAAM,KAAK,IAAK,OAAO,OAAO,SAAS,MAAa;AAAA,IACtD;AAGA,SAAK,SAAS,WAAW,WAAW,OAAO;AAE3C,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,mBAAmB;AAAA,cAChC,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,mBAAmB;AAAA,EAChC;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
+ {"version":3,"sources":["../../../src/addons/vehicle-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 { VEHICLE_TYPE_MODELS } from '../../catalogs/vehicle-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 VEHICLE_TYPE_LABEL: LabelDefinition = { id: 'vehicle-type', name: 'Vehicle Type' }\nconst VEHICLE_TYPE_LABELS: readonly LabelDefinition[] = [VEHICLE_TYPE_LABEL]\nconst VEHICLE_CLASS_MAP: ClassMapDefinition = { mapping: {}, preserveOriginal: true }\n\n/** Load vehicle type labels from JSON file in modelsDir */\nfunction loadLabels(modelsDir: string, modelId: string): readonly string[] {\n const labelNames = [\n `camstack-${modelId}-labels.json`,\n `camstack-vehicle-type-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(`VehicleClassifierAddon: 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 VehicleClassifierAddon implements IClassifierProvider, IDetectionAddon {\n readonly id = 'vehicle-classifier'\n readonly slot = 'classifier' as const\n readonly inputClasses = ['vehicle'] as const\n readonly outputClasses = ['vehicle-type:*'] as const\n readonly slotPriority = 0\n readonly requiredSteps = [] as const\n readonly manifest: AddonManifest = {\n id: 'vehicle-classifier',\n name: 'Vehicle Classifier',\n version: '0.1.0',\n\n description: 'EfficientNet-B4 — 8,949 vehicle make/model/year classes from VMMRdb',\n\n slot: 'classifier',\n labelOutputType: 'classification',\n inputClasses: ['vehicle'],\n outputClasses: ['vehicle-type:*'],\n supportsCustomModels: false,\n mayRequirePython: false,\n defaultConfig: {\n modelId: 'vehicle-type-efficientnet',\n runtime: 'node',\n backend: 'cpu',\n confidence: 0.05, // Low pipeline threshold for 8949-class model\n minConfidence: 0.05,\n },\n }\n\n private engine: IInferenceEngine | null = null\n private modelEntry!: ModelCatalogEntry\n private labels: readonly string[] = []\n private minConfidence = 0.05\n private resolvedConfig: ResolvedInferenceConfig | null = null\n private ctx: AddonContext | null = null\n\n getModelRequirements(): ModelRequirement[] {\n return VEHICLE_TYPE_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 ?? 'vehicle-type-efficientnet'\n this.minConfidence = (cfg['minConfidence'] as number | undefined) ?? 0.05\n\n const entry = VEHICLE_TYPE_MODELS.find((m) => m.id === modelId)\n if (!entry) {\n throw new Error(`VehicleClassifierAddon: 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 vehicle region\n const vehicleCrop = await cropRegion(input.frame.data, input.roi)\n\n // Resize to 224x224, ImageNet normalization, NCHW\n const normalized = await resizeAndNormalize(vehicleCrop, inputW, inputH, 'imagenet', 'nchw')\n\n // Run inference\n const rawOutput = await this.engine!.run(normalized, [1, 3, inputH, inputW])\n\n console.log(`[VehicleClassifier] Output length: ${rawOutput.length}, labels: ${this.labels.length}, first 5 raw: [${Array.from(rawOutput.slice(0, 5)).map(v => v.toFixed(3)).join(', ')}]`)\n\n // Softmax to get probabilities\n const probs = softmax(rawOutput)\n\n // Find top-3 for debugging\n const indexed = Array.from(probs).map((v, i) => ({ score: v, idx: i }))\n indexed.sort((a, b) => b.score - a.score)\n const top3 = indexed.slice(0, 3)\n console.log(`[VehicleClassifier] Top-3: ${top3.map(t => `${this.labels[t.idx] ?? t.idx}=${(t.score * 100).toFixed(1)}%`).join(', ')}`)\n\n // Find argmax\n const maxIdx = top3[0]?.idx ?? 0\n const maxScore = top3[0]?.score ?? 0\n\n if (maxScore < this.minConfidence) {\n console.log(`[VehicleClassifier] Below confidence threshold: ${(maxScore * 100).toFixed(1)}% < ${(this.minConfidence * 100).toFixed(0)}%`)\n return {\n classifications: [],\n inferenceMs: Date.now() - start,\n modelId: this.modelEntry.id,\n }\n }\n\n const label = this.labels[maxIdx] ?? `vehicle_type_${maxIdx}`\n console.log(`[VehicleClassifier] Result: ${label} (${(maxScore * 100).toFixed(1)}%)`)\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 = VEHICLE_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 // 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: [...VEHICLE_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 VEHICLE_CLASS_MAP\n }\n\n getModelCatalog(): ModelCatalogEntry[] {\n return [...VEHICLE_TYPE_MODELS]\n }\n\n getAvailableModels(): DetectionModel[] {\n return []\n }\n\n getActiveLabels(): readonly LabelDefinition[] {\n return VEHICLE_TYPE_LABELS\n }\n\n async probe(): Promise<ProbeResult> {\n return {\n available: true,\n runtime: this.engine?.runtime ?? 'onnx',\n device: this.engine?.device ?? 'cpu',\n capabilities: ['fp32'],\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,193 @@
1
+ import {
2
+ require_yolo
3
+ } from "./chunk-DE7I3VHO.mjs";
4
+ import {
5
+ require_object_detection_models
6
+ } from "./chunk-MGT6RUVX.mjs";
7
+ import {
8
+ __commonJS,
9
+ __require
10
+ } from "./chunk-3IIFBJCD.mjs";
11
+
12
+ // src/shared/postprocess/scrfd.js
13
+ var require_scrfd = __commonJS({
14
+ "src/shared/postprocess/scrfd.js"(exports) {
15
+ "use strict";
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.scrfdPostprocess = scrfdPostprocess;
18
+ var yolo_js_1 = require_yolo();
19
+ var STRIDES = [8, 16, 32];
20
+ var NUM_ANCHORS_PER_STRIDE = 2;
21
+ function generateAnchors(stride, inputSize) {
22
+ const featureSize = Math.ceil(inputSize / stride);
23
+ const anchors = [];
24
+ for (let y = 0; y < featureSize; y++) {
25
+ for (let x = 0; x < featureSize; x++) {
26
+ for (let k = 0; k < NUM_ANCHORS_PER_STRIDE; k++) {
27
+ anchors.push({
28
+ cx: (x + 0.5) * stride,
29
+ cy: (y + 0.5) * stride
30
+ });
31
+ }
32
+ }
33
+ }
34
+ return anchors;
35
+ }
36
+ function scrfdPostprocess(outputs, confidence, inputSize, originalWidth, originalHeight) {
37
+ const scaleX = originalWidth / inputSize;
38
+ const scaleY = originalHeight / inputSize;
39
+ const candidates = [];
40
+ for (const stride of STRIDES) {
41
+ const scoreKey = Object.keys(outputs).find((k) => k.includes(`score_${stride}`) || k.includes(`_${stride}_score`));
42
+ const bboxKey = Object.keys(outputs).find((k) => k.includes(`bbox_${stride}`) || k.includes(`_${stride}_bbox`));
43
+ const kpsKey = Object.keys(outputs).find((k) => k.includes(`kps_${stride}`) || k.includes(`_${stride}_kps`));
44
+ if (!scoreKey || !bboxKey)
45
+ continue;
46
+ const scores = outputs[scoreKey];
47
+ const bboxes = outputs[bboxKey];
48
+ const kps = kpsKey ? outputs[kpsKey] : void 0;
49
+ const anchors = generateAnchors(stride, inputSize);
50
+ const n = anchors.length;
51
+ for (let i = 0; i < n; i++) {
52
+ const score = scores[i];
53
+ if (score < confidence)
54
+ continue;
55
+ const anchor = anchors[i];
56
+ const x1 = anchor.cx - bboxes[i * 4] * stride;
57
+ const y1 = anchor.cy - bboxes[i * 4 + 1] * stride;
58
+ const x2 = anchor.cx + bboxes[i * 4 + 2] * stride;
59
+ const y2 = anchor.cy + bboxes[i * 4 + 3] * stride;
60
+ const bbox = {
61
+ x: x1 * scaleX,
62
+ y: y1 * scaleY,
63
+ w: (x2 - x1) * scaleX,
64
+ h: (y2 - y1) * scaleY
65
+ };
66
+ let landmarks;
67
+ if (kps) {
68
+ const pts = [];
69
+ for (let p = 0; p < 5; p++) {
70
+ pts.push({
71
+ x: (anchor.cx + kps[i * 10 + p * 2] * stride) * scaleX,
72
+ y: (anchor.cy + kps[i * 10 + p * 2 + 1] * stride) * scaleY
73
+ });
74
+ }
75
+ landmarks = pts;
76
+ }
77
+ candidates.push({ bbox, score, landmarks });
78
+ }
79
+ }
80
+ if (candidates.length === 0)
81
+ return [];
82
+ const keptIndices = (0, yolo_js_1.nms)(candidates, 0.45);
83
+ return keptIndices.map((idx) => {
84
+ const { bbox, score, landmarks } = candidates[idx];
85
+ return {
86
+ class: "face",
87
+ originalClass: "face",
88
+ score,
89
+ bbox,
90
+ ...landmarks ? { landmarks } : {}
91
+ };
92
+ });
93
+ }
94
+ }
95
+ });
96
+
97
+ // src/catalogs/face-detection-models.js
98
+ var require_face_detection_models = __commonJS({
99
+ "src/catalogs/face-detection-models.js"(exports) {
100
+ "use strict";
101
+ Object.defineProperty(exports, "__esModule", { value: true });
102
+ exports.FACE_DETECTION_MODELS = void 0;
103
+ var types_1 = __require("@camstack/types");
104
+ var object_detection_models_js_1 = require_object_detection_models();
105
+ var HF_REPO = "camstack/camstack-models";
106
+ var FACE_LABELS = [
107
+ { id: "face", name: "Face" }
108
+ ];
109
+ exports.FACE_DETECTION_MODELS = [
110
+ {
111
+ id: "scrfd-500m",
112
+ name: "SCRFD 500M",
113
+ description: "SCRFD 500M \u2014 ultra-lightweight face detector",
114
+ inputSize: { width: 640, height: 640 },
115
+ labels: FACE_LABELS,
116
+ formats: {
117
+ onnx: {
118
+ url: (0, types_1.hfModelUrl)(HF_REPO, "faceDetection/scrfd/onnx/camstack-scrfd-500m.onnx"),
119
+ sizeMB: 2.2
120
+ },
121
+ coreml: {
122
+ url: (0, types_1.hfModelUrl)(HF_REPO, "faceDetection/scrfd/coreml/camstack-scrfd-500m.mlpackage"),
123
+ sizeMB: 1.2,
124
+ isDirectory: true,
125
+ files: object_detection_models_js_1.MLPACKAGE_FILES,
126
+ runtimes: ["python"]
127
+ },
128
+ openvino: {
129
+ url: (0, types_1.hfModelUrl)(HF_REPO, "faceDetection/scrfd/openvino/camstack-scrfd-500m.xml"),
130
+ sizeMB: 1.3,
131
+ runtimes: ["python"]
132
+ }
133
+ }
134
+ },
135
+ {
136
+ id: "scrfd-2.5g",
137
+ name: "SCRFD 2.5G",
138
+ description: "SCRFD 2.5G \u2014 balanced face detection model",
139
+ inputSize: { width: 640, height: 640 },
140
+ labels: FACE_LABELS,
141
+ formats: {
142
+ onnx: {
143
+ url: (0, types_1.hfModelUrl)(HF_REPO, "faceDetection/scrfd/onnx/camstack-scrfd-2.5g.onnx"),
144
+ sizeMB: 3.1
145
+ },
146
+ coreml: {
147
+ url: (0, types_1.hfModelUrl)(HF_REPO, "faceDetection/scrfd/coreml/camstack-scrfd-2.5g.mlpackage"),
148
+ sizeMB: 1.7,
149
+ isDirectory: true,
150
+ files: object_detection_models_js_1.MLPACKAGE_FILES,
151
+ runtimes: ["python"]
152
+ },
153
+ openvino: {
154
+ url: (0, types_1.hfModelUrl)(HF_REPO, "faceDetection/scrfd/openvino/camstack-scrfd-2.5g.xml"),
155
+ sizeMB: 1.8,
156
+ runtimes: ["python"]
157
+ }
158
+ }
159
+ },
160
+ {
161
+ id: "scrfd-10g",
162
+ name: "SCRFD 10G",
163
+ description: "SCRFD 10G \u2014 high-accuracy face detector",
164
+ inputSize: { width: 640, height: 640 },
165
+ labels: FACE_LABELS,
166
+ formats: {
167
+ onnx: {
168
+ url: (0, types_1.hfModelUrl)(HF_REPO, "faceDetection/scrfd/onnx/camstack-scrfd-10g.onnx"),
169
+ sizeMB: 16
170
+ },
171
+ coreml: {
172
+ url: (0, types_1.hfModelUrl)(HF_REPO, "faceDetection/scrfd/coreml/camstack-scrfd-10g.mlpackage"),
173
+ sizeMB: 8.2,
174
+ isDirectory: true,
175
+ files: object_detection_models_js_1.MLPACKAGE_FILES,
176
+ runtimes: ["python"]
177
+ },
178
+ openvino: {
179
+ url: (0, types_1.hfModelUrl)(HF_REPO, "faceDetection/scrfd/openvino/camstack-scrfd-10g.xml"),
180
+ sizeMB: 8.3,
181
+ runtimes: ["python"]
182
+ }
183
+ }
184
+ }
185
+ ];
186
+ }
187
+ });
188
+
189
+ export {
190
+ require_scrfd,
191
+ require_face_detection_models
192
+ };
193
+ //# sourceMappingURL=chunk-2YMA6QOV.mjs.map