edge-impulse-linux 1.17.2 → 1.17.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 (46) hide show
  1. package/build/cli/linux/runner-downloader.js +7 -2
  2. package/build/cli/linux/runner-downloader.js.map +1 -1
  3. package/build/cli/linux/runner-utils.d.ts +9 -3
  4. package/build/cli/linux/runner-utils.js +112 -79
  5. package/build/cli/linux/runner-utils.js.map +1 -1
  6. package/build/cli/linux/runner.js +35 -0
  7. package/build/cli/linux/runner.js.map +1 -1
  8. package/build/cli/linux/webserver/public/webserver.js +125 -8
  9. package/build/cli/linux/webserver/public/webserver.js.map +1 -1
  10. package/build/cli/linux/webserver/views/index.d.ts +2 -0
  11. package/build/cli/linux/webserver/views/index.js +28 -4
  12. package/build/cli/linux/webserver/views/index.js.map +1 -1
  13. package/build/cli-common/config.d.ts +10 -0
  14. package/build/cli-common/config.js +34 -2
  15. package/build/cli-common/config.js.map +1 -1
  16. package/build/cli-common/init-cli-app.js +4 -0
  17. package/build/cli-common/init-cli-app.js.map +1 -1
  18. package/build/cli-common/model-monitor.js +6 -1
  19. package/build/cli-common/model-monitor.js.map +1 -1
  20. package/build/library/classifier/linux-impulse-runner-types.d.ts +1 -0
  21. package/build/sdk/studio/sdk/api/classifyApi.d.ts +1 -33
  22. package/build/sdk/studio/sdk/api/classifyApi.js +1 -65
  23. package/build/sdk/studio/sdk/api/classifyApi.js.map +1 -1
  24. package/build/sdk/studio/sdk/api/rawDataApi.d.ts +12 -12
  25. package/build/sdk/studio/sdk/api/rawDataApi.js +12 -12
  26. package/build/sdk/studio/sdk/model/classifyJobResponsePage.d.ts +4 -0
  27. package/build/sdk/studio/sdk/model/classifyJobResponsePage.js +5 -0
  28. package/build/sdk/studio/sdk/model/classifyJobResponsePage.js.map +1 -1
  29. package/build/sdk/studio/sdk/model/classifyJobResponsePageAllOf.d.ts +4 -0
  30. package/build/sdk/studio/sdk/model/classifyJobResponsePageAllOf.js +5 -0
  31. package/build/sdk/studio/sdk/model/classifyJobResponsePageAllOf.js.map +1 -1
  32. package/build/sdk/studio/sdk/model/getOrganizationPortalResponse.d.ts +4 -0
  33. package/build/sdk/studio/sdk/model/getOrganizationPortalResponse.js +5 -0
  34. package/build/sdk/studio/sdk/model/getOrganizationPortalResponse.js.map +1 -1
  35. package/build/sdk/studio/sdk/model/getOrganizationPortalResponseAllOf.d.ts +4 -0
  36. package/build/sdk/studio/sdk/model/getOrganizationPortalResponseAllOf.js +5 -0
  37. package/build/sdk/studio/sdk/model/getOrganizationPortalResponseAllOf.js.map +1 -1
  38. package/build/sdk/studio/sdk/model/listOrganizationPortalsResponseAllOfPortals.d.ts +1 -0
  39. package/build/sdk/studio/sdk/model/listOrganizationPortalsResponseAllOfPortals.js +5 -0
  40. package/build/sdk/studio/sdk/model/listOrganizationPortalsResponseAllOfPortals.js.map +1 -1
  41. package/build/sdk/studio/sdk/model/modelPrediction.d.ts +5 -0
  42. package/build/sdk/studio/sdk/model/modelPrediction.js +5 -0
  43. package/build/sdk/studio/sdk/model/modelPrediction.js.map +1 -1
  44. package/cli/linux/webserver/public/assets/mobileclient.css +10 -0
  45. package/cli/linux/webserver/public/webserver.js +144 -8
  46. package/package.json +1 -1
@@ -60,6 +60,11 @@ ListOrganizationPortalsResponseAllOfPortals.attributeTypeMap = [
60
60
  "baseName": "bucketUrl",
61
61
  "type": "string"
62
62
  },
