@camstack/addon-pipeline 1.1.54 → 1.1.55

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 (33) hide show
  1. package/dist/audio-analyzer/index.js +1 -1
  2. package/dist/audio-analyzer/index.mjs +1 -1
  3. package/dist/detection-pipeline/index.js +149 -1468
  4. package/dist/detection-pipeline/index.mjs +129 -1448
  5. package/dist/{dist-BbaoC680.js → dist-DI57FC8K.js} +59 -3
  6. package/dist/{dist-CEcTeu1h.mjs → dist-RWGGPwVx.mjs} +59 -3
  7. package/dist/motion-wasm/index.js +1 -1
  8. package/dist/motion-wasm/index.mjs +1 -1
  9. package/dist/pipeline-runner/index.js +665 -23
  10. package/dist/pipeline-runner/index.mjs +665 -24
  11. package/dist/recorder/index.js +1 -1
  12. package/dist/recorder/index.mjs +1 -1
  13. package/dist/{remote-source-plane-vN45KFSm.js → remote-source-plane-CHgvzzA6.js} +1 -1
  14. package/dist/{remote-source-plane-CNCqC_XG.mjs → remote-source-plane-DU0aRSPv.mjs} +1 -1
  15. package/dist/session-decode/decode-worker-child.js +222 -3
  16. package/dist/session-decode/decode-worker-child.mjs +222 -3
  17. package/dist/step-definitions-CNBFKjZe.js +1514 -0
  18. package/dist/step-definitions-CP9kVSml.mjs +1479 -0
  19. package/dist/stream-broker/_stub.js +1 -1
  20. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-HS0tc6Sa.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-mDCPzmT3.mjs} +3 -3
  21. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-B_v5r5ya.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-Dn_pxzP-.mjs} +1 -1
  22. package/dist/stream-broker/{hostInit-DgohfEDA.mjs → hostInit-GM_CI22k.mjs} +3 -3
  23. package/dist/stream-broker/index.js +2 -2
  24. package/dist/stream-broker/index.mjs +2 -2
  25. package/dist/stream-broker/remoteEntry.js +1 -1
  26. package/dist/{worker-protocol-DGIt_waM.mjs → worker-protocol-CyVJTZEO.mjs} +1 -0
  27. package/dist/{worker-protocol-DkL6GDxe.js → worker-protocol-PP4jKHHJ.js} +1 -0
  28. package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-CA2Hd6xx.js → MaskShapeCanvas-DI4BY7W2-BChW0ntM.js} +1 -1
  29. package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-DUN1VeC7.js → MotionZonesSettings-NcxxQN8r-DDzqEbSe.js} +1 -1
  30. package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-BqZIAU5s.js → PrivacyMaskSettings-APgPLF7p-B879GXaf.js} +1 -1
  31. package/embed-dist/assets/{index-CMkYypxB.js → index-CNjQ5rAE.js} +10 -10
  32. package/embed-dist/index.html +1 -1
  33. package/package.json +1 -1
@@ -3,7 +3,8 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../chunk-D6vf50IK.js");
6
- const require_dist = require("../dist-BbaoC680.js");
6
+ const require_dist = require("../dist-DI57FC8K.js");
7
+ const require_step_definitions = require("../step-definitions-CNBFKjZe.js");
7
8
  const require_node_topology_platform = require("../node-topology-platform-CFZ7F4xW.js");
8
9
  const require_model_download_service_C_IHWnXx = require("../model-download-service-C-IHWnXx-BLXzxpRB.js");
9
10
  let _camstack_shm_ring = require("@camstack/shm-ring");
@@ -298,1437 +299,6 @@ function projectNodeEngine(store, nodeId) {
298
299
  return out;
299
300
  }
300
301
  //#endregion
