@camstack/addon-vision 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/dist/addons/animal-classifier/index.js +999 -822
  2. package/dist/addons/animal-classifier/index.js.map +1 -1
  3. package/dist/addons/animal-classifier/index.mjs +242 -7
  4. package/dist/addons/animal-classifier/index.mjs.map +1 -1
  5. package/dist/addons/audio-classification/index.js +501 -378
  6. package/dist/addons/audio-classification/index.js.map +1 -1
  7. package/dist/addons/audio-classification/index.mjs +224 -4
  8. package/dist/addons/audio-classification/index.mjs.map +1 -1
  9. package/dist/addons/bird-global-classifier/index.js +1002 -825
  10. package/dist/addons/bird-global-classifier/index.js.map +1 -1
  11. package/dist/addons/bird-global-classifier/index.mjs +248 -7
  12. package/dist/addons/bird-global-classifier/index.mjs.map +1 -1
  13. package/dist/addons/bird-nabirds-classifier/index.js +1002 -825
  14. package/dist/addons/bird-nabirds-classifier/index.js.map +1 -1
  15. package/dist/addons/bird-nabirds-classifier/index.mjs +289 -7
  16. package/dist/addons/bird-nabirds-classifier/index.mjs.map +1 -1
  17. package/dist/addons/face-detection/index.js +1196 -934
  18. package/dist/addons/face-detection/index.js.map +1 -1
  19. package/dist/addons/face-detection/index.mjs +227 -7
  20. package/dist/addons/face-detection/index.mjs.map +1 -1
  21. package/dist/addons/face-recognition/index.js +1003 -807
  22. package/dist/addons/face-recognition/index.js.map +1 -1
  23. package/dist/addons/face-recognition/index.mjs +197 -6
  24. package/dist/addons/face-recognition/index.mjs.map +1 -1
  25. package/dist/addons/motion-detection/index.js +214 -111
  26. package/dist/addons/motion-detection/index.js.map +1 -1
  27. package/dist/addons/motion-detection/index.mjs +12 -9
  28. package/dist/addons/motion-detection/index.mjs.map +1 -1
  29. package/dist/addons/object-detection/index.js +1287 -1082
  30. package/dist/addons/object-detection/index.js.map +1 -1
  31. package/dist/addons/object-detection/index.mjs +373 -7
  32. package/dist/addons/object-detection/index.mjs.map +1 -1
  33. package/dist/addons/plate-detection/index.js +1075 -868
  34. package/dist/addons/plate-detection/index.js.map +1 -1
  35. package/dist/addons/plate-detection/index.mjs +230 -7
  36. package/dist/addons/plate-detection/index.mjs.map +1 -1
  37. package/dist/addons/plate-recognition/index.js +684 -505
  38. package/dist/addons/plate-recognition/index.js.map +1 -1
  39. package/dist/addons/plate-recognition/index.mjs +244 -5
  40. package/dist/addons/plate-recognition/index.mjs.map +1 -1
  41. package/dist/addons/segmentation-refiner/index.js +967 -790
  42. package/dist/addons/segmentation-refiner/index.js.map +1 -1
  43. package/dist/addons/segmentation-refiner/index.mjs +21 -17
  44. package/dist/addons/segmentation-refiner/index.mjs.map +1 -1
  45. package/dist/addons/vehicle-classifier/index.js +581 -410
  46. package/dist/addons/vehicle-classifier/index.js.map +1 -1
  47. package/dist/addons/vehicle-classifier/index.mjs +20 -16
  48. package/dist/addons/vehicle-classifier/index.mjs.map +1 -1
  49. package/dist/chunk-2YMA6QOV.mjs +193 -0
  50. package/dist/chunk-2YMA6QOV.mjs.map +1 -0
  51. package/dist/chunk-3IIFBJCD.mjs +45 -0
  52. package/dist/chunk-BS4DKYGN.mjs +48 -0
  53. package/dist/{chunk-7DYHXUPZ.mjs.map → chunk-BS4DKYGN.mjs.map} +1 -1
  54. package/dist/chunk-DE7I3VHO.mjs +106 -0
  55. package/dist/{chunk-KUO2BVFY.mjs.map → chunk-DE7I3VHO.mjs.map} +1 -1
  56. package/dist/chunk-F6D2OZ36.mjs +89 -0
  57. package/dist/chunk-F6D2OZ36.mjs.map +1 -0
  58. package/dist/chunk-GAOIFQDX.mjs +59 -0
  59. package/dist/chunk-GAOIFQDX.mjs.map +1 -0
  60. package/dist/chunk-HUIX2XVR.mjs +159 -0
  61. package/dist/chunk-HUIX2XVR.mjs.map +1 -0
  62. package/dist/chunk-K36R6HWY.mjs +51 -0
  63. package/dist/{chunk-XZ6ZMXXU.mjs.map → chunk-K36R6HWY.mjs.map} +1 -1
  64. package/dist/chunk-MBTAI3WE.mjs +78 -0
  65. package/dist/chunk-MBTAI3WE.mjs.map +1 -0
  66. package/dist/chunk-MGT6RUVX.mjs +423 -0
  67. package/dist/{chunk-BP7H4NFS.mjs.map → chunk-MGT6RUVX.mjs.map} +1 -1
  68. package/dist/chunk-PIFS7AIT.mjs +446 -0
  69. package/dist/{chunk-2IOKI4ES.mjs.map → chunk-PIFS7AIT.mjs.map} +1 -1
  70. package/dist/chunk-WG66JYYW.mjs +116 -0
  71. package/dist/{chunk-22BHCDT5.mjs.map → chunk-WG66JYYW.mjs.map} +1 -1
  72. package/dist/chunk-XD7WGXHZ.mjs +82 -0
  73. package/dist/{chunk-DUN6XU3N.mjs.map → chunk-XD7WGXHZ.mjs.map} +1 -1
  74. package/dist/chunk-YYDM6V2F.mjs +113 -0
  75. package/dist/{chunk-BR2FPGOX.mjs.map → chunk-YYDM6V2F.mjs.map} +1 -1
  76. package/dist/chunk-ZK7P3TZN.mjs +286 -0
  77. package/dist/chunk-ZK7P3TZN.mjs.map +1 -0
  78. package/dist/index.js +4443 -3924
  79. package/dist/index.js.map +1 -1
  80. package/dist/index.mjs +2698 -250
  81. package/dist/index.mjs.map +1 -1
  82. package/package.json +3 -2
  83. package/dist/chunk-22BHCDT5.mjs +0 -101
  84. package/dist/chunk-2IOKI4ES.mjs +0 -335
  85. package/dist/chunk-7DYHXUPZ.mjs +0 -36
  86. package/dist/chunk-BJTO5JO5.mjs +0 -11
  87. package/dist/chunk-BP7H4NFS.mjs +0 -412
  88. package/dist/chunk-BR2FPGOX.mjs +0 -98
  89. package/dist/chunk-D6WEHN33.mjs +0 -276
  90. package/dist/chunk-D6WEHN33.mjs.map +0 -1
  91. package/dist/chunk-DRYFGARD.mjs +0 -289
  92. package/dist/chunk-DRYFGARD.mjs.map +0 -1
  93. package/dist/chunk-DUN6XU3N.mjs +0 -72
  94. package/dist/chunk-ESLHNWWE.mjs +0 -387
  95. package/dist/chunk-ESLHNWWE.mjs.map +0 -1
  96. package/dist/chunk-JUQEW6ON.mjs +0 -256
  97. package/dist/chunk-JUQEW6ON.mjs.map +0 -1
  98. package/dist/chunk-KUO2BVFY.mjs +0 -90
  99. package/dist/chunk-R5J3WAUI.mjs +0 -645
  100. package/dist/chunk-R5J3WAUI.mjs.map +0 -1
  101. package/dist/chunk-XZ6ZMXXU.mjs +0 -39
  102. package/dist/chunk-YPU4WTXZ.mjs +0 -269
  103. package/dist/chunk-YPU4WTXZ.mjs.map +0 -1
  104. package/dist/chunk-YUCD2TFH.mjs +0 -242
  105. package/dist/chunk-YUCD2TFH.mjs.map +0 -1
  106. package/dist/chunk-ZTJENCFC.mjs +0 -379
  107. package/dist/chunk-ZTJENCFC.mjs.map +0 -1
  108. package/dist/chunk-ZWYXXCXP.mjs +0 -248
  109. package/dist/chunk-ZWYXXCXP.mjs.map +0 -1
  110. /package/dist/{chunk-BJTO5JO5.mjs.map → chunk-3IIFBJCD.mjs.map} +0 -0
