brilliantsole 0.0.29 → 0.0.30

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 (105) hide show
  1. package/build/brilliantsole.cjs +5630 -494
  2. package/build/brilliantsole.cjs.map +1 -1
  3. package/build/brilliantsole.js +21293 -3088
  4. package/build/brilliantsole.js.map +1 -1
  5. package/build/brilliantsole.ls.js +23153 -6240
  6. package/build/brilliantsole.ls.js.map +1 -1
  7. package/build/brilliantsole.min.js +1 -1
  8. package/build/brilliantsole.min.js.map +1 -1
  9. package/build/brilliantsole.module.d.ts +1158 -74
  10. package/build/brilliantsole.module.js +21259 -3089
  11. package/build/brilliantsole.module.js.map +1 -1
  12. package/build/brilliantsole.module.min.d.ts +1158 -74
  13. package/build/brilliantsole.module.min.js +1 -1
  14. package/build/brilliantsole.module.min.js.map +1 -1
  15. package/build/brilliantsole.node.module.d.ts +869 -70
  16. package/build/brilliantsole.node.module.js +5608 -495
  17. package/build/brilliantsole.node.module.js.map +1 -1
  18. package/build/dts/BS.d.ts +20 -1
  19. package/build/dts/Device.d.ts +135 -13
  20. package/build/dts/DeviceManager.d.ts +3 -3
  21. package/build/dts/DisplayManager.d.ts +320 -0
  22. package/build/dts/FileTransferManager.d.ts +10 -4
  23. package/build/dts/connection/BaseConnectionManager.d.ts +2 -2
  24. package/build/dts/connection/bluetooth/BluetoothUUID.d.ts +12 -0
  25. package/build/dts/devicePair/DevicePair.d.ts +5 -5
  26. package/build/dts/sensor/SensorConfigurationManager.d.ts +2 -1
  27. package/build/dts/server/BaseClient.d.ts +4 -4
  28. package/build/dts/server/udp/UDPUtils.d.ts +1 -1
  29. package/build/dts/utils/ArrayBufferUtils.d.ts +1 -0
  30. package/build/dts/utils/BitmapUtils.d.ts +17 -0
  31. package/build/dts/utils/ColorUtils.d.ts +5 -0
  32. package/build/dts/utils/DisplayBitmapUtils.d.ts +47 -0
  33. package/build/dts/utils/DisplayCanvasHelper.d.ts +270 -0
  34. package/build/dts/utils/DisplayContextCommand.d.ts +300 -0
  35. package/build/dts/utils/DisplayContextState.d.ts +51 -0
  36. package/build/dts/utils/DisplayContextStateHelper.d.ts +9 -0
  37. package/build/dts/utils/DisplayManagerInterface.d.ts +173 -0
  38. package/build/dts/utils/DisplaySpriteSheetUtils.d.ts +72 -0
  39. package/build/dts/utils/DisplayUtils.d.ts +70 -0
  40. package/build/dts/utils/MathUtils.d.ts +16 -0
  41. package/build/dts/utils/PathUtils.d.ts +4 -0
  42. package/build/dts/utils/RangeHelper.d.ts +7 -0
  43. package/build/dts/utils/SpriteSheetUtils.d.ts +20 -0
  44. package/build/index.d.ts +1156 -72
  45. package/build/index.node.d.ts +867 -68
  46. package/examples/3d-generic/index.html +5 -0
  47. package/examples/3d-generic/script.js +1 -0
  48. package/examples/basic/index.html +335 -0
  49. package/examples/basic/script.js +1303 -3
  50. package/examples/camera/utils.js +1 -1
  51. package/examples/display-3d/index.html +195 -0
  52. package/examples/display-3d/script.js +1235 -0
  53. package/examples/display-canvas/aframe.js +42950 -0
  54. package/examples/display-canvas/index.html +245 -0
  55. package/examples/display-canvas/script.js +2312 -0
  56. package/examples/display-image/index.html +189 -0
  57. package/examples/display-image/script.js +1093 -0
  58. package/examples/display-spritesheet/index.html +960 -0
  59. package/examples/display-spritesheet/script.js +4243 -0
  60. package/examples/display-text/index.html +195 -0
  61. package/examples/display-text/script.js +1418 -0
  62. package/examples/display-wireframe/index.html +204 -0
  63. package/examples/display-wireframe/script.js +1167 -0
  64. package/examples/glasses-gestures/index.html +6 -1
  65. package/examples/glasses-gestures/script.js +10 -8
  66. package/examples/microphone/index.html +3 -1
  67. package/examples/punch/index.html +4 -1
  68. package/examples/server/script.js +0 -1
  69. package/package.json +10 -2
  70. package/src/BS.ts +92 -1
  71. package/src/CameraManager.ts +6 -2
  72. package/src/Device.ts +544 -13
  73. package/src/DisplayManager.ts +2989 -0
  74. package/src/FileTransferManager.ts +79 -26
  75. package/src/InformationManager.ts +8 -7
  76. package/src/MicrophoneManager.ts +10 -3
  77. package/src/TfliteManager.ts +4 -2
  78. package/src/WifiManager.ts +4 -1
  79. package/src/connection/BaseConnectionManager.ts +2 -0
  80. package/src/connection/bluetooth/bluetoothUUIDs.ts +36 -1
  81. package/src/devicePair/DevicePairPressureSensorDataManager.ts +1 -1
  82. package/src/scanner/NobleScanner.ts +1 -1
  83. package/src/sensor/SensorConfigurationManager.ts +16 -8
  84. package/src/server/udp/UDPServer.ts +4 -4
  85. package/src/server/udp/UDPUtils.ts +1 -1
  86. package/src/server/websocket/WebSocketClient.ts +50 -1
  87. package/src/utils/ArrayBufferUtils.ts +23 -5
  88. package/src/utils/AudioUtils.ts +1 -1
  89. package/src/utils/ColorUtils.ts +66 -0
  90. package/src/utils/DisplayBitmapUtils.ts +695 -0
  91. package/src/utils/DisplayCanvasHelper.ts +4222 -0
  92. package/src/utils/DisplayContextCommand.ts +1566 -0
  93. package/src/utils/DisplayContextState.ts +138 -0
  94. package/src/utils/DisplayContextStateHelper.ts +48 -0
  95. package/src/utils/DisplayManagerInterface.ts +1356 -0
  96. package/src/utils/DisplaySpriteSheetUtils.ts +782 -0
  97. package/src/utils/DisplayUtils.ts +529 -0
  98. package/src/utils/EventDispatcher.ts +59 -14
  99. package/src/utils/MathUtils.ts +88 -2
  100. package/src/utils/ObjectUtils.ts +6 -1
  101. package/src/utils/PathUtils.ts +192 -0
  102. package/src/utils/RangeHelper.ts +15 -3
  103. package/src/utils/Timer.ts +1 -1
  104. package/src/utils/environment.ts +15 -6
  105. package/examples/microphone/gender.js +0 -54