63
+ {
64
+ "name": "storageProvider",
65
+ "baseName": "storageProvider",
66
+ "type": "string"
67
+ },
63
68
  {
64
69
  "name": "created",
65
70
  "baseName": "created",
@@ -1 +1 @@
1
- {"version":3,"file":"listOrganizationPortalsResponseAllOfPortals.js","sourceRoot":"","sources":["../../../../../sdk/studio/sdk/model/listOrganizationPortalsResponseAllOfPortals.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;AAGH,MAAa,2CAA2C;IA4DpD,MAAM,CAAC,mBAAmB;QACtB,OAAO,2CAA2C,CAAC,gBAAgB,CAAC;IACxE,CAAC;;AA9DL,kGA+DC;AApDU,yDAAa,GAAuB,SAAS,CAAC;AAE9C,4DAAgB,GAA0D;IAC7E;QACI,MAAM,EAAE,IAAI;QACZ,UAAU,EAAE,IAAI;QAChB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,MAAM;QACd,UAAU,EAAE,MAAM;QAClB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,aAAa;QACrB,UAAU,EAAE,aAAa;QACzB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,KAAK;QACb,UAAU,EAAE,KAAK;QACjB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,UAAU;QAClB,UAAU,EAAE,UAAU;QACtB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,YAAY;QACpB,UAAU,EAAE,YAAY;QACxB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,YAAY;QACpB,UAAU,EAAE,YAAY;QACxB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,WAAW;QACnB,UAAU,EAAE,WAAW;QACvB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,SAAS;QACjB,UAAU,EAAE,SAAS;QACrB,MAAM,EAAE,MAAM;KACjB;CAAK,CAAC"}
1
+ {"version":3,"file":"listOrganizationPortalsResponseAllOfPortals.js","sourceRoot":"","sources":["../../../../../sdk/studio/sdk/model/listOrganizationPortalsResponseAllOfPortals.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;AAGH,MAAa,2CAA2C;IAkEpD,MAAM,CAAC,mBAAmB;QACtB,OAAO,2CAA2C,CAAC,gBAAgB,CAAC;IACxE,CAAC;;AApEL,kGAqEC;AAzDU,yDAAa,GAAuB,SAAS,CAAC;AAE9C,4DAAgB,GAA0D;IAC7E;QACI,MAAM,EAAE,IAAI;QACZ,UAAU,EAAE,IAAI;QAChB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,MAAM;QACd,UAAU,EAAE,MAAM;QAClB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,aAAa;QACrB,UAAU,EAAE,aAAa;QACzB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,KAAK;QACb,UAAU,EAAE,KAAK;QACjB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,UAAU;QAClB,UAAU,EAAE,UAAU;QACtB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,YAAY;QACpB,UAAU,EAAE,YAAY;QACxB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,YAAY;QACpB,UAAU,EAAE,YAAY;QACxB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,WAAW;QACnB,UAAU,EAAE,WAAW;QACvB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,iBAAiB;QACzB,UAAU,EAAE,iBAAiB;QAC7B,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,SAAS;QACjB,UAAU,EAAE,SAAS;QACrB,MAAM,EAAE,MAAM;KACjB;CAAK,CAAC"}
@@ -9,6 +9,7 @@
9
9
  * https://openapi-generator.tech
10
10
  * Do not edit the class manually.
11
11
  */
12
+ import { BoundingBoxWithScore } from './boundingBoxWithScore';
12
13
  export declare class ModelPrediction {
13
14
  'sampleId': number;
14
15
  'startMs': number;
@@ -24,6 +25,10 @@ export declare class ModelPrediction {
24
25
  * Only set for visual anomaly projects. 2D array of shape (n, n) with raw anomaly scores, where n varies based on the image input size and the specific visual anomaly algorithm used. The scores corresponds to each grid cell in the image\'s spatial matrix.
25
26
  */
26
27
  'anomalyScores'?: Array<Array<number>>;
28
+ /**
29
+ * Only set for object detection projects. Coordinates are scaled 0..1, not absolute values.
30
+ */
31
+ 'boundingBoxes'?: Array<BoundingBoxWithScore>;
27
32
  static discriminator: string | undefined;
28
33
  static attributeTypeMap: Array<{
29
34
  name: string;
@@ -59,6 +59,11 @@ ModelPrediction.attributeTypeMap = [
59
59
  "name": "anomalyScores",
60
60
  "baseName": "anomalyScores",
61
61
  "type": "Array<Array<number>>"
62
+ },
63
+ {
64
+ "name": "boundingBoxes",
65
+ "baseName": "boundingBoxes",
66
+ "type": "Array<BoundingBoxWithScore>"
62
67
  }
63
68
  ];
64
69
  //# sourceMappingURL=modelPrediction.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"modelPrediction.js","sourceRoot":"","sources":["../../../../../sdk/studio/sdk/model/modelPrediction.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;AAGH,MAAa,eAAe;IA4DxB,MAAM,CAAC,mBAAmB;QACtB,OAAO,eAAe,CAAC,gBAAgB,CAAC;IAC5C,CAAC;;AA9DL,0CA+DC;AA/CU,6BAAa,GAAuB,SAAS,CAAC;AAE9C,gCAAgB,GAA0D;IAC7E;QACI,MAAM,EAAE,UAAU;QAClB,UAAU,EAAE,UAAU;QACtB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,SAAS;QACjB,UAAU,EAAE,SAAS;QACrB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,OAAO;QACf,UAAU,EAAE,OAAO;QACnB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,OAAO;QACf,UAAU,EAAE,OAAO;QACnB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,YAAY;QACpB,UAAU,EAAE,YAAY;QACxB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,mBAAmB;QAC3B,UAAU,EAAE,mBAAmB;QAC/B,MAAM,EAAE,SAAS;KACpB;IACD;QACI,MAAM,EAAE,SAAS;QACjB,UAAU,EAAE,SAAS;QACrB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,eAAe;QACvB,UAAU,EAAE,eAAe;QAC3B,MAAM,EAAE,sBAAsB;KACjC;CAAK,CAAC"}
1
+ {"version":3,"file":"modelPrediction.js","sourceRoot":"","sources":["../../../../../sdk/studio/sdk/model/modelPrediction.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;AAIH,MAAa,eAAe;IAqExB,MAAM,CAAC,mBAAmB;QACtB,OAAO,eAAe,CAAC,gBAAgB,CAAC;IAC5C,CAAC;;AAvEL,0CAwEC;AApDU,6BAAa,GAAuB,SAAS,CAAC;AAE9C,gCAAgB,GAA0D;IAC7E;QACI,MAAM,EAAE,UAAU;QAClB,UAAU,EAAE,UAAU;QACtB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,SAAS;QACjB,UAAU,EAAE,SAAS;QACrB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,OAAO;QACf,UAAU,EAAE,OAAO;QACnB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,OAAO;QACf,UAAU,EAAE,OAAO;QACnB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,YAAY;QACpB,UAAU,EAAE,YAAY;QACxB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,mBAAmB;QAC3B,UAAU,EAAE,mBAAmB;QAC/B,MAAM,EAAE,SAAS;KACpB;IACD;QACI,MAAM,EAAE,SAAS;QACjB,UAAU,EAAE,SAAS;QACrB,MAAM,EAAE,QAAQ;KACnB;IACD;QACI,MAAM,EAAE,eAAe;QACvB,UAAU,EAAE,eAAe;QAC3B,MAAM,EAAE,sBAAsB;KACjC;IACD;QACI,MAAM,EAAE,eAAe;QACvB,UAAU,EAAE,eAAe;QAC3B,MAAM,EAAE,6BAA6B;KACxC;CAAK,CAAC"}
@@ -4239,3 +4239,13 @@ video {
4239
4239
  .embed-view #capture-camera {
4240
4240
  margin-top: -68px !important;
4241
4241
  }
4242
+
4243
+ .classification-top-overlay {
4244
+ position: absolute;
4245
+ left: 0.5rem;
4246
+ top: 0.5rem;
4247
+ color: white;
4248
+ text-align: left;;
4249
+ font-family: monospace;
4250
+ font-size: 0.75rem;
4251
+ }
@@ -1,4 +1,7 @@
1
- window.WebServer = async () => {
1
+ window.WebServer = async (vmStr) => {
2
+
3
+ const vm = JSON.parse(decodeURIComponent(vmStr));
4
+ console.log('vm', vm);
2
5
 
3
6
  const els = {
4
7
  title: document.querySelector('#header-row h1'),
@@ -255,6 +258,8 @@ window.WebServer = async () => {
255
258
  });
256
259
  window.addEventListener('resize', onWindowResize);
257
260
 
261
+ let lastFivePerfCalPredictions = [];
262
+
258
263
  function onClassification(opts) {
259
264
  lastClassification = opts;
260
265
 
@@ -263,7 +268,9 @@ window.WebServer = async () => {
263
268
 
264
269
  els.timePerInference.textContent = opts.timeMs;
265
270
  els.additionalInfo.textContent = opts.additionalInfo;
266
- els.timePerInferenceContainer.style.display = '';
271
+ if (!vm.isEmbedView) {
272
+ els.timePerInferenceContainer.style.display = '';
273
+ }
267
274
  els.additionalInfoContainer.style.display = '';
268
275
 
269
276
  console.log('classification', opts.result, opts.timeMs);
@@ -323,12 +330,31 @@ window.WebServer = async () => {
323
330
 
324
331
  els.imageClassify.row.style.display = '';
325
332
 
326
- let conclusion = 'uncertain';
333
+ let conclusion = vm.hasPerformanceCalibration ? '...' : 'uncertain';
327
334
  let highest = Math.max(...Object.values(result.classification));
328
335
 
329
336
  for (let k of Object.keys(result.classification)) {
330
337
  if (result.classification[k] >= 0.55) {
331
- conclusion = k + ' (' + result.classification[k].toFixed(2) + ')';
338
+ if (vm.hasPerformanceCalibration) {
339
+ conclusion = k;
340
+ }
341
+ else {
342
+ conclusion = k + ' (' + result.classification[k].toFixed(2) + ')';
343
+ }
344
+ }
345
+ }
346
+
347
+ // for perfcal models, if one of the last 5 predictions was the keyword => select that
348
+ if (vm.hasPerformanceCalibration) {
349
+ lastFivePerfCalPredictions.push(conclusion);
350
+ if (lastFivePerfCalPredictions.length > 5) {
351
+ lastFivePerfCalPredictions = lastFivePerfCalPredictions.slice(
352
+ lastFivePerfCalPredictions.length - 5);
353
+ }
354
+
355
+ const dedup = Array.from(new Set(lastFivePerfCalPredictions));
356
+ if (dedup.length === 2 && dedup.find(x => x === '...')) {
357
+ conclusion = dedup.find(x => x !== '...');
332
358
  }
333
359
  }
334
360
 
@@ -417,18 +443,61 @@ window.WebServer = async () => {
417
443
  }
418
444
 
419
445
  els.imageClassify.text.textContent = conclusion;
446
+
447
+ // for image classification models, draw overlay on top of the image with top 3 conclusions in embed mode
448
+ if (vm.sensorType === 'camera' && vm.isEmbedView) {
449
+ let results = [];
450
+ for (const key of Object.keys(result.classification)) {
451
+ if (key === 'anomaly') continue;
452
+
453
+ results.push({
454
+ label: key,
455
+ value: result.classification[key],
456
+ });
457
+ }
458
+
459
+ // don't change order if we only have 3
460
+ const top = results.length > 3 ?
461
+ results.sort((a, b) => b.value - a.value).slice(0, 3) :
462
+ results;
463
+ if (result.visual_anomaly_grid) {
464
+ // also visual AD? add to top results
465
+ top.push({
466
+ label: 'anomaly',
467
+ value: result.visual_anomaly_max,
468
+ });
469
+ }
470
+
471
+ let classificationOverlayEl = els.cameraContainer.querySelector('.classification-top-overlay');
472
+ if (!classificationOverlayEl) {
473
+ classificationOverlayEl = document.createElement('div');
474
+ classificationOverlayEl.classList.add('classification-top-overlay');
475
+ els.cameraContainer.appendChild(classificationOverlayEl);
476
+ }
477
+ classificationOverlayEl.textContent = '';
478
+
479
+ for (const topRes of top) {
480
+ const topDiv = document.createElement('div');
481
+ topDiv.textContent = `${topRes.label} (${topRes.value.toFixed(2)})`;
482
+ classificationOverlayEl.appendChild(topDiv);
483
+ }
484
+
485
+ els.imageClassify.row.style.display = 'none';
486
+ }
420
487
  }
421
488
  if (result.bounding_boxes) {
422
489
  let factor = els.cameraImg.naturalHeight / els.cameraImg.clientHeight;
423
490
 
424
- for (let b of result.bounding_boxes) {
491
+ for (let b of result.object_tracking || result.bounding_boxes) {
425
492
  let bb = {
426
493
  x: b.x / factor,
427
494
  y: b.y / factor,
428
495
  width: b.width / factor,
429
496
  height: b.height / factor,
430
- label: b.label,
431
- value: b.value
497
+ label: 'object_id' in b ?
498
+ `${b.label} (ID ${b.object_id})` :
499
+ b.label,
500
+ value: 'value' in b ? b.value : undefined,
432
501
  };
433
502
 
434
503
  if (!labelToColor[bb.label]) {
@@ -462,7 +531,10 @@ window.WebServer = async () => {
462
531
  let label = document.createElement('div');
463
532
  label.classList.add('bounding-box-label');
464
533
  label.style.background = color;
465
- label.textContent = bb.label + ' (' + bb.value.toFixed(2) + ')';
534
+ label.textContent = bb.label;
535
+ if (typeof bb.value === 'number') {
536
+ label.textContent += ' (' + bb.value.toFixed(2) + ')';
537
+ }
466
538
  if (modelType === 'constrained_object_detection') {
467
539
  el.style.whiteSpace = 'nowrap';
468
540
  }
@@ -530,4 +602,68 @@ window.WebServer = async () => {
530
602
  if (els.websocketAddress) {
531
603
  els.websocketAddress.textContent = `ws://${location.host}`;
532
604
  }
605
+
606
+ // Here's a helper function that'll loop every second, checks if "classification-top-overlay" is present
607
+ // and then switches between white/black text automatically. If this element is not created, it just sits idle
608
+ (async () => {
609
+ function getAvgBrightness(img, x, y, w, h) {
610
+ const canvas = document.createElement('canvas');
611
+ const ctx = canvas.getContext('2d');
612
+ canvas.width = img.naturalWidth;
613
+ canvas.height = img.naturalHeight;
614
+ ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
615
+
616
+ // Scale from CSS coords (relative to image on page) to natural pixel coords
617
+ const scaleX = img.naturalWidth / img.clientWidth;
618
+ const scaleY = img.naturalHeight / img.clientHeight;
619
+
620
+ const sx = (x - img.getBoundingClientRect().x) * scaleX;
621
+ const sy = (y - img.getBoundingClientRect().y) * scaleY;
622
+ const sw = w * scaleX;
623
+ const sh = h * scaleY;
624
+
625
+ const data = ctx.getImageData(sx, sy, sw, sh).data;
626
+
627
+ let total = 0;
628
+ for (let i = 0; i < data.length; i += 4) {
629
+ const r = data[i];
630
+ const g = data[i + 1];
631
+ const b = data[i + 2];
632
+ total += (r * 299 + g * 587 + b * 114) / 1000;
633
+ }
634
+
635
+ // debug info
636
+ // const cropCanvas = document.createElement('canvas');
637
+ // cropCanvas.width = sw;
638
+ // cropCanvas.height = sh;
639
+ // const cctx = cropCanvas.getContext('2d');
640
+ // cctx.drawImage(img, sx, sy, sw, sh, 0, 0, sw, sh);
641
+ // const cropEl = document.querySelector('#crop') || document.createElement('img');
642
+ // cropEl.id = 'crop';
643
+ // cropEl.src = cropCanvas.toDataURL('image/png');
644
+ // document.body.appendChild(cropEl);
645
+
646
+ return total / (data.length / 4);
647
+ }
648
+
649
+ if (!vm.isEmbedView || vm.sensorType !== 'camera') return;
650
+
651
+ while (1) {
652
+ const labelEl = document.querySelector('.classification-top-overlay');
653
+ if (!labelEl) {
654
+ await new Promise(resolve => setTimeout(resolve, 1000));
655
+ continue;
656
+ }
657
+
658
+ const rect = labelEl.getBoundingClientRect();
659
+ const brightness = getAvgBrightness(els.cameraImg, rect.x, rect.y, rect.width, rect.height);
660
+ if (brightness > 180) {
661
+ labelEl.style.color = 'black';
662
+ }
663
+ else {
664
+ labelEl.style.color = 'white';
665
+ }
666
+ await new Promise(resolve => setTimeout(resolve, 1000));
667
+ }
668
+ })();
533
669
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "edge-impulse-linux",
3
- "version": "1.17.2",
3
+ "version": "1.17.4",
4
4
  "description": "Node.js SDK and tools for Edge Impulse for Linux",
5
5
  "directories": {
6
6
  "example": "examples"