@@ -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/addons/motion-detection/index.ts
31
- var motion_detection_exports = {};
32
- __export(motion_detection_exports, {
33
- default: () => MotionDetectionAddon
34
- });
35
- module.exports = __toCommonJS(motion_detection_exports);
36
-
37
- // src/shared/image-utils.ts
38
- var import_sharp = __toESM(require("sharp"));
39
- async function jpegToRgb(jpeg) {
40
- const { data, info } = await (0, import_sharp.default)(jpeg).removeAlpha().raw().toBuffer({ resolveWithObject: true });
41
- return { data, width: info.width, height: info.height };
42
- }
43
- function rgbToGrayscale(rgb, width, height) {
44
- const numPixels = width * height;
45
- const gray = new Uint8Array(numPixels);
46
- for (let i = 0; i < numPixels; i++) {
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
- return x;
71
- }
72
- function union(a, b) {
73
- const ra = findRoot(a);
74
- const rb = findRoot(b);
75
- if (ra !== rb) parent[rb] = ra;
76
- return ra;
77
- }
78
- for (let i = 0; i <= numPixels; i++) {
79
- parent[i] = i;
80
- }
81
- for (let y = 0; y < height; y++) {
82
- for (let x = 0; x < width; x++) {
83
- const idx = y * width + x;
84
- if (!mask[idx]) continue;
85
- const above = y > 0 ? labels[(y - 1) * width + x] ?? 0 : 0;
86
- const left = x > 0 ? labels[y * width + (x - 1)] ?? 0 : 0;
87
- if (above === 0 && left === 0) {
88
- labels[idx] = nextLabel;
89
- parent[nextLabel] = nextLabel;
90
- nextLabel++;
91
- } else if (above !== 0 && left === 0) {
92
- labels[idx] = above;
93
- } else if (above === 0 && left !== 0) {
94
- labels[idx] = left;
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
- labels[idx] = union(above, left);
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
- for (let i = 0; i < numPixels; i++) {
101
- if (labels[i]) {
102
- labels[i] = findRoot(labels[i]);
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
- const bboxMap = /* @__PURE__ */ new Map();
106
- for (let y = 0; y < height; y++) {
107
- for (let x = 0; x < width; x++) {
108
- const idx = y * width + x;
109
- const label = labels[idx];
110
- if (!label) continue;
111
- const diff = Math.abs((current[idx] ?? 0) - (previous[idx] ?? 0));
112
- const existing = bboxMap.get(label);
113
- if (existing) {
114
- existing.minX = Math.min(existing.minX, x);
115
- existing.minY = Math.min(existing.minY, y);
116
- existing.maxX = Math.max(existing.maxX, x);
117
- existing.maxY = Math.max(existing.maxY, y);
118
- existing.count++;
119
- existing.intensitySum += diff;
120
- } else {
121
- bboxMap.set(label, {
122
- minX: x,
123
- minY: y,
124
- maxX: x,
125
- maxY: y,
126
- count: 1,
127
- intensitySum: diff
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
- const regions = [];
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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAAkB;AAIlB,eAAsB,UACpB,MAC0D;AAC1D,QAAM,EAAE,MAAM,KAAK,IAAI,UAAM,aAAAA,SAAM,IAAI,EACpC,YAAY,EACZ,IAAI,EACJ,SAAS,EAAE,mBAAmB,KAAK,CAAC;AACvC,SAAO,EAAE,MAAM,OAAO,KAAK,OAAO,QAAQ,KAAK,OAAO;AACxD;AA4HO,SAAS,eAAe,KAAa,OAAe,QAA4B;AACrF,QAAM,YAAY,QAAQ;AAC1B,QAAM,OAAO,IAAI,WAAW,SAAS;AACrC,WAAS,IAAI,GAAG,IAAI,WAAW,KAAK;AAClC,UAAM,IAAI,IAAI,IAAI,CAAC;AACnB,UAAM,IAAI,IAAI,IAAI,IAAI,CAAC;AACvB,UAAM,IAAI,IAAI,IAAI,IAAI,CAAC;AAEvB,SAAK,CAAC,IAAI,KAAK,MAAM,QAAQ,IAAI,QAAQ,IAAI,QAAQ,CAAC;AAAA,EACxD;AACA,SAAO;AACT;;;ACjIO,SAAS,aACd,SACA,UACA,OACA,QACA,WACA,SACgB;AAChB,QAAM,YAAY,QAAQ;AAG1B,QAAM,OAAO,IAAI,WAAW,SAAS;AACrC,WAAS,IAAI,GAAG,IAAI,WAAW,KAAK;AAClC,SAAK,CAAC,IAAI,KAAK,KAAK,QAAQ,CAAC,KAAK,MAAM,SAAS,CAAC,KAAK,EAAE,IAAI,YAAY,IAAI;AAAA,EAC/E;AAGA,QAAM,SAAS,IAAI,WAAW,SAAS,EAAE,KAAK,CAAC;AAC/C,QAAM,SAAS,IAAI,WAAW,YAAY,CAAC,EAAE,KAAK,CAAC;AACnD,MAAI,YAAY;AAGhB,WAAS,SAAS,GAAmB;AACnC,WAAO,OAAO,CAAC,MAAM,GAAG;AACtB,aAAO,CAAC,IAAI,OAAO,OAAO,CAAC,CAAE;AAC7B,UAAI,OAAO,CAAC;AAAA,IACd;AACA,WAAO;AAAA,EACT;AAEA,WAAS,MAAM,GAAW,GAAmB;AAC3C,UAAM,KAAK,SAAS,CAAC;AACrB,UAAM,KAAK,SAAS,CAAC;AACrB,QAAI,OAAO,GAAI,QAAO,EAAE,IAAI;AAC5B,WAAO;AAAA,EACT;AAGA,WAAS,IAAI,GAAG,KAAK,WAAW,KAAK;AACnC,WAAO,CAAC,IAAI;AAAA,EACd;AAGA,WAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC/B,aAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC9B,YAAM,MAAM,IAAI,QAAQ;AACxB,UAAI,CAAC,KAAK,GAAG,EAAG;AAEhB,YAAM,QAAQ,IAAI,IAAI,QAAQ,IAAI,KAAK,QAAQ,CAAC,KAAK,IAAI;AACzD,YAAM,OAAO,IAAI,IAAI,OAAO,IAAI,SAAS,IAAI,EAAE,KAAK,IAAI;AAExD,UAAI,UAAU,KAAK,SAAS,GAAG;AAC7B,eAAO,GAAG,IAAI;AACd,eAAO,SAAS,IAAI;AACpB;AAAA,MACF,WAAW,UAAU,KAAK,SAAS,GAAG;AACpC,eAAO,GAAG,IAAI;AAAA,MAChB,WAAW,UAAU,KAAK,SAAS,GAAG;AACpC,eAAO,GAAG,IAAI;AAAA,MAChB,OAAO;AAEL,eAAO,GAAG,IAAI,MAAM,OAAO,IAAI;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AAGA,WAAS,IAAI,GAAG,IAAI,WAAW,KAAK;AAClC,QAAI,OAAO,CAAC,GAAG;AACb,aAAO,CAAC,IAAI,SAAS,OAAO,CAAC,CAAE;AAAA,IACjC;AAAA,EACF;AAGA,QAAM,UAAU,oBAAI,IAGlB;AAEF,WAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC/B,aAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC9B,YAAM,MAAM,IAAI,QAAQ;AACxB,YAAM,QAAQ,OAAO,GAAG;AACxB,UAAI,CAAC,MAAO;AAEZ,YAAM,OAAO,KAAK,KAAK,QAAQ,GAAG,KAAK,MAAM,SAAS,GAAG,KAAK,EAAE;AAChE,YAAM,WAAW,QAAQ,IAAI,KAAK;AAClC,UAAI,UAAU;AACZ,iBAAS,OAAO,KAAK,IAAI,SAAS,MAAM,CAAC;AACzC,iBAAS,OAAO,KAAK,IAAI,SAAS,MAAM,CAAC;AACzC,iBAAS,OAAO,KAAK,IAAI,SAAS,MAAM,CAAC;AACzC,iBAAS,OAAO,KAAK,IAAI,SAAS,MAAM,CAAC;AACzC,iBAAS;AACT,iBAAS,gBAAgB;AAAA,MAC3B,OAAO;AACL,gBAAQ,IAAI,OAAO;AAAA,UACjB,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO;AAAA,UACP,cAAc;AAAA,QAChB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAGA,QAAM,UAA0B,CAAC;AACjC,aAAW,CAAC,EAAE,IAAI,KAAK,SAAS;AAC9B,QAAI,KAAK,QAAQ,QAAS;AAC1B,YAAQ,KAAK;AAAA,MACX,MAAM;AAAA,QACJ,GAAG,KAAK;AAAA,QACR,GAAG,KAAK;AAAA,QACR,GAAG,KAAK,OAAO,KAAK,OAAO;AAAA,QAC3B,GAAG,KAAK,OAAO,KAAK,OAAO;AAAA,MAC7B;AAAA,MACA,YAAY,KAAK;AAAA,MACjB,WAAW,KAAK,eAAe,KAAK;AAAA,IACtC,CAAC;AAAA,EACH;AAEA,SAAO;AACT;;;AF3HA,IAAM,eAAgC,EAAE,IAAI,UAAU,MAAM,SAAS;AACrE,IAAM,gBAA4C,CAAC,YAAY;AAC/D,IAAM,kBAAsC,EAAE,SAAS,CAAC,GAAG,kBAAkB,KAAK;AAElF,IAAqB,uBAArB,MAAwF;AAAA,EAC7E,KAAK;AAAA,EACL,OAAO;AAAA,EACP,eAAyC;AAAA,EACzC,gBAAgB,CAAC,QAAQ;AAAA,EACzB,eAAe;AAAA;AAAA;AAAA,EAGxB,uBAA2C;AACzC,WAAO,CAAC;AAAA,EACV;AAAA,EAES,WAA0B;AAAA,IACjC,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,SAAS;AAAA,IAET,aAAa;AAAA,IAEb,MAAM;AAAA,IACN,cAAc;AAAA,IACd,eAAe,CAAC,QAAQ;AAAA,IACxB,sBAAsB;AAAA,IACtB,kBAAkB;AAAA,IAClB,SAAS;AAAA,IACT,eAAe;AAAA,MACb,WAAW;AAAA,MACX,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EAEQ,eAAkC;AAAA,EAClC,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,UAAU;AAAA,EAElB,MAAM,WAAW,KAAkC;AACjD,UAAM,MAAM,IAAI;AAChB,SAAK,YAAa,IAAI,WAAW,KAA4B;AAC7D,SAAK,UAAW,IAAI,SAAS,KAA4B;AAAA,EAC3D;AAAA,EAEA,MAAM,OAAO,OAA4C;AACvD,UAAM,QAAQ,KAAK,IAAI;AAEvB,UAAM,EAAE,MAAM,OAAO,OAAO,IAAI,MAAM,UAAU,MAAM,IAAI;AAC1D,UAAM,cAAc,eAAe,MAAM,OAAO,MAAM;AAEtD,QAAI,CAAC,KAAK,gBAAgB,KAAK,kBAAkB,SAAS,KAAK,mBAAmB,QAAQ;AAExF,WAAK,eAAe;AACpB,WAAK,gBAAgB;AACrB,WAAK,iBAAiB;AACtB,aAAO,EAAE,YAAY,CAAC,GAAG,aAAa,KAAK,IAAI,IAAI,OAAO,SAAS,aAAa;AAAA,IAClF;AAEA,UAAM,UAAU;AAAA,MACd;AAAA,MACA,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA,KAAK;AAAA,MACL,KAAK;AAAA,IACP;AAEA,SAAK,eAAe;AAEpB,UAAM,aAAiC,QAAQ,IAAI,CAAC,OAAO;AAAA,MACzD,OAAO;AAAA,MACP,eAAe;AAAA,MACf,OAAO,KAAK,IAAI,GAAG,EAAE,YAAY,GAAG;AAAA,MACpC,MAAM,EAAE;AAAA,IACV,EAAE;AAEF,WAAO;AAAA,MACL;AAAA,MACA,aAAa,KAAK,IAAI,IAAI;AAAA,MAC1B,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EAEA,MAAM,WAA0B;AAC9B,SAAK,eAAe;AAAA,EACtB;AAAA,EAEA,kBAAkC;AAChC,WAAO;AAAA,MACL,UAAU;AAAA,QACR;AAAA,UACE,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,SAAS;AAAA,UACT,QAAQ;AAAA,YACN;AAAA,cACE,KAAK;AAAA,cACL,OAAO;AAAA,cACP,aAAa;AAAA,cACb,MAAM;AAAA,cACN,KAAK;AAAA,cACL,KAAK;AAAA,cACL,MAAM;AAAA,cACN,SAAS;AAAA,YACX;AAAA,YACA;AAAA,cACE,KAAK;AAAA,cACL,OAAO;AAAA,cACP,aAAa;AAAA,cACb,MAAM;AAAA,cACN,KAAK;AAAA,cACL,KAAK;AAAA,cACL,MAAM;AAAA,cACN,SAAS;AAAA,cACT,WAAW;AAAA,cACX,MAAM;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,cAAkC;AAChC,WAAO;AAAA,EACT;AAAA,EAEA,kBAAuC;AACrC,WAAO,CAAC;AAAA,EACV;AAAA,EAEA,qBAAuC;AACrC,WAAO,CAAC;AAAA,EACV;AAAA,EAEA,kBAA8C;AAC5C,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,QAA8B;AAClC,WAAO;AAAA,MACL,WAAW;AAAA,MACX,SAAS;AAAA;AAAA,MACT,QAAQ;AAAA,MACR,cAAc,CAAC,MAAM;AAAA,IACvB;AAAA,EACF;AACF;","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
- detectMotion
3
- } from "../../chunk-BR2FPGOX.mjs";
2
+ require_frame_diff
3
+ } from "../../chunk-YYDM6V2F.mjs";
4
4
  import {
5
- jpegToRgb,
6
- rgbToGrayscale
7
- } from "../../chunk-22BHCDT5.mjs";
8
- import "../../chunk-BJTO5JO5.mjs";
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":"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","names":[]}
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":[]}