@camstack/addon-vision 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/addons/animal-classifier/index.js +999 -822
- 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 -378
- 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 -825
- 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 -825
- 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 -934
- 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 -807
- 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 -1082
- 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 -868
- 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 -505
- 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 -790
- 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 -410
- 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-2IOKI4ES.mjs.map → chunk-PIFS7AIT.mjs.map} +1 -1
- 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 -3924
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2698 -250
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-22BHCDT5.mjs +0 -101
- package/dist/chunk-2IOKI4ES.mjs +0 -335
- 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-D6WEHN33.mjs +0 -276
- package/dist/chunk-D6WEHN33.mjs.map +0 -1
- package/dist/chunk-DRYFGARD.mjs +0 -289
- package/dist/chunk-DRYFGARD.mjs.map +0 -1
- package/dist/chunk-DUN6XU3N.mjs +0 -72
- package/dist/chunk-ESLHNWWE.mjs +0 -387
- package/dist/chunk-ESLHNWWE.mjs.map +0 -1
- package/dist/chunk-JUQEW6ON.mjs +0 -256
- package/dist/chunk-JUQEW6ON.mjs.map +0 -1
- package/dist/chunk-KUO2BVFY.mjs +0 -90
- package/dist/chunk-R5J3WAUI.mjs +0 -645
- package/dist/chunk-R5J3WAUI.mjs.map +0 -1
- package/dist/chunk-XZ6ZMXXU.mjs +0 -39
- package/dist/chunk-YPU4WTXZ.mjs +0 -269
- package/dist/chunk-YPU4WTXZ.mjs.map +0 -1
- package/dist/chunk-YUCD2TFH.mjs +0 -242
- package/dist/chunk-YUCD2TFH.mjs.map +0 -1
- package/dist/chunk-ZTJENCFC.mjs +0 -379
- package/dist/chunk-ZTJENCFC.mjs.map +0 -1
- package/dist/chunk-ZWYXXCXP.mjs +0 -248
- package/dist/chunk-ZWYXXCXP.mjs.map +0 -1
- /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,126 +30,226 @@ 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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const r = rgb[i * 3];
|
|
48
|
-
const g = rgb[i * 3 + 1];
|
|
49
|
-
const b = rgb[i * 3 + 2];
|
|
50
|
-
gray[i] = Math.round(0.299 * r + 0.587 * g + 0.114 * b);
|
|
51
|
-
}
|
|
52
|
-
return gray;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// src/addons/motion-detection/frame-diff.ts
|
|
56
|
-
function detectMotion(current, previous, width, height, threshold, minArea) {
|
|
57
|
-
const numPixels = width * height;
|
|
58
|
-
const mask = new Uint8Array(numPixels);
|
|
59
|
-
for (let i = 0; i < numPixels; i++) {
|
|
60
|
-
mask[i] = Math.abs((current[i] ?? 0) - (previous[i] ?? 0)) > threshold ? 1 : 0;
|
|
61
|
-
}
|
|
62
|
-
const labels = new Int32Array(numPixels).fill(0);
|
|
63
|
-
const parent = new Int32Array(numPixels + 1).fill(0);
|
|
64
|
-
let nextLabel = 1;
|
|
65
|
-
function findRoot(x) {
|
|
66
|
-
while (parent[x] !== x) {
|
|
67
|
-
parent[x] = parent[parent[x]];
|
|
68
|
-
x = parent[x];
|
|
33
|
+
// src/shared/image-utils.js
|
|
34
|
+
var require_image_utils = __commonJS({
|
|
35
|
+
"src/shared/image-utils.js"(exports2) {
|
|
36
|
+
"use strict";
|
|
37
|
+
var __importDefault = exports2 && exports2.__importDefault || function(mod) {
|
|
38
|
+
return mod && mod.__esModule ? mod : { "default": mod };
|
|
39
|
+
};
|
|
40
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
41
|
+
exports2.jpegToRgb = jpegToRgb2;
|
|
42
|
+
exports2.cropRegion = cropRegion;
|
|
43
|
+
exports2.letterbox = letterbox;
|
|
44
|
+
exports2.resizeAndNormalize = resizeAndNormalize;
|
|
45
|
+
exports2.rgbToGrayscale = rgbToGrayscale2;
|
|
46
|
+
var sharp_1 = __importDefault(require("sharp"));
|
|
47
|
+
async function jpegToRgb2(jpeg) {
|
|
48
|
+
const { data, info } = await (0, sharp_1.default)(jpeg).removeAlpha().raw().toBuffer({ resolveWithObject: true });
|
|
49
|
+
return { data, width: info.width, height: info.height };
|
|
69
50
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
|
|
51
|
+
async function cropRegion(jpeg, roi) {
|
|
52
|
+
return (0, sharp_1.default)(jpeg).extract({
|
|
53
|
+
left: Math.round(roi.x),
|
|
54
|
+
top: Math.round(roi.y),
|
|
55
|
+
width: Math.round(roi.w),
|
|
56
|
+
height: Math.round(roi.h)
|
|
57
|
+
}).jpeg().toBuffer();
|
|
58
|
+
}
|
|
59
|
+
async function letterbox(jpeg, targetSize) {
|
|
60
|
+
const meta = await (0, sharp_1.default)(jpeg).metadata();
|
|
61
|
+
const originalWidth = meta.width ?? 0;
|
|
62
|
+
const originalHeight = meta.height ?? 0;
|
|
63
|
+
const scale = Math.min(targetSize / originalWidth, targetSize / originalHeight);
|
|
64
|
+
const scaledWidth = Math.round(originalWidth * scale);
|
|
65
|
+
const scaledHeight = Math.round(originalHeight * scale);
|
|
66
|
+
const padX = Math.floor((targetSize - scaledWidth) / 2);
|
|
67
|
+
const padY = Math.floor((targetSize - scaledHeight) / 2);
|
|
68
|
+
const { data } = await (0, sharp_1.default)(jpeg).resize(scaledWidth, scaledHeight).extend({
|
|
69
|
+
top: padY,
|
|
70
|
+
bottom: targetSize - scaledHeight - padY,
|
|
71
|
+
left: padX,
|
|
72
|
+
right: targetSize - scaledWidth - padX,
|
|
73
|
+
background: { r: 114, g: 114, b: 114 }
|
|
74
|
+
}).removeAlpha().raw().toBuffer({ resolveWithObject: true });
|
|
75
|
+
const numPixels = targetSize * targetSize;
|
|
76
|
+
const float32 = new Float32Array(3 * numPixels);
|
|
77
|
+
for (let i = 0; i < numPixels; i++) {
|
|
78
|
+
const srcBase = i * 3;
|
|
79
|
+
float32[0 * numPixels + i] = data[srcBase] / 255;
|
|
80
|
+
float32[1 * numPixels + i] = data[srcBase + 1] / 255;
|
|
81
|
+
float32[2 * numPixels + i] = data[srcBase + 2] / 255;
|
|
82
|
+
}
|
|
83
|
+
return { data: float32, scale, padX, padY, originalWidth, originalHeight };
|
|
84
|
+
}
|
|
85
|
+
async function resizeAndNormalize(jpeg, targetWidth, targetHeight, normalization, layout) {
|
|
86
|
+
const { data } = await (0, sharp_1.default)(jpeg).resize(targetWidth, targetHeight, { fit: "fill" }).removeAlpha().raw().toBuffer({ resolveWithObject: true });
|
|
87
|
+
const numPixels = targetWidth * targetHeight;
|
|
88
|
+
const float32 = new Float32Array(3 * numPixels);
|
|
89
|
+
const mean = [0.485, 0.456, 0.406];
|
|
90
|
+
const std = [0.229, 0.224, 0.225];
|
|
91
|
+
if (layout === "nchw") {
|
|
92
|
+
for (let i = 0; i < numPixels; i++) {
|
|
93
|
+
const srcBase = i * 3;
|
|
94
|
+
for (let c = 0; c < 3; c++) {
|
|
95
|
+
const raw = data[srcBase + c] / 255;
|
|
96
|
+
let val;
|
|
97
|
+
if (normalization === "zero-one") {
|
|
98
|
+
val = raw;
|
|
99
|
+
} else if (normalization === "imagenet") {
|
|
100
|
+
val = (raw - mean[c]) / std[c];
|
|
101
|
+
} else {
|
|
102
|
+
val = data[srcBase + c];
|
|
103
|
+
}
|
|
104
|
+
float32[c * numPixels + i] = val;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
95
107
|
} else {
|
|
96
|
-
|
|
108
|
+
for (let i = 0; i < numPixels; i++) {
|
|
109
|
+
const srcBase = i * 3;
|
|
110
|
+
for (let c = 0; c < 3; c++) {
|
|
111
|
+
const raw = data[srcBase + c] / 255;
|
|
112
|
+
let val;
|
|
113
|
+
if (normalization === "zero-one") {
|
|
114
|
+
val = raw;
|
|
115
|
+
} else if (normalization === "imagenet") {
|
|
116
|
+
val = (raw - mean[c]) / std[c];
|
|
117
|
+
} else {
|
|
118
|
+
val = data[srcBase + c];
|
|
119
|
+
}
|
|
120
|
+
float32[i * 3 + c] = val;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
97
123
|
}
|
|
124
|
+
return float32;
|
|
98
125
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
126
|
+
function rgbToGrayscale2(rgb, width, height) {
|
|
127
|
+
const numPixels = width * height;
|
|
128
|
+
const gray = new Uint8Array(numPixels);
|
|
129
|
+
for (let i = 0; i < numPixels; i++) {
|
|
130
|
+
const r = rgb[i * 3];
|
|
131
|
+
const g = rgb[i * 3 + 1];
|
|
132
|
+
const b = rgb[i * 3 + 2];
|
|
133
|
+
gray[i] = Math.round(0.299 * r + 0.587 * g + 0.114 * b);
|
|
134
|
+
}
|
|
135
|
+
return gray;
|
|
103
136
|
}
|
|
104
137
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// src/addons/motion-detection/frame-diff.js
|
|
141
|
+
var require_frame_diff = __commonJS({
|
|
142
|
+
"src/addons/motion-detection/frame-diff.js"(exports2) {
|
|
143
|
+
"use strict";
|
|
144
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
145
|
+
exports2.detectMotion = detectMotion2;
|
|
146
|
+
function detectMotion2(current, previous, width, height, threshold, minArea) {
|
|
147
|
+
const numPixels = width * height;
|
|
148
|
+
const mask = new Uint8Array(numPixels);
|
|
149
|
+
for (let i = 0; i < numPixels; i++) {
|
|
150
|
+
mask[i] = Math.abs((current[i] ?? 0) - (previous[i] ?? 0)) > threshold ? 1 : 0;
|
|
151
|
+
}
|
|
152
|
+
const labels = new Int32Array(numPixels).fill(0);
|
|
153
|
+
const parent = new Int32Array(numPixels + 1).fill(0);
|
|
154
|
+
let nextLabel = 1;
|
|
155
|
+
function findRoot(x) {
|
|
156
|
+
while (parent[x] !== x) {
|
|
157
|
+
parent[x] = parent[parent[x]];
|
|
158
|
+
x = parent[x];
|
|
159
|
+
}
|
|
160
|
+
return x;
|
|
161
|
+
}
|
|
162
|
+
function union(a, b) {
|
|
163
|
+
const ra = findRoot(a);
|
|
164
|
+
const rb = findRoot(b);
|
|
165
|
+
if (ra !== rb)
|
|
166
|
+
parent[rb] = ra;
|
|
167
|
+
return ra;
|
|
168
|
+
}
|
|
169
|
+
for (let i = 0; i <= numPixels; i++) {
|
|
170
|
+
parent[i] = i;
|
|
171
|
+
}
|
|
172
|
+
for (let y = 0; y < height; y++) {
|
|
173
|
+
for (let x = 0; x < width; x++) {
|
|
174
|
+
const idx = y * width + x;
|
|
175
|
+
if (!mask[idx])
|
|
176
|
+
continue;
|
|
177
|
+
const above = y > 0 ? labels[(y - 1) * width + x] ?? 0 : 0;
|
|
178
|
+
const left = x > 0 ? labels[y * width + (x - 1)] ?? 0 : 0;
|
|
179
|
+
if (above === 0 && left === 0) {
|
|
180
|
+
labels[idx] = nextLabel;
|
|
181
|
+
parent[nextLabel] = nextLabel;
|
|
182
|
+
nextLabel++;
|
|
183
|
+
} else if (above !== 0 && left === 0) {
|
|
184
|
+
labels[idx] = above;
|
|
185
|
+
} else if (above === 0 && left !== 0) {
|
|
186
|
+
labels[idx] = left;
|
|
187
|
+
} else {
|
|
188
|
+
labels[idx] = union(above, left);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
for (let i = 0; i < numPixels; i++) {
|
|
193
|
+
if (labels[i]) {
|
|
194
|
+
labels[i] = findRoot(labels[i]);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const bboxMap = /* @__PURE__ */ new Map();
|
|
198
|
+
for (let y = 0; y < height; y++) {
|
|
199
|
+
for (let x = 0; x < width; x++) {
|
|
200
|
+
const idx = y * width + x;
|
|
201
|
+
const label = labels[idx];
|
|
202
|
+
if (!label)
|
|
203
|
+
continue;
|
|
204
|
+
const diff = Math.abs((current[idx] ?? 0) - (previous[idx] ?? 0));
|
|
205
|
+
const existing = bboxMap.get(label);
|
|
206
|
+
if (existing) {
|
|
207
|
+
existing.minX = Math.min(existing.minX, x);
|
|
208
|
+
existing.minY = Math.min(existing.minY, y);
|
|
209
|
+
existing.maxX = Math.max(existing.maxX, x);
|
|
210
|
+
existing.maxY = Math.max(existing.maxY, y);
|
|
211
|
+
existing.count++;
|
|
212
|
+
existing.intensitySum += diff;
|
|
213
|
+
} else {
|
|
214
|
+
bboxMap.set(label, {
|
|
215
|
+
minX: x,
|
|
216
|
+
minY: y,
|
|
217
|
+
maxX: x,
|
|
218
|
+
maxY: y,
|
|
219
|
+
count: 1,
|
|
220
|
+
intensitySum: diff
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const regions = [];
|
|
226
|
+
for (const [, info] of bboxMap) {
|
|
227
|
+
if (info.count < minArea)
|
|
228
|
+
continue;
|
|
229
|
+
regions.push({
|
|
230
|
+
bbox: {
|
|
231
|
+
x: info.minX,
|
|
232
|
+
y: info.minY,
|
|
233
|
+
w: info.maxX - info.minX + 1,
|
|
234
|
+
h: info.maxY - info.minY + 1
|
|
235
|
+
},
|
|
236
|
+
pixelCount: info.count,
|
|
237
|
+
intensity: info.intensitySum / info.count
|
|
128
238
|
});
|
|
129
239
|
}
|
|
240
|
+
return regions;
|
|
130
241
|
}
|
|
131
242
|
}
|
|
132
|
-
|
|
133
|
-
for (const [, info] of bboxMap) {
|
|
134
|
-
if (info.count < minArea) continue;
|
|
135
|
-
regions.push({
|
|
136
|
-
bbox: {
|
|
137
|
-
x: info.minX,
|
|
138
|
-
y: info.minY,
|
|
139
|
-
w: info.maxX - info.minX + 1,
|
|
140
|
-
h: info.maxY - info.minY + 1
|
|
141
|
-
},
|
|
142
|
-
pixelCount: info.count,
|
|
143
|
-
intensity: info.intensitySum / info.count
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
return regions;
|
|
147
|
-
}
|
|
243
|
+
});
|
|
148
244
|
|
|
149
245
|
// src/addons/motion-detection/index.ts
|
|
246
|
+
var motion_detection_exports = {};
|
|
247
|
+
__export(motion_detection_exports, {
|
|
248
|
+
default: () => MotionDetectionAddon
|
|
249
|
+
});
|
|
250
|
+
module.exports = __toCommonJS(motion_detection_exports);
|
|
251
|
+
var import_image_utils = __toESM(require_image_utils());
|
|
252
|
+
var import_frame_diff = __toESM(require_frame_diff());
|
|
150
253
|
var MOTION_LABEL = { id: "motion", name: "Motion" };
|
|
151
254
|
var MOTION_LABELS = [MOTION_LABEL];
|
|
152
255
|
var EMPTY_CLASS_MAP = { mapping: {}, preserveOriginal: true };
|
|
@@ -189,15 +292,15 @@ var MotionDetectionAddon = class {
|
|
|
189
292
|
}
|
|
190
293
|
async detect(frame) {
|
|
191
294
|
const start = Date.now();
|
|
192
|
-
const { data, width, height } = await jpegToRgb(frame.data);
|
|
193
|
-
const currentGray = rgbToGrayscale(data, width, height);
|
|
295
|
+
const { data, width, height } = await (0, import_image_utils.jpegToRgb)(frame.data);
|
|
296
|
+
const currentGray = (0, import_image_utils.rgbToGrayscale)(data, width, height);
|
|
194
297
|
if (!this.previousGray || this.previousWidth !== width || this.previousHeight !== height) {
|
|
195
298
|
this.previousGray = currentGray;
|
|
196
299
|
this.previousWidth = width;
|
|
197
300
|
this.previousHeight = height;
|
|
198
301
|
return { detections: [], inferenceMs: Date.now() - start, modelId: "frame-diff" };
|
|
199
302
|
}
|
|
200
|
-
const regions = detectMotion(
|
|
303
|
+
const regions = (0, import_frame_diff.detectMotion)(
|
|
201
304
|
currentGray,
|
|
202
305
|
this.previousGray,
|
|
203
306
|
width,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/addons/motion-detection/index.ts","../../../src/shared/image-utils.ts","../../../src/addons/motion-detection/frame-diff.ts"],"sourcesContent":["import type {\n IDetectorProvider,\n IDetectionAddon,\n AddonManifest,\n AddonContext,\n FrameInput,\n DetectorOutput,\n ConfigUISchema,\n ClassMapDefinition,\n ProbeResult,\n ModelCatalogEntry,\n DetectionModel,\n LabelDefinition,\n SpatialDetection,\n ModelRequirement,\n} from '@camstack/types'\nimport { jpegToRgb, rgbToGrayscale } from '../../shared/image-utils.js'\nimport { detectMotion } from './frame-diff.js'\n\nconst MOTION_LABEL: LabelDefinition = { id: 'motion', name: 'Motion' }\nconst MOTION_LABELS: readonly LabelDefinition[] = [MOTION_LABEL]\nconst EMPTY_CLASS_MAP: ClassMapDefinition = { mapping: {}, preserveOriginal: true }\n\nexport default class MotionDetectionAddon implements IDetectorProvider, IDetectionAddon {\n readonly id = 'motion-detection'\n readonly slot = 'detector' as const\n readonly inputClasses: readonly string[] | null = null\n readonly outputClasses = ['motion'] as const\n readonly slotPriority = 10 // runs first — feeds other detectors\n\n /** Motion detection has no ML models — returns empty requirements */\n getModelRequirements(): ModelRequirement[] {\n return []\n }\n\n readonly manifest: AddonManifest = {\n id: 'motion-detection',\n name: 'Motion Detection',\n version: '0.1.0',\n\n description: 'Frame-differencing motion detector — no inference engine required',\n\n slot: 'detector',\n inputClasses: undefined,\n outputClasses: ['motion'],\n supportsCustomModels: false,\n mayRequirePython: false,\n passive: true,\n defaultConfig: {\n threshold: 25,\n minArea: 500,\n },\n }\n\n private previousGray: Uint8Array | null = null\n private previousWidth = 0\n private previousHeight = 0\n private threshold = 25\n private minArea = 500\n\n async initialize(ctx: AddonContext): Promise<void> {\n const cfg = ctx.addonConfig\n this.threshold = (cfg['threshold'] as number | undefined) ?? 25\n this.minArea = (cfg['minArea'] as number | undefined) ?? 500\n }\n\n async detect(frame: FrameInput): Promise<DetectorOutput> {\n const start = Date.now()\n\n const { data, width, height } = await jpegToRgb(frame.data)\n const currentGray = rgbToGrayscale(data, width, height)\n\n if (!this.previousGray || this.previousWidth !== width || this.previousHeight !== height) {\n // Store first frame and return empty — no previous to diff against\n this.previousGray = currentGray\n this.previousWidth = width\n this.previousHeight = height\n return { detections: [], inferenceMs: Date.now() - start, modelId: 'frame-diff' }\n }\n\n const regions = detectMotion(\n currentGray,\n this.previousGray,\n width,\n height,\n this.threshold,\n this.minArea,\n )\n\n this.previousGray = currentGray\n\n const detections: SpatialDetection[] = regions.map((r) => ({\n class: 'motion',\n originalClass: 'motion',\n score: Math.min(1, r.intensity / 255),\n bbox: r.bbox,\n }))\n\n return {\n detections,\n inferenceMs: Date.now() - start,\n modelId: 'frame-diff',\n }\n }\n\n async shutdown(): Promise<void> {\n this.previousGray = null\n }\n\n getConfigSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'motion',\n title: 'Motion Detection',\n columns: 2,\n fields: [\n {\n key: 'threshold',\n label: 'Pixel Difference Threshold',\n description: 'Minimum per-pixel intensity change to count as motion (0-255)',\n type: 'slider',\n min: 5,\n max: 100,\n step: 5,\n default: 25,\n },\n {\n key: 'minArea',\n label: 'Minimum Region Area (px)',\n description: 'Minimum number of changed pixels to report a motion region',\n type: 'slider',\n min: 50,\n max: 10000,\n step: 50,\n default: 500,\n showValue: true,\n unit: 'px',\n },\n ],\n },\n ],\n }\n }\n\n getClassMap(): ClassMapDefinition {\n return EMPTY_CLASS_MAP\n }\n\n getModelCatalog(): ModelCatalogEntry[] {\n return []\n }\n\n getAvailableModels(): DetectionModel[] {\n return []\n }\n\n getActiveLabels(): readonly LabelDefinition[] {\n return MOTION_LABELS\n }\n\n async probe(): Promise<ProbeResult> {\n return {\n available: true,\n runtime: 'onnx', // no inference; satisfies the type (any runtime works)\n device: 'cpu',\n capabilities: ['fp32'],\n }\n }\n}\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 { BoundingBox } from '@camstack/types'\n\nexport interface MotionRegion {\n readonly bbox: BoundingBox\n readonly pixelCount: number\n readonly intensity: number\n}\n\n/**\n * Detect motion by frame differencing.\n *\n * @param current - Grayscale pixel array for the current frame (Uint8Array, length = width * height)\n * @param previous - Grayscale pixel array for the previous frame\n * @param width - Frame width in pixels\n * @param height - Frame height in pixels\n * @param threshold - Pixel diff threshold 0-255; differences below this are ignored\n * @param minArea - Minimum number of changed pixels for a region to be reported\n */\nexport function detectMotion(\n current: Uint8Array,\n previous: Uint8Array,\n width: number,\n height: number,\n threshold: number,\n minArea: number,\n): MotionRegion[] {\n const numPixels = width * height\n\n // Step 1: Compute binary mask — 1 where abs(current - previous) > threshold\n const mask = new Uint8Array(numPixels)\n for (let i = 0; i < numPixels; i++) {\n mask[i] = Math.abs((current[i] ?? 0) - (previous[i] ?? 0)) > threshold ? 1 : 0\n }\n\n // Step 2: Two-pass connected component labeling\n const labels = new Int32Array(numPixels).fill(0)\n const parent = new Int32Array(numPixels + 1).fill(0)\n let nextLabel = 1\n\n // Union-Find helpers\n function findRoot(x: number): number {\n while (parent[x] !== x) {\n parent[x] = parent[parent[x]!]! // path compression\n x = parent[x]!\n }\n return x\n }\n\n function union(a: number, b: number): number {\n const ra = findRoot(a)\n const rb = findRoot(b)\n if (ra !== rb) parent[rb] = ra\n return ra\n }\n\n // Initialize parent array as identity\n for (let i = 0; i <= numPixels; i++) {\n parent[i] = i\n }\n\n // First pass: assign provisional labels\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const idx = y * width + x\n if (!mask[idx]) continue\n\n const above = y > 0 ? labels[(y - 1) * width + x] ?? 0 : 0\n const left = x > 0 ? labels[y * width + (x - 1)] ?? 0 : 0\n\n if (above === 0 && left === 0) {\n labels[idx] = nextLabel\n parent[nextLabel] = nextLabel\n nextLabel++\n } else if (above !== 0 && left === 0) {\n labels[idx] = above\n } else if (above === 0 && left !== 0) {\n labels[idx] = left\n } else {\n // Both neighbors — merge\n labels[idx] = union(above, left)\n }\n }\n }\n\n // Second pass: resolve all labels to roots\n for (let i = 0; i < numPixels; i++) {\n if (labels[i]) {\n labels[i] = findRoot(labels[i]!)\n }\n }\n\n // Step 3: Collect bounding boxes and pixel counts per root label\n const bboxMap = new Map<\n number,\n { minX: number; minY: number; maxX: number; maxY: number; count: number; intensitySum: number }\n >()\n\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const idx = y * width + x\n const label = labels[idx]\n if (!label) continue\n\n const diff = Math.abs((current[idx] ?? 0) - (previous[idx] ?? 0))\n const existing = bboxMap.get(label)\n if (existing) {\n existing.minX = Math.min(existing.minX, x)\n existing.minY = Math.min(existing.minY, y)\n existing.maxX = Math.max(existing.maxX, x)\n existing.maxY = Math.max(existing.maxY, y)\n existing.count++\n existing.intensitySum += diff\n } else {\n bboxMap.set(label, {\n minX: x,\n minY: y,\n maxX: x,\n maxY: y,\n count: 1,\n intensitySum: diff,\n })\n }\n }\n }\n\n // Step 4: Filter by minimum area and build result\n const regions: MotionRegion[] = []\n for (const [, info] of bboxMap) {\n if (info.count < minArea) continue\n regions.push({\n bbox: {\n x: info.minX,\n y: info.minY,\n w: info.maxX - info.minX + 1,\n h: info.maxY - info.minY + 1,\n },\n pixelCount: info.count,\n intensity: info.intensitySum / info.count,\n })\n }\n\n return regions\n}\n"],"mappings":"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","names":["sharp"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/shared/image-utils.ts","../../../src/addons/motion-detection/frame-diff.ts","../../../src/addons/motion-detection/index.ts"],"sourcesContent":["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 { BoundingBox } from '@camstack/types'\n\nexport interface MotionRegion {\n readonly bbox: BoundingBox\n readonly pixelCount: number\n readonly intensity: number\n}\n\n/**\n * Detect motion by frame differencing.\n *\n * @param current - Grayscale pixel array for the current frame (Uint8Array, length = width * height)\n * @param previous - Grayscale pixel array for the previous frame\n * @param width - Frame width in pixels\n * @param height - Frame height in pixels\n * @param threshold - Pixel diff threshold 0-255; differences below this are ignored\n * @param minArea - Minimum number of changed pixels for a region to be reported\n */\nexport function detectMotion(\n current: Uint8Array,\n previous: Uint8Array,\n width: number,\n height: number,\n threshold: number,\n minArea: number,\n): MotionRegion[] {\n const numPixels = width * height\n\n // Step 1: Compute binary mask — 1 where abs(current - previous) > threshold\n const mask = new Uint8Array(numPixels)\n for (let i = 0; i < numPixels; i++) {\n mask[i] = Math.abs((current[i] ?? 0) - (previous[i] ?? 0)) > threshold ? 1 : 0\n }\n\n // Step 2: Two-pass connected component labeling\n const labels = new Int32Array(numPixels).fill(0)\n const parent = new Int32Array(numPixels + 1).fill(0)\n let nextLabel = 1\n\n // Union-Find helpers\n function findRoot(x: number): number {\n while (parent[x] !== x) {\n parent[x] = parent[parent[x]!]! // path compression\n x = parent[x]!\n }\n return x\n }\n\n function union(a: number, b: number): number {\n const ra = findRoot(a)\n const rb = findRoot(b)\n if (ra !== rb) parent[rb] = ra\n return ra\n }\n\n // Initialize parent array as identity\n for (let i = 0; i <= numPixels; i++) {\n parent[i] = i\n }\n\n // First pass: assign provisional labels\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const idx = y * width + x\n if (!mask[idx]) continue\n\n const above = y > 0 ? labels[(y - 1) * width + x] ?? 0 : 0\n const left = x > 0 ? labels[y * width + (x - 1)] ?? 0 : 0\n\n if (above === 0 && left === 0) {\n labels[idx] = nextLabel\n parent[nextLabel] = nextLabel\n nextLabel++\n } else if (above !== 0 && left === 0) {\n labels[idx] = above\n } else if (above === 0 && left !== 0) {\n labels[idx] = left\n } else {\n // Both neighbors — merge\n labels[idx] = union(above, left)\n }\n }\n }\n\n // Second pass: resolve all labels to roots\n for (let i = 0; i < numPixels; i++) {\n if (labels[i]) {\n labels[i] = findRoot(labels[i]!)\n }\n }\n\n // Step 3: Collect bounding boxes and pixel counts per root label\n const bboxMap = new Map<\n number,\n { minX: number; minY: number; maxX: number; maxY: number; count: number; intensitySum: number }\n >()\n\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const idx = y * width + x\n const label = labels[idx]\n if (!label) continue\n\n const diff = Math.abs((current[idx] ?? 0) - (previous[idx] ?? 0))\n const existing = bboxMap.get(label)\n if (existing) {\n existing.minX = Math.min(existing.minX, x)\n existing.minY = Math.min(existing.minY, y)\n existing.maxX = Math.max(existing.maxX, x)\n existing.maxY = Math.max(existing.maxY, y)\n existing.count++\n existing.intensitySum += diff\n } else {\n bboxMap.set(label, {\n minX: x,\n minY: y,\n maxX: x,\n maxY: y,\n count: 1,\n intensitySum: diff,\n })\n }\n }\n }\n\n // Step 4: Filter by minimum area and build result\n const regions: MotionRegion[] = []\n for (const [, info] of bboxMap) {\n if (info.count < minArea) continue\n regions.push({\n bbox: {\n x: info.minX,\n y: info.minY,\n w: info.maxX - info.minX + 1,\n h: info.maxY - info.minY + 1,\n },\n pixelCount: info.count,\n intensity: info.intensitySum / info.count,\n })\n }\n\n return regions\n}\n","import type {\n IDetectorProvider,\n IDetectionAddon,\n AddonManifest,\n AddonContext,\n FrameInput,\n DetectorOutput,\n ConfigUISchema,\n ClassMapDefinition,\n ProbeResult,\n ModelCatalogEntry,\n DetectionModel,\n LabelDefinition,\n SpatialDetection,\n ModelRequirement,\n} from '@camstack/types'\nimport { jpegToRgb, rgbToGrayscale } from '../../shared/image-utils.js'\nimport { detectMotion } from './frame-diff.js'\n\nconst MOTION_LABEL: LabelDefinition = { id: 'motion', name: 'Motion' }\nconst MOTION_LABELS: readonly LabelDefinition[] = [MOTION_LABEL]\nconst EMPTY_CLASS_MAP: ClassMapDefinition = { mapping: {}, preserveOriginal: true }\n\nexport default class MotionDetectionAddon implements IDetectorProvider, IDetectionAddon {\n readonly id = 'motion-detection'\n readonly slot = 'detector' as const\n readonly inputClasses: readonly string[] | null = null\n readonly outputClasses = ['motion'] as const\n readonly slotPriority = 10 // runs first — feeds other detectors\n\n /** Motion detection has no ML models — returns empty requirements */\n getModelRequirements(): ModelRequirement[] {\n return []\n }\n\n readonly manifest: AddonManifest = {\n id: 'motion-detection',\n name: 'Motion Detection',\n version: '0.1.0',\n\n description: 'Frame-differencing motion detector — no inference engine required',\n\n slot: 'detector',\n inputClasses: undefined,\n outputClasses: ['motion'],\n supportsCustomModels: false,\n mayRequirePython: false,\n passive: true,\n defaultConfig: {\n threshold: 25,\n minArea: 500,\n },\n }\n\n private previousGray: Uint8Array | null = null\n private previousWidth = 0\n private previousHeight = 0\n private threshold = 25\n private minArea = 500\n\n async initialize(ctx: AddonContext): Promise<void> {\n const cfg = ctx.addonConfig\n this.threshold = (cfg['threshold'] as number | undefined) ?? 25\n this.minArea = (cfg['minArea'] as number | undefined) ?? 500\n }\n\n async detect(frame: FrameInput): Promise<DetectorOutput> {\n const start = Date.now()\n\n const { data, width, height } = await jpegToRgb(frame.data)\n const currentGray = rgbToGrayscale(data, width, height)\n\n if (!this.previousGray || this.previousWidth !== width || this.previousHeight !== height) {\n // Store first frame and return empty — no previous to diff against\n this.previousGray = currentGray\n this.previousWidth = width\n this.previousHeight = height\n return { detections: [], inferenceMs: Date.now() - start, modelId: 'frame-diff' }\n }\n\n const regions = detectMotion(\n currentGray,\n this.previousGray,\n width,\n height,\n this.threshold,\n this.minArea,\n )\n\n this.previousGray = currentGray\n\n const detections: SpatialDetection[] = regions.map((r) => ({\n class: 'motion',\n originalClass: 'motion',\n score: Math.min(1, r.intensity / 255),\n bbox: r.bbox,\n }))\n\n return {\n detections,\n inferenceMs: Date.now() - start,\n modelId: 'frame-diff',\n }\n }\n\n async shutdown(): Promise<void> {\n this.previousGray = null\n }\n\n getConfigSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'motion',\n title: 'Motion Detection',\n columns: 2,\n fields: [\n {\n key: 'threshold',\n label: 'Pixel Difference Threshold',\n description: 'Minimum per-pixel intensity change to count as motion (0-255)',\n type: 'slider',\n min: 5,\n max: 100,\n step: 5,\n default: 25,\n },\n {\n key: 'minArea',\n label: 'Minimum Region Area (px)',\n description: 'Minimum number of changed pixels to report a motion region',\n type: 'slider',\n min: 50,\n max: 10000,\n step: 50,\n default: 500,\n showValue: true,\n unit: 'px',\n },\n ],\n },\n ],\n }\n }\n\n getClassMap(): ClassMapDefinition {\n return EMPTY_CLASS_MAP\n }\n\n getModelCatalog(): ModelCatalogEntry[] {\n return []\n }\n\n getAvailableModels(): DetectionModel[] {\n return []\n }\n\n getActiveLabels(): readonly LabelDefinition[] {\n return MOTION_LABELS\n }\n\n async probe(): Promise<ProbeResult> {\n return {\n available: true,\n runtime: 'onnx', // no inference; satisfies the type (any runtime works)\n device: 'cpu',\n capabilities: ['fp32'],\n }\n }\n}\n"],"mappings":"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","names":["exports","jpegToRgb","rgbToGrayscale","exports","detectMotion"]}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "../../chunk-
|
|
2
|
+
require_frame_diff
|
|
3
|
+
} from "../../chunk-YYDM6V2F.mjs";
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
require_image_utils
|
|
6
|
+
} from "../../chunk-WG66JYYW.mjs";
|
|
7
|
+
import {
|
|
8
|
+
__toESM
|
|
9
|
+
} from "../../chunk-3IIFBJCD.mjs";
|
|
9
10
|
|
|
10
11
|
// src/addons/motion-detection/index.ts
|
|
12
|
+
var import_image_utils = __toESM(require_image_utils());
|
|
13
|
+
var import_frame_diff = __toESM(require_frame_diff());
|
|
11
14
|
var MOTION_LABEL = { id: "motion", name: "Motion" };
|
|
12
15
|
var MOTION_LABELS = [MOTION_LABEL];
|
|
13
16
|
var EMPTY_CLASS_MAP = { mapping: {}, preserveOriginal: true };
|
|
@@ -50,15 +53,15 @@ var MotionDetectionAddon = class {
|
|
|
50
53
|
}
|
|
51
54
|
async detect(frame) {
|
|
52
55
|
const start = Date.now();
|
|
53
|
-
const { data, width, height } = await jpegToRgb(frame.data);
|
|
54
|
-
const currentGray = rgbToGrayscale(data, width, height);
|
|
56
|
+
const { data, width, height } = await (0, import_image_utils.jpegToRgb)(frame.data);
|
|
57
|
+
const currentGray = (0, import_image_utils.rgbToGrayscale)(data, width, height);
|
|
55
58
|
if (!this.previousGray || this.previousWidth !== width || this.previousHeight !== height) {
|
|
56
59
|
this.previousGray = currentGray;
|
|
57
60
|
this.previousWidth = width;
|
|
58
61
|
this.previousHeight = height;
|
|
59
62
|
return { detections: [], inferenceMs: Date.now() - start, modelId: "frame-diff" };
|
|
60
63
|
}
|
|
61
|
-
const regions = detectMotion(
|
|
64
|
+
const regions = (0, import_frame_diff.detectMotion)(
|
|
62
65
|
currentGray,
|
|
63
66
|
this.previousGray,
|
|
64
67
|
width,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/addons/motion-detection/index.ts"],"sourcesContent":["import type {\n IDetectorProvider,\n IDetectionAddon,\n AddonManifest,\n AddonContext,\n FrameInput,\n DetectorOutput,\n ConfigUISchema,\n ClassMapDefinition,\n ProbeResult,\n ModelCatalogEntry,\n DetectionModel,\n LabelDefinition,\n SpatialDetection,\n ModelRequirement,\n} from '@camstack/types'\nimport { jpegToRgb, rgbToGrayscale } from '../../shared/image-utils.js'\nimport { detectMotion } from './frame-diff.js'\n\nconst MOTION_LABEL: LabelDefinition = { id: 'motion', name: 'Motion' }\nconst MOTION_LABELS: readonly LabelDefinition[] = [MOTION_LABEL]\nconst EMPTY_CLASS_MAP: ClassMapDefinition = { mapping: {}, preserveOriginal: true }\n\nexport default class MotionDetectionAddon implements IDetectorProvider, IDetectionAddon {\n readonly id = 'motion-detection'\n readonly slot = 'detector' as const\n readonly inputClasses: readonly string[] | null = null\n readonly outputClasses = ['motion'] as const\n readonly slotPriority = 10 // runs first — feeds other detectors\n\n /** Motion detection has no ML models — returns empty requirements */\n getModelRequirements(): ModelRequirement[] {\n return []\n }\n\n readonly manifest: AddonManifest = {\n id: 'motion-detection',\n name: 'Motion Detection',\n version: '0.1.0',\n\n description: 'Frame-differencing motion detector — no inference engine required',\n\n slot: 'detector',\n inputClasses: undefined,\n outputClasses: ['motion'],\n supportsCustomModels: false,\n mayRequirePython: false,\n passive: true,\n defaultConfig: {\n threshold: 25,\n minArea: 500,\n },\n }\n\n private previousGray: Uint8Array | null = null\n private previousWidth = 0\n private previousHeight = 0\n private threshold = 25\n private minArea = 500\n\n async initialize(ctx: AddonContext): Promise<void> {\n const cfg = ctx.addonConfig\n this.threshold = (cfg['threshold'] as number | undefined) ?? 25\n this.minArea = (cfg['minArea'] as number | undefined) ?? 500\n }\n\n async detect(frame: FrameInput): Promise<DetectorOutput> {\n const start = Date.now()\n\n const { data, width, height } = await jpegToRgb(frame.data)\n const currentGray = rgbToGrayscale(data, width, height)\n\n if (!this.previousGray || this.previousWidth !== width || this.previousHeight !== height) {\n // Store first frame and return empty — no previous to diff against\n this.previousGray = currentGray\n this.previousWidth = width\n this.previousHeight = height\n return { detections: [], inferenceMs: Date.now() - start, modelId: 'frame-diff' }\n }\n\n const regions = detectMotion(\n currentGray,\n this.previousGray,\n width,\n height,\n this.threshold,\n this.minArea,\n )\n\n this.previousGray = currentGray\n\n const detections: SpatialDetection[] = regions.map((r) => ({\n class: 'motion',\n originalClass: 'motion',\n score: Math.min(1, r.intensity / 255),\n bbox: r.bbox,\n }))\n\n return {\n detections,\n inferenceMs: Date.now() - start,\n modelId: 'frame-diff',\n }\n }\n\n async shutdown(): Promise<void> {\n this.previousGray = null\n }\n\n getConfigSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'motion',\n title: 'Motion Detection',\n columns: 2,\n fields: [\n {\n key: 'threshold',\n label: 'Pixel Difference Threshold',\n description: 'Minimum per-pixel intensity change to count as motion (0-255)',\n type: 'slider',\n min: 5,\n max: 100,\n step: 5,\n default: 25,\n },\n {\n key: 'minArea',\n label: 'Minimum Region Area (px)',\n description: 'Minimum number of changed pixels to report a motion region',\n type: 'slider',\n min: 50,\n max: 10000,\n step: 50,\n default: 500,\n showValue: true,\n unit: 'px',\n },\n ],\n },\n ],\n }\n }\n\n getClassMap(): ClassMapDefinition {\n return EMPTY_CLASS_MAP\n }\n\n getModelCatalog(): ModelCatalogEntry[] {\n return []\n }\n\n getAvailableModels(): DetectionModel[] {\n return []\n }\n\n getActiveLabels(): readonly LabelDefinition[] {\n return MOTION_LABELS\n }\n\n async probe(): Promise<ProbeResult> {\n return {\n available: true,\n runtime: 'onnx', // no inference; satisfies the type (any runtime works)\n device: 'cpu',\n capabilities: ['fp32'],\n }\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../src/addons/motion-detection/index.ts"],"sourcesContent":["import type {\n IDetectorProvider,\n IDetectionAddon,\n AddonManifest,\n AddonContext,\n FrameInput,\n DetectorOutput,\n ConfigUISchema,\n ClassMapDefinition,\n ProbeResult,\n ModelCatalogEntry,\n DetectionModel,\n LabelDefinition,\n SpatialDetection,\n ModelRequirement,\n} from '@camstack/types'\nimport { jpegToRgb, rgbToGrayscale } from '../../shared/image-utils.js'\nimport { detectMotion } from './frame-diff.js'\n\nconst MOTION_LABEL: LabelDefinition = { id: 'motion', name: 'Motion' }\nconst MOTION_LABELS: readonly LabelDefinition[] = [MOTION_LABEL]\nconst EMPTY_CLASS_MAP: ClassMapDefinition = { mapping: {}, preserveOriginal: true }\n\nexport default class MotionDetectionAddon implements IDetectorProvider, IDetectionAddon {\n readonly id = 'motion-detection'\n readonly slot = 'detector' as const\n readonly inputClasses: readonly string[] | null = null\n readonly outputClasses = ['motion'] as const\n readonly slotPriority = 10 // runs first — feeds other detectors\n\n /** Motion detection has no ML models — returns empty requirements */\n getModelRequirements(): ModelRequirement[] {\n return []\n }\n\n readonly manifest: AddonManifest = {\n id: 'motion-detection',\n name: 'Motion Detection',\n version: '0.1.0',\n\n description: 'Frame-differencing motion detector — no inference engine required',\n\n slot: 'detector',\n inputClasses: undefined,\n outputClasses: ['motion'],\n supportsCustomModels: false,\n mayRequirePython: false,\n passive: true,\n defaultConfig: {\n threshold: 25,\n minArea: 500,\n },\n }\n\n private previousGray: Uint8Array | null = null\n private previousWidth = 0\n private previousHeight = 0\n private threshold = 25\n private minArea = 500\n\n async initialize(ctx: AddonContext): Promise<void> {\n const cfg = ctx.addonConfig\n this.threshold = (cfg['threshold'] as number | undefined) ?? 25\n this.minArea = (cfg['minArea'] as number | undefined) ?? 500\n }\n\n async detect(frame: FrameInput): Promise<DetectorOutput> {\n const start = Date.now()\n\n const { data, width, height } = await jpegToRgb(frame.data)\n const currentGray = rgbToGrayscale(data, width, height)\n\n if (!this.previousGray || this.previousWidth !== width || this.previousHeight !== height) {\n // Store first frame and return empty — no previous to diff against\n this.previousGray = currentGray\n this.previousWidth = width\n this.previousHeight = height\n return { detections: [], inferenceMs: Date.now() - start, modelId: 'frame-diff' }\n }\n\n const regions = detectMotion(\n currentGray,\n this.previousGray,\n width,\n height,\n this.threshold,\n this.minArea,\n )\n\n this.previousGray = currentGray\n\n const detections: SpatialDetection[] = regions.map((r) => ({\n class: 'motion',\n originalClass: 'motion',\n score: Math.min(1, r.intensity / 255),\n bbox: r.bbox,\n }))\n\n return {\n detections,\n inferenceMs: Date.now() - start,\n modelId: 'frame-diff',\n }\n }\n\n async shutdown(): Promise<void> {\n this.previousGray = null\n }\n\n getConfigSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'motion',\n title: 'Motion Detection',\n columns: 2,\n fields: [\n {\n key: 'threshold',\n label: 'Pixel Difference Threshold',\n description: 'Minimum per-pixel intensity change to count as motion (0-255)',\n type: 'slider',\n min: 5,\n max: 100,\n step: 5,\n default: 25,\n },\n {\n key: 'minArea',\n label: 'Minimum Region Area (px)',\n description: 'Minimum number of changed pixels to report a motion region',\n type: 'slider',\n min: 50,\n max: 10000,\n step: 50,\n default: 500,\n showValue: true,\n unit: 'px',\n },\n ],\n },\n ],\n }\n }\n\n getClassMap(): ClassMapDefinition {\n return EMPTY_CLASS_MAP\n }\n\n getModelCatalog(): ModelCatalogEntry[] {\n return []\n }\n\n getAvailableModels(): DetectionModel[] {\n return []\n }\n\n getActiveLabels(): readonly LabelDefinition[] {\n return MOTION_LABELS\n }\n\n async probe(): Promise<ProbeResult> {\n return {\n available: true,\n runtime: 'onnx', // no inference; satisfies the type (any runtime works)\n device: 'cpu',\n capabilities: ['fp32'],\n }\n }\n}\n"],"mappings":"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","names":[]}
|