@@ -0,0 +1,1093 @@
1
+ import * as BS from "../../build/brilliantsole.module.js";
2
+
3
+ // DEVICE
4
+ const device = new BS.Device();
5
+ window.device = device;
6
+ window.BS = BS;
7
+
8
+ // CONNECT
9
+
10
+ const toggleConnectionButton = document.getElementById("toggleConnection");
11
+ toggleConnectionButton.addEventListener("click", () =>
12
+ device.toggleConnection()
13
+ );
14
+ device.addEventListener("connectionStatus", () => {
15
+ let disabled = false;
16
+ let innerText = device.connectionStatus;
17
+ switch (device.connectionStatus) {
18
+ case "notConnected":
19
+ innerText = "connect";
20
+ break;
21
+ case "connected":
22
+ innerText = "disconnect";
23
+ break;
24
+ }
25
+ toggleConnectionButton.disabled = disabled;
26
+ toggleConnectionButton.innerText = innerText;
27
+ });
28
+
29
+ // WEBSOCKET CLIENT
30
+
31
+ const client = new BS.WebSocketClient();
32
+ window.client = client;
33
+
34
+ // WEBSOCKET URL SEARCH PARAMS
35
+
36
+ const url = new URL(location);
37
+ function setUrlParam(key, value) {
38
+ if (history.pushState) {
39
+ let searchParams = new URLSearchParams(window.location.search);
40
+ if (value) {
41
+ searchParams.set(key, value);
42
+ } else {
43
+ searchParams.delete(key);
44
+ }
45
+ let newUrl =
46
+ window.location.protocol +
47
+ "//" +
48
+ window.location.host +
49
+ window.location.pathname +
50
+ "?" +
51
+ searchParams.toString();
52
+ window.history.pushState({ path: newUrl }, "", newUrl);
53
+ }
54
+ }
55
+ client.addEventListener("isConnected", () => {
56
+ if (client.isConnected) {
57
+ setUrlParam("webSocketUrl", client.webSocket.url);
58
+ webSocketUrlInput.value = client.webSocket.url;
59
+ webSocketUrlInput.dispatchEvent(new Event("input"));
60
+ } else {
61
+ setUrlParam("webSocketUrl");
62
+ }
63
+ });
64
+
65
+ // WEBSOCKET SERVER URL
66
+
67
+ /** @type {HTMLInputElement} */
68
+ const webSocketUrlInput = document.getElementById("webSocketUrl");
69
+ webSocketUrlInput.value = url.searchParams.get("webSocketUrl") || "";
70
+ webSocketUrlInput.dispatchEvent(new Event("input"));
71
+
72
+ // WEBSOCKET CONNECTION
73
+
74
+ /** @type {HTMLButtonElement} */
75
+ const toggleClientConnectionButton = document.getElementById(
76
+ "toggleClientConnection"
77
+ );
78
+ toggleClientConnectionButton.addEventListener("click", () => {
79
+ if (client.isConnected) {
80
+ client.disconnect();
81
+ } else {
82
+ /** @type {string?} */
83
+ let webSocketUrl;
84
+ if (webSocketUrlInput.value.length > 0) {
85
+ webSocketUrl = webSocketUrlInput.value;
86
+ }
87
+ client.connect(webSocketUrl);
88
+ }
89
+ });
90
+ client.addEventListener("connectionStatus", () => {
91
+ switch (client.connectionStatus) {
92
+ case "connected":
93
+ case "notConnected":
94
+ toggleClientConnectionButton.disabled = false;
95
+ toggleClientConnectionButton.innerText = client.isConnected
96
+ ? "disconnect from server"
97
+ : "connect to server";
98
+ break;
99
+ case "connecting":
100
+ case "disconnecting":
101
+ toggleClientConnectionButton.innerText = client.connectionStatus;
102
+ toggleClientConnectionButton.disabled = true;
103
+ break;
104
+ }
105
+ });
106
+
107
+ // WEBSOCKET SCANNER
108
+
109
+ /** @type {HTMLInputElement} */
110
+ const isScanningAvailableCheckbox = document.getElementById(
111
+ "isScanningAvailable"
112
+ );
113
+ client.addEventListener("isScanningAvailable", () => {
114
+ isScanningAvailableCheckbox.checked = client.isScanningAvailable;
115
+ });
116
+
117
+ /** @type {HTMLButtonElement} */
118
+ const toggleScanButton = document.getElementById("toggleScan");
119
+ toggleScanButton.addEventListener("click", () => {
120
+ client.toggleScan();
121
+ });
122
+ client.addEventListener("isScanningAvailable", () => {
123
+ toggleScanButton.disabled = !client.isScanningAvailable;
124
+ });
125
+ client.addEventListener("isScanning", () => {
126
+ toggleScanButton.innerText = client.isScanning ? "stop scanning" : "scan";
127
+ });
128
+
129
+ /** @type {BS.Device?} */
130
+ let clientDevice;
131
+ client.addEventListener("discoveredDevice", (event) => {
132
+ console.log(event);
133
+ if (clientDevice) {
134
+ return;
135
+ }
136
+ const { discoveredDevice } = event.message;
137
+ if (discoveredDevice.deviceType == "glasses") {
138
+ console.log("connecting to discoveredDevice", discoveredDevice);
139
+ clientDevice = client.connectToDevice(discoveredDevice.bluetoothId);
140
+ }
141
+ });
142
+
143
+ // DEVICE
144
+ BS.DeviceManager.AddEventListener("deviceConnected", (event) => {
145
+ if (event.message.device.connectionType != "client") {
146
+ return;
147
+ }
148
+ if (event.message.device.isDisplayAvailable) {
149
+ clientDevice = event.message.device;
150
+ if (client.isScanning) {
151
+ client.stopScan();
152
+ }
153
+ displayCanvasHelper.device = clientDevice;
154
+ } else {
155
+ console.log("display not available");
156
+ // event.message.device.disconnect();
157
+ }
158
+ });
159
+
160
+ // CANVAS
161
+ /** @type {HTMLCanvasElement} */
162
+ const displayCanvas = document.getElementById("display");
163
+
164
+ // DISPLAY CANVAS HELPER
165
+ const displayCanvasHelper = new BS.DisplayCanvasHelper();
166
+ // displayCanvasHelper.setBrightness("veryLow");
167
+ displayCanvasHelper.canvas = displayCanvas;
168
+ window.displayCanvasHelper = displayCanvasHelper;
169
+
170
+ device.addEventListener("connected", () => {
171
+ if (device.isDisplayAvailable) {
172
+ displayCanvasHelper.device = device;
173
+ } else {
174
+ console.error("device doesn't have a display");
175
+ device.disconnect();
176
+ }
177
+ });
178
+
179
+ // BRIGHTNESS
180
+ /** @type {HTMLSelectElement} */
181
+ const setDisplayBrightnessSelect = document.getElementById(
182
+ "setDisplayBrightnessSelect"
183
+ );
184
+ /** @type {HTMLOptGroupElement} */
185
+ const setDisplayBrightnessSelectOptgroup =
186
+ setDisplayBrightnessSelect.querySelector("optgroup");
187
+ BS.DisplayBrightnesses.forEach((displayBrightness) => {
188
+ setDisplayBrightnessSelectOptgroup.appendChild(new Option(displayBrightness));
189
+ });
190
+ setDisplayBrightnessSelect.addEventListener("input", () => {
191
+ displayCanvasHelper.setBrightness(setDisplayBrightnessSelect.value);
192
+ });
193
+
194
+ setDisplayBrightnessSelect.value = displayCanvasHelper.brightness;
195
+
196
+ // COLORS
197
+
198
+ /** @type {HTMLTemplateElement} */
199
+ const displayColorTemplate = document.getElementById("displayColorTemplate");
200
+ const displayColorsContainer = document.getElementById("displayColors");
201
+ const setDisplayColor = BS.ThrottleUtils.throttle(
202
+ (colorIndex, colorString) => {
203
+ console.log({ colorIndex, colorString });
204
+ displayCanvasHelper.setColor(colorIndex, colorString, true);
205
+ },
206
+ 100,
207
+ true
208
+ );
209
+ /** @type {HTMLInputElement[]} */
210
+ const displayColorInputs = [];
211
+ const setupColors = () => {
212
+ displayColorsContainer.innerHTML = "";
213
+ for (
214
+ let colorIndex = 0;
215
+ colorIndex < displayCanvasHelper.numberOfColors;
216
+ colorIndex++
217
+ ) {
218
+ const displayColorContainer = displayColorTemplate.content
219
+ .cloneNode(true)
220
+ .querySelector(".displayColor");
221
+
222
+ const colorIndexSpan = displayColorContainer.querySelector(".colorIndex");
223
+ colorIndexSpan.innerText = `color #${colorIndex}`;
224
+ const colorInput = displayColorContainer.querySelector("input");
225
+ displayColorInputs[colorIndex] = colorInput;
226
+ colorInput.addEventListener("input", () => {
227
+ setDisplayColor(colorIndex, colorInput.value);
228
+ });
229
+ displayColorsContainer.appendChild(displayColorContainer);
230
+ }
231
+ };
232
+ setupColors();
233
+ displayCanvasHelper.addEventListener("numberOfColors", () => setupColors());
234
+ displayCanvasHelper.addEventListener("color", (event) => {
235
+ const { colorHex, colorIndex } = event.message;
236
+ displayColorInputs[colorIndex].value = colorHex;
237
+ });
238
+
239
+ // IMAGE PREVIEW
240
+
241
+ /** @type {HTMLInputElement} */
242
+ const imageInput = document.getElementById("imageInput");
243
+ /** @type {HTMLImageElement} */
244
+ const image = document.getElementById("image");
245
+ imageInput.addEventListener("input", () => {
246
+ const file = imageInput.files[0];
247
+ if (!file) return;
248
+ loadImage(file);
249
+ imageInput.value = "";
250
+ });
251
+ const loadImage = (file) => {
252
+ const reader = new FileReader();
253
+ reader.onload = () => {
254
+ image.src = reader.result;
255
+ };
256
+ reader.readAsDataURL(file);
257
+ };
258
+ image.addEventListener("load", () => {
259
+ drawImage();
260
+ });
261
+
262
+ const redrawImageButton = document.getElementById("redrawImage");
263
+ redrawImageButton.addEventListener("click", () => {
264
+ drawImage();
265
+ });
266
+
267
+ const tempCanvas = document.createElement("canvas");
268
+ const tempCtx = tempCanvas.getContext("2d");
269
+ const drawImage = async () => {
270
+ let srcWidth, srcHeight, src;
271
+ let useCameraVideo = cameraVideo.srcObject;
272
+ if (useCameraVideo) {
273
+ srcWidth = cameraVideo.videoWidth;
274
+ srcHeight = cameraVideo.videoHeight;
275
+ src = cameraVideo;
276
+ } else {
277
+ srcWidth = image.naturalWidth;
278
+ srcHeight = image.naturalHeight;
279
+ src = image;
280
+ }
281
+
282
+ let inputImageScale = 1;
283
+ inputImageScale = drawInputHeight / srcHeight;
284
+ const inputImageWidth = Math.round(srcWidth * inputImageScale);
285
+ const inputImageHeight = Math.round(srcHeight * inputImageScale);
286
+
287
+ canvas.width = inputImageWidth;
288
+ canvas.height = inputImageHeight;
289
+
290
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
291
+
292
+ if (useGrayscale) {
293
+ ctx.filter = "grayscale(100%)";
294
+ }
295
+ ctx.resetTransform();
296
+ if (useCameraVideo && mirrorCamera) {
297
+ ctx.scale(-1, 1);
298
+ ctx.translate(-canvas.width, 0);
299
+ }
300
+
301
+ if (useImageSegmentation && imageSegmenter) {
302
+ tempCanvas.width = srcWidth;
303
+ tempCanvas.height = srcHeight;
304
+
305
+ tempCtx.drawImage(
306
+ src,
307
+ 0,
308
+ 0,
309
+ srcWidth,
310
+ srcHeight,
311
+ 0,
312
+ 0,
313
+ tempCanvas.width,
314
+ tempCanvas.height
315
+ );
316
+
317
+ const result = imageSegmenter.segmentForVideo(
318
+ tempCanvas,
319
+ performance.now()
320
+ );
321
+ console.log("imageSegmenter result", result);
322
+
323
+ const { width, height } = result.categoryMask;
324
+ let imageData = tempCtx.getImageData(0, 0, width, height).data;
325
+ tempCanvas.width = width;
326
+ tempCanvas.height = height;
327
+ const mask = result.categoryMask.getAsUint8Array();
328
+ for (let i in mask) {
329
+ const isPerson = mask[i] == 0 ? 1 : 0;
330
+ imageData[i * 4] *= isPerson;
331
+ imageData[i * 4 + 1] *= isPerson;
332
+ imageData[i * 4 + 2] *= isPerson;
333
+ //imageData[i * 4 + 3] = 255;
334
+ }
335
+ const uint8Array = new Uint8ClampedArray(imageData.buffer);
336
+ const dataNew = new ImageData(uint8Array, width, height);
337
+ tempCtx.putImageData(dataNew, 0, 0);
338
+ src = tempCanvas;
339
+ }
340
+
341
+ ctx.drawImage(
342
+ src,
343
+ 0,
344
+ 0,
345
+ srcWidth,
346
+ srcHeight,
347
+ 0,
348
+ 0,
349
+ canvas.width,
350
+ canvas.height
351
+ );
352
+
353
+ draw();
354
+ };
355
+
356
+ // DRAGOVER
357
+ window.addEventListener("dragover", (e) => {
358
+ e.preventDefault();
359
+ });
360
+
361
+ window.addEventListener("drop", (e) => {
362
+ e.preventDefault();
363
+ const file = e.dataTransfer.files[0];
364
+ if (file) {
365
+ if (file.type.startsWith("image/")) {
366
+ loadImage(file);
367
+ }
368
+ }
369
+ });
370
+
371
+ // CONTEXT FILTER
372
+ let useGrayscale = false;
373
+ const useGrayscaleInput = document.getElementById("useGrayscale");
374
+ useGrayscaleInput.addEventListener("input", () => {
375
+ setUseGrayscale(useGrayscaleInput.checked);
376
+ });
377
+ const setUseGrayscale = (newUseGrayscale) => {
378
+ useGrayscale = newUseGrayscale;
379
+ console.log({ useGrayscale });
380
+ useGrayscaleInput.checked = useGrayscale;
381
+ if (redrawOnChange) {
382
+ drawImage();
383
+ }
384
+ };
385
+
386
+ // REDRAW ON CHANGE
387
+ let redrawOnChange = false;
388
+ const redrawOnChangeInput = document.getElementById("redrawOnChange");
389
+ redrawOnChangeInput.addEventListener("input", () => {
390
+ setRedrawOnChange(redrawOnChangeInput.checked);
391
+ });
392
+ const setRedrawOnChange = (newRedrawOnChange) => {
393
+ redrawOnChange = newRedrawOnChange;
394
+ console.log({ redrawOnChange });
395
+ redrawOnChangeInput.checked = redrawOnChange;
396
+ };
397
+
398
+ // AUTO DRAW VIDEO
399
+ let autoDrawVideo = false;
400
+ const autoDrawVideoInput = document.getElementById("autoDrawVideo");
401
+ autoDrawVideoInput.addEventListener("input", () => {
402
+ setAutoDrawVideo(autoDrawVideoInput.checked);
403
+ });
404
+ const setAutoDrawVideo = (newAutoDrawVideo) => {
405
+ autoDrawVideo = newAutoDrawVideo;
406
+ console.log({ autoDrawVideo });
407
+ autoDrawVideoInput.checked = autoDrawVideo;
408
+ };
409
+
410
+ // PASTE
411
+ function isValidUrl(string) {
412
+ try {
413
+ new URL(string);
414
+ return true;
415
+ } catch (_) {
416
+ return false;
417
+ }
418
+ }
419
+ window.addEventListener("paste", (event) => {
420
+ const string = event.clipboardData.getData("text");
421
+ if (!isValidUrl(string)) {
422
+ return;
423
+ }
424
+ image.src = string;
425
+ });
426
+ window.addEventListener("paste", (event) => {
427
+ const items = event.clipboardData.items;
428
+ for (let i = 0; i < items.length; i++) {
429
+ const item = items[i];
430
+ if (item.type.startsWith("image/")) {
431
+ const file = item.getAsFile();
432
+ loadImage(file);
433
+ return;
434
+ }
435
+ }
436
+ });
437
+
438
+ // CAMERA
439
+ /** @type {HTMLVideoElement} */
440
+ const cameraVideo = document.getElementById("cameraVideo");
441
+ cameraVideo.volume = 0.0001;
442
+ cameraVideo.addEventListener("loadedmetadata", () => {
443
+ const { videoWidth, videoHeight } = cameraVideo;
444
+ cameraVideo.removeAttribute("hidden");
445
+ });
446
+ const toggleMirrorCameraButton = document.getElementById("toggleMirrorCamera");
447
+ let mirrorCamera = false;
448
+ const setMirrorCamera = (newMirrorCamera) => {
449
+ mirrorCamera = newMirrorCamera;
450
+ // console.log({ mirrorCamera });
451
+ cameraVideo.style.transform = mirrorCamera ? "scaleX(-1)" : "";
452
+ toggleMirrorCameraButton.innerText = mirrorCamera
453
+ ? "unmirror camera"
454
+ : "mirror camera";
455
+ };
456
+ toggleMirrorCameraButton.addEventListener("click", () => {
457
+ setMirrorCamera(!mirrorCamera);
458
+ });
459
+ setMirrorCamera(true);
460
+
461
+ /** @type {HTMLSelectElement} */
462
+ const cameraInput = document.getElementById("cameraInput");
463
+ const cameraInputOptgroup = cameraInput.querySelector("optgroup");
464
+ cameraInput.addEventListener("input", () => {
465
+ selectCameraInput(cameraInput.value);
466
+ });
467
+
468
+ cameraInput.addEventListener("click", async () => {
469
+ const devices = await navigator.mediaDevices.enumerateDevices();
470
+ const videoDevices = devices.filter((device) => device.kind == "videoinput");
471
+ console.log("videoDevices", videoDevices);
472
+ if (videoDevices.length == 1 && videoDevices[0].deviceId == "") {
473
+ console.log("getting camera");
474
+ const cameraStream = await navigator.mediaDevices.getUserMedia({
475
+ video: true,
476
+ });
477
+ cameraStream.getVideoTracks().forEach((track) => track.stop());
478
+ updateCameraSources();
479
+ }
480
+ });
481
+
482
+ const updateCameraSources = async () => {
483
+ const devices = await navigator.mediaDevices.enumerateDevices();
484
+ cameraInputOptgroup.innerHTML = "";
485
+ cameraInputOptgroup.appendChild(new Option("none"));
486
+ devices
487
+ .filter((device) => device.kind == "videoinput")
488
+ .forEach((videoInputDevice) => {
489
+ cameraInputOptgroup.appendChild(
490
+ new Option(videoInputDevice.label, videoInputDevice.deviceId)
491
+ );
492
+ });
493
+ cameraInput.value = "none";
494
+ selectCameraInput(cameraInput.value);
495
+ };
496
+ /** @type {MediaStream?} */
497
+ let cameraStream;
498
+ const selectCameraInput = async (deviceId) => {
499
+ stopCameraStream();
500
+ if (deviceId != "none") {
501
+ cameraStream = await navigator.mediaDevices.getUserMedia({
502
+ video: {
503
+ deviceId: { exact: deviceId },
504
+ width: { ideal: 1280 },
505
+ height: { ideal: 1280 },
506
+ },
507
+ });
508
+
509
+ cameraVideo.srcObject = cameraStream;
510
+ console.log("got cameraStream", deviceId, cameraStream);
511
+ }
512
+ };
513
+ const stopCameraStream = () => {
514
+ if (cameraStream) {
515
+ console.log("stopping cameraStream");
516
+ cameraStream.getVideoTracks().forEach((track) => track.stop());
517
+ }
518
+ cameraStream = undefined;
519
+ cameraVideo.srcObject = undefined;
520
+ cameraVideo.setAttribute("hidden", "");
521
+ };
522
+ navigator.mediaDevices.addEventListener("devicechange", () =>
523
+ updateCameraSources()
524
+ );
525
+ updateCameraSources();
526
+
527
+ // DRAW PARAMS
528
+
529
+ const drawXContainer = document.getElementById("drawX");
530
+ const drawXInput = drawXContainer.querySelector("input");
531
+ const drawXSpan = drawXContainer.querySelector("span.value");
532
+ let drawX = Number(drawXInput.value);
533
+
534
+ drawXInput.addEventListener("input", () => {
535
+ drawX = Number(drawXInput.value);
536
+ // console.log({ drawX });
537
+ drawXSpan.innerText = drawX;
538
+ });
539
+ drawXInput.addEventListener("input", () => {
540
+ if (redrawOnChange) {
541
+ drawImage();
542
+ }
543
+ });
544
+
545
+ const drawYContainer = document.getElementById("drawY");
546
+ const drawYInput = drawYContainer.querySelector("input");
547
+ const drawYSpan = drawYContainer.querySelector("span.value");
548
+ let drawY = Number(drawYInput.value);
549
+
550
+ drawYInput.addEventListener("input", () => {
551
+ drawY = Number(drawYInput.value);
552
+ //console.log({ drawY });
553
+ drawYSpan.innerText = drawY;
554
+ });
555
+ drawYInput.addEventListener("input", () => {
556
+ if (redrawOnChange) {
557
+ drawImage();
558
+ }
559
+ });
560
+
561
+ const drawInputHeightContainer = document.getElementById("drawInputHeight");
562
+ const drawInputHeightInput = drawInputHeightContainer.querySelector("input");
563
+ const drawInputHeightSpan =
564
+ drawInputHeightContainer.querySelector("span.value");
565
+ let drawInputHeight = Number(drawInputHeightInput.value);
566
+
567
+ drawInputHeightInput.addEventListener("input", () => {
568
+ drawInputHeight = Number(drawInputHeightInput.value);
569
+ //console.log({ drawInputHeight });
570
+ drawInputHeightSpan.innerText = drawInputHeight;
571
+ });
572
+ drawInputHeightInput.addEventListener("change", () => {
573
+ if (redrawOnChange) {
574
+ drawImage();
575
+ }
576
+ });
577
+
578
+ const drawOutputHeightContainer = document.getElementById("drawOutputHeight");
579
+ const drawOutputHeightInput = drawOutputHeightContainer.querySelector("input");
580
+ const drawOutputHeightSpan =
581
+ drawOutputHeightContainer.querySelector("span.value");
582
+ let drawOutputHeight = Number(drawOutputHeightInput.value);
583
+
584
+ drawOutputHeightInput.addEventListener("input", () => {
585
+ drawOutputHeight = Number(drawOutputHeightInput.value);
586
+ //console.log({ drawOutputHeight });
587
+ drawOutputHeightSpan.innerText = drawOutputHeight;
588
+ });
589
+ drawOutputHeightInput.addEventListener("change", () => {
590
+ if (redrawOnChange) {
591
+ drawImage();
592
+ }
593
+ });
594
+
595
+ // PIXEL DEPTH
596
+
597
+ let pixelDepth = BS.DisplayPixelDepths[2];
598
+ const setPixelDepth = (newPixelDepth) => {
599
+ pixelDepth = newPixelDepth;
600
+ console.log({ pixelDepth });
601
+ if (redrawOnChange) {
602
+ drawImage();
603
+ }
604
+ };
605
+ const pixelDepthSelect = document.getElementById("pixelDepth");
606
+ const pixelDepthOptgroup = pixelDepthSelect.querySelector("optgroup");
607
+ pixelDepthSelect.addEventListener("input", () => {
608
+ setPixelDepth(pixelDepthSelect.value);
609
+ });
610
+ BS.DisplayPixelDepths.forEach((pixelDepth) => {
611
+ pixelDepthOptgroup.appendChild(
612
+ new Option(
613
+ `${BS.pixelDepthToNumberOfColors(pixelDepth)} colors`,
614
+ pixelDepth
615
+ )
616
+ );
617
+ });
618
+ pixelDepthSelect.value = pixelDepth;
619
+
620
+ // DRAW
621
+ let defaultMaxFileLength = 10 * 1024; // 10kb
622
+ let defaultMtu = 247;
623
+ let currentSpriteIndexBeingDrawn = 0;
624
+ let isDrawing = false;
625
+ /** @type {BS.DisplaySpriteSheet} */
626
+ let spriteSheet;
627
+ let drawWhenReady = false;
628
+
629
+ /** @type {HTMLCanvasElement} */
630
+ const canvas = document.getElementById("canvas");
631
+ const ctx = canvas.getContext("2d");
632
+
633
+ let drawUsingBitmap = false;
634
+
635
+ const draw = async () => {
636
+ if (!canvas.height || !canvas.width) {
637
+ return;
638
+ }
639
+ if (isDrawing) {
640
+ //console.warn("busy drawing");
641
+ drawWhenReady = true;
642
+ return;
643
+ }
644
+ isDrawing = true;
645
+
646
+ console.log("drawing...");
647
+
648
+ canvas.removeAttribute("hidden");
649
+ cameraVideo.setAttribute("hidden", "");
650
+
651
+ const { width, height } = canvas;
652
+
653
+ let spriteScale = 1;
654
+ spriteScale = drawOutputHeight / height;
655
+
656
+ const outputImageWidth = Math.round(width * spriteScale);
657
+ const outputImageHeight = Math.round(height * spriteScale);
658
+
659
+ console.log({ spriteScale, outputImageHeight, outputImageWidth });
660
+
661
+ const numberOfColors = 2 ** pixelDepth;
662
+
663
+ if (drawUsingBitmap) {
664
+ const mtu = displayCanvasHelper.device?.isConnected
665
+ ? displayCanvasHelper.device.mtu
666
+ : defaultMtu;
667
+
668
+ const resizedCanvas = BS.resizeImage(
669
+ canvas,
670
+ outputImageWidth,
671
+ outputImageHeight
672
+ );
673
+
674
+ const { bitmapRows, colors } = await BS.canvasToBitmaps(
675
+ resizedCanvas,
676
+ numberOfColors,
677
+ mtu
678
+ );
679
+ console.log("bitmapRows", bitmapRows, colors);
680
+
681
+ let offsetX = drawX - outputImageWidth / 2;
682
+ let offsetY = drawY - outputImageHeight / 2;
683
+ await displayCanvasHelper.setHorizontalAlignment("start");
684
+ await displayCanvasHelper.setVerticalAlignment("start");
685
+ for (let bitmapRowIndex in bitmapRows) {
686
+ const bitmapRow = bitmapRows[bitmapRowIndex];
687
+ offsetX = drawX - outputImageWidth / 2;
688
+ for (let bitmapIndex in bitmapRow) {
689
+ const bitmap = bitmapRow[bitmapIndex];
690
+ //console.log("drawing bitmap", { offsetX, offsetY }, bitmap);
691
+ await displayCanvasHelper.drawBitmap(offsetX, offsetY, bitmap);
692
+ offsetX += bitmap.width;
693
+ }
694
+ offsetY += bitmapRow[0].height;
695
+ }
696
+ for (let colorIndex in colors) {
697
+ await displayCanvasHelper.setColor(colorIndex, colors[colorIndex]);
698
+ await displayCanvasHelper.selectBitmapColor(colorIndex, colorIndex);
699
+ }
700
+ } else {
701
+ const maxFileLength = displayCanvasHelper.device?.isConnected
702
+ ? displayCanvasHelper.device.maxFileLength
703
+ : defaultMaxFileLength;
704
+
705
+ spriteSheet = await BS.canvasToSpriteSheet(
706
+ canvas,
707
+ "image",
708
+ numberOfColors,
709
+ "image",
710
+ maxFileLength
711
+ );
712
+ console.log("spriteSheet", spriteSheet);
713
+
714
+ await displayCanvasHelper.setSpriteScale(spriteScale);
715
+ await displayCanvasHelper.resetSpriteColors();
716
+ /** @type {BS.DisplaySpriteColorPair[]} */
717
+ const spriteColorPairs = [];
718
+ for (let i = 0; i < numberOfColors; i++) {
719
+ spriteColorPairs.push({ colorIndex: i, spriteColorIndex: i });
720
+ }
721
+ await displayCanvasHelper.selectSpriteColors(spriteColorPairs);
722
+
723
+ const offsetX = drawX;
724
+ let offsetYTop = drawY - outputImageHeight / 2;
725
+ drawProgress.value = 0;
726
+
727
+ for (
728
+ currentSpriteIndexBeingDrawn = 0;
729
+ currentSpriteIndexBeingDrawn < spriteSheet.sprites.length;
730
+ currentSpriteIndexBeingDrawn++
731
+ ) {
732
+ const sprite = spriteSheet.sprites[currentSpriteIndexBeingDrawn];
733
+ const scaledSpriteHeight = sprite.height * spriteScale;
734
+ let offsetY = offsetYTop + scaledSpriteHeight / 2;
735
+ console.log(`drawing sprite "${sprite.name}"`, sprite, {
736
+ offsetX,
737
+ offsetY,
738
+ });
739
+ await displayCanvasHelper.drawSpriteFromSpriteSheet(
740
+ offsetX,
741
+ offsetY,
742
+ sprite.name,
743
+ spriteSheet,
744
+ undefined,
745
+ true
746
+ );
747
+ offsetYTop += scaledSpriteHeight;
748
+ }
749
+ drawProgress.value = 0;
750
+
751
+ for (let i = 0; i < displayCanvasHelper.numberOfColors; i++) {
752
+ if (i >= numberOfColors) {
753
+ await displayCanvasHelper.setColor(i, "black");
754
+ }
755
+ }
756
+ await displayCanvasHelper.selectSpriteSheetPalette("image");
757
+ }
758
+
759
+ await displayCanvasHelper.show();
760
+
761
+ canvas.setAttribute("hidden", "");
762
+ if (cameraVideo.srcObject) {
763
+ cameraVideo.removeAttribute("hidden");
764
+ }
765
+ };
766
+
767
+ displayCanvasHelper.addEventListener("ready", () => {
768
+ isDrawing = false;
769
+ if (drawWhenReady) {
770
+ drawWhenReady = false;
771
+ //drawImage();
772
+ }
773
+ if (cameraVideo.srcObject && autoDrawVideo) {
774
+ console.log("redrawing video");
775
+ drawImage();
776
+ }
777
+ if (autoPicture && device.isConnected) {
778
+ device.takePicture();
779
+ }
780
+ });
781
+
782
+ // PROGRESS
783
+
784
+ /** @type {HTMLProgressElement} */
785
+ const fileTransferProgress = document.getElementById("fileTransferProgress");
786
+
787
+ device.addEventListener("fileTransferProgress", (event) => {
788
+ const progress = event.message.progress;
789
+ //console.log({ progress });
790
+ fileTransferProgress.value = progress == 1 ? 0 : progress;
791
+ });
792
+ device.addEventListener("fileTransferStatus", () => {
793
+ if (device.fileTransferStatus == "idle") {
794
+ fileTransferProgress.value = 0;
795
+ }
796
+ });
797
+
798
+ /** @type {HTMLProgressElement} */
799
+ const drawProgress = document.getElementById("drawProgress");
800
+
801
+ device.addEventListener("fileTransferProgress", (event) => {
802
+ const progress = event.message.progress;
803
+ console.log({ progress });
804
+ const baseProgress =
805
+ (currentSpriteIndexBeingDrawn + progress) / spriteSheet.sprites.length;
806
+ drawProgress.value = baseProgress;
807
+ });
808
+
809
+ // FRAME CAMERA
810
+
811
+ /** @type {HTMLButtonElement} */
812
+ const takePictureButton = document.getElementById("takePicture");
813
+ takePictureButton.addEventListener("click", () => {
814
+ if (device.cameraStatus == "idle") {
815
+ device.takePicture(10);
816
+ } else {
817
+ device.stopCamera();
818
+ }
819
+ });
820
+ device.addEventListener("connected", () => {
821
+ updateTakePictureButton();
822
+ });
823
+ device.addEventListener("getSensorConfiguration", () => {
824
+ updateTakePictureButton();
825
+ });
826
+ const updateTakePictureButton = () => {
827
+ takePictureButton.disabled =
828
+ !device.isConnected || device.cameraStatus != "idle";
829
+ };
830
+ device.addEventListener("cameraStatus", () => {
831
+ updateTakePictureButton();
832
+ });
833
+
834
+ /** @type {HTMLProgressElement} */
835
+ const cameraImageProgress = document.getElementById("cameraImageProgress");
836
+ device.addEventListener("cameraImageProgress", (event) => {
837
+ if (event.message.type == "image") {
838
+ cameraImageProgress.value = event.message.progress;
839
+ }
840
+ });
841
+
842
+ device.addEventListener("cameraImage", (event) => {
843
+ image.src = event.message.url;
844
+ });
845
+
846
+ /** @type {HTMLButtonElement} */
847
+ const focusCameraButton = document.getElementById("focusCamera");
848
+ focusCameraButton.addEventListener("click", () => {
849
+ if (device.cameraStatus == "idle") {
850
+ device.focusCamera();
851
+ } else {
852
+ device.stopCamera();
853
+ }
854
+ });
855
+ device.addEventListener("connected", () => {
856
+ updateFocusCameraButton();
857
+ });
858
+ device.addEventListener("getSensorConfiguration", () => {
859
+ updateFocusCameraButton();
860
+ });
861
+ const updateFocusCameraButton = () => {
862
+ focusCameraButton.disabled =
863
+ !device.isConnected || device.cameraStatus != "idle";
864
+ };
865
+ device.addEventListener("cameraStatus", (event) => {
866
+ updateFocusCameraButton();
867
+ if (
868
+ device.cameraStatus == "idle" &&
869
+ event.message.previousCameraStatus == "focusing"
870
+ ) {
871
+ device.takePicture();
872
+ }
873
+ });
874
+
875
+ // CAMERA CONFIG
876
+
877
+ /** @type {HTMLInputElement} */
878
+ const autoPictureCheckbox = document.getElementById("autoPicture");
879
+ let autoPicture = autoPictureCheckbox.checked;
880
+ autoPictureCheckbox.addEventListener("input", () => {
881
+ autoPicture = autoPictureCheckbox.checked;
882
+ });
883
+
884
+ /** @type {HTMLPreElement} */
885
+ const cameraConfigurationPre = document.getElementById(
886
+ "cameraConfigurationPre"
887
+ );
888
+ device.addEventListener("getCameraConfiguration", () => {
889
+ cameraConfigurationPre.textContent = JSON.stringify(
890
+ device.cameraConfiguration,
891
+ null,
892
+ 2
893
+ );
894
+ });
895
+
896
+ const cameraConfigurationContainer = document.getElementById(
897
+ "cameraConfiguration"
898
+ );
899
+ /** @type {HTMLTemplateElement} */
900
+ const cameraConfigurationTypeTemplate = document.getElementById(
901
+ "cameraConfigurationTypeTemplate"
902
+ );
903
+ BS.CameraConfigurationTypes.forEach((cameraConfigurationType) => {
904
+ const cameraConfigurationTypeContainer =
905
+ cameraConfigurationTypeTemplate.content
906
+ .cloneNode(true)
907
+ .querySelector(".cameraConfigurationType");
908
+
909
+ cameraConfigurationContainer.appendChild(cameraConfigurationTypeContainer);
910
+
911
+ cameraConfigurationTypeContainer.querySelector(".type").innerText =
912
+ cameraConfigurationType;
913
+
914
+ /** @type {HTMLInputElement} */
915
+ const input = cameraConfigurationTypeContainer.querySelector("input");
916
+
917
+ /** @type {HTMLSpanElement} */
918
+ const span = cameraConfigurationTypeContainer.querySelector("span");
919
+
920
+ device.addEventListener("isConnected", () => {
921
+ updateisInputDisabled();
922
+ });
923
+ device.addEventListener("cameraStatus", () => {
924
+ updateisInputDisabled();
925
+ });
926
+ const updateisInputDisabled = () => {
927
+ input.disabled =
928
+ !device.isConnected || !device.hasCamera || device.cameraStatus != "idle";
929
+ };
930
+
931
+ const updateInput = () => {
932
+ const value = device.cameraConfiguration[cameraConfigurationType];
933
+ span.innerText = value;
934
+ input.value = value;
935
+ };
936
+
937
+ device.addEventListener("connected", () => {
938
+ if (!device.hasCamera) {
939
+ return;
940
+ }
941
+ const range = device.cameraConfigurationRanges[cameraConfigurationType];
942
+ input.min = range.min;
943
+ input.max = range.max;
944
+
945
+ updateInput();
946
+ });
947
+
948
+ device.addEventListener("getCameraConfiguration", () => {
949
+ updateInput();
950
+ });
951
+
952
+ input.addEventListener("change", () => {
953
+ const value = Number(input.value);
954
+ // console.log(`updating ${cameraConfigurationType} to ${value}`);
955
+ device.setCameraConfiguration({
956
+ [cameraConfigurationType]: value,
957
+ });
958
+ if (takePictureAfterUpdate) {
959
+ device.addEventListener(
960
+ "getCameraConfiguration",
961
+ () => {
962
+ setTimeout(() => device.takePicture()), 100;
963
+ },
964
+ { once: true }
965
+ );
966
+ }
967
+ });
968
+ });
969
+
970
+ /** @type {HTMLInputElement} */
971
+ const takePictureAfterUpdateCheckbox = document.getElementById(
972
+ "takePictureAfterUpdate"
973
+ );
974
+ let takePictureAfterUpdate = false;
975
+ takePictureAfterUpdateCheckbox.addEventListener("input", () => {
976
+ takePictureAfterUpdate = takePictureAfterUpdateCheckbox.checked;
977
+ console.log({ takePictureAfterUpdate });
978
+ });
979
+
980
+ /** @type {HTMLInputElement} */
981
+ const cameraWhiteBalanceInput = document.getElementById("cameraWhiteBalance");
982
+ const updateWhiteBalance = BS.ThrottleUtils.throttle(
983
+ (config) => {
984
+ if (device.cameraStatus != "idle") {
985
+ return;
986
+ }
987
+
988
+ device.setCameraConfiguration(config);
989
+
990
+ if (takePictureAfterUpdate) {
991
+ device.addEventListener(
992
+ "getCameraConfiguration",
993
+ () => {
994
+ setTimeout(() => device.takePicture()), 100;
995
+ },
996
+ { once: true }
997
+ );
998
+ }
999
+ },
1000
+ 200,
1001
+ true
1002
+ );
1003
+ cameraWhiteBalanceInput.addEventListener("input", () => {
1004
+ let [redGain, greenGain, blueGain] = cameraWhiteBalanceInput.value
1005
+ .replace("#", "")
1006
+ .match(/.{1,2}/g)
1007
+ .map((value) => Number(`0x${value}`))
1008
+ .map((value) => value / 255)
1009
+ .map((value) => value * device.cameraConfigurationRanges.blueGain.max)
1010
+ .map((value) => Math.round(value));
1011
+
1012
+ updateWhiteBalance({ redGain, greenGain, blueGain });
1013
+ });
1014
+ const updateCameraWhiteBalanceInput = () => {
1015
+ if (!device.hasCamera) {
1016
+ return;
1017
+ }
1018
+ cameraWhiteBalanceInput.disabled =
1019
+ !device.isConnected || !device.hasCamera || device.cameraStatus != "idle";
1020
+
1021
+ const { redGain, blueGain, greenGain } = device.cameraConfiguration;
1022
+
1023
+ cameraWhiteBalanceInput.value = `#${[redGain, blueGain, greenGain]
1024
+ .map((value) => value / device.cameraConfigurationRanges.redGain.max)
1025
+ .map((value) => value * 255)
1026
+ .map((value) => Math.round(value))
1027
+ .map((value) => value.toString(16))
1028
+ .join("")}`;
1029
+ };
1030
+ device.addEventListener("connected", () => {
1031
+ updateCameraWhiteBalanceInput();
1032
+ });
1033
+ device.addEventListener("getCameraConfiguration", () => {
1034
+ updateCameraWhiteBalanceInput();
1035
+ });
1036
+
1037
+ // IMAGE SEGMENTATION
1038
+
1039
+ let imageSegmenter = undefined;
1040
+ let runningMode = "LIVE_STREAM";
1041
+ let labels;
1042
+
1043
+ import {
1044
+ ImageSegmenter,
1045
+ FilesetResolver,
1046
+ } from "https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.2";
1047
+
1048
+ let useImageSegmentation = false;
1049
+ const useImageSegmentationInput = document.getElementById(
1050
+ "useImageSegmentation"
1051
+ );
1052
+ useImageSegmentationInput.addEventListener("input", () => {
1053
+ setUseImageSegmentation(useImageSegmentationInput.checked);
1054
+ });
1055
+ const setUseImageSegmentation = (newUseImageSegmentation) => {
1056
+ useImageSegmentation = newUseImageSegmentation;
1057
+ console.log({ useImageSegmentation });
1058
+ useImageSegmentationInput.checked = useImageSegmentation;
1059
+
1060
+ if (!imageSegmenter) {
1061
+ createImageSegmenter();
1062
+ }
1063
+ };
1064
+
1065
+ const modelAssetPaths = {
1066
+ selfieMulticlass:
1067
+ "https://storage.googleapis.com/mediapipe-models/image_segmenter/selfie_multiclass_256x256/float32/latest/selfie_multiclass_256x256.tflite",
1068
+ hairSegmenter:
1069
+ "https://storage.googleapis.com/mediapipe-models/image_segmenter/hair_segmenter/float32/latest/hair_segmenter.tflite",
1070
+ selfieSegmenter:
1071
+ "https://storage.googleapis.com/mediapipe-models/image_segmenter/selfie_segmenter/float16/latest/selfie_segmenter.tflite",
1072
+ deeplab:
1073
+ "https://storage.googleapis.com/mediapipe-models/image_segmenter/deeplab_v3/float32/latest/deeplab_v3.tflite",
1074
+ };
1075
+ const createImageSegmenter = async () => {
1076
+ const vision = await FilesetResolver.forVisionTasks(
1077
+ "https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.2/wasm"
1078
+ );
1079
+
1080
+ imageSegmenter = await ImageSegmenter.createFromOptions(vision, {
1081
+ baseOptions: {
1082
+ modelAssetPath: modelAssetPaths.selfieSegmenter,
1083
+ delegate: "GPU",
1084
+ },
1085
+
1086
+ runningMode: runningMode,
1087
+ outputCategoryMask: true,
1088
+ outputConfidenceMasks: false,
1089
+ });
1090
+ labels = imageSegmenter.getLabels();
1091
+ console.log("created imageSegmenter", imageSegmenter, labels);
1092
+ };
1093
+ createImageSegmenter();