301
- //#region src/detection-pipeline/registry/model-catalogs.ts
302
- var HF_REPO = "camstack/camstack-models";
303
- var HF_SCRYPTED = "scrypted/plugin-models";
304
- var hf = (path) => require_dist.hfModelUrl(HF_REPO, path);
305
- var hfScrypted = (path) => require_dist.hfModelUrl(HF_SCRYPTED, path);
306
- /**
307
- * Build an OpenVINO format entry (always python runtime).
308
- *
309
- * OpenVINO IR is a two-file bundle: a `.xml` topology + a sibling `.bin`
310
- * weights file with the same basename. We declare the `.bin` in `files` so
311
- * the (format-agnostic) downloader fetches it alongside the `.xml` — without
312
- * the weights, OpenVINO compile fails with "Empty weights data in bin file".
313
- * A plain `.onnx` run through the OpenVINO runtime (e.g. yamnet) has no
314
- * sibling, so none is added.
315
- */
316
- var ovFormat = (url, sizeMB) => {
317
- const base = url.split("/").pop() ?? "";
318
- const files = base.endsWith(".xml") ? [base.replace(/\.xml$/, ".bin")] : void 0;
319
- return {
320
- url,
321
- sizeMB,
322
- runtimes: ["python"],
323
- ...files ? { files } : {}
324
- };
325
- };
326
- /**
327
- * Build a precision-variant catalog entry (OpenVINO-only) derived from a base
328
- * detection model. fp16 halves the weights (Intel iGPU/NPU sweet spot); int8 is
329
- * NNCF post-training-quantized (~4× smaller, fastest on CPU/iGPU at a small
330
- * accuracy cost). The IRs live next to the base `.xml` on HF as
331
- * `camstack-<id>-<precision>.xml`. Lets an operator scale the model to the node
332
- * (e.g. yolo26x-int8 on a 265K, yolo26n-int8 on an N100).
333
- */
334
- var ovPrecisionVariant = (baseId, ovDir, baseName, precision, sizeMB, legacy = false) => ({
335
- id: `${baseId}-${precision}`,
336
- name: `${baseName} (${precision.toUpperCase()})`,
337
- description: `${baseName} — OpenVINO ${precision.toUpperCase()} variant for Intel iGPU/NPU; scale by hardware`,
338
- inputSize: {
339
- width: 640,
340
- height: 640
341
- },
342
- labels: [],
343
- preprocessMode: "letterbox",
344
- formats: { openvino: ovFormat(hf(`${ovDir}/camstack-${baseId}-${precision}.xml`), sizeMB) },
345
- ...legacy ? { legacy: true } : {},
346
- ...precision === "int8" ? { group: {
347
- family: baseId.slice(0, -1),
348
- tier: baseId.slice(-1),
349
- precision: "int8"
350
- } } : {}
351
- });
352
- var MLPACKAGE_FILES = [
353
- "Manifest.json",
354
- "Data/com.apple.CoreML/model.mlmodel",
355
- "Data/com.apple.CoreML/weights/weight.bin"
356
- ];
357
- var YOLO26_TIER_NAME = {
358
- n: "Nano",
359
- s: "Small",
360
- m: "Medium",
361
- l: "Large"
362
- };
363
- /**
364
- * Reduced-input YOLO26 variants (2026-07-11). A lower square input (320 / 256)
365
- * is the biggest, cheapest latency lever measured across every runtime — Mac
366
- * ANE, Intel Ultra NPU, and especially the N100 — at a small-object accuracy
367
- * cost. Each resolution ships a base (fp32) entry (onnx + coreml + openvino)
368
- * grouped under `{family, tier, resolution}`, plus an OpenVINO INT8 entry (best
369
- * on Intel/N100) grouped with `precision:'int8'`. The grouped selector surfaces
370
- * `resolution` as a variant axis alongside precision.
371
- */
372
- var yolo26Reduced = (tier, res, sizes) => {
373
- const name = YOLO26_TIER_NAME[tier] ?? tier.toUpperCase();
374
- return [{
375
- id: `yolo26${tier}-${res}`,
376
- group: {
377
- family: "yolo26",
378
- tier,
379
- resolution: res
380
- },
381
- name: `YOLO26 ${name} @${res}`,
382
- description: `YOLO26 ${name}, reduced ${res}×${res} input — faster on ANE / hub NPU / N100 at a small-object accuracy cost`,
383
- inputSize: {
384
- width: res,
385
- height: res
386
- },
387
- labels: [],
388
- preprocessMode: "letterbox",
389
- formats: {
390
- onnx: {
391
- url: hf(`objectDetection/yolo26/onnx/camstack-yolo26${tier}-${res}.onnx`),
392
- sizeMB: sizes.onnx
393
- },
394
- coreml: {
395
- url: hf(`objectDetection/yolo26/coreml/camstack-yolo26${tier}-${res}.mlpackage`),
396
- sizeMB: sizes.coreml,
397
- isDirectory: true,
398
- files: [...MLPACKAGE_FILES],
399
- runtimes: ["python"]
400
- },
401
- openvino: ovFormat(hf(`objectDetection/yolo26/openvino/camstack-yolo26${tier}-${res}.xml`), sizes.ovFp32)
402
- }
403
- }, {
404
- id: `yolo26${tier}-${res}-int8`,
405
- group: {
406
- family: "yolo26",
407
- tier,
408
- precision: "int8",
409
- resolution: res
410
- },
411
- name: `YOLO26 ${name} @${res} (INT8)`,
412
- description: `YOLO26 ${name} @${res} — OpenVINO INT8 for Intel iGPU/CPU; the N100's fastest config`,
413
- inputSize: {
414
- width: res,
415
- height: res
416
- },
417
- labels: [],
418
- preprocessMode: "letterbox",
419
- formats: { openvino: ovFormat(hf(`objectDetection/yolo26/openvino/camstack-yolo26${tier}-${res}-int8.xml`), sizes.ovInt8) }
420
- }];
421
- };
422
- var OBJECT_DETECTION_MODELS = [
423
- {
424
- id: "yolov9t",
425
- legacy: true,
426
- name: "YOLOv9 Tiny",
427
- description: "YOLOv9 Tiny — ultra-lightweight next-gen detector",
428
- inputSize: {
429
- width: 640,
430
- height: 640
431
- },
432
- labels: [],
433
- preprocessMode: "letterbox",
434
- formats: {
435
- onnx: {
436
- url: hf("objectDetection/yolov9/onnx/camstack-yolov9t.onnx"),
437
- sizeMB: 8
438
- },
439
- coreml: {
440
- url: hf("objectDetection/yolov9/coreml/camstack-yolov9t.mlpackage"),
441
- sizeMB: 4,
442
- isDirectory: true,
443
- files: [...MLPACKAGE_FILES],
444
- runtimes: ["python"]
445
- },
446
- openvino: ovFormat(hf("objectDetection/yolov9/openvino/camstack-yolov9t.xml"), 6)
447
- }
448
- },
449
- {
450
- id: "yolov9s",
451
- legacy: true,
452
- name: "YOLOv9 Small",
453
- description: "YOLOv9 Small — improved efficiency over YOLOv8s",
454
- inputSize: {
455
- width: 640,
456
- height: 640
457
- },
458
- labels: [],
459
- preprocessMode: "letterbox",
460
- formats: {
461
- onnx: {
462
- url: hf("objectDetection/yolov9/onnx/camstack-yolov9s.onnx"),
463
- sizeMB: 28
464
- },
465
- coreml: {
466
- url: hf("objectDetection/yolov9/coreml/camstack-yolov9s.mlpackage"),
467
- sizeMB: 14,
468
- isDirectory: true,
469
- files: [...MLPACKAGE_FILES],
470
- runtimes: ["python"]
471
- },
472
- openvino: ovFormat(hf("objectDetection/yolov9/openvino/camstack-yolov9s.xml"), 16)
473
- }
474
- },
475
- {
476
- id: "yolov9c",
477
- legacy: true,
478
- name: "YOLOv9 C",
479
- description: "YOLOv9 C — high-accuracy compact model",
480
- inputSize: {
481
- width: 640,
482
- height: 640
483
- },
484
- labels: [],
485
- preprocessMode: "letterbox",
486
- formats: {
487
- onnx: {
488
- url: hf("objectDetection/yolov9/onnx/camstack-yolov9c.onnx"),
489
- sizeMB: 97
490
- },
491
- coreml: {
492
- url: hf("objectDetection/yolov9/coreml/camstack-yolov9c.mlpackage"),
493
- sizeMB: 48,
494
- isDirectory: true,
495
- files: [...MLPACKAGE_FILES],
496
- runtimes: ["python"]
497
- },
498
- openvino: ovFormat(hf("objectDetection/yolov9/openvino/camstack-yolov9c.xml"), 49)
499
- }
500
- },
501
- {
502
- id: "yolo26n",
503
- group: {
504
- family: "yolo26",
505
- tier: "n"
506
- },
507
- name: "YOLO26 Nano",
508
- description: "YOLO26 Nano — latest generation ultra-lightweight detector with attention mechanism",
509
- inputSize: {
510
- width: 640,
511
- height: 640
512
- },
513
- labels: [],
514
- preprocessMode: "letterbox",
515
- formats: {
516
- onnx: {
517
- url: hf("objectDetection/yolo26/onnx/camstack-yolo26n.onnx"),
518
- sizeMB: 10
519
- },
520
- coreml: {
521
- url: hf("objectDetection/yolo26/coreml/camstack-yolo26n.mlpackage"),
522
- sizeMB: 5,
523
- isDirectory: true,
524
- files: [...MLPACKAGE_FILES],
525
- runtimes: ["python"]
526
- },
527
- openvino: ovFormat(hf("objectDetection/yolo26/openvino/camstack-yolo26n.xml"), 9)
528
- }
529
- },
530
- {
531
- id: "yolo26s",
532
- group: {
533
- family: "yolo26",
534
- tier: "s"
535
- },
536
- name: "YOLO26 Small",
537
- description: "YOLO26 Small — balanced speed/accuracy with attention-based architecture",
538
- inputSize: {
539
- width: 640,
540
- height: 640
541
- },
542
- labels: [],
543
- preprocessMode: "letterbox",
544
- formats: {
545
- onnx: {
546
- url: hf("objectDetection/yolo26/onnx/camstack-yolo26s.onnx"),
547
- sizeMB: 37
548
- },
549
- coreml: {
550
- url: hf("objectDetection/yolo26/coreml/camstack-yolo26s.mlpackage"),
551
- sizeMB: 18,
552
- isDirectory: true,
553
- files: [...MLPACKAGE_FILES],
554
- runtimes: ["python"]
555
- },
556
- openvino: ovFormat(hf("objectDetection/yolo26/openvino/camstack-yolo26s.xml"), 36)
557
- }
558
- },
559
- {
560
- id: "yolo26m",
561
- group: {
562
- family: "yolo26",
563
- tier: "m"
564
- },
565
- name: "YOLO26 Medium",
566
- description: "YOLO26 Medium — high accuracy general-purpose detector",
567
- inputSize: {
568
- width: 640,
569
- height: 640
570
- },
571
- labels: [],
572
- preprocessMode: "letterbox",
573
- formats: {
574
- onnx: {
575
- url: hf("objectDetection/yolo26/onnx/camstack-yolo26m.onnx"),
576
- sizeMB: 78
577
- },
578
- coreml: {
579
- url: hf("objectDetection/yolo26/coreml/camstack-yolo26m.mlpackage"),
580
- sizeMB: 39,
581
- isDirectory: true,
582
- files: [...MLPACKAGE_FILES],
583
- runtimes: ["python"]
584
- },
585
- openvino: ovFormat(hf("objectDetection/yolo26/openvino/camstack-yolo26m.xml"), 78)
586
- }
587
- },
588
- {
589
- id: "yolo26l",
590
- group: {
591
- family: "yolo26",
592
- tier: "l"
593
- },
594
- name: "YOLO26 Large",
595
- description: "YOLO26 Large — maximum accuracy for demanding deployments",
596
- inputSize: {
597
- width: 640,
598
- height: 640
599
- },
600
- labels: [],
601
- preprocessMode: "letterbox",
602
- formats: {
603
- onnx: {
604
- url: hf("objectDetection/yolo26/onnx/camstack-yolo26l.onnx"),
605
- sizeMB: 95
606
- },
607
- coreml: {
608
- url: hf("objectDetection/yolo26/coreml/camstack-yolo26l.mlpackage"),
609
- sizeMB: 48,
610
- isDirectory: true,
611
- files: [...MLPACKAGE_FILES],
612
- runtimes: ["python"]
613
- },
614
- openvino: ovFormat(hf("objectDetection/yolo26/openvino/camstack-yolo26l.xml"), 95)
615
- }
616
- },
617
- {
618
- id: "yolo26x",
619
- legacy: true,
620
- name: "YOLO26 XLarge",
621
- description: "YOLO26 XLarge — highest accuracy, attention-based architecture",
622
- inputSize: {
623
- width: 640,
624
- height: 640
625
- },
626
- labels: [],
627
- preprocessMode: "letterbox",
628
- formats: {
629
- onnx: {
630
- url: hf("objectDetection/yolo26/onnx/camstack-yolo26x.onnx"),
631
- sizeMB: 213
632
- },
633
- coreml: {
634
- url: hf("objectDetection/yolo26/coreml/camstack-yolo26x.mlpackage"),
635
- sizeMB: 107,
636
- isDirectory: true,
637
- files: [...MLPACKAGE_FILES],
638
- runtimes: ["python"]
639
- },
640
- openvino: ovFormat(hf("objectDetection/yolo26/openvino/camstack-yolo26x.xml"), 213)
641
- }
642
- },
643
- {
644
- id: "scrypted-yolov9t-relu",
645
- legacy: true,
646
- name: "YOLOv9 Tiny ReLU",
647
- description: "YOLOv9t ReLU — optimized for int8 quantization",
648
- inputSize: {
649
- width: 320,
650
- height: 320
651
- },
652
- labels: [],
653
- preprocessMode: "letterbox",
654
- formats: {
655
- onnx: {
656
- url: hfScrypted("onnx/scrypted_yolov9t_relu/scrypted_yolov9t_relu.onnx"),
657
- sizeMB: 8.1
658
- },
659
- coreml: {
660
- url: hfScrypted("coreml/scrypted_yolov9t_relu/scrypted_yolov9t_relu.mlpackage"),
661
- sizeMB: 4,
662
- isDirectory: true,
663
- files: [...MLPACKAGE_FILES],
664
- runtimes: ["python"]
665
- },
666
- openvino: ovFormat(hf("objectDetection/scrypted-yolov9-relu/openvino/scrypted_yolov9t_relu.xml"), 6)
667
- }
668
- },
669
- {
670
- id: "scrypted-yolov9s-relu",
671
- legacy: true,
672
- name: "YOLOv9 Small ReLU",
673
- description: "YOLOv9s ReLU — improved efficiency, int8 ready",
674
- inputSize: {
675
- width: 320,
676
- height: 320
677
- },
678
- labels: [],
679
- preprocessMode: "letterbox",
680
- formats: {
681
- onnx: {
682
- url: hfScrypted("onnx/scrypted_yolov9s_relu/scrypted_yolov9s_relu.onnx"),
683
- sizeMB: 27.9
684
- },
685
- coreml: {
686
- url: hfScrypted("coreml/scrypted_yolov9s_relu/scrypted_yolov9s_relu.mlpackage"),
687
- sizeMB: 14,
688
- isDirectory: true,
689
- files: [...MLPACKAGE_FILES],
690
- runtimes: ["python"]
691
- },
692
- openvino: ovFormat(hf("objectDetection/scrypted-yolov9-relu/openvino/scrypted_yolov9s_relu.xml"), 16)
693
- }
694
- },
695
- {
696
- id: "scrypted-yolov9c-relu",
697
- legacy: true,
698
- name: "YOLOv9 C ReLU",
699
- description: "YOLOv9c ReLU — high-accuracy, int8 ready",
700
- inputSize: {
701
- width: 320,
702
- height: 320
703
- },
704
- labels: [],
705
- preprocessMode: "letterbox",
706
- formats: {
707
- onnx: {
708
- url: hfScrypted("onnx/scrypted_yolov9c_relu/scrypted_yolov9c_relu.onnx"),
709
- sizeMB: 96.9
710
- },
711
- coreml: {
712
- url: hfScrypted("coreml/scrypted_yolov9c_relu/scrypted_yolov9c_relu.mlpackage"),
713
- sizeMB: 48,
714
- isDirectory: true,
715
- files: [...MLPACKAGE_FILES],
716
- runtimes: ["python"]
717
- },
718
- openvino: ovFormat(hf("objectDetection/scrypted-yolov9-relu/openvino/scrypted_yolov9c_relu.xml"), 49)
719
- }
720
- },
721
- {
722
- id: "scrypted-yolov9m-relu",
723
- legacy: true,
724
- name: "YOLOv9 M ReLU",
725
- description: "YOLOv9m ReLU — medium, int8 ready",
726
- inputSize: {
727
- width: 320,
728
- height: 320
729
- },
730
- labels: [],
731
- preprocessMode: "letterbox",
732
- formats: {
733
- onnx: {
734
- url: hfScrypted("onnx/scrypted_yolov9m_relu/scrypted_yolov9m_relu.onnx"),
735
- sizeMB: 76.6
736
- },
737
- coreml: {
738
- url: hfScrypted("coreml/scrypted_yolov9m_relu/scrypted_yolov9m_relu.mlpackage"),
739
- sizeMB: 38,
740
- isDirectory: true,
741
- files: [...MLPACKAGE_FILES],
742
- runtimes: ["python"]
743
- },
744
- openvino: ovFormat(hf("objectDetection/scrypted-yolov9-relu/openvino/scrypted_yolov9m_relu.xml"), 38)
745
- }
746
- },
747
- ovPrecisionVariant("yolov9t", "objectDetection/yolov9/openvino", "YOLOv9 Tiny", "fp16", 5, true),
748
- ovPrecisionVariant("yolov9t", "objectDetection/yolov9/openvino", "YOLOv9 Tiny", "int8", 3, true),
749
- ovPrecisionVariant("yolov9s", "objectDetection/yolov9/openvino", "YOLOv9 Small", "fp16", 15, true),
750
- ovPrecisionVariant("yolov9s", "objectDetection/yolov9/openvino", "YOLOv9 Small", "int8", 8, true),
751
- ovPrecisionVariant("yolo26n", "objectDetection/yolo26/openvino", "YOLO26 Nano", "fp16", 5, true),
752
- ovPrecisionVariant("yolo26n", "objectDetection/yolo26/openvino", "YOLO26 Nano", "int8", 3),
753
- ovPrecisionVariant("yolo26s", "objectDetection/yolo26/openvino", "YOLO26 Small", "fp16", 19, true),
754
- ovPrecisionVariant("yolo26s", "objectDetection/yolo26/openvino", "YOLO26 Small", "int8", 10),
755
- ovPrecisionVariant("yolo26m", "objectDetection/yolo26/openvino", "YOLO26 Medium", "fp16", 41, true),
756
- ovPrecisionVariant("yolo26m", "objectDetection/yolo26/openvino", "YOLO26 Medium", "int8", 21),
757
- ovPrecisionVariant("yolo26l", "objectDetection/yolo26/openvino", "YOLO26 Large", "fp16", 50, true),
758
- ovPrecisionVariant("yolo26l", "objectDetection/yolo26/openvino", "YOLO26 Large", "int8", 25),
759
- ...yolo26Reduced("n", 320, {
760
- onnx: 10.47,
761
- coreml: 5.05,
762
- ovFp32: 10.05,
763
- ovInt8: 3.29
764
- }),
765
- ...yolo26Reduced("n", 256, {
766
- onnx: 10.47,
767
- coreml: 5.05,
768
- ovFp32: 10.05,
769
- ovInt8: 3.29
770
- }),
771
- ...yolo26Reduced("s", 320, {
772
- onnx: 38.82,
773
- coreml: 19.23,
774
- ovFp32: 38.4,
775
- ovInt8: 10.43
776
- }),
777
- ...yolo26Reduced("s", 256, {
778
- onnx: 38.82,
779
- coreml: 19.23,
780
- ovFp32: 38.4,
781
- ovInt8: 10.43
782
- }),
783
- ...yolo26Reduced("m", 320, {
784
- onnx: 83.31,
785
- coreml: 41.07,
786
- ovFp32: 82.09,
787
- ovInt8: 21.47
788
- }),
789
- ...yolo26Reduced("m", 256, {
790
- onnx: 83.31,
791
- coreml: 41.07,
792
- ovFp32: 82.09,
793
- ovInt8: 21.47
794
- }),
795
- ...yolo26Reduced("l", 320, {
796
- onnx: 101.74,
797
- coreml: 49.97,
798
- ovFp32: 99.85,
799
- ovInt8: 26.31
800
- }),
801
- ...yolo26Reduced("l", 256, {
802
- onnx: 101.74,
803
- coreml: 49.97,
804
- ovFp32: 99.85,
805
- ovInt8: 26.31
806
- }),
807
- ovPrecisionVariant("yolo26x", "objectDetection/yolo26/openvino", "YOLO26 XLarge", "fp16", 112, true),
808
- ovPrecisionVariant("yolo26x", "objectDetection/yolo26/openvino", "YOLO26 XLarge", "int8", 56, true)
809
- ];
810
- var FACE_DETECTION_MODELS = [{
811
- id: "scrfd-2.5g",
812
- name: "SCRFD 2.5G",
813
- description: "SCRFD 2.5G — balanced face detection model",
814
- inputSize: {
815
- width: 640,
816
- height: 640
817
- },
818
- labels: [{
819
- id: "face",
820
- name: "Face"
821
- }],
822
- preprocessMode: "letterbox",
823
- formats: {
824
- onnx: {
825
- url: hf("faceDetection/scrfd/onnx/camstack-scrfd-2.5g.onnx"),
826
- sizeMB: 3.1
827
- },
828
- coreml: {
829
- url: hf("faceDetection/scrfd/coreml/camstack-scrfd-2.5g.mlpackage"),
830
- sizeMB: 1.7,
831
- isDirectory: true,
832
- files: [...MLPACKAGE_FILES],
833
- runtimes: ["python"]
834
- },
835
- openvino: ovFormat(hf("faceDetection/scrfd/openvino/camstack-scrfd-2.5g.xml"), 1.8)
836
- }
837
- }, {
838
- id: "scrypted-yolov9t-face",
839
- name: "YOLOv9t Face ReLU",
840
- description: "YOLOv9t face detection — YOLO-based, fast, 320x320 ReLU",
841
- inputSize: {
842
- width: 320,
843
- height: 320
844
- },
845
- labels: [{
846
- id: "face",
847
- name: "Face"
848
- }],
849
- preprocessMode: "letterbox",
850
- formats: {
851
- onnx: {
852
- url: hfScrypted("onnx/scrypted_yolov9t_relu_face/scrypted_yolov9t_relu_face.onnx"),
853
- sizeMB: 8.1
854
- },
855
- coreml: {
856
- url: hfScrypted("coreml/scrypted_yolov9t_relu_face/scrypted_yolov9t_relu_face.mlpackage"),
857
- sizeMB: 4,
858
- isDirectory: true,
859
- files: [...MLPACKAGE_FILES],
860
- runtimes: ["python"]
861
- },
862
- openvino: ovFormat(hf("faceDetection/scrypted-yolov9-face/openvino/scrypted_yolov9t_relu_face.xml"), 6)
863
- }
864
- }];
865
- var FACE_EMBEDDING_MODELS = [{
866
- id: "arcface-r100",
867
- name: "ArcFace R100",
868
- description: "ArcFace ResNet-100 — high-accuracy face recognition embeddings (512-d)",
869
- inputSize: {
870
- width: 112,
871
- height: 112
872
- },
873
- inputLayout: "nhwc",
874
- labels: [{
875
- id: "embedding",
876
- name: "Face Embedding"
877
- }],
878
- preprocessMode: "resize",
879
- faceAlignment: true,
880
- formats: {
881
- onnx: {
882
- url: hf("faceRecognition/arcface/onnx/camstack-arcface-arcface.onnx"),
883
- sizeMB: 130
884
- },
885
- coreml: {
886
- url: hf("faceRecognition/arcface/coreml/camstack-arcface-r100.mlpackage"),
887
- sizeMB: 65,
888
- isDirectory: true,
889
- files: [...MLPACKAGE_FILES],
890
- runtimes: ["python"]
891
- },
892
- openvino: ovFormat(hf("faceRecognition/arcface/openvino/camstack-arcface-r100.xml"), 65)
893
- }
894
- }, {
895
- id: "inception-resnet-v1",
896
- name: "Inception ResNet V1",
897
- description: "FaceNet-style face recognition embeddings (512-d) — hosted on plugin-models HF repo",
898
- inputSize: {
899
- width: 160,
900
- height: 160
901
- },
902
- labels: [{
903
- id: "embedding",
904
- name: "Face Embedding"
905
- }],
906
- preprocessMode: "resize",
907
- formats: {
908
- onnx: {
909
- url: hfScrypted("onnx/inception_resnet_v1/inception_resnet_v1.onnx"),
910
- sizeMB: 89.6
911
- },
912
- coreml: {
913
- url: hfScrypted("coreml/inception_resnet_v1/inception_resnet_v1.mlpackage"),
914
- sizeMB: 45,
915
- isDirectory: true,
916
- files: [...MLPACKAGE_FILES],
917
- runtimes: ["python"]
918
- },
919
- openvino: ovFormat(hf("faceRecognition/inception-resnet-v1/openvino/camstack-inception-resnet-v1.xml"), 45)
920
- }
921
- }];
922
- var PLATE_DETECTION_MODELS = [{
923
- id: "yolov8n-plate",
924
- name: "YOLOv8 Nano — License Plate",
925
- description: "YOLOv8 Nano fine-tuned for license plate detection",
926
- inputSize: {
927
- width: 640,
928
- height: 640
929
- },
930
- labels: [{
931
- id: "plate",
932
- name: "License Plate"
933
- }],
934
- preprocessMode: "letterbox",
935
- formats: {
936
- onnx: {
937
- url: hf("plateDetection/yolov8-plate/onnx/camstack-yolov8n-plate.onnx"),
938
- sizeMB: 12
939
- },
940
- coreml: {
941
- url: hf("plateDetection/yolov8-plate/coreml/camstack-yolov8n-plate.mlpackage"),
942
- sizeMB: 5.9,
943
- isDirectory: true,
944
- files: [...MLPACKAGE_FILES],
945
- runtimes: ["python"]
946
- },
947
- openvino: ovFormat(hf("plateDetection/yolov8-plate/openvino/camstack-yolov8n-plate.xml"), 6.1)
948
- }
949
- }];
950
- var PLATE_OCR_MODELS = [{
951
- id: "vgg-english-g2",
952
- name: "VGG English G2",
953
- description: "EasyOCR VGG English G2 — text recognition for license plates and scene text",
954
- inputSize: {
955
- width: 384,
956
- height: 64
957
- },
958
- labels: [{
959
- id: "text",
960
- name: "Plate Text"
961
- }],
962
- preprocessMode: "resize",
963
- formats: {
964
- onnx: {
965
- url: hfScrypted("onnx/vgg_english_g2/vgg_english_g2.onnx"),
966
- sizeMB: 14.4
967
- },
968
- coreml: {
969
- url: hfScrypted("coreml/vgg_english_g2/vgg_english_g2.mlpackage"),
970
- sizeMB: 11.2,
971
- isDirectory: true,
972
- files: [...MLPACKAGE_FILES],
973
- runtimes: ["python"]
974
- },
975
- openvino: ovFormat(hf("plateRecognition/vgg_english_g2/openvino/vgg_english_g2.xml"), 7.2)
976
- }
977
- }];
978
- var ANIMAL_CLASSIFIER_MODELS = [{
979
- id: "animals-10",
980
- name: "Animal Classifier (10)",
981
- description: "ViT-based animal type classifier — cat, cow, dog, dolphin, eagle, panda, horse, monkey, sheep, spider",
982
- inputSize: {
983
- width: 224,
984
- height: 224
985
- },
986
- inputNormalization: "imagenet",
987
- labels: [{
988
- id: "animal-type",
989
- name: "Animal Type"
990
- }],
991
- preprocessMode: "resize",
992
- formats: {
993
- onnx: {
994
- url: hf("animalClassification/animals-10/onnx/camstack-animals-10.onnx"),
995
- sizeMB: 328
996
- },
997
- coreml: {
998
- url: hf("animalClassification/animals-10/coreml/camstack-animals-10.mlpackage"),
999
- sizeMB: 164,
1000
- isDirectory: true,
1001
- files: [...MLPACKAGE_FILES],
1002
- runtimes: ["python"]
1003
- },
1004
- openvino: ovFormat(hf("animalClassification/animals-10/openvino/camstack-animals-10.xml"), 164)
1005
- }
1006
- }];
1007
- var BIRD_CLASSIFIER_MODELS = [{
1008
- id: "bird-nabirds-404",
1009
- name: "NABirds (404 species)",
1010
- description: "ResNet50 trained on NABirds — 404 North American species",
1011
- inputSize: {
1012
- width: 224,
1013
- height: 224
1014
- },
1015
- inputNormalization: "imagenet",
1016
- labels: [{
1017
- id: "species",
1018
- name: "Bird Species"
1019
- }],
1020
- preprocessMode: "resize",
1021
- formats: {
1022
- onnx: {
1023
- url: hf("animalClassification/bird-nabirds/onnx/camstack-bird-nabirds-404.onnx"),
1024
- sizeMB: 93
1025
- },
1026
- coreml: {
1027
- url: hf("animalClassification/bird-nabirds/coreml/camstack-bird-nabirds-404.mlpackage"),
1028
- sizeMB: 47,
1029
- isDirectory: true,
1030
- files: [...MLPACKAGE_FILES],
1031
- runtimes: ["python"]
1032
- },
1033
- openvino: ovFormat(hf("animalClassification/bird-nabirds/openvino/camstack-bird-nabirds-404.xml"), 47)
1034
- },
1035
- extraFiles: [{
1036
- url: hf("animalClassification/bird-nabirds/onnx/camstack-bird-nabirds-404-labels.json"),
1037
- filename: "camstack-bird-nabirds-404-labels.json",
1038
- sizeMB: .02
1039
- }]
1040
- }];
1041
- var VEHICLE_CLASSIFIER_MODELS = [{
1042
- id: "vehicle-type-efficientnet",
1043
- name: "Vehicle Type (EfficientNet)",
1044
- description: "EfficientNet-B4 vehicle make/model/year classifier — 8,949 classes from VMMRdb",
1045
- inputSize: {
1046
- width: 380,
1047
- height: 380
1048
- },
1049
- inputNormalization: "imagenet",
1050
- labels: [{
1051
- id: "vehicle-type",
1052
- name: "Vehicle Type"
1053
- }],
1054
- preprocessMode: "resize",
1055
- formats: {
1056
- onnx: {
1057
- url: hf("vehicleClassification/efficientnet/onnx/camstack-vehicle-type-efficientnet.onnx"),
1058
- sizeMB: 135
1059
- },
1060
- coreml: {
1061
- url: hf("vehicleClassification/efficientnet/coreml/camstack-vehicle-type-efficientnet.mlpackage"),
1062
- sizeMB: 10,
1063
- isDirectory: true,
1064
- files: [...MLPACKAGE_FILES],
1065
- runtimes: ["python"]
1066
- },
1067
- openvino: ovFormat(hf("vehicleClassification/efficientnet/openvino/camstack-vehicle-type-efficientnet.xml"), 68)
1068
- },
1069
- extraFiles: [{
1070
- url: hf("vehicleClassification/efficientnet/camstack-vehicle-type-labels.json"),
1071
- filename: "camstack-vehicle-type-labels.json",
1072
- sizeMB: .2
1073
- }]
1074
- }];
1075
- var SEGMENTATION_REFINER_MODELS = [{
1076
- id: "u2netp",
1077
- name: "U2-Net Portable",
1078
- description: "U2-Net-P — ultra-lightweight salient object segmentation (4.7 MB)",
1079
- inputSize: {
1080
- width: 320,
1081
- height: 320
1082
- },
1083
- labels: [{
1084
- id: "mask",
1085
- name: "Segmentation Mask"
1086
- }],
1087
- preprocessMode: "resize",
1088
- formats: {
1089
- onnx: {
1090
- url: hf("segmentationRefiner/u2netp/onnx/camstack-u2netp.onnx"),
1091
- sizeMB: 5
1092
- },
1093
- coreml: {
1094
- url: hf("segmentationRefiner/u2netp/coreml/camstack-u2netp.mlpackage"),
1095
- sizeMB: 3,
1096
- isDirectory: true,
1097
- files: [...MLPACKAGE_FILES],
1098
- runtimes: ["python"]
1099
- },
1100
- openvino: ovFormat(hf("segmentationRefiner/u2netp/openvino/camstack-u2netp.xml"), 2.5)
1101
- }
1102
- }];
1103
- var INSTANCE_SEGMENTATION_MODELS = [
1104
- {
1105
- id: "yolo26n-seg",
1106
- name: "YOLO26 Nano Seg",
1107
- description: "YOLO26 Nano Segmentation — ultra-lightweight instance segmentation with masks",
1108
- inputSize: {
1109
- width: 640,
1110
- height: 640
1111
- },
1112
- labels: [],
1113
- preprocessMode: "letterbox",
1114
- formats: {
1115
- onnx: {
1116
- url: hf("segmentation/yolo26-seg/onnx/camstack-yolo26n-seg.onnx"),
1117
- sizeMB: 11
1118
- },
1119
- coreml: {
1120
- url: hf("segmentation/yolo26-seg/coreml/camstack-yolo26n-seg.mlpackage"),
1121
- sizeMB: 6,
1122
- isDirectory: true,
1123
- files: [...MLPACKAGE_FILES],
1124
- runtimes: ["python"]
1125
- },
1126
- openvino: ovFormat(hf("segmentation/yolo26-seg/openvino/camstack-yolo26n-seg.xml"), 11)
1127
- }
1128
- },
1129
- {
1130
- id: "yolo26s-seg",
1131
- name: "YOLO26 Small Seg",
1132
- description: "YOLO26 Small Segmentation — balanced instance segmentation",
1133
- inputSize: {
1134
- width: 640,
1135
- height: 640
1136
- },
1137
- labels: [],
1138
- preprocessMode: "letterbox",
1139
- formats: {
1140
- onnx: {
1141
- url: hf("segmentation/yolo26-seg/onnx/camstack-yolo26s-seg.onnx"),
1142
- sizeMB: 40
1143
- },
1144
- coreml: {
1145
- url: hf("segmentation/yolo26-seg/coreml/camstack-yolo26s-seg.mlpackage"),
1146
- sizeMB: 20,
1147
- isDirectory: true,
1148
- files: [...MLPACKAGE_FILES],
1149
- runtimes: ["python"]
1150
- },
1151
- openvino: ovFormat(hf("segmentation/yolo26-seg/openvino/camstack-yolo26s-seg.xml"), 40)
1152
- }
1153
- },
1154
- {
1155
- id: "yolo26m-seg",
1156
- name: "YOLO26 Medium Seg",
1157
- description: "YOLO26 Medium Segmentation — high-accuracy instance segmentation",
1158
- inputSize: {
1159
- width: 640,
1160
- height: 640
1161
- },
1162
- labels: [],
1163
- preprocessMode: "letterbox",
1164
- formats: {
1165
- onnx: {
1166
- url: hf("segmentation/yolo26-seg/onnx/camstack-yolo26m-seg.onnx"),
1167
- sizeMB: 90
1168
- },
1169
- coreml: {
1170
- url: hf("segmentation/yolo26-seg/coreml/camstack-yolo26m-seg.mlpackage"),
1171
- sizeMB: 45,
1172
- isDirectory: true,
1173
- files: [...MLPACKAGE_FILES],
1174
- runtimes: ["python"]
1175
- },
1176
- openvino: ovFormat(hf("segmentation/yolo26-seg/openvino/camstack-yolo26m-seg.xml"), 90)
1177
- }
1178
- }
1179
- ];
1180
- var CLIP_EMBEDDING_MODELS = [
1181
- {
1182
- id: "mobileclip-s0",
1183
- name: "MobileCLIP S0",
1184
- description: "MobileCLIP S0 — Apple ultra-fast CLIP vision encoder, 512-dim, 256×256 (40 MB ONNX)",
1185
- inputSize: {
1186
- width: 256,
1187
- height: 256
1188
- },
1189
- labels: [{
1190
- id: "embedding",
1191
- name: "CLIP Embedding"
1192
- }],
1193
- preprocessMode: "resize",
1194
- inputNormalization: "none",
1195
- formats: {
1196
- onnx: {
1197
- url: hf("clip/mobileclip-s0/onnx/camstack-mobileclip-s0-vision.onnx"),
1198
- sizeMB: 40
1199
- },
1200
- openvino: ovFormat(hf("clip/mobileclip-s0/openvino/camstack-mobileclip-s0-vision.xml"), 25),
1201
- coreml: {
1202
- url: hf("clip/mobileclip-s0/coreml/camstack-mobileclip-s0-vision.mlpackage"),
1203
- sizeMB: 30,
1204
- isDirectory: true,
1205
- files: [...MLPACKAGE_FILES],
1206
- runtimes: ["python"]
1207
- }
1208
- }
1209
- },
1210
- {
1211
- id: "mobileclip-s1",
1212
- name: "MobileCLIP S1",
1213
- description: "MobileCLIP S1 — Apple balanced CLIP vision encoder, 512-dim, 256×256 (90 MB ONNX)",
1214
- inputSize: {
1215
- width: 256,
1216
- height: 256
1217
- },
1218
- labels: [{
1219
- id: "embedding",
1220
- name: "CLIP Embedding"
1221
- }],
1222
- preprocessMode: "resize",
1223
- inputNormalization: "none",
1224
- formats: {
1225
- onnx: {
1226
- url: hf("clip/mobileclip-s1/onnx/camstack-mobileclip-s1-vision.onnx"),
1227
- sizeMB: 90
1228
- },
1229
- openvino: ovFormat(hf("clip/mobileclip-s1/openvino/camstack-mobileclip-s1-vision.xml"), 55),
1230
- coreml: {
1231
- url: hf("clip/mobileclip-s1/coreml/camstack-mobileclip-s1-vision.mlpackage"),
1232
- sizeMB: 65,
1233
- isDirectory: true,
1234
- files: [...MLPACKAGE_FILES],
1235
- runtimes: ["python"]
1236
- }
1237
- }
1238
- },
1239
- {
1240
- id: "mobileclip-s2",
1241
- name: "MobileCLIP S2",
1242
- description: "MobileCLIP S2 — Apple high-accuracy CLIP vision encoder, 512-dim, 256×256 (150 MB ONNX)",
1243
- inputSize: {
1244
- width: 256,
1245
- height: 256
1246
- },
1247
- labels: [{
1248
- id: "embedding",
1249
- name: "CLIP Embedding"
1250
- }],
1251
- preprocessMode: "resize",
1252
- inputNormalization: "none",
1253
- formats: {
1254
- onnx: {
1255
- url: hf("clip/mobileclip-s2/onnx/camstack-mobileclip-s2-vision.onnx"),
1256
- sizeMB: 150
1257
- },
1258
- openvino: ovFormat(hf("clip/mobileclip-s2/openvino/camstack-mobileclip-s2-vision.xml"), 90),
1259
- coreml: {
1260
- url: hf("clip/mobileclip-s2/coreml/camstack-mobileclip-s2-vision.mlpackage"),
1261
- sizeMB: 110,
1262
- isDirectory: true,
1263
- files: [...MLPACKAGE_FILES],
1264
- runtimes: ["python"]
1265
- }
1266
- }
1267
- }
1268
- ];
1269
- var AUDIO_CLASSIFIER_MODELS = [{
1270
- id: "yamnet-onnx",
1271
- name: "YAMNet",
1272
- description: "Google YAMNet — 521-class audio event classifier (3.2 MB ONNX, runs on any platform)",
1273
- inputSize: {
1274
- width: 1,
1275
- height: 16e3
1276
- },
1277
- labels: [],
1278
- preprocessMode: "resize",
1279
- formats: {
1280
- onnx: {
1281
- url: hf("audioClassification/yamnet/onnx/camstack-yamnet.onnx"),
1282
- sizeMB: 3.2
1283
- },
1284
- openvino: ovFormat(hf("audioClassification/yamnet/openvino/camstack-yamnet.xml"), 3.2),
1285
- coreml: {
1286
- url: hf("audioClassification/yamnet/onnx/camstack-yamnet.onnx"),
1287
- sizeMB: 3.2,
1288
- runtimes: ["python"]
1289
- }
1290
- }
1291
- }, {
1292
- id: "apple-soundanalysis",
1293
- name: "Apple SoundAnalysis",
1294
- description: "macOS built-in — 303 sound categories, Neural Engine accelerated, zero download",
1295
- inputSize: {
1296
- width: 1,
1297
- height: 16e3
1298
- },
1299
- labels: [],
1300
- preprocessMode: "resize",
1301
- formats: { coreml: {
1302
- url: "",
1303
- sizeMB: 0
1304
- } }
1305
- }];
1306
- //#endregion
1307
- //#region src/detection-pipeline/registry/step-definitions.ts
1308
- var ANIMAL_10_LABELS = [
1309
- "cat",
1310
- "cow",
1311
- "dog",
1312
- "dolphin",
1313
- "eagle",
1314
- "giant panda",
1315
- "horse",
1316
- "monkey",
1317
- "sheep",
1318
- "spider"
1319
- ];
1320
- var VGG_G2_CHARSET = ["", ..."0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split("")];
1321
- var PipelineStepBase = class {
1322
- definition;
1323
- constructor(definition) {
1324
- this.definition = definition;
1325
- }
1326
- getConfigSchema() {
1327
- return [];
1328
- }
1329
- };
1330
- var ObjectDetectionStep = class {
1331
- definition = {
1332
- id: "object-detection",
1333
- name: "Object Detection",
1334
- slot: "detector",
1335
- postprocessor: "yolo",
1336
- extractMode: "full-frame",
1337
- inputClasses: null,
1338
- outputClasses: [
1339
- "person",
1340
- "vehicle",
1341
- "animal"
1342
- ],
1343
- models: [...OBJECT_DETECTION_MODELS],
1344
- defaultModelId: "yolo26n",
1345
- defaultConfidence: .5,
1346
- labels: require_dist.COCO_80_LABELS.map((l) => l.id),
1347
- classMap: require_dist.COCO_TO_MACRO
1348
- };
1349
- getConfigSchema() {
1350
- return [
1351
- {
1352
- type: "multiselect",
1353
- key: "enabledMacroClasses",
1354
- label: "Enabled Classes",
1355
- description: "Which macro classes to detect (empty = all three).",
1356
- options: [
1357
- {
1358
- value: "person",
1359
- label: "Person"
1360
- },
1361
- {
1362
- value: "vehicle",
1363
- label: "Vehicle"
1364
- },
1365
- {
1366
- value: "animal",
1367
- label: "Animal"
1368
- }
1369
- ],
1370
- default: [
1371
- "person",
1372
- "vehicle",
1373
- "animal"
1374
- ]
1375
- },
1376
- {
1377
- type: "slider",
1378
- key: "minConfidencePerson",
1379
- label: "Min Confidence — Person",
1380
- description: "Drop person predictions below this score.",
1381
- min: 0,
1382
- max: 1,
1383
- step: .05,
1384
- default: .5,
1385
- showValue: true
1386
- },
1387
- {
1388
- type: "slider",
1389
- key: "minConfidenceVehicle",
1390
- label: "Min Confidence — Vehicle",
1391
- description: "Drop vehicle predictions below this score.",
1392
- min: 0,
1393
- max: 1,
1394
- step: .05,
1395
- default: .5,
1396
- showValue: true
1397
- },
1398
- {
1399
- type: "slider",
1400
- key: "minConfidenceAnimal",
1401
- label: "Min Confidence — Animal",
1402
- description: "Drop animal predictions below this score.",
1403
- min: 0,
1404
- max: 1,
1405
- step: .05,
1406
- default: .5,
1407
- showValue: true
1408
- }
1409
- ];
1410
- }
1411
- };
1412
- var AudioClassifierStep = class {
1413
- definition = {
1414
- id: "audio-classifier",
1415
- name: "Audio Classifier",
1416
- slot: "audio-classifier",
1417
- postprocessor: "yamnet",
1418
- extractMode: "full-frame",
1419
- inputClasses: null,
1420
- outputClasses: ["audio"],
1421
- models: [...AUDIO_CLASSIFIER_MODELS],
1422
- defaultModelId: "yamnet-onnx",
1423
- defaultModelIdByFormat: { coreml: "apple-soundanalysis" },
1424
- enabledByDefault: true,
1425
- defaultConfidence: .3
1426
- };
1427
- getConfigSchema() {
1428
- return [{
1429
- type: "multiselect",
1430
- key: "enabledAudioClasses",
1431
- label: "Audio Classes",
1432
- description: "Which audio macro classes to surface (empty = all).",
1433
- options: require_dist.AUDIO_MACRO_LABELS.map((l) => ({
1434
- value: l.id,
1435
- label: `${l.icon ?? ""} ${l.name}`.trim()
1436
- })),
1437
- default: [
1438
- "scream",
1439
- "crying",
1440
- "dog",
1441
- "alarm",
1442
- "doorbell",
1443
- "glass_breaking",
1444
- "gunshot",
1445
- "siren",
1446
- "footsteps"
1447
- ]
1448
- }, {
1449
- type: "slider",
1450
- key: "minConfidence",
1451
- label: "Min Confidence",
1452
- description: "Drop predictions whose score is below this threshold.",
1453
- default: .3,
1454
- min: 0,
1455
- max: 1,
1456
- step: .05,
1457
- showValue: true
1458
- }];
1459
- }
1460
- };
1461
- var AnimalClassifierStep = class {
1462
- definition = {
1463
- id: "animal-classifier",
1464
- name: "Animal Classifier",
1465
- slot: "classifier",
1466
- postprocessor: "softmax",
1467
- extractMode: "crop-roi",
1468
- inputClasses: ["animal"],
1469
- outputClasses: ["animal-type"],
1470
- models: [...ANIMAL_CLASSIFIER_MODELS],
1471
- defaultModelId: "animals-10",
1472
- enabledByDefault: false,
1473
- defaultConfidence: .2,
1474
- labels: [...ANIMAL_10_LABELS]
1475
- };
1476
- getConfigSchema() {
1477
- return [{
1478
- type: "multiselect",
1479
- key: "enabledClasses",
1480
- label: "Species",
1481
- description: "Which animal species to keep (empty = all). The classifier still scores every label but only the selected classes are returned on the detection.",
1482
- options: ANIMAL_10_LABELS.map((l) => ({
1483
- value: l,
1484
- label: l
1485
- })),
1486
- default: []
1487
- }, {
1488
- type: "slider",
1489
- key: "minConfidence",
1490
- label: "Min Confidence",
1491
- description: "Drop classifications whose score is below this threshold.",
1492
- default: this.definition.defaultConfidence,
1493
- min: 0,
1494
- max: 1,
1495
- step: .05,
1496
- showValue: true
1497
- }];
1498
- }
1499
- };
1500
- var ClassifierWithMinConfidence = class extends PipelineStepBase {
1501
- getConfigSchema() {
1502
- return [{
1503
- type: "slider",
1504
- key: "minConfidence",
1505
- label: "Min Confidence",
1506
- description: "Drop classifications whose score is below this threshold.",
1507
- default: this.definition.defaultConfidence,
1508
- min: 0,
1509
- max: 1,
1510
- step: .05,
1511
- showValue: true
1512
- }];
1513
- }
1514
- };
1515
- var STEP_FACE_DETECTION = new PipelineStepBase({
1516
- id: "face-detection",
1517
- name: "Face Detection",
1518
- slot: "cropper",
1519
- postprocessor: "scrfd",
1520
- extractMode: "crop-roi",
1521
- inputClasses: ["person"],
1522
- outputClasses: ["face"],
1523
- models: [...FACE_DETECTION_MODELS],
1524
- defaultModelId: "scrfd-2.5g",
1525
- defaultConfidence: .5,
1526
- defaultMinParentScore: .7
1527
- });
1528
- var STEP_FACE_EMBEDDING = new PipelineStepBase({
1529
- id: "face-embedding",
1530
- name: "Face Embedding",
1531
- slot: "classifier",
1532
- postprocessor: "arcface",
1533
- extractMode: "crop-roi",
1534
- inputClasses: ["face"],
1535
- outputClasses: ["identity"],
1536
- models: [...FACE_EMBEDDING_MODELS],
1537
- defaultModelId: "arcface-r100",
1538
- defaultConfidence: 0
1539
- });
1540
- var STEP_CLIP_EMBEDDING = new PipelineStepBase({
1541
- id: "clip-embedding",
1542
- name: "CLIP Embedding",
1543
- slot: "classifier",
1544
- postprocessor: "clip",
1545
- extractMode: "crop-roi",
1546
- inputClasses: [
1547
- "person",
1548
- "vehicle",
1549
- "animal"
1550
- ],
1551
- outputClasses: ["embedding"],
1552
- models: [...CLIP_EMBEDDING_MODELS],
1553
- defaultModelId: "mobileclip-s1",
1554
- enabledByDefault: true,
1555
- backfillIntoExistingOverrides: true,
1556
- defaultConfidence: 0,
1557
- defaultMinParentScore: .4,
1558
- group: "CLIP"
1559
- });
1560
- var STEP_PLATE_DETECTION = new PipelineStepBase({
1561
- id: "plate-detection",
1562
- name: "Plate Detection",
1563
- slot: "cropper",
1564
- postprocessor: "yolo",
1565
- extractMode: "crop-roi",
1566
- inputClasses: ["vehicle"],
1567
- outputClasses: ["plate"],
1568
- models: [...PLATE_DETECTION_MODELS],
1569
- defaultModelId: "yolov8n-plate",
1570
- defaultConfidence: .5,
1571
- defaultMinParentScore: .7,
1572
- labels: ["plate"]
1573
- });
1574
- var STEP_PLATE_OCR = new PipelineStepBase({
1575
- id: "plate-ocr",
1576
- name: "Plate OCR",
1577
- slot: "classifier",
1578
- postprocessor: "ctc",
1579
- extractMode: "crop-roi",
1580
- inputClasses: ["plate"],
1581
- outputClasses: ["plate-text"],
1582
- models: [...PLATE_OCR_MODELS],
1583
- defaultModelId: "vgg-english-g2",
1584
- defaultConfidence: 0,
1585
- charset: [...VGG_G2_CHARSET]
1586
- });
1587
- var STEP_BIRD_CLASSIFIER = new ClassifierWithMinConfidence({
1588
- id: "bird-classifier",
1589
- name: "Bird Classifier",
1590
- slot: "classifier",
1591
- postprocessor: "softmax",
1592
- extractMode: "crop-roi",
1593
- inputClasses: ["animal"],
1594
- outputClasses: ["species"],
1595
- models: [...BIRD_CLASSIFIER_MODELS],
1596
- defaultModelId: "bird-nabirds-404",
1597
- enabledByDefault: false,
1598
- defaultConfidence: .2
1599
- });
1600
- var STEP_VEHICLE_CLASSIFIER = new ClassifierWithMinConfidence({
1601
- id: "vehicle-classifier",
1602
- name: "Vehicle Classifier",
1603
- slot: "classifier",
1604
- postprocessor: "softmax",
1605
- extractMode: "crop-roi",
1606
- inputClasses: ["vehicle"],
1607
- outputClasses: ["vehicle-type"],
1608
- models: [...VEHICLE_CLASSIFIER_MODELS],
1609
- defaultModelId: "vehicle-type-efficientnet",
1610
- enabledByDefault: false,
1611
- defaultConfidence: .3
1612
- });
1613
- var STEP_SEGMENTATION = new PipelineStepBase({
1614
- id: "segmentation",
1615
- name: "Segmentation",
1616
- slot: "refiner",
1617
- postprocessor: "saliency",
1618
- extractMode: "crop-roi",
1619
- inputClasses: null,
1620
- outputClasses: ["mask"],
1621
- models: [...SEGMENTATION_REFINER_MODELS],
1622
- defaultModelId: "u2netp",
1623
- enabledByDefault: false,
1624
- defaultConfidence: 0,
1625
- group: "Segmentation"
1626
- });
1627
- new PipelineStepBase({
1628
- id: "instance-segmentation",
1629
- name: "Instance Segmentation",
1630
- slot: "refiner",
1631
- postprocessor: "yolo-seg",
1632
- extractMode: "full-frame",
1633
- inputClasses: null,
1634
- outputClasses: ["mask"],
1635
- models: [...INSTANCE_SEGMENTATION_MODELS],
1636
- defaultModelId: "yolo26n-seg",
1637
- enabledByDefault: false,
1638
- defaultConfidence: .25,
1639
- labels: require_dist.COCO_80_LABELS.map((l) => l.id),
1640
- classMap: require_dist.COCO_TO_MACRO,
1641
- group: "Segmentation"
1642
- });
1643
- var STEP_OBJECT_DETECTION_INSTANCE = new ObjectDetectionStep();
1644
- var STEP_AUDIO_CLASSIFIER_INSTANCE = new AudioClassifierStep();
1645
- var ALL_PIPELINE_STEPS = [
1646
- STEP_OBJECT_DETECTION_INSTANCE,
1647
- STEP_FACE_DETECTION,
1648
- STEP_FACE_EMBEDDING,
1649
- STEP_CLIP_EMBEDDING,
1650
- STEP_PLATE_DETECTION,
1651
- STEP_PLATE_OCR,
1652
- new AnimalClassifierStep(),
1653
- STEP_BIRD_CLASSIFIER,
1654
- STEP_VEHICLE_CLASSIFIER,
1655
- STEP_SEGMENTATION,
1656
- STEP_AUDIO_CLASSIFIER_INSTANCE
1657
- ];
1658
- /** Compat: flat array of StepDefinition for existing consumers */
1659
- var ALL_STEPS = ALL_PIPELINE_STEPS.map((s) => s.definition);
1660
- var STEP_MAP = new Map(ALL_PIPELINE_STEPS.map((s) => [s.definition.id, s]));
1661
- /**
1662
- * Look up a pipeline step by ID (class instance with getConfigSchema).
1663
- * @throws if the step ID is not registered.
1664
- */
1665
- function getStep(stepId) {
1666
- const step = STEP_MAP.get(stepId);
1667
- if (!step) throw new Error(`Unknown pipeline step: "${stepId}". Available: ${ALL_PIPELINE_STEPS.map((s) => s.definition.id).join(", ")}`);
1668
- return step;
1669
- }
1670
- /**
1671
- * Look up a step definition by ID (compat shortcut).
1672
- * @throws if the step ID is not registered.
1673
- */
1674
- function getStepDefinition(stepId) {
1675
- return getStep(stepId).definition;
1676
- }
1677
- /**
1678
- * Get the default model ID for a step given the current model format.
1679
- *
1680
- * Resolution order:
1681
- * 1. `def.defaultModelIdByFormat?.[format]` — the step's declared
1682
- * per-format preference — if that model exists in `def.models` AND
1683
- * has a build for `format`.
1684
- * 2. `def.defaultModelId` — the step's plain declared default — if it
1685
- * exists in `def.models` AND has a build for `format`.
1686
- * 3. The smallest-by-size model among those with a `format` build
1687
- * (legacy fallback, preserved for steps/formats with no declared
1688
- * preference reachable).
1689
- * 4. `def.defaultModelId` unchanged, when ZERO models have a `format`
1690
- * build — an unloadable case flagged elsewhere, not resolved here.
1691
- */
1692
- function getDefaultModelForFormat(stepId, format) {
1693
- const def = getStepDefinition(stepId);
1694
- const hasFormatBuild = (modelId) => def.models.find((m) => m.id === modelId)?.formats[format] !== void 0;
1695
- const declaredForFormat = def.defaultModelIdByFormat?.[format];
1696
- if (declaredForFormat !== void 0 && hasFormatBuild(declaredForFormat)) return declaredForFormat;
1697
- if (hasFormatBuild(def.defaultModelId)) return def.defaultModelId;
1698
- const available = def.models.filter((m) => m.formats[format] && m.legacy !== true);
1699
- if (available.length === 0) return def.defaultModelId;
1700
- return [...available].toSorted((a, b) => {
1701
- return (a.formats[format]?.sizeMB ?? Infinity) - (b.formats[format]?.sizeMB ?? Infinity);
1702
- })[0].id;
1703
- }
1704
- /**
1705
- * The single node-local authority for turning a CHOSEN model (or none) into a
1706
- * concrete model that has a build for this node's engine `format`. Fuses the
1707
- * two previously-duplicated decisions (orchestrator `pickDefaultModelId` +
1708
- * provider `substituteIncompatibleModels`):
1709
- *
1710
- * - chosen === null → the addon's default model for `format`.
1711
- * - chosen is a catalog model → keep it if it has a `format` build; else
1712
- * the addon's default model for `format`
1713
- * (a substitution — the caller surfaces it).
1714
- * - chosen is NOT in the catalog (custom/unknown) → returned unchanged; the
1715
- * operator owns a custom model's format compat.
1716
- * - unknown stepId → chosen returned unchanged (never throws).
1717
- */
1718
- function resolveModelForFormat(stepId, chosenModelId, format) {
1719
- let def;
1720
- try {
1721
- def = getStepDefinition(stepId);
1722
- } catch {
1723
- return chosenModelId ?? "";
1724
- }
1725
- if (chosenModelId === null) return getDefaultModelForFormat(stepId, format);
1726
- const entry = def.models.find((m) => m.id === chosenModelId);
1727
- if (!entry) return chosenModelId;
1728
- if (entry.formats[format]) return chosenModelId;
1729
- return getDefaultModelForFormat(stepId, format);
1730
- }
1731
- //#endregion
1732
302
  //#region src/detection-pipeline/default-detection-model.ts
1733
303
  /** The object-detection step id — the only slot this resolver applies to. */
1734
304
  var OBJECT_DETECTION_STEP_ID = "object-detection";
@@ -1788,7 +358,7 @@ function classifyAccelerator(backend, device) {
1788
358
  * missing-build fallback branch is unit-testable without a live catalog —
1789
359
  * mirrors `collectZeroBuildIssues`' injection seam.
1790
360
  */
1791
- function resolveDefaultDetectionModel(backend, device, format, getStepDef = getStepDefinition) {
361
+ function resolveDefaultDetectionModel(backend, device, format, getStepDef = require_step_definitions.getStepDefinition) {
1792
362
  let def;
1793
363
  try {
1794
364
  def = getStepDef(OBJECT_DETECTION_STEP_ID);
@@ -2819,7 +1389,7 @@ var EngineFactory = class {
2819
1389
  await this.poolManager.applyConfig(steps);
2820
1390
  }
2821
1391
  buildPoolModelConfig(stepId, modelId, poolRuntime) {
2822
- const def = getStepDefinition(stepId);
1392
+ const def = require_step_definitions.getStepDefinition(stepId);
2823
1393
  const modelEntry = def.models.find((m) => m.id === modelId);
2824
1394
  if (!modelEntry) throw new Error(`Model "${modelId}" not found in step "${stepId}" catalog`);
2825
1395
  const format = RUNTIME_TO_FORMAT[poolRuntime];
@@ -3801,7 +2371,8 @@ async function buildAlignedFaceCrop(args) {
3801
2371
  const native = await args.nativeCropProvider(roi, args.nativeMaxWidth);
3802
2372
  if (native && native.width >= 2 && native.height >= 2 && native.bytes.length >= native.width * native.height * 3) return {
3803
2373
  ...await alignFaceCropFromRgb(native, region, args.landmarksImageSpace, { outSize: args.outSize }),
3804
- source: "native"
2374
+ source: "native",
2375
+ nativeFaceShortSidePx: nativeFaceShortSide(args.faceBbox, region, native)
3805
2376
  };
3806
2377
  } catch {}
3807
2378
  return {
@@ -3810,6 +2381,22 @@ async function buildAlignedFaceCrop(args) {
3810
2381
  };
3811
2382
  }
3812
2383
  /**
2384
+ * Short side (px) of `faceBbox` (in detection space) re-measured in the NATIVE
2385
+ * crop's resolution. The native crop covers `region` (detection-space padded
2386
+ * region) rendered at `native.width × native.height` native pixels, so the
2387
+ * per-axis scale `native / region` maps the detection face size to its native
2388
+ * size. This is the size an embedder actually sees, so the gate uses it.
2389
+ */
2390
+ function nativeFaceShortSide(faceBbox, region, native) {
2391
+ const regionW = Math.max(1, region.right - region.left);
2392
+ const regionH = Math.max(1, region.bottom - region.top);
2393
+ const scaleX = native.width / regionW;
2394
+ const scaleY = native.height / regionH;
2395
+ const faceW = (faceBbox[2] - faceBbox[0]) * scaleX;
2396
+ const faceH = (faceBbox[3] - faceBbox[1]) * scaleY;
2397
+ return Math.min(faceW, faceH);
2398
+ }
2399
+ /**
3813
2400
  * Produce an aligned 112x112 JPEG of a face from the full-frame JPEG and the
3814
2401
  * face's 5 landmarks (in absolute image-space coordinates). Decodes a padded
3815
2402
  * region around the face bbox to raw RGB, warps to the ArcFace template, and
@@ -4037,7 +2624,8 @@ function buildFrameResult(input) {
4037
2624
  ...embedding !== void 0 ? {
4038
2625
  embedding,
4039
2626
  ...embeddingModelId !== void 0 ? { embeddingModelId } : {}
4040
- } : {}
2627
+ } : {},
2628
+ ...m.faceAlignedCrop !== void 0 ? { faceAlignedCrop: m.faceAlignedCrop } : {}
4041
2629
  };
4042
2630
  if (input.debug) {
4043
2631
  const cleanDebug = pruneUndefined({
@@ -4118,7 +2706,8 @@ function toMutableRootDetection(det, rootStep, idGen, rootStepLatencyMs) {
4118
2706
  mask: det.mask,
4119
2707
  maskWidth: det.maskWidth,
4120
2708
  maskHeight: det.maskHeight
4121
- } : {}
2709
+ } : {},
2710
+ ...det.landmarks !== void 0 ? { landmarks: det.landmarks } : {}
4122
2711
  };
4123
2712
  }
4124
2713
  //#endregion
@@ -4237,6 +2826,14 @@ function formatTopResult(output) {
4237
2826
  */
4238
2827
  var DEFAULT_MIN_FACE_SIZE_PX = 28;
4239
2828
  /**
2829
+ * Cheap pre-native floor (px, detection space): below this a face is treated as
2830
+ * genuinely degenerate — SCRFD landmarks are unreliable and even a large native
2831
+ * upscale can't rescue it — so it is skipped WITHOUT paying a native-crop
2832
+ * round-trip. Above the floor, the real {@link DEFAULT_MIN_FACE_SIZE_PX} gate is
2833
+ * evaluated on the NATIVE face size (see the face branch of `executeChildren`).
2834
+ */
2835
+ var MIN_FACE_DETECTION_FLOOR_PX = 6;
2836
+ /**
4240
2837
  * Cap (px) on the width of the NATIVE face crop fetched for embedding (design
4241
2838
  * B). The arcface input is a fixed 112² warp, so a wider native crop buys no
4242
2839
  * embedding quality past a modest margin but does inflate the per-face transport
@@ -4244,6 +2841,17 @@ var DEFAULT_MIN_FACE_SIZE_PX = 28;
4244
2841
  * above the downscaled detection resolution while bounding the crop payload.
4245
2842
  */
4246
2843
  var NATIVE_FACE_CROP_MAX_WIDTH = 320;
2844
+ /**
2845
+ * StepOutput kinds that synthesize a full-frame detection when they run at
2846
+ * the tree ROOT (see `synthesizeRootDetection`) — classifier/embedding steps
2847
+ * promoted to root by `pipelineRunner.runDetailSubtree` (task 6b), e.g.
2848
+ * `clip-embedding`, `animal-classifier`. Detector (`'detections'`) roots take
2849
+ * the normal path; `'text'`/`'mask'` roots are out of scope and still
2850
+ * `continue` with no output, matching pre-existing behaviour.
2851
+ */
2852
+ function isEnrichmentOutput(output) {
2853
+ return output.kind === "classifications" || output.kind === "embedding";
2854
+ }
4247
2855
  var PipelineExecutor = class {
4248
2856
  opts;
4249
2857
  constructor(opts) {
@@ -4261,6 +2869,23 @@ var PipelineExecutor = class {
4261
2869
  logger.warn(message, { meta });
4262
2870
  }
4263
2871
  /**
2872
+ * Surface WHY a face embedding was skipped — a detected face simply too small
2873
+ * (distance / night) is the common silent cause of "faces stopped collecting".
2874
+ * Reports the detection-space short side AND, when the skip was decided on the
2875
+ * native crop, the NATIVE face size so an operator sees the size that actually
2876
+ * gated. Throttled so it isn't per-frame spam.
2877
+ */
2878
+ warnFaceTooSmall(child, detShortSidePx, minFaceSizePx, nativeFaceShortSidePx) {
2879
+ this.throttledWarn(`face-small:${child.stepId}`, 3e4, "face detected but too small to embed — skipping", {
2880
+ step: child.stepId,
2881
+ model: child.modelId,
2882
+ faceShortSidePx: Math.round(detShortSidePx),
2883
+ minFaceSizePx,
2884
+ gatedOn: nativeFaceShortSidePx === void 0 ? "detection" : "native",
2885
+ ...nativeFaceShortSidePx === void 0 ? {} : { nativeFaceShortSidePx: Math.round(nativeFaceShortSidePx) }
2886
+ });
2887
+ }
2888
+ /**
4264
2889
  * Execute the full pipeline tree against a frame.
4265
2890
  *
4266
2891
  * `rootInput` carries the frame in its source representation
@@ -4296,7 +2921,27 @@ var PipelineExecutor = class {
4296
2921
  const rootStart = Date.now();
4297
2922
  const rootOutput = await this.executeStep(rootStep, rootInput, imageWidth, imageHeight, "full-frame", void 0, void 0, traceBuilder, stepTimings, poolAgg);
4298
2923
  const rootMs = Date.now() - rootStart;
4299
- if (rootOutput.kind !== "detections") continue;
2924
+ if (rootOutput.kind !== "detections") {
2925
+ if (isEnrichmentOutput(rootOutput)) {
2926
+ const mutable = this.synthesizeRootDetection(rootOutput, rootStep, idGen, rootMs, imageWidth, imageHeight);
2927
+ applyChildOutput(mutable, rootStep, rootOutput, rootMs, ctx);
2928
+ try {
2929
+ await this.executeChildren(rootStep.children, mutable, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, runOpts?.plane, nativeFaceCropProvider);
2930
+ } catch (err) {
2931
+ this.opts.logger?.warn("Pipeline child execution failed — keeping parent detection", {
2932
+ tags: { deviceId },
2933
+ meta: {
2934
+ rootStepId: rootStep.stepId,
2935
+ parentClass: mutable.macroClass,
2936
+ parentScore: mutable.score,
2937
+ error: require_dist.errMsg(err)
2938
+ }
2939
+ });
2940
+ }
2941
+ firstLevel.push(mutable);
2942
+ }
2943
+ continue;
2944
+ }
4300
2945
  for (const det of rootOutput.detections) {
4301
2946
  const mutable = toMutableRootDetection(det, rootStep, idGen, rootMs);
4302
2947
  if (rootStep.definition.classMap) {
@@ -4311,7 +2956,7 @@ var PipelineExecutor = class {
4311
2956
  continue;
4312
2957
  }
4313
2958
  try {
4314
- await this.executeChildren(rootStep.children, mutable, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, nativeFaceCropProvider);
2959
+ await this.executeChildren(rootStep.children, mutable, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, runOpts?.plane, nativeFaceCropProvider);
4315
2960
  } catch (err) {
4316
2961
  this.opts.logger?.warn("Pipeline child execution failed — keeping parent detection", {
4317
2962
  tags: { deviceId },
@@ -4347,6 +2992,29 @@ var PipelineExecutor = class {
4347
2992
  trace: traceBuilder.build(firstLevel.length + details.length)
4348
2993
  };
4349
2994
  }
2995
+ /**
2996
+ * Build a full-frame `MutableObjectDetection` for a ROOT step whose engine
2997
+ * output is a classification/embedding rather than detections — there is
2998
+ * no detector to bound a smaller box, so the synthesized detection covers
2999
+ * the entire input frame (`bbox = [0, 0, imageWidth, imageHeight]`).
3000
+ * `macroClass` is the step's own output semantic (`outputClasses[0]`,
3001
+ * falling back to `inputClasses[0]` when `outputClasses` is empty, then
3002
+ * the step id as a last resort so the field is never empty). `score` is
3003
+ * the output's own confidence when it has one (the classifier's top-1
3004
+ * score) — an embedding carries no confidence, so it defaults to 1.
3005
+ */
3006
+ synthesizeRootDetection(output, rootStep, idGen, rootStepLatencyMs, imageWidth, imageHeight) {
3007
+ return toMutableRootDetection({
3008
+ class: rootStep.definition.outputClasses[0] ?? rootStep.definition.inputClasses?.[0] ?? rootStep.stepId,
3009
+ score: output.kind === "classifications" ? output.classifications[0]?.score ?? 1 : 1,
3010
+ bbox: [
3011
+ 0,
3012
+ 0,
3013
+ imageWidth,
3014
+ imageHeight
3015
+ ]
3016
+ }, rootStep, idGen, rootStepLatencyMs);
3017
+ }
4350
3018
  async executeStep(step, input, inputWidth, inputHeight, inputType, _parentBbox, parentClass, traceBuilder, stepTimings, poolAgg) {
4351
3019
  const preprocessStart = Date.now();
4352
3020
  const preprocessMs = Date.now() - preprocessStart;
@@ -4413,8 +3081,9 @@ var PipelineExecutor = class {
4413
3081
  });
4414
3082
  return output;
4415
3083
  }
4416
- async executeChildren(children, parentDetection, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, nativeFaceCropProvider) {
3084
+ async executeChildren(children, parentDetection, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, plane, nativeFaceCropProvider) {
4417
3085
  for (const child of children) {
3086
+ if (plane === "frame" && child.definition.inputClasses !== null) continue;
4418
3087
  if (!this.matchesInputClasses(parentDetection.macroClass, child.inputClasses)) continue;
4419
3088
  const minParentScore = child.settings?.minParentScore ?? child.definition?.defaultMinParentScore;
4420
3089
  if (typeof minParentScore === "number" && parentDetection.score < minParentScore) continue;
@@ -4428,18 +3097,17 @@ var PipelineExecutor = class {
4428
3097
  let cropH;
4429
3098
  if (modelEntry?.faceAlignment) {
4430
3099
  const minFaceSize = typeof child.settings?.["minFaceSize"] === "number" ? child.settings["minFaceSize"] : DEFAULT_MIN_FACE_SIZE_PX;
4431
- const faceShortSide = bboxShortSide(parentDetection.bbox);
4432
- if (faceShortSide < minFaceSize) {
4433
- this.throttledWarn(`face-small:${child.stepId}`, 3e4, "face detected but too small to embed — skipping", {
4434
- step: child.stepId,
4435
- model: child.modelId,
4436
- faceShortSidePx: Math.round(faceShortSide),
4437
- minFaceSizePx: minFaceSize
4438
- });
3100
+ const detShortSide = bboxShortSide(parentDetection.bbox);
3101
+ if (detShortSide < MIN_FACE_DETECTION_FLOOR_PX) {
3102
+ this.warnFaceTooSmall(child, detShortSide, minFaceSize, void 0);
4439
3103
  continue;
4440
3104
  }
4441
3105
  const lms = parentDetection.landmarks;
4442
3106
  if (lms && lms.length >= 5) {
3107
+ if (!nativeFaceCropProvider && detShortSide < minFaceSize) {
3108
+ this.warnFaceTooSmall(child, detShortSide, minFaceSize, void 0);
3109
+ continue;
3110
+ }
4443
3111
  const aligned = await buildAlignedFaceCrop({
4444
3112
  fullFrameJpeg,
4445
3113
  faceBbox: parentDetection.bbox,
@@ -4452,10 +3120,20 @@ var PipelineExecutor = class {
4452
3120
  nativeMaxWidth: NATIVE_FACE_CROP_MAX_WIDTH
4453
3121
  } : {}
4454
3122
  });
3123
+ const gateShortSide = aligned.nativeFaceShortSidePx ?? detShortSide;
3124
+ if (gateShortSide < minFaceSize) {
3125
+ this.warnFaceTooSmall(child, detShortSide, minFaceSize, aligned.source === "native" ? gateShortSide : void 0);
3126
+ continue;
3127
+ }
4455
3128
  cropJpegBuf = aligned.jpeg;
4456
3129
  cropW = aligned.width;
4457
3130
  cropH = aligned.height;
3131
+ parentDetection.faceAlignedCrop = cropJpegBuf.toString("base64");
4458
3132
  } else {
3133
+ if (detShortSide < minFaceSize) {
3134
+ this.warnFaceTooSmall(child, detShortSide, minFaceSize, void 0);
3135
+ continue;
3136
+ }
4459
3137
  const crop = await cropJpeg(fullFrameJpeg, parentDetection.bbox, imageWidth, imageHeight);
4460
3138
  cropJpegBuf = crop.jpeg;
4461
3139
  cropW = crop.width;
@@ -4485,7 +3163,7 @@ var PipelineExecutor = class {
4485
3163
  y: l.y + py1
4486
3164
  }));
4487
3165
  }
4488
- await this.executeChildren(child.children, detail, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, nativeFaceCropProvider);
3166
+ await this.executeChildren(child.children, detail, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, plane, nativeFaceCropProvider);
4489
3167
  }
4490
3168
  }
4491
3169
  } catch (err) {
@@ -4538,7 +3216,7 @@ function buildExecutableTree(steps, getEngine) {
4538
3216
  return { roots: steps.filter((s) => s.enabled).filter((s) => s.slot !== "audio-classifier").map((s) => buildNode(s, getEngine)) };
4539
3217
  }
4540
3218
  function buildNode(step, getEngine) {
4541
- const definition = getStepDefinition(step.addonId);
3219
+ const definition = require_step_definitions.getStepDefinition(step.addonId);
4542
3220
  const engine = getEngine(step.addonId);
4543
3221
  const children = (step.children ?? []).filter((c) => c.enabled).map((c) => buildNode(c, getEngine));
4544
3222
  const mergedSettings = {
@@ -4563,7 +3241,7 @@ function buildNode(step, getEngine) {
4563
3241
  * entered recursively. Fields without a declared default are skipped.
4564
3242
  */
4565
3243
  function collectSchemaDefaults(stepId) {
4566
- const schema = getStep(stepId).getConfigSchema();
3244
+ const schema = require_step_definitions.getStep(stepId).getConfigSchema();
4567
3245
  const out = {};
4568
3246
  walkFieldsForDefaults(schema, out);
4569
3247
  return out;
@@ -4628,7 +3306,7 @@ function collectSubstitutions(steps, format) {
4628
3306
  for (const step of steps) {
4629
3307
  const chosen = step.modelId;
4630
3308
  if (chosen != null) {
4631
- const running = resolveModelForFormat(step.addonId, chosen, format);
3309
+ const running = require_step_definitions.resolveModelForFormat(step.addonId, chosen, format);
4632
3310
  if (running !== chosen) result.push({
4633
3311
  addonId: step.addonId,
4634
3312
  chosen,
@@ -4654,7 +3332,7 @@ function collectSubstitutions(steps, format) {
4654
3332
  * black-box test. Unknown/custom addonIds (no registered `StepDefinition`)
4655
3333
  * are skipped, never thrown.
4656
3334
  */
4657
- function collectZeroBuildIssues(steps, format, getStepDef = getStepDefinition) {
3335
+ function collectZeroBuildIssues(steps, format, getStepDef = require_step_definitions.getStepDefinition) {
4658
3336
  const result = [];
4659
3337
  for (const step of steps) {
4660
3338
  let def;
@@ -4680,7 +3358,7 @@ function collectZeroBuildIssues(steps, format, getStepDef = getStepDefinition) {
4680
3358
  * tests can exercise the unknown-addon branch with a fabricated resolver
4681
3359
  * without `vi.mock`.
4682
3360
  */
4683
- function collectUnknownAddonIssues(steps, getStepDef = getStepDefinition) {
3361
+ function collectUnknownAddonIssues(steps, getStepDef = require_step_definitions.getStepDefinition) {
4684
3362
  const result = [];
4685
3363
  for (const step of steps) {
4686
3364
  try {
@@ -4720,13 +3398,13 @@ function resolveInputSteps(steps, format, engine) {
4720
3398
  for (const s of steps) {
4721
3399
  let def;
4722
3400
  try {
4723
- def = getStepDefinition(s.addonId);
3401
+ def = require_step_definitions.getStepDefinition(s.addonId);
4724
3402
  } catch {
4725
3403
  unknownAddonIds.push(s.addonId);
4726
3404
  continue;
4727
3405
  }
4728
3406
  const chosenModelId = s.modelId ?? null;
4729
- const runningModelId = chosenModelId === null && engine !== void 0 && s.addonId === "object-detection" ? resolveDefaultDetectionModel(engine.backend, engine.device, format) : resolveModelForFormat(s.addonId, chosenModelId, format);
3407
+ const runningModelId = chosenModelId === null && engine !== void 0 && s.addonId === "object-detection" ? resolveDefaultDetectionModel(engine.backend, engine.device, format) : require_step_definitions.resolveModelForFormat(s.addonId, chosenModelId, format);
4730
3408
  if (chosenModelId !== null && runningModelId !== chosenModelId) substitutions.push({
4731
3409
  addonId: s.addonId,
4732
3410
  chosen: chosenModelId,
@@ -5529,7 +4207,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
5529
4207
  * first, then the custom registry. Returns undefined if neither has it.
5530
4208
  */
5531
4209
  async resolveModelEntry(addonId, modelId) {
5532
- const fromCatalog = getStepDefinition(addonId).models.find((m) => m.id === modelId);
4210
+ const fromCatalog = require_step_definitions.getStepDefinition(addonId).models.find((m) => m.id === modelId);
5533
4211
  if (fromCatalog) return fromCatalog;
5534
4212
  return (await this.getCustomModels()).get(addonId)?.find((m) => m.id === modelId);
5535
4213
  }
@@ -5625,7 +4303,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
5625
4303
  /** Pure recursive resolver — builds the resolved step tree without touching `modelSubstitutions`. */
5626
4304
  resolveStepModelTree(steps, format) {
5627
4305
  return steps.map((step) => {
5628
- const resolved = resolveModelForFormat(step.addonId, step.modelId ?? null, format);
4306
+ const resolved = require_step_definitions.resolveModelForFormat(step.addonId, step.modelId ?? null, format);
5629
4307
  const children = step.children?.length ? this.resolveStepModelTree([...step.children], format) : step.children;
5630
4308
  return resolved === step.modelId && children === step.children ? step : {
5631
4309
  ...step,
@@ -5773,7 +4451,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
5773
4451
  for (const step of nodes) {
5774
4452
  if (!step.enabled) continue;
5775
4453
  try {
5776
- if (getStepDefinition(step.addonId).slot === "audio-classifier") continue;
4454
+ if (require_step_definitions.getStepDefinition(step.addonId).slot === "audio-classifier") continue;
5777
4455
  } catch {
5778
4456
  continue;
5779
4457
  }
@@ -5793,7 +4471,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
5793
4471
  async getCapabilities(_forceRefresh) {
5794
4472
  const agentId = (this.addonCtx?.kernel?.cluster?.broker)?.nodeID ?? node_os.hostname();
5795
4473
  const agentName = agentId;
5796
- const addons = ALL_PIPELINE_STEPS.map((s) => ({
4474
+ const addons = require_step_definitions.ALL_PIPELINE_STEPS.map((s) => ({
5797
4475
  id: s.definition.id,
5798
4476
  name: s.definition.name,
5799
4477
  models: s.definition.models.filter((m) => m.legacy !== true).map((m) => {
@@ -6266,7 +4944,10 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
6266
4944
  const deviceOverrides = effectiveDeviceId > 0 ? await this.readDeviceStore(effectiveDeviceId) : {};
6267
4945
  const effectiveTree = Object.keys(deviceOverrides).length > 0 ? applyDeviceOverridesToTree(tree, "object-detection", deviceOverrides) : tree;
6268
4946
  const nativeFaceCropProvider = this.buildNativeFaceCropProvider(input.frameHandle);
6269
- const { result, trace } = await executor.run(effectiveTree, rootInput, jpegProvider, imageWidth, imageHeight, effectiveDeviceId, { traceVerbosity: isRuntime ? this.eventBus ? "summary" : "off" : "full" }, nativeFaceCropProvider);
4947
+ const { result, trace } = await executor.run(effectiveTree, rootInput, jpegProvider, imageWidth, imageHeight, effectiveDeviceId, {
4948
+ traceVerbosity: isRuntime ? this.eventBus ? "summary" : "off" : "full",
4949
+ plane: input.plane
4950
+ }, nativeFaceCropProvider);
6270
4951
  if (isRuntime) {
6271
4952
  if (trace && this.eventBus) this.eventBus.emit(require_dist.createEvent(require_dist.EventCategory.PipelineTrace, {
6272
4953
  type: "device",
@@ -7324,7 +6005,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
7324
6005
  };
7325
6006
  function buildSchemaSlots(format, modelsDir, customByStep) {
7326
6007
  const slotMap = /* @__PURE__ */ new Map();
7327
- for (const pipelineStep of ALL_PIPELINE_STEPS) {
6008
+ for (const pipelineStep of require_step_definitions.ALL_PIPELINE_STEPS) {
7328
6009
  const step = pipelineStep.definition;
7329
6010
  const availableModels = mergeCustomModels(step.models, customByStep?.get(step.id) ?? []).filter((m) => m.formats[format] && m.legacy !== true);
7330
6011
  if (availableModels.length === 0) continue;
@@ -7411,7 +6092,7 @@ function resolveChildSlots(slot) {
7411
6092
  */
7412
6093
  function buildDefaultStepTree(format, objectDetectionModelId) {
7413
6094
  const makeStep = (stepId, children, opts) => {
7414
- const def = getStepDefinition(stepId);
6095
+ const def = require_step_definitions.getStepDefinition(stepId);
7415
6096
  if (!def.models.some((m) => m.formats[format])) return null;
7416
6097
  return {
7417
6098
  addonId: def.id,
@@ -7420,7 +6101,7 @@ function buildDefaultStepTree(format, objectDetectionModelId) {
7420
6101
  inputClasses: def.inputClasses ?? [],
7421
6102
  outputClasses: def.outputClasses,
7422
6103
  enabled: opts?.enabled ?? true,
7423
- modelId: opts?.modelId ?? getDefaultModelForFormat(stepId, format),
6104
+ modelId: opts?.modelId ?? require_step_definitions.getDefaultModelForFormat(stepId, format),
7424
6105
  children,
7425
6106
  ...def.group ? { group: def.group } : {}
7426
6107
  };
@@ -8167,7 +6848,7 @@ var DetectionPipelineAddon = class DetectionPipelineAddon extends require_dist.B
8167
6848
  }).catch(() => {});
8168
6849
  }
8169
6850
  getModelCatalog() {
8170
- return ALL_STEPS.flatMap((s) => s.models.filter((m) => m.legacy !== true));
6851
+ return require_step_definitions.ALL_STEPS.flatMap((s) => s.models.filter((m) => m.legacy !== true));
8171
6852
  }
8172
6853
  /**
8173
6854
  * Proactively install the OpenVINO Python package when Intel hardware
@@ -8275,10 +6956,10 @@ var DetectionPipelineAddon = class DetectionPipelineAddon extends require_dist.B
8275
6956
  }
8276
6957
  };
8277
6958
  //#endregion
8278
- exports.ALL_STEPS = ALL_STEPS;
6959
+ exports.ALL_STEPS = require_step_definitions.ALL_STEPS;
8279
6960
  exports.DetectionPipelineProvider = DetectionPipelineProvider;
8280
6961
  exports.backendToFormat = backendToFormat;
8281
6962
  exports.default = DetectionPipelineAddon;
8282
- exports.getDefaultModelForFormat = getDefaultModelForFormat;
8283
- exports.getStepDefinition = getStepDefinition;
6963
+ exports.getDefaultModelForFormat = require_step_definitions.getDefaultModelForFormat;
6964
+ exports.getStepDefinition = require_step_definitions.getStepDefinition;
8284
6965
  exports.shouldInstallOpenvino = shouldInstallOpenvino;