@camstack/addon-vision 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/addons/animal-classifier/index.js +999 -823
- package/dist/addons/animal-classifier/index.js.map +1 -1
- package/dist/addons/animal-classifier/index.mjs +242 -7
- package/dist/addons/animal-classifier/index.mjs.map +1 -1
- package/dist/addons/audio-classification/index.js +501 -379
- package/dist/addons/audio-classification/index.js.map +1 -1
- package/dist/addons/audio-classification/index.mjs +224 -4
- package/dist/addons/audio-classification/index.mjs.map +1 -1
- package/dist/addons/bird-global-classifier/index.js +1002 -826
- package/dist/addons/bird-global-classifier/index.js.map +1 -1
- package/dist/addons/bird-global-classifier/index.mjs +248 -7
- package/dist/addons/bird-global-classifier/index.mjs.map +1 -1
- package/dist/addons/bird-nabirds-classifier/index.js +1002 -826
- package/dist/addons/bird-nabirds-classifier/index.js.map +1 -1
- package/dist/addons/bird-nabirds-classifier/index.mjs +289 -7
- package/dist/addons/bird-nabirds-classifier/index.mjs.map +1 -1
- package/dist/addons/face-detection/index.js +1196 -935
- package/dist/addons/face-detection/index.js.map +1 -1
- package/dist/addons/face-detection/index.mjs +227 -7
- package/dist/addons/face-detection/index.mjs.map +1 -1
- package/dist/addons/face-recognition/index.js +1003 -808
- package/dist/addons/face-recognition/index.js.map +1 -1
- package/dist/addons/face-recognition/index.mjs +197 -6
- package/dist/addons/face-recognition/index.mjs.map +1 -1
- package/dist/addons/motion-detection/index.js +214 -111
- package/dist/addons/motion-detection/index.js.map +1 -1
- package/dist/addons/motion-detection/index.mjs +12 -9
- package/dist/addons/motion-detection/index.mjs.map +1 -1
- package/dist/addons/object-detection/index.js +1287 -1083
- package/dist/addons/object-detection/index.js.map +1 -1
- package/dist/addons/object-detection/index.mjs +373 -7
- package/dist/addons/object-detection/index.mjs.map +1 -1
- package/dist/addons/plate-detection/index.js +1075 -869
- package/dist/addons/plate-detection/index.js.map +1 -1
- package/dist/addons/plate-detection/index.mjs +230 -7
- package/dist/addons/plate-detection/index.mjs.map +1 -1
- package/dist/addons/plate-recognition/index.js +684 -506
- package/dist/addons/plate-recognition/index.js.map +1 -1
- package/dist/addons/plate-recognition/index.mjs +244 -5
- package/dist/addons/plate-recognition/index.mjs.map +1 -1
- package/dist/addons/segmentation-refiner/index.js +967 -791
- package/dist/addons/segmentation-refiner/index.js.map +1 -1
- package/dist/addons/segmentation-refiner/index.mjs +21 -17
- package/dist/addons/segmentation-refiner/index.mjs.map +1 -1
- package/dist/addons/vehicle-classifier/index.js +581 -411
- package/dist/addons/vehicle-classifier/index.js.map +1 -1
- package/dist/addons/vehicle-classifier/index.mjs +20 -16
- package/dist/addons/vehicle-classifier/index.mjs.map +1 -1
- package/dist/chunk-2YMA6QOV.mjs +193 -0
- package/dist/chunk-2YMA6QOV.mjs.map +1 -0
- package/dist/chunk-3IIFBJCD.mjs +45 -0
- package/dist/chunk-BS4DKYGN.mjs +48 -0
- package/dist/{chunk-7DYHXUPZ.mjs.map → chunk-BS4DKYGN.mjs.map} +1 -1
- package/dist/chunk-DE7I3VHO.mjs +106 -0
- package/dist/{chunk-KUO2BVFY.mjs.map → chunk-DE7I3VHO.mjs.map} +1 -1
- package/dist/chunk-F6D2OZ36.mjs +89 -0
- package/dist/chunk-F6D2OZ36.mjs.map +1 -0
- package/dist/chunk-GAOIFQDX.mjs +59 -0
- package/dist/chunk-GAOIFQDX.mjs.map +1 -0
- package/dist/chunk-HUIX2XVR.mjs +159 -0
- package/dist/chunk-HUIX2XVR.mjs.map +1 -0
- package/dist/chunk-K36R6HWY.mjs +51 -0
- package/dist/{chunk-XZ6ZMXXU.mjs.map → chunk-K36R6HWY.mjs.map} +1 -1
- package/dist/chunk-MBTAI3WE.mjs +78 -0
- package/dist/chunk-MBTAI3WE.mjs.map +1 -0
- package/dist/chunk-MGT6RUVX.mjs +423 -0
- package/dist/{chunk-BP7H4NFS.mjs.map → chunk-MGT6RUVX.mjs.map} +1 -1
- package/dist/chunk-PIFS7AIT.mjs +446 -0
- package/dist/chunk-PIFS7AIT.mjs.map +1 -0
- package/dist/chunk-WG66JYYW.mjs +116 -0
- package/dist/{chunk-22BHCDT5.mjs.map → chunk-WG66JYYW.mjs.map} +1 -1
- package/dist/chunk-XD7WGXHZ.mjs +82 -0
- package/dist/{chunk-DUN6XU3N.mjs.map → chunk-XD7WGXHZ.mjs.map} +1 -1
- package/dist/chunk-YYDM6V2F.mjs +113 -0
- package/dist/{chunk-BR2FPGOX.mjs.map → chunk-YYDM6V2F.mjs.map} +1 -1
- package/dist/chunk-ZK7P3TZN.mjs +286 -0
- package/dist/chunk-ZK7P3TZN.mjs.map +1 -0
- package/dist/index.js +4443 -3925
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2698 -250
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -3
- package/dist/chunk-22BHCDT5.mjs +0 -101
- package/dist/chunk-6DJZZR64.mjs +0 -336
- package/dist/chunk-6DJZZR64.mjs.map +0 -1
- package/dist/chunk-7DYHXUPZ.mjs +0 -36
- package/dist/chunk-BJTO5JO5.mjs +0 -11
- package/dist/chunk-BP7H4NFS.mjs +0 -412
- package/dist/chunk-BR2FPGOX.mjs +0 -98
- package/dist/chunk-DNQNGDR4.mjs +0 -256
- package/dist/chunk-DNQNGDR4.mjs.map +0 -1
- package/dist/chunk-DUN6XU3N.mjs +0 -72
- package/dist/chunk-EPNWLSCG.mjs +0 -387
- package/dist/chunk-EPNWLSCG.mjs.map +0 -1
- package/dist/chunk-G32RCIUI.mjs +0 -645
- package/dist/chunk-G32RCIUI.mjs.map +0 -1
- package/dist/chunk-GR65KM6X.mjs +0 -289
- package/dist/chunk-GR65KM6X.mjs.map +0 -1
- package/dist/chunk-H7LMBTS5.mjs +0 -276
- package/dist/chunk-H7LMBTS5.mjs.map +0 -1
- package/dist/chunk-IK4XIQPC.mjs +0 -242
- package/dist/chunk-IK4XIQPC.mjs.map +0 -1
- package/dist/chunk-J6VNIIYX.mjs +0 -269
- package/dist/chunk-J6VNIIYX.mjs.map +0 -1
- package/dist/chunk-KUO2BVFY.mjs +0 -90
- package/dist/chunk-ML2JX43J.mjs +0 -248
- package/dist/chunk-ML2JX43J.mjs.map +0 -1
- package/dist/chunk-WUMV524J.mjs +0 -379
- package/dist/chunk-WUMV524J.mjs.map +0 -1
- package/dist/chunk-XZ6ZMXXU.mjs +0 -39
- /package/dist/{chunk-BJTO5JO5.mjs.map → chunk-3IIFBJCD.mjs.map} +0 -0
|
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
9
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
10
|
+
};
|
|
8
11
|
var __export = (target, all) => {
|
|
9
12
|
for (var name in all)
|
|
10
13
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -27,434 +30,601 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
30
|
));
|
|
28
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
32
|
|
|
30
|
-
// src/
|
|
31
|
-
var
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
var
|
|
39
|
-
var
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
];
|
|
44
|
-
var VEHICLE_TYPE_MODELS = [
|
|
45
|
-
{
|
|
46
|
-
id: "vehicle-type-efficientnet",
|
|
47
|
-
name: "Vehicle Type (EfficientNet)",
|
|
48
|
-
description: "EfficientNet-B4 vehicle make/model/year classifier \u2014 8,949 classes from VMMRdb",
|
|
49
|
-
inputSize: { width: 380, height: 380 },
|
|
50
|
-
inputNormalization: "imagenet",
|
|
51
|
-
labels: VEHICLE_LABELS,
|
|
52
|
-
formats: {
|
|
53
|
-
onnx: { url: hf("vehicleClassification/efficientnet/onnx/camstack-vehicle-type-efficientnet.onnx"), sizeMB: 135 },
|
|
54
|
-
coreml: {
|
|
55
|
-
url: hf("vehicleClassification/efficientnet/coreml/camstack-vehicle-type-efficientnet.mlpackage"),
|
|
56
|
-
sizeMB: 10,
|
|
57
|
-
isDirectory: true,
|
|
58
|
-
files: ["Manifest.json", "Data/com.apple.CoreML/model.mlmodel", "Data/com.apple.CoreML/weights/weight.bin"],
|
|
59
|
-
runtimes: ["python"]
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
extraFiles: [
|
|
33
|
+
// src/catalogs/vehicle-classification-models.js
|
|
34
|
+
var require_vehicle_classification_models = __commonJS({
|
|
35
|
+
"src/catalogs/vehicle-classification-models.js"(exports2) {
|
|
36
|
+
"use strict";
|
|
37
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
38
|
+
exports2.VEHICLE_TYPE_MODELS = void 0;
|
|
39
|
+
var types_1 = require("@camstack/types");
|
|
40
|
+
var HF_REPO = "camstack/camstack-models";
|
|
41
|
+
var hf = (path2) => (0, types_1.hfModelUrl)(HF_REPO, path2);
|
|
42
|
+
var VEHICLE_LABELS = [
|
|
43
|
+
{ id: "vehicle-type", name: "Vehicle Type" }
|
|
44
|
+
];
|
|
45
|
+
exports2.VEHICLE_TYPE_MODELS = [
|
|
63
46
|
{
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
47
|
+
id: "vehicle-type-efficientnet",
|
|
48
|
+
name: "Vehicle Type (EfficientNet)",
|
|
49
|
+
description: "EfficientNet-B4 vehicle make/model/year classifier \u2014 8,949 classes from VMMRdb",
|
|
50
|
+
inputSize: { width: 380, height: 380 },
|
|
51
|
+
inputNormalization: "imagenet",
|
|
52
|
+
labels: VEHICLE_LABELS,
|
|
53
|
+
formats: {
|
|
54
|
+
onnx: { url: hf("vehicleClassification/efficientnet/onnx/camstack-vehicle-type-efficientnet.onnx"), sizeMB: 135 },
|
|
55
|
+
coreml: {
|
|
56
|
+
url: hf("vehicleClassification/efficientnet/coreml/camstack-vehicle-type-efficientnet.mlpackage"),
|
|
57
|
+
sizeMB: 10,
|
|
58
|
+
isDirectory: true,
|
|
59
|
+
files: ["Manifest.json", "Data/com.apple.CoreML/model.mlmodel", "Data/com.apple.CoreML/weights/weight.bin"],
|
|
60
|
+
runtimes: ["python"]
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
extraFiles: [
|
|
64
|
+
{
|
|
65
|
+
url: hf("vehicleClassification/efficientnet/camstack-vehicle-type-labels.json"),
|
|
66
|
+
filename: "camstack-vehicle-type-labels.json",
|
|
67
|
+
sizeMB: 0.2
|
|
68
|
+
}
|
|
69
|
+
]
|
|
67
70
|
}
|
|
68
|
-
]
|
|
71
|
+
];
|
|
69
72
|
}
|
|
70
|
-
|
|
73
|
+
});
|
|
71
74
|
|
|
72
|
-
// src/shared/image-utils.
|
|
73
|
-
var
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
75
|
+
// src/shared/image-utils.js
|
|
76
|
+
var require_image_utils = __commonJS({
|
|
77
|
+
"src/shared/image-utils.js"(exports2) {
|
|
78
|
+
"use strict";
|
|
79
|
+
var __importDefault = exports2 && exports2.__importDefault || function(mod) {
|
|
80
|
+
return mod && mod.__esModule ? mod : { "default": mod };
|
|
81
|
+
};
|
|
82
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
83
|
+
exports2.jpegToRgb = jpegToRgb;
|
|
84
|
+
exports2.cropRegion = cropRegion2;
|
|
85
|
+
exports2.letterbox = letterbox;
|
|
86
|
+
exports2.resizeAndNormalize = resizeAndNormalize2;
|
|
87
|
+
exports2.rgbToGrayscale = rgbToGrayscale;
|
|
88
|
+
var sharp_1 = __importDefault(require("sharp"));
|
|
89
|
+
async function jpegToRgb(jpeg) {
|
|
90
|
+
const { data, info } = await (0, sharp_1.default)(jpeg).removeAlpha().raw().toBuffer({ resolveWithObject: true });
|
|
91
|
+
return { data, width: info.width, height: info.height };
|
|
92
|
+
}
|
|
93
|
+
async function cropRegion2(jpeg, roi) {
|
|
94
|
+
return (0, sharp_1.default)(jpeg).extract({
|
|
95
|
+
left: Math.round(roi.x),
|
|
96
|
+
top: Math.round(roi.y),
|
|
97
|
+
width: Math.round(roi.w),
|
|
98
|
+
height: Math.round(roi.h)
|
|
99
|
+
}).jpeg().toBuffer();
|
|
100
|
+
}
|
|
101
|
+
async function letterbox(jpeg, targetSize) {
|
|
102
|
+
const meta = await (0, sharp_1.default)(jpeg).metadata();
|
|
103
|
+
const originalWidth = meta.width ?? 0;
|
|
104
|
+
const originalHeight = meta.height ?? 0;
|
|
105
|
+
const scale = Math.min(targetSize / originalWidth, targetSize / originalHeight);
|
|
106
|
+
const scaledWidth = Math.round(originalWidth * scale);
|
|
107
|
+
const scaledHeight = Math.round(originalHeight * scale);
|
|
108
|
+
const padX = Math.floor((targetSize - scaledWidth) / 2);
|
|
109
|
+
const padY = Math.floor((targetSize - scaledHeight) / 2);
|
|
110
|
+
const { data } = await (0, sharp_1.default)(jpeg).resize(scaledWidth, scaledHeight).extend({
|
|
111
|
+
top: padY,
|
|
112
|
+
bottom: targetSize - scaledHeight - padY,
|
|
113
|
+
left: padX,
|
|
114
|
+
right: targetSize - scaledWidth - padX,
|
|
115
|
+
background: { r: 114, g: 114, b: 114 }
|
|
116
|
+
}).removeAlpha().raw().toBuffer({ resolveWithObject: true });
|
|
117
|
+
const numPixels = targetSize * targetSize;
|
|
118
|
+
const float32 = new Float32Array(3 * numPixels);
|
|
119
|
+
for (let i = 0; i < numPixels; i++) {
|
|
120
|
+
const srcBase = i * 3;
|
|
121
|
+
float32[0 * numPixels + i] = data[srcBase] / 255;
|
|
122
|
+
float32[1 * numPixels + i] = data[srcBase + 1] / 255;
|
|
123
|
+
float32[2 * numPixels + i] = data[srcBase + 2] / 255;
|
|
102
124
|
}
|
|
125
|
+
return { data: float32, scale, padX, padY, originalWidth, originalHeight };
|
|
103
126
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
127
|
+
async function resizeAndNormalize2(jpeg, targetWidth, targetHeight, normalization, layout) {
|
|
128
|
+
const { data } = await (0, sharp_1.default)(jpeg).resize(targetWidth, targetHeight, { fit: "fill" }).removeAlpha().raw().toBuffer({ resolveWithObject: true });
|
|
129
|
+
const numPixels = targetWidth * targetHeight;
|
|
130
|
+
const float32 = new Float32Array(3 * numPixels);
|
|
131
|
+
const mean = [0.485, 0.456, 0.406];
|
|
132
|
+
const std = [0.229, 0.224, 0.225];
|
|
133
|
+
if (layout === "nchw") {
|
|
134
|
+
for (let i = 0; i < numPixels; i++) {
|
|
135
|
+
const srcBase = i * 3;
|
|
136
|
+
for (let c = 0; c < 3; c++) {
|
|
137
|
+
const raw = data[srcBase + c] / 255;
|
|
138
|
+
let val;
|
|
139
|
+
if (normalization === "zero-one") {
|
|
140
|
+
val = raw;
|
|
141
|
+
} else if (normalization === "imagenet") {
|
|
142
|
+
val = (raw - mean[c]) / std[c];
|
|
143
|
+
} else {
|
|
144
|
+
val = data[srcBase + c];
|
|
145
|
+
}
|
|
146
|
+
float32[c * numPixels + i] = val;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
for (let i = 0; i < numPixels; i++) {
|
|
151
|
+
const srcBase = i * 3;
|
|
152
|
+
for (let c = 0; c < 3; c++) {
|
|
153
|
+
const raw = data[srcBase + c] / 255;
|
|
154
|
+
let val;
|
|
155
|
+
if (normalization === "zero-one") {
|
|
156
|
+
val = raw;
|
|
157
|
+
} else if (normalization === "imagenet") {
|
|
158
|
+
val = (raw - mean[c]) / std[c];
|
|
159
|
+
} else {
|
|
160
|
+
val = data[srcBase + c];
|
|
161
|
+
}
|
|
162
|
+
float32[i * 3 + c] = val;
|
|
163
|
+
}
|
|
116
164
|
}
|
|
117
|
-
float32[i * 3 + c] = val;
|
|
118
165
|
}
|
|
166
|
+
return float32;
|
|
119
167
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
var BACKEND_TO_PROVIDER = {
|
|
131
|
-
cpu: "cpu",
|
|
132
|
-
coreml: "coreml",
|
|
133
|
-
cuda: "cuda",
|
|
134
|
-
tensorrt: "tensorrt",
|
|
135
|
-
dml: "dml"
|
|
136
|
-
};
|
|
137
|
-
var BACKEND_TO_DEVICE = {
|
|
138
|
-
cpu: "cpu",
|
|
139
|
-
coreml: "gpu-mps",
|
|
140
|
-
cuda: "gpu-cuda",
|
|
141
|
-
tensorrt: "tensorrt"
|
|
142
|
-
};
|
|
143
|
-
var NodeInferenceEngine = class {
|
|
144
|
-
constructor(modelPath, backend) {
|
|
145
|
-
this.modelPath = modelPath;
|
|
146
|
-
this.backend = backend;
|
|
147
|
-
this.device = BACKEND_TO_DEVICE[backend] ?? "cpu";
|
|
148
|
-
}
|
|
149
|
-
runtime = "onnx";
|
|
150
|
-
device;
|
|
151
|
-
session = null;
|
|
152
|
-
async initialize() {
|
|
153
|
-
const ort = await import("onnxruntime-node");
|
|
154
|
-
const provider = BACKEND_TO_PROVIDER[this.backend] ?? "cpu";
|
|
155
|
-
const absModelPath = path.isAbsolute(this.modelPath) ? this.modelPath : path.resolve(process.cwd(), this.modelPath);
|
|
156
|
-
const sessionOptions = {
|
|
157
|
-
executionProviders: [provider]
|
|
158
|
-
};
|
|
159
|
-
this.session = await ort.InferenceSession.create(absModelPath, sessionOptions);
|
|
160
|
-
}
|
|
161
|
-
async run(input, inputShape) {
|
|
162
|
-
if (!this.session) {
|
|
163
|
-
throw new Error("NodeInferenceEngine: not initialized \u2014 call initialize() first");
|
|
164
|
-
}
|
|
165
|
-
const ort = await import("onnxruntime-node");
|
|
166
|
-
const sess = this.session;
|
|
167
|
-
const inputName = sess.inputNames[0];
|
|
168
|
-
const tensor = new ort.Tensor("float32", input, [...inputShape]);
|
|
169
|
-
const feeds = { [inputName]: tensor };
|
|
170
|
-
const results = await sess.run(feeds);
|
|
171
|
-
const outputName = sess.outputNames[0];
|
|
172
|
-
const outputTensor = results[outputName];
|
|
173
|
-
return outputTensor.data;
|
|
174
|
-
}
|
|
175
|
-
async runMultiOutput(input, inputShape) {
|
|
176
|
-
if (!this.session) {
|
|
177
|
-
throw new Error("NodeInferenceEngine: not initialized \u2014 call initialize() first");
|
|
178
|
-
}
|
|
179
|
-
const ort = await import("onnxruntime-node");
|
|
180
|
-
const sess = this.session;
|
|
181
|
-
const inputName = sess.inputNames[0];
|
|
182
|
-
const tensor = new ort.Tensor("float32", input, [...inputShape]);
|
|
183
|
-
const feeds = { [inputName]: tensor };
|
|
184
|
-
const results = await sess.run(feeds);
|
|
185
|
-
const out = {};
|
|
186
|
-
for (const name of sess.outputNames) {
|
|
187
|
-
out[name] = results[name].data;
|
|
168
|
+
function rgbToGrayscale(rgb, width, height) {
|
|
169
|
+
const numPixels = width * height;
|
|
170
|
+
const gray = new Uint8Array(numPixels);
|
|
171
|
+
for (let i = 0; i < numPixels; i++) {
|
|
172
|
+
const r = rgb[i * 3];
|
|
173
|
+
const g = rgb[i * 3 + 1];
|
|
174
|
+
const b = rgb[i * 3 + 2];
|
|
175
|
+
gray[i] = Math.round(0.299 * r + 0.587 * g + 0.114 * b);
|
|
176
|
+
}
|
|
177
|
+
return gray;
|
|
188
178
|
}
|
|
189
|
-
return out;
|
|
190
|
-
}
|
|
191
|
-
async dispose() {
|
|
192
|
-
this.session = null;
|
|
193
179
|
}
|
|
194
|
-
};
|
|
180
|
+
});
|
|
195
181
|
|
|
196
|
-
// src/shared/
|
|
197
|
-
var
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
182
|
+
// src/shared/node-engine.js
|
|
183
|
+
var require_node_engine = __commonJS({
|
|
184
|
+
"src/shared/node-engine.js"(exports2) {
|
|
185
|
+
"use strict";
|
|
186
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
187
|
+
if (k2 === void 0) k2 = k;
|
|
188
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
189
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
190
|
+
desc = { enumerable: true, get: function() {
|
|
191
|
+
return m[k];
|
|
192
|
+
} };
|
|
193
|
+
}
|
|
194
|
+
Object.defineProperty(o, k2, desc);
|
|
195
|
+
}) : (function(o, m, k, k2) {
|
|
196
|
+
if (k2 === void 0) k2 = k;
|
|
197
|
+
o[k2] = m[k];
|
|
198
|
+
}));
|
|
199
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
200
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
201
|
+
}) : function(o, v) {
|
|
202
|
+
o["default"] = v;
|
|
203
|
+
});
|
|
204
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
|
|
205
|
+
var ownKeys = function(o) {
|
|
206
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
207
|
+
var ar = [];
|
|
208
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
209
|
+
return ar;
|
|
210
|
+
};
|
|
211
|
+
return ownKeys(o);
|
|
212
|
+
};
|
|
213
|
+
return function(mod) {
|
|
214
|
+
if (mod && mod.__esModule) return mod;
|
|
215
|
+
var result = {};
|
|
216
|
+
if (mod != null) {
|
|
217
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
218
|
+
}
|
|
219
|
+
__setModuleDefault(result, mod);
|
|
220
|
+
return result;
|
|
221
|
+
};
|
|
222
|
+
})();
|
|
223
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
224
|
+
exports2.NodeInferenceEngine = void 0;
|
|
225
|
+
var path2 = __importStar(require("path"));
|
|
226
|
+
var BACKEND_TO_PROVIDER = {
|
|
227
|
+
cpu: "cpu",
|
|
228
|
+
coreml: "coreml",
|
|
229
|
+
cuda: "cuda",
|
|
230
|
+
tensorrt: "tensorrt",
|
|
231
|
+
dml: "dml"
|
|
232
|
+
};
|
|
233
|
+
var BACKEND_TO_DEVICE = {
|
|
234
|
+
cpu: "cpu",
|
|
208
235
|
coreml: "gpu-mps",
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
tflite: "cpu"
|
|
236
|
+
cuda: "gpu-cuda",
|
|
237
|
+
tensorrt: "tensorrt"
|
|
212
238
|
};
|
|
213
|
-
|
|
239
|
+
var NodeInferenceEngine = class {
|
|
240
|
+
modelPath;
|
|
241
|
+
backend;
|
|
242
|
+
runtime = "onnx";
|
|
243
|
+
device;
|
|
244
|
+
session = null;
|
|
245
|
+
constructor(modelPath, backend) {
|
|
246
|
+
this.modelPath = modelPath;
|
|
247
|
+
this.backend = backend;
|
|
248
|
+
this.device = BACKEND_TO_DEVICE[backend] ?? "cpu";
|
|
249
|
+
}
|
|
250
|
+
async initialize() {
|
|
251
|
+
const ort = await Promise.resolve().then(() => __importStar(require("onnxruntime-node")));
|
|
252
|
+
const provider = BACKEND_TO_PROVIDER[this.backend] ?? "cpu";
|
|
253
|
+
const absModelPath = path2.isAbsolute(this.modelPath) ? this.modelPath : path2.resolve(process.cwd(), this.modelPath);
|
|
254
|
+
const sessionOptions = {
|
|
255
|
+
executionProviders: [provider]
|
|
256
|
+
};
|
|
257
|
+
this.session = await ort.InferenceSession.create(absModelPath, sessionOptions);
|
|
258
|
+
}
|
|
259
|
+
async run(input, inputShape) {
|
|
260
|
+
if (!this.session) {
|
|
261
|
+
throw new Error("NodeInferenceEngine: not initialized \u2014 call initialize() first");
|
|
262
|
+
}
|
|
263
|
+
const ort = await Promise.resolve().then(() => __importStar(require("onnxruntime-node")));
|
|
264
|
+
const sess = this.session;
|
|
265
|
+
const inputName = sess.inputNames[0];
|
|
266
|
+
const tensor = new ort.Tensor("float32", input, [...inputShape]);
|
|
267
|
+
const feeds = { [inputName]: tensor };
|
|
268
|
+
const results = await sess.run(feeds);
|
|
269
|
+
const outputName = sess.outputNames[0];
|
|
270
|
+
const outputTensor = results[outputName];
|
|
271
|
+
return outputTensor.data;
|
|
272
|
+
}
|
|
273
|
+
async runMultiOutput(input, inputShape) {
|
|
274
|
+
if (!this.session) {
|
|
275
|
+
throw new Error("NodeInferenceEngine: not initialized \u2014 call initialize() first");
|
|
276
|
+
}
|
|
277
|
+
const ort = await Promise.resolve().then(() => __importStar(require("onnxruntime-node")));
|
|
278
|
+
const sess = this.session;
|
|
279
|
+
const inputName = sess.inputNames[0];
|
|
280
|
+
const tensor = new ort.Tensor("float32", input, [...inputShape]);
|
|
281
|
+
const feeds = { [inputName]: tensor };
|
|
282
|
+
const results = await sess.run(feeds);
|
|
283
|
+
const out = {};
|
|
284
|
+
for (const name of sess.outputNames) {
|
|
285
|
+
out[name] = results[name].data;
|
|
286
|
+
}
|
|
287
|
+
return out;
|
|
288
|
+
}
|
|
289
|
+
async dispose() {
|
|
290
|
+
this.session = null;
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
exports2.NodeInferenceEngine = NodeInferenceEngine;
|
|
214
294
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
this.
|
|
241
|
-
this.
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// src/shared/python-engine.js
|
|
298
|
+
var require_python_engine = __commonJS({
|
|
299
|
+
"src/shared/python-engine.js"(exports2) {
|
|
300
|
+
"use strict";
|
|
301
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
302
|
+
exports2.PythonInferenceEngine = void 0;
|
|
303
|
+
exports2.resolvePythonBinary = resolvePythonBinary;
|
|
304
|
+
var node_child_process_1 = require("child_process");
|
|
305
|
+
var PythonInferenceEngine = class {
|
|
306
|
+
pythonPath;
|
|
307
|
+
scriptPath;
|
|
308
|
+
modelPath;
|
|
309
|
+
extraArgs;
|
|
310
|
+
runtime;
|
|
311
|
+
device;
|
|
312
|
+
process = null;
|
|
313
|
+
receiveBuffer = Buffer.alloc(0);
|
|
314
|
+
pendingResolve = null;
|
|
315
|
+
pendingReject = null;
|
|
316
|
+
constructor(pythonPath, scriptPath, runtime, modelPath, extraArgs = []) {
|
|
317
|
+
this.pythonPath = pythonPath;
|
|
318
|
+
this.scriptPath = scriptPath;
|
|
319
|
+
this.modelPath = modelPath;
|
|
320
|
+
this.extraArgs = extraArgs;
|
|
321
|
+
this.runtime = runtime;
|
|
322
|
+
const runtimeDeviceMap = {
|
|
323
|
+
onnx: "cpu",
|
|
324
|
+
coreml: "gpu-mps",
|
|
325
|
+
pytorch: "cpu",
|
|
326
|
+
openvino: "cpu",
|
|
327
|
+
tflite: "cpu"
|
|
328
|
+
};
|
|
329
|
+
this.device = runtimeDeviceMap[runtime];
|
|
330
|
+
}
|
|
331
|
+
async initialize() {
|
|
332
|
+
const args = [this.scriptPath, this.modelPath, ...this.extraArgs];
|
|
333
|
+
this.process = (0, node_child_process_1.spawn)(this.pythonPath, args, {
|
|
334
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
335
|
+
});
|
|
336
|
+
if (!this.process.stdout || !this.process.stdin) {
|
|
337
|
+
throw new Error("PythonInferenceEngine: failed to create process pipes");
|
|
338
|
+
}
|
|
339
|
+
this.process.stderr?.on("data", (chunk) => {
|
|
340
|
+
process.stderr.write(`[python-engine] ${chunk.toString()}`);
|
|
341
|
+
});
|
|
342
|
+
this.process.on("error", (err) => {
|
|
343
|
+
this.pendingReject?.(err);
|
|
344
|
+
this.pendingReject = null;
|
|
345
|
+
this.pendingResolve = null;
|
|
346
|
+
});
|
|
347
|
+
this.process.on("exit", (code) => {
|
|
348
|
+
if (code !== 0) {
|
|
349
|
+
const err = new Error(`PythonInferenceEngine: process exited with code ${code}`);
|
|
350
|
+
this.pendingReject?.(err);
|
|
351
|
+
this.pendingReject = null;
|
|
352
|
+
this.pendingResolve = null;
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
this.process.stdout.on("data", (chunk) => {
|
|
356
|
+
this.receiveBuffer = Buffer.concat([this.receiveBuffer, chunk]);
|
|
357
|
+
this._tryReceive();
|
|
358
|
+
});
|
|
359
|
+
await new Promise((resolve, reject) => {
|
|
360
|
+
const timeout = setTimeout(() => resolve(), 2e3);
|
|
361
|
+
this.process?.on("error", (err) => {
|
|
362
|
+
clearTimeout(timeout);
|
|
363
|
+
reject(err);
|
|
364
|
+
});
|
|
365
|
+
this.process?.on("exit", (code) => {
|
|
366
|
+
clearTimeout(timeout);
|
|
367
|
+
if (code !== 0) {
|
|
368
|
+
reject(new Error(`PythonInferenceEngine: process exited early with code ${code}`));
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
_tryReceive() {
|
|
374
|
+
if (this.receiveBuffer.length < 4)
|
|
375
|
+
return;
|
|
376
|
+
const length = this.receiveBuffer.readUInt32LE(0);
|
|
377
|
+
if (this.receiveBuffer.length < 4 + length)
|
|
378
|
+
return;
|
|
379
|
+
const jsonBytes = this.receiveBuffer.subarray(4, 4 + length);
|
|
380
|
+
this.receiveBuffer = this.receiveBuffer.subarray(4 + length);
|
|
381
|
+
const resolve = this.pendingResolve;
|
|
382
|
+
const reject = this.pendingReject;
|
|
242
383
|
this.pendingResolve = null;
|
|
384
|
+
this.pendingReject = null;
|
|
385
|
+
if (!resolve)
|
|
386
|
+
return;
|
|
387
|
+
try {
|
|
388
|
+
const parsed = JSON.parse(jsonBytes.toString("utf8"));
|
|
389
|
+
resolve(parsed);
|
|
390
|
+
} catch (err) {
|
|
391
|
+
reject?.(err instanceof Error ? err : new Error(String(err)));
|
|
392
|
+
}
|
|
243
393
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
});
|
|
249
|
-
await new Promise((resolve2, reject) => {
|
|
250
|
-
const timeout = setTimeout(() => resolve2(), 2e3);
|
|
251
|
-
this.process?.on("error", (err) => {
|
|
252
|
-
clearTimeout(timeout);
|
|
253
|
-
reject(err);
|
|
254
|
-
});
|
|
255
|
-
this.process?.on("exit", (code) => {
|
|
256
|
-
clearTimeout(timeout);
|
|
257
|
-
if (code !== 0) {
|
|
258
|
-
reject(new Error(`PythonInferenceEngine: process exited early with code ${code}`));
|
|
394
|
+
/** Send JPEG buffer, receive JSON detection results */
|
|
395
|
+
async runJpeg(jpeg) {
|
|
396
|
+
if (!this.process?.stdin) {
|
|
397
|
+
throw new Error("PythonInferenceEngine: process not initialized");
|
|
259
398
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
const lengthBuf = Buffer.allocUnsafe(4);
|
|
290
|
-
lengthBuf.writeUInt32LE(jpeg.length, 0);
|
|
291
|
-
this.process.stdin.write(Buffer.concat([lengthBuf, jpeg]));
|
|
292
|
-
});
|
|
293
|
-
}
|
|
294
|
-
/** IInferenceEngine.run — wraps runJpeg for compatibility */
|
|
295
|
-
async run(_input, _inputShape) {
|
|
296
|
-
throw new Error(
|
|
297
|
-
"PythonInferenceEngine: use runJpeg() directly \u2014 this engine operates on JPEG input"
|
|
298
|
-
);
|
|
299
|
-
}
|
|
300
|
-
/** IInferenceEngine.runMultiOutput — not supported by Python engine (operates on JPEG input) */
|
|
301
|
-
async runMultiOutput(_input, _inputShape) {
|
|
302
|
-
throw new Error(
|
|
303
|
-
"PythonInferenceEngine: runMultiOutput() is not supported \u2014 this engine operates on JPEG input"
|
|
304
|
-
);
|
|
305
|
-
}
|
|
306
|
-
async dispose() {
|
|
307
|
-
if (this.process) {
|
|
308
|
-
this.process.stdin?.end();
|
|
309
|
-
this.process.kill("SIGTERM");
|
|
310
|
-
this.process = null;
|
|
399
|
+
return new Promise((resolve, reject) => {
|
|
400
|
+
this.pendingResolve = resolve;
|
|
401
|
+
this.pendingReject = reject;
|
|
402
|
+
const lengthBuf = Buffer.allocUnsafe(4);
|
|
403
|
+
lengthBuf.writeUInt32LE(jpeg.length, 0);
|
|
404
|
+
this.process.stdin.write(Buffer.concat([lengthBuf, jpeg]));
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
/** IInferenceEngine.run — wraps runJpeg for compatibility */
|
|
408
|
+
async run(_input, _inputShape) {
|
|
409
|
+
throw new Error("PythonInferenceEngine: use runJpeg() directly \u2014 this engine operates on JPEG input");
|
|
410
|
+
}
|
|
411
|
+
/** IInferenceEngine.runMultiOutput — not supported by Python engine (operates on JPEG input) */
|
|
412
|
+
async runMultiOutput(_input, _inputShape) {
|
|
413
|
+
throw new Error("PythonInferenceEngine: runMultiOutput() is not supported \u2014 this engine operates on JPEG input");
|
|
414
|
+
}
|
|
415
|
+
async dispose() {
|
|
416
|
+
if (this.process) {
|
|
417
|
+
this.process.stdin?.end();
|
|
418
|
+
this.process.kill("SIGTERM");
|
|
419
|
+
this.process = null;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
exports2.PythonInferenceEngine = PythonInferenceEngine;
|
|
424
|
+
async function resolvePythonBinary(configPath, deps) {
|
|
425
|
+
if (configPath)
|
|
426
|
+
return configPath;
|
|
427
|
+
return deps.ensurePython();
|
|
311
428
|
}
|
|
312
429
|
}
|
|
313
|
-
};
|
|
430
|
+
});
|
|
314
431
|
|
|
315
|
-
// src/shared/engine-resolver.
|
|
316
|
-
var
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
function
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
432
|
+
// src/shared/engine-resolver.js
|
|
433
|
+
var require_engine_resolver = __commonJS({
|
|
434
|
+
"src/shared/engine-resolver.js"(exports2) {
|
|
435
|
+
"use strict";
|
|
436
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
437
|
+
if (k2 === void 0) k2 = k;
|
|
438
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
439
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
440
|
+
desc = { enumerable: true, get: function() {
|
|
441
|
+
return m[k];
|
|
442
|
+
} };
|
|
443
|
+
}
|
|
444
|
+
Object.defineProperty(o, k2, desc);
|
|
445
|
+
}) : (function(o, m, k, k2) {
|
|
446
|
+
if (k2 === void 0) k2 = k;
|
|
447
|
+
o[k2] = m[k];
|
|
448
|
+
}));
|
|
449
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
450
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
451
|
+
}) : function(o, v) {
|
|
452
|
+
o["default"] = v;
|
|
453
|
+
});
|
|
454
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
|
|
455
|
+
var ownKeys = function(o) {
|
|
456
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
457
|
+
var ar = [];
|
|
458
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
459
|
+
return ar;
|
|
460
|
+
};
|
|
461
|
+
return ownKeys(o);
|
|
462
|
+
};
|
|
463
|
+
return function(mod) {
|
|
464
|
+
if (mod && mod.__esModule) return mod;
|
|
465
|
+
var result = {};
|
|
466
|
+
if (mod != null) {
|
|
467
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
468
|
+
}
|
|
469
|
+
__setModuleDefault(result, mod);
|
|
470
|
+
return result;
|
|
471
|
+
};
|
|
472
|
+
})();
|
|
473
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
474
|
+
exports2.resolveEngine = resolveEngine2;
|
|
475
|
+
exports2.probeOnnxBackends = probeOnnxBackends;
|
|
476
|
+
var fs2 = __importStar(require("fs"));
|
|
477
|
+
var path2 = __importStar(require("path"));
|
|
478
|
+
var node_engine_js_1 = require_node_engine();
|
|
479
|
+
var python_engine_js_1 = require_python_engine();
|
|
480
|
+
var AUTO_BACKEND_PRIORITY = ["coreml", "cuda", "tensorrt", "cpu"];
|
|
481
|
+
var BACKEND_TO_FORMAT = {
|
|
482
|
+
cpu: "onnx",
|
|
483
|
+
coreml: "onnx",
|
|
484
|
+
cuda: "onnx",
|
|
485
|
+
tensorrt: "onnx"
|
|
486
|
+
};
|
|
487
|
+
var RUNTIME_TO_FORMAT = {
|
|
488
|
+
onnx: "onnx",
|
|
489
|
+
coreml: "coreml",
|
|
490
|
+
openvino: "openvino",
|
|
491
|
+
tflite: "tflite",
|
|
492
|
+
pytorch: "pt"
|
|
493
|
+
};
|
|
494
|
+
function modelFilePath(modelsDir, modelEntry, format) {
|
|
495
|
+
const formatEntry = modelEntry.formats[format];
|
|
496
|
+
if (!formatEntry) {
|
|
497
|
+
throw new Error(`Model ${modelEntry.id} has no ${format} format`);
|
|
498
|
+
}
|
|
499
|
+
const urlParts = formatEntry.url.split("/");
|
|
500
|
+
const filename = urlParts[urlParts.length - 1] ?? `${modelEntry.id}.${format}`;
|
|
501
|
+
return path2.join(modelsDir, filename);
|
|
377
502
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
} else {
|
|
385
|
-
modelPath = modelFilePath(modelsDir, modelEntry, selectedFormat);
|
|
386
|
-
if (!modelExists(modelPath)) {
|
|
387
|
-
throw new Error(
|
|
388
|
-
`resolveEngine: model file not found at ${modelPath} and no model service provided`
|
|
389
|
-
);
|
|
503
|
+
function modelExists(filePath) {
|
|
504
|
+
try {
|
|
505
|
+
return fs2.existsSync(filePath);
|
|
506
|
+
} catch {
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
390
509
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
510
|
+
async function resolveEngine2(options) {
|
|
511
|
+
const { runtime, backend, modelEntry, modelsDir, models } = options;
|
|
512
|
+
let selectedFormat;
|
|
513
|
+
let selectedBackend;
|
|
514
|
+
if (runtime === "auto") {
|
|
515
|
+
const available = await probeOnnxBackends();
|
|
516
|
+
let chosen = null;
|
|
517
|
+
for (const b of AUTO_BACKEND_PRIORITY) {
|
|
518
|
+
if (!available.includes(b))
|
|
519
|
+
continue;
|
|
520
|
+
const fmt = BACKEND_TO_FORMAT[b];
|
|
521
|
+
if (!fmt)
|
|
522
|
+
continue;
|
|
523
|
+
if (!modelEntry.formats[fmt])
|
|
524
|
+
continue;
|
|
525
|
+
chosen = { backend: b, format: fmt };
|
|
526
|
+
break;
|
|
527
|
+
}
|
|
528
|
+
if (!chosen) {
|
|
529
|
+
throw new Error(`resolveEngine: no compatible backend found for model ${modelEntry.id}. Available backends: ${available.join(", ")}`);
|
|
530
|
+
}
|
|
531
|
+
selectedFormat = chosen.format;
|
|
532
|
+
selectedBackend = chosen.backend;
|
|
533
|
+
} else {
|
|
534
|
+
const fmt = RUNTIME_TO_FORMAT[runtime];
|
|
535
|
+
if (!fmt) {
|
|
536
|
+
throw new Error(`resolveEngine: unsupported runtime "${runtime}"`);
|
|
537
|
+
}
|
|
538
|
+
if (!modelEntry.formats[fmt]) {
|
|
539
|
+
throw new Error(`resolveEngine: model ${modelEntry.id} has no ${fmt} format for runtime ${runtime}`);
|
|
540
|
+
}
|
|
541
|
+
selectedFormat = fmt;
|
|
542
|
+
selectedBackend = runtime === "onnx" ? backend || "cpu" : runtime;
|
|
543
|
+
}
|
|
544
|
+
let modelPath;
|
|
545
|
+
if (models) {
|
|
546
|
+
modelPath = await models.ensure(modelEntry.id, selectedFormat);
|
|
547
|
+
} else {
|
|
548
|
+
modelPath = modelFilePath(modelsDir, modelEntry, selectedFormat);
|
|
549
|
+
if (!modelExists(modelPath)) {
|
|
550
|
+
throw new Error(`resolveEngine: model file not found at ${modelPath} and no model service provided`);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
if (selectedFormat === "onnx") {
|
|
554
|
+
const engine = new node_engine_js_1.NodeInferenceEngine(modelPath, selectedBackend);
|
|
555
|
+
await engine.initialize();
|
|
556
|
+
return { engine, format: selectedFormat, modelPath };
|
|
557
|
+
}
|
|
558
|
+
const { pythonPath } = options;
|
|
559
|
+
const PYTHON_SCRIPT_MAP = {
|
|
560
|
+
coreml: "coreml_inference.py",
|
|
561
|
+
pytorch: "pytorch_inference.py",
|
|
562
|
+
openvino: "openvino_inference.py"
|
|
563
|
+
};
|
|
564
|
+
const effectiveRuntime = runtime === "auto" ? selectedBackend : runtime;
|
|
565
|
+
const scriptName = PYTHON_SCRIPT_MAP[effectiveRuntime];
|
|
566
|
+
if (scriptName && pythonPath) {
|
|
567
|
+
const candidates = [
|
|
568
|
+
path2.join(__dirname, "../../python", scriptName),
|
|
569
|
+
path2.join(__dirname, "../python", scriptName),
|
|
570
|
+
path2.join(__dirname, "../../../python", scriptName)
|
|
571
|
+
];
|
|
572
|
+
const scriptPath = candidates.find((p) => fs2.existsSync(p));
|
|
573
|
+
if (!scriptPath) {
|
|
574
|
+
throw new Error(`resolveEngine: Python script "${scriptName}" not found. Searched:
|
|
575
|
+
${candidates.join("\n")}`);
|
|
576
|
+
}
|
|
577
|
+
const inputSize = Math.max(modelEntry.inputSize.width, modelEntry.inputSize.height);
|
|
578
|
+
const engine = new python_engine_js_1.PythonInferenceEngine(pythonPath, scriptPath, effectiveRuntime, modelPath, [
|
|
579
|
+
`--input-size=${inputSize}`,
|
|
580
|
+
`--confidence=0.25`
|
|
581
|
+
]);
|
|
582
|
+
await engine.initialize();
|
|
583
|
+
return { engine, format: selectedFormat, modelPath };
|
|
584
|
+
}
|
|
585
|
+
const fallbackPath = modelFilePath(modelsDir, modelEntry, "onnx");
|
|
586
|
+
if (modelEntry.formats["onnx"] && modelExists(fallbackPath)) {
|
|
587
|
+
const engine = new node_engine_js_1.NodeInferenceEngine(fallbackPath, "cpu");
|
|
588
|
+
await engine.initialize();
|
|
589
|
+
return { engine, format: "onnx", modelPath: fallbackPath };
|
|
590
|
+
}
|
|
591
|
+
throw new Error(`resolveEngine: format ${selectedFormat} is not yet supported by NodeInferenceEngine, no Python runtime is available, and no ONNX fallback exists`);
|
|
417
592
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
try {
|
|
439
|
-
const ort = await import("onnxruntime-node");
|
|
440
|
-
const providers = ort.env?.webgl?.disabled !== void 0 ? ort.InferenceSession?.getAvailableProviders?.() ?? [] : [];
|
|
441
|
-
for (const p of providers) {
|
|
442
|
-
const normalized = p.toLowerCase().replace("executionprovider", "");
|
|
443
|
-
if (normalized === "coreml") available.push("coreml");
|
|
444
|
-
else if (normalized === "cuda") available.push("cuda");
|
|
445
|
-
else if (normalized === "tensorrt") available.push("tensorrt");
|
|
593
|
+
async function probeOnnxBackends() {
|
|
594
|
+
const available = ["cpu"];
|
|
595
|
+
try {
|
|
596
|
+
const ort = await Promise.resolve().then(() => __importStar(require("onnxruntime-node")));
|
|
597
|
+
const providers = ort.env?.webgl?.disabled !== void 0 ? ort.InferenceSession?.getAvailableProviders?.() ?? [] : [];
|
|
598
|
+
for (const p of providers) {
|
|
599
|
+
const normalized = p.toLowerCase().replace("executionprovider", "");
|
|
600
|
+
if (normalized === "coreml")
|
|
601
|
+
available.push("coreml");
|
|
602
|
+
else if (normalized === "cuda")
|
|
603
|
+
available.push("cuda");
|
|
604
|
+
else if (normalized === "tensorrt")
|
|
605
|
+
available.push("tensorrt");
|
|
606
|
+
}
|
|
607
|
+
} catch {
|
|
608
|
+
}
|
|
609
|
+
if (process.platform === "darwin" && !available.includes("coreml")) {
|
|
610
|
+
available.push("coreml");
|
|
611
|
+
}
|
|
612
|
+
return [...new Set(available)];
|
|
446
613
|
}
|
|
447
|
-
} catch {
|
|
448
614
|
}
|
|
449
|
-
|
|
450
|
-
available.push("coreml");
|
|
451
|
-
}
|
|
452
|
-
return [...new Set(available)];
|
|
453
|
-
}
|
|
615
|
+
});
|
|
454
616
|
|
|
455
617
|
// src/addons/vehicle-classifier/index.ts
|
|
456
|
-
var
|
|
457
|
-
|
|
618
|
+
var vehicle_classifier_exports = {};
|
|
619
|
+
__export(vehicle_classifier_exports, {
|
|
620
|
+
default: () => VehicleClassifierAddon
|
|
621
|
+
});
|
|
622
|
+
module.exports = __toCommonJS(vehicle_classifier_exports);
|
|
623
|
+
var import_vehicle_classification_models = __toESM(require_vehicle_classification_models());
|
|
624
|
+
var import_image_utils = __toESM(require_image_utils());
|
|
625
|
+
var import_engine_resolver = __toESM(require_engine_resolver());
|
|
626
|
+
var fs = __toESM(require("fs"));
|
|
627
|
+
var path = __toESM(require("path"));
|
|
458
628
|
var VEHICLE_TYPE_LABEL = { id: "vehicle-type", name: "Vehicle Type" };
|
|
459
629
|
var VEHICLE_TYPE_LABELS = [VEHICLE_TYPE_LABEL];
|
|
460
630
|
var VEHICLE_CLASS_MAP = { mapping: {}, preserveOriginal: true };
|
|
@@ -464,9 +634,9 @@ function loadLabels(modelsDir, modelId) {
|
|
|
464
634
|
`camstack-vehicle-type-labels.json`
|
|
465
635
|
];
|
|
466
636
|
for (const name of labelNames) {
|
|
467
|
-
const labelPath =
|
|
468
|
-
if (
|
|
469
|
-
const raw =
|
|
637
|
+
const labelPath = path.join(modelsDir, name);
|
|
638
|
+
if (fs.existsSync(labelPath)) {
|
|
639
|
+
const raw = fs.readFileSync(labelPath, "utf-8");
|
|
470
640
|
return JSON.parse(raw);
|
|
471
641
|
}
|
|
472
642
|
}
|
|
@@ -512,7 +682,7 @@ var VehicleClassifierAddon = class {
|
|
|
512
682
|
resolvedConfig = null;
|
|
513
683
|
ctx = null;
|
|
514
684
|
getModelRequirements() {
|
|
515
|
-
return VEHICLE_TYPE_MODELS.map((m) => ({
|
|
685
|
+
return import_vehicle_classification_models.VEHICLE_TYPE_MODELS.map((m) => ({
|
|
516
686
|
modelId: m.id,
|
|
517
687
|
name: m.name,
|
|
518
688
|
minRAM_MB: 120,
|
|
@@ -528,7 +698,7 @@ var VehicleClassifierAddon = class {
|
|
|
528
698
|
const cfg = ctx.addonConfig;
|
|
529
699
|
const modelId = cfg["modelId"] ?? this.resolvedConfig?.modelId ?? "vehicle-type-efficientnet";
|
|
530
700
|
this.minConfidence = cfg["minConfidence"] ?? 0.05;
|
|
531
|
-
const entry = VEHICLE_TYPE_MODELS.find((m) => m.id === modelId);
|
|
701
|
+
const entry = import_vehicle_classification_models.VEHICLE_TYPE_MODELS.find((m) => m.id === modelId);
|
|
532
702
|
if (!entry) {
|
|
533
703
|
throw new Error(`VehicleClassifierAddon: unknown modelId "${modelId}"`);
|
|
534
704
|
}
|
|
@@ -538,8 +708,8 @@ var VehicleClassifierAddon = class {
|
|
|
538
708
|
if (!this.engine) await this.ensureEngine();
|
|
539
709
|
const start = Date.now();
|
|
540
710
|
const { width: inputW, height: inputH } = this.modelEntry.inputSize;
|
|
541
|
-
const vehicleCrop = await cropRegion(input.frame.data, input.roi);
|
|
542
|
-
const normalized = await resizeAndNormalize(vehicleCrop, inputW, inputH, "imagenet", "nchw");
|
|
711
|
+
const vehicleCrop = await (0, import_image_utils.cropRegion)(input.frame.data, input.roi);
|
|
712
|
+
const normalized = await (0, import_image_utils.resizeAndNormalize)(vehicleCrop, inputW, inputH, "imagenet", "nchw");
|
|
543
713
|
const rawOutput = await this.engine.run(normalized, [1, 3, inputH, inputW]);
|
|
544
714
|
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(", ")}]`);
|
|
545
715
|
const probs = softmax(rawOutput);
|
|
@@ -576,14 +746,14 @@ var VehicleClassifierAddon = class {
|
|
|
576
746
|
const runtime = config?.runtime === "python" ? "coreml" : config?.runtime === "node" ? "onnx" : "auto";
|
|
577
747
|
const backend = config?.backend ?? "cpu";
|
|
578
748
|
const format = config?.format ?? "onnx";
|
|
579
|
-
const entry = VEHICLE_TYPE_MODELS.find((m) => m.id === modelId) ?? this.modelEntry;
|
|
749
|
+
const entry = import_vehicle_classification_models.VEHICLE_TYPE_MODELS.find((m) => m.id === modelId) ?? this.modelEntry;
|
|
580
750
|
this.modelEntry = entry;
|
|
581
751
|
const modelsDir = this.ctx.models?.getModelsDir() ?? this.ctx.locationPaths.models;
|
|
582
752
|
if (this.ctx.models) {
|
|
583
753
|
await this.ctx.models.ensure(modelId, format);
|
|
584
754
|
}
|
|
585
755
|
this.labels = loadLabels(modelsDir, modelId);
|
|
586
|
-
const resolved = await resolveEngine({
|
|
756
|
+
const resolved = await (0, import_engine_resolver.resolveEngine)({
|
|
587
757
|
runtime,
|
|
588
758
|
backend,
|
|
589
759
|
modelEntry: entry,
|
|
@@ -607,7 +777,7 @@ var VehicleClassifierAddon = class {
|
|
|
607
777
|
key: "modelId",
|
|
608
778
|
label: "Model",
|
|
609
779
|
type: "model-selector",
|
|
610
|
-
catalog: [...VEHICLE_TYPE_MODELS],
|
|
780
|
+
catalog: [...import_vehicle_classification_models.VEHICLE_TYPE_MODELS],
|
|
611
781
|
allowCustom: false,
|
|
612
782
|
allowConversion: false,
|
|
613
783
|
acceptFormats: ["onnx", "coreml", "openvino"],
|
|
@@ -669,7 +839,7 @@ var VehicleClassifierAddon = class {
|
|
|
669
839
|
return VEHICLE_CLASS_MAP;
|
|
670
840
|
}
|
|
671
841
|
getModelCatalog() {
|
|
672
|
-
return [...VEHICLE_TYPE_MODELS];
|
|
842
|
+
return [...import_vehicle_classification_models.VEHICLE_TYPE_MODELS];
|
|
673
843
|
}
|
|
674
844
|
getAvailableModels() {
|
|
675
845
|
return [];
|