@camstack/addon-pipeline 1.1.64 → 1.1.66

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 (42) 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 +188 -26
  4. package/dist/detection-pipeline/index.mjs +188 -26
  5. package/dist/{dist-BzVZ2I4P.mjs → dist-BpWot5-w.mjs} +13 -55
  6. package/dist/{dist-B2jTt7Lq.js → dist-D5o9fGxq.js} +13 -55
  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 +88 -22
  10. package/dist/pipeline-runner/index.mjs +88 -22
  11. package/dist/recorder/index.js +141 -1
  12. package/dist/recorder/index.mjs +141 -1
  13. package/dist/session-decode/decode-worker-child.js +9 -3
  14. package/dist/session-decode/decode-worker-child.mjs +9 -3
  15. package/dist/{step-definitions-DXkY5Zor.js → step-definitions-81eYyJbW.js} +138 -8
  16. package/dist/{step-definitions-B07M9qhj.mjs → step-definitions-D6R9X0V7.mjs} +138 -8
  17. package/dist/stream-broker/_stub.js +2 -2
  18. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-dTMki_1D.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CIIIVPzB.mjs} +2 -2
  19. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CPbLcza9.mjs +26 -0
  20. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DlALQ_wp.mjs +26 -0
  21. package/dist/stream-broker/{hostInit-BY2oQAEX.mjs → hostInit-CeBNudbY.mjs} +2 -2
  22. package/dist/stream-broker/index.js +51 -3
  23. package/dist/stream-broker/index.mjs +51 -3
  24. package/dist/stream-broker/remoteEntry.js +1 -1
  25. package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-C03fx7Sp.js → MaskShapeCanvas-DI4BY7W2-CbBbcdZb.js} +1 -1
  26. package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-BTxzn1rQ.js → MotionZonesSettings-NcxxQN8r-aA6zvms_.js} +1 -1
  27. package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-GKO8O7rC.js → PrivacyMaskSettings-APgPLF7p-CcGB7iAE.js} +1 -1
  28. package/embed-dist/assets/index-BRG_Fqb5.js +114 -0
  29. package/embed-dist/assets/index-D2pF9Z3W.css +2 -0
  30. package/embed-dist/index.html +2 -2
  31. package/package.json +1 -1
  32. package/python/inference_pool.py +30 -0
  33. package/python/postprocessors/scrfd.py +4 -1
  34. package/python/postprocessors/softmax.py +17 -5
  35. package/python/postprocessors/test_softmax.py +52 -0
  36. package/python/postprocessors/test_yolo_nms.py +45 -0
  37. package/python/postprocessors/yolo.py +4 -1
  38. package/python/postprocessors/yolo_seg.py +4 -1
  39. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-bAh96WNb.mjs +0 -26
  40. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CV2WbUaO.mjs +0 -26
  41. package/embed-dist/assets/index-Bm9LaQPH.css +0 -2
  42. package/embed-dist/assets/index-QdssILAA.js +0 -81
@@ -1,4 +1,4 @@
1
- import { B as errMsg, D as migrateConfigToBands, I as storageEvictableCapability, K as hydrateSchema, M as recordingCapability, N as recordingExportCapability, P as resolveScrubThumbnailGeometry, Q as selectAssignedProfileSlots, V as BaseAddon, W as DeviceType, ct as record, d as OpsLogEntrySchema, dt as EventCategory, f as RecordingConfigSchema, k as nodePin, l as ExportRecordSchema, lt as string, q as isEvent, s as EVENT_PAD_MS, tt as array } from "../dist-BzVZ2I4P.mjs";
1
+ import { B as errMsg, D as migrateConfigToBands, I as storageEvictableCapability, K as hydrateSchema, M as recordingCapability, N as recordingExportCapability, P as resolveScrubThumbnailGeometry, Q as selectAssignedProfileSlots, V as BaseAddon, W as DeviceType, ct as record, d as OpsLogEntrySchema, dt as EventCategory, f as RecordingConfigSchema, k as nodePin, l as ExportRecordSchema, lt as string, q as isEvent, s as EVENT_PAD_MS, tt as array } from "../dist-BpWot5-w.mjs";
2
2
  import { n as computeSheetGeometry, r as resolveHubHostname, t as allTilePlacements } from "../sheet-geometry-DcpuGTCm.mjs";
3
3
  import { n as createFileDataPlaneHandler, s as parseRangeHeader, t as contentTypeFor } from "../model-download-service-Cp9f4dk6-CwUb5v3Y.mjs";
4
4
  import { spawn } from "node:child_process";
@@ -2590,6 +2590,122 @@ function makeBrokerCall(handle) {
2590
2590
  return (fn) => handle.call(fn, BROKER_READY_TIMEOUT_MS);
2591
2591
  }
2592
2592
  //#endregion
2593
+ //#region src/recorder/addon/retention-sweep.ts
2594
+ /**
2595
+ * Periodic footage-retention sweep (recording-spec §6).
2596
+ *
2597
+ * The disk-pressure eviction path only fires when a volume is TOO FULL
2598
+ * (`shouldSaveDiskSpace` + `minFreePercent`); it never enforces a camera's
2599
+ * age/size policy. This sweeper closes that gap: on an hourly cadence it prunes
2600
+ * footage for every device whose operator set an actionable `retention` policy,
2601
+ * delegating the actual selection + delete to the `recording` cap's
2602
+ * `pruneFootage` (which owns the retention FLOOR via `selectEvictions` — this
2603
+ * sweeper only decides WHICH devices to prune and WHEN).
2604
+ *
2605
+ * Owns its own timer + single-flight guard so a slow sweep is never overlapped
2606
+ * by the next tick — the same subsystem-owns-its-timer shape as
2607
+ * `ThumbnailService`. Pure over injected deps (config read + prune + logger +
2608
+ * clock via `setInterval`), so it is unit-testable with fake timers and a mock
2609
+ * `prune`. Every pass is best-effort: a per-device prune failure is logged and
2610
+ * never aborts the rest, and the sweep never throws.
2611
+ */
2612
+ /**
2613
+ * True when the operator set an ACTIONABLE retention policy on a device — at
2614
+ * least one of `maxAgeDays` / `maxSizeGb` is present and positive. Mirrors the
2615
+ * `> 0` guards `selectEvictions` itself applies, so a `0`/absent limit (which
2616
+ * inherits the node default = "keep forever") is treated as NO policy and the
2617
+ * device is skipped. NEVER prune a device without an explicit policy — that
2618
+ * would delete footage the operator never asked to age out.
2619
+ */
2620
+ function hasRetentionPolicy(config) {
2621
+ const retention = config.retention;
2622
+ if (!retention) return false;
2623
+ const byAge = retention.maxAgeDays != null && retention.maxAgeDays > 0;
2624
+ const bySize = retention.maxSizeGb != null && retention.maxSizeGb > 0;
2625
+ return byAge || bySize;
2626
+ }
2627
+ var RetentionSweeper = class {
2628
+ deps;
2629
+ timer = null;
2630
+ /** Single-flight guard: true while a sweep is in flight. */
2631
+ sweeping = false;
2632
+ constructor(deps) {
2633
+ this.deps = deps;
2634
+ }
2635
+ /**
2636
+ * Arm the sweep: run immediately, then on the interval. Idempotent — a second
2637
+ * `start()` while already armed is a no-op. The timer is `unref`'d so it never
2638
+ * keeps the process alive.
2639
+ */
2640
+ start() {
2641
+ if (this.timer !== null) return;
2642
+ const tick = () => {
2643
+ this.sweep();
2644
+ };
2645
+ tick();
2646
+ this.timer = setInterval(tick, this.deps.intervalMs);
2647
+ this.timer.unref?.();
2648
+ }
2649
+ /** Stop the sweep timer. Idempotent. An in-flight sweep still completes. */
2650
+ stop() {
2651
+ if (this.timer !== null) {
2652
+ clearInterval(this.timer);
2653
+ this.timer = null;
2654
+ }
2655
+ }
2656
+ /**
2657
+ * One retention pass. Single-flighted: a call that fires while a previous
2658
+ * sweep is still running is a no-op. Never throws — a config-read failure
2659
+ * aborts THIS pass (logged); a per-device prune failure is logged and the
2660
+ * sweep continues with the remaining devices.
2661
+ */
2662
+ async sweep() {
2663
+ if (this.sweeping) {
2664
+ this.deps.logger.debug("recorder: retention sweep already running — skipping this tick");
2665
+ return;
2666
+ }
2667
+ this.sweeping = true;
2668
+ try {
2669
+ let configs;
2670
+ try {
2671
+ configs = await this.deps.readConfigs();
2672
+ } catch (err) {
2673
+ this.deps.logger.warn("recorder: retention sweep could not read device configs", { meta: { error: errMsg(err) } });
2674
+ return;
2675
+ }
2676
+ let devicesSwept = 0;
2677
+ let devicesSkipped = 0;
2678
+ let bucketsPruned = 0;
2679
+ let bytesReclaimed = 0;
2680
+ for (const [deviceId, config] of configs) {
2681
+ if (!hasRetentionPolicy(config)) {
2682
+ devicesSkipped += 1;
2683
+ continue;
2684
+ }
2685
+ try {
2686
+ const result = await this.deps.prune(deviceId);
2687
+ devicesSwept += 1;
2688
+ bucketsPruned += result.deletedBuckets;
2689
+ bytesReclaimed += result.reclaimedBytes;
2690
+ } catch (err) {
2691
+ this.deps.logger.warn("recorder: retention prune failed for device", {
2692
+ tags: { deviceId },
2693
+ meta: { error: errMsg(err) }
2694
+ });
2695
+ }
2696
+ }
2697
+ this.deps.logger.info("recorder: retention sweep complete", { meta: {
2698
+ devicesSwept,
2699
+ devicesSkipped,
2700
+ bucketsPruned,
2701
+ bytesReclaimed
2702
+ } });
2703
+ } finally {
2704
+ this.sweeping = false;
2705
+ }
2706
+ }
2707
+ };
2708
+ //#endregion
2593
2709
  //#region src/recorder/addon/recording-hub-host.ts
2594
2710
  /**
2595
2711
  * Resolve the host a REMOTE recording node should use to dial the hub-only
@@ -3596,6 +3712,12 @@ var PLAYBACK_PREFIX = "playback";
3596
3712
  var EXPORTS_PREFIX = "exports";
3597
3713
  /** How often the export janitor sweeps expired exports. */
3598
3714
  var EXPORT_SWEEP_INTERVAL_MS = 5 * 6e4;
3715
+ /** How often the retention sweep prunes footage past each camera's per-device
3716
+ * age/size policy. Retention is NOT time-critical (footage ages out over days,
3717
+ * and the disk-pressure eviction path already bounds the worst case), so an
3718
+ * hourly cadence keeps the storage.list/evict cost negligible while honouring
3719
+ * the recording-spec §6 periodic retention. */
3720
+ var RETENTION_SWEEP_INTERVAL_MS = 60 * 6e4;
3599
3721
  /** Grace after a completed download before a `deleteAfterDownload` file is removed. */
3600
3722
  var EXPORT_DELETE_GRACE_MS = 6e4;
3601
3723
  /** Only `.mp4` export ids matching this shape are servable (traversal guard). */
@@ -3631,6 +3753,12 @@ var RecorderV2Addon = class extends BaseAddon {
3631
3753
  exportEngine = null;
3632
3754
  /** Periodic export-expiry sweep timer. Cleared on shutdown. */
3633
3755
  exportSweepTimer = null;
3756
+ /** The `recording` cap provider — retained so the periodic retention sweep can
3757
+ * call its `pruneFootage`. Null until built in `onInitialize`. */
3758
+ recordingProvider = null;
3759
+ /** Periodic footage-retention sweep (owns its timer + single-flight guard).
3760
+ * Constructed + started in `onHubReachable`, stopped on shutdown. */
3761
+ retentionSweeper = null;
3634
3762
  constructor() {
3635
3763
  super({ ...DEFAULT_CONFIG });
3636
3764
  }
@@ -3739,6 +3867,7 @@ var RecorderV2Addon = class extends BaseAddon {
3739
3867
  }
3740
3868
  };
3741
3869
  const recordingProvider = buildRecordingProvider(recordingDeps);
3870
+ this.recordingProvider = recordingProvider;
3742
3871
  const evictableProvider = new StorageEvictableProvider({
3743
3872
  index: this.index,
3744
3873
  store: this.segmentStore
@@ -3836,7 +3965,10 @@ var RecorderV2Addon = class extends BaseAddon {
3836
3965
  clearInterval(this.exportSweepTimer);
3837
3966
  this.exportSweepTimer = null;
3838
3967
  }
3968
+ this.retentionSweeper?.stop();
3969
+ this.retentionSweeper = null;
3839
3970
  this.exportEngine = null;
3971
+ this.recordingProvider = null;
3840
3972
  }
3841
3973
  /**
3842
3974
  * Boot hydrate: once the hub is reachable (`ctx.api.*` safe), resolve the
@@ -3895,6 +4027,14 @@ var RecorderV2Addon = class extends BaseAddon {
3895
4027
  this.ctx.logger.warn("recorder: thumbnail service start failed", { meta: { error: errMsg(err) } });
3896
4028
  }
3897
4029
  await this.bootstrapExports();
4030
+ const provider = this.recordingProvider;
4031
+ if (provider && this.retentionSweeper === null) this.retentionSweeper = new RetentionSweeper({
4032
+ prune: (deviceId) => provider.pruneFootage({ deviceId }),
4033
+ readConfigs: () => readDeviceConfigs(this.configStore()),
4034
+ logger: this.ctx.logger,
4035
+ intervalMs: RETENTION_SWEEP_INTERVAL_MS
4036
+ });
4037
+ this.retentionSweeper?.start();
3898
4038
  }
3899
4039
  /**
3900
4040
  * Resume interrupted exports and arm the expiry sweep. A row left `rendering`
@@ -460,12 +460,18 @@ var NATIVE_LEASE_BUDGET_BYTES = (() => {
460
460
  /**
461
461
  * TTL (ms) after which a native-frame lease is treated as a miss and reclaimed.
462
462
  * The backstop that bounds in-flight RAM even if the explicit `releaseNativeLease`
463
- * is dropped comfortably longer than the detection inference + crop round-trip
464
- * (~40-200ms). `CAMSTACK_SESSION_NATIVE_LEASE_TTL_MS` (default 500).
463
+ * is dropped. Must comfortably exceed the FULL late-crop horizon: detection
464
+ * inference + the cross-process inference-result hop to hub post-analysis +
465
+ * tracking + the tRPC crop round-trip back — on a busy camera with many
466
+ * concurrent tracks that is well past the old 500 ms (which the busiest cameras'
467
+ * subject crops outran, missing to the ≤640 fallback). Raised to 1200 ms; still
468
+ * bounded by the per-session {@link NATIVE_LEASE_BUDGET_BYTES} ceiling and the
469
+ * {@link NATIVE_LEASE_ACTIVITY_WINDOW_MS} demand gate, so idle cameras retain
470
+ * nothing. `CAMSTACK_SESSION_NATIVE_LEASE_TTL_MS` (default 1200).
465
471
  */
466
472
  var NATIVE_LEASE_TTL_MS = (() => {
467
473
  const raw = Number(process.env["CAMSTACK_SESSION_NATIVE_LEASE_TTL_MS"]);
468
- return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 500;
474
+ return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 1200;
469
475
  })();
470
476
  /**
471
477
  * Demand window (ms) for the lease-capture {@link LeaseActivityGate}: eager
@@ -456,12 +456,18 @@ var NATIVE_LEASE_BUDGET_BYTES = (() => {
456
456
  /**
457
457
  * TTL (ms) after which a native-frame lease is treated as a miss and reclaimed.
458
458
  * The backstop that bounds in-flight RAM even if the explicit `releaseNativeLease`
459
- * is dropped comfortably longer than the detection inference + crop round-trip
460
- * (~40-200ms). `CAMSTACK_SESSION_NATIVE_LEASE_TTL_MS` (default 500).
459
+ * is dropped. Must comfortably exceed the FULL late-crop horizon: detection
460
+ * inference + the cross-process inference-result hop to hub post-analysis +
461
+ * tracking + the tRPC crop round-trip back — on a busy camera with many
462
+ * concurrent tracks that is well past the old 500 ms (which the busiest cameras'
463
+ * subject crops outran, missing to the ≤640 fallback). Raised to 1200 ms; still
464
+ * bounded by the per-session {@link NATIVE_LEASE_BUDGET_BYTES} ceiling and the
465
+ * {@link NATIVE_LEASE_ACTIVITY_WINDOW_MS} demand gate, so idle cameras retain
466
+ * nothing. `CAMSTACK_SESSION_NATIVE_LEASE_TTL_MS` (default 1200).
461
467
  */
462
468
  var NATIVE_LEASE_TTL_MS = (() => {
463
469
  const raw = Number(process.env["CAMSTACK_SESSION_NATIVE_LEASE_TTL_MS"]);
464
- return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 500;
470
+ return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 1200;
465
471
  })();
466
472
  /**
467
473
  * Demand window (ms) for the lease-capture {@link LeaseActivityGate}: eager
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-B2jTt7Lq.js");
1
+ const require_dist = require("./dist-D5o9fGxq.js");
2
2
  //#region src/detection-pipeline/registry/model-catalogs.ts
3
3
  var HF_REPO = "camstack/camstack-models";
4
4
  var HF_SCRYPTED = "scrypted/plugin-models";
@@ -764,9 +764,45 @@ var PLATE_OCR_MODELS = [{
764
764
  }
765
765
  }];
766
766
  var ANIMAL_CLASSIFIER_MODELS = [{
767
+ id: "animal-classifier",
768
+ name: "Animal Classifier (8)",
769
+ description: "EfficientNet-Lite0 animal classifier — bird, cat, cow, deer, dog, horse, sheep, squirrel (Open Images V7, publicly distributable)",
770
+ inputSize: {
771
+ width: 224,
772
+ height: 224
773
+ },
774
+ inputLayout: "nchw",
775
+ inputNormalization: "imagenet",
776
+ license: "Apache-2.0",
777
+ labels: [{
778
+ id: "animal-type",
779
+ name: "Animal Type"
780
+ }],
781
+ preprocessMode: "resize",
782
+ formats: {
783
+ onnx: {
784
+ url: hf("animalClassification/animal-classifier/onnx/animal-classifier.onnx"),
785
+ sizeMB: 13
786
+ },
787
+ coreml: {
788
+ url: hf("animalClassification/animal-classifier/coreml/animal-classifier.mlpackage"),
789
+ sizeMB: 6.5,
790
+ isDirectory: true,
791
+ files: [...MLPACKAGE_FILES],
792
+ runtimes: ["python"]
793
+ },
794
+ openvino: ovFormat(hf("animalClassification/animal-classifier/openvino/animal-classifier.xml"), 6.9)
795
+ },
796
+ extraFiles: [{
797
+ url: hf("animalClassification/animal-classifier/onnx/animal-classifier-labels.json"),
798
+ filename: "animal-classifier-labels.json",
799
+ sizeMB: .01
800
+ }]
801
+ }, {
767
802
  id: "animals-10",
768
803
  name: "Animal Classifier (10)",
769
804
  description: "ViT-based animal type classifier — cat, cow, dog, dolphin, eagle, panda, horse, monkey, sheep, spider",
805
+ legacy: true,
770
806
  inputSize: {
771
807
  width: 224,
772
808
  height: 224
@@ -793,9 +829,46 @@ var ANIMAL_CLASSIFIER_MODELS = [{
793
829
  }
794
830
  }];
795
831
  var BIRD_CLASSIFIER_MODELS = [{
832
+ id: "bird-classifier",
833
+ name: "Bird Classifier (AIY 965)",
834
+ description: "Google AIY Birds V1 (MobileNetV2) — 964 species + background; publicly distributable (Apache-2.0)",
835
+ inputSize: {
836
+ width: 224,
837
+ height: 224
838
+ },
839
+ inputLayout: "nhwc",
840
+ inputNormalization: "none",
841
+ outputProbabilities: true,
842
+ license: "Apache-2.0",
843
+ labels: [{
844
+ id: "species",
845
+ name: "Bird Species"
846
+ }],
847
+ preprocessMode: "resize",
848
+ formats: {
849
+ onnx: {
850
+ url: hf("animalClassification/bird-classifier/onnx/bird-classifier.onnx"),
851
+ sizeMB: 14
852
+ },
853
+ coreml: {
854
+ url: hf("animalClassification/bird-classifier/coreml/bird-classifier.mlpackage"),
855
+ sizeMB: 6.8,
856
+ isDirectory: true,
857
+ files: [...MLPACKAGE_FILES],
858
+ runtimes: ["python"]
859
+ },
860
+ openvino: ovFormat(hf("animalClassification/bird-classifier/openvino/bird-classifier.xml"), 14)
861
+ },
862
+ extraFiles: [{
863
+ url: hf("animalClassification/bird-classifier/onnx/bird-classifier-labels.json"),
864
+ filename: "bird-classifier-labels.json",
865
+ sizeMB: .03
866
+ }]
867
+ }, {
796
868
  id: "bird-nabirds-404",
797
869
  name: "NABirds (404 species)",
798
870
  description: "ResNet50 trained on NABirds — 404 North American species",
871
+ legacy: true,
799
872
  inputSize: {
800
873
  width: 224,
801
874
  height: 224
@@ -827,9 +900,44 @@ var BIRD_CLASSIFIER_MODELS = [{
827
900
  }]
828
901
  }];
829
902
  var VEHICLE_CLASSIFIER_MODELS = [{
903
+ id: "vehicle-type-v1",
904
+ name: "Vehicle Type (8)",
905
+ description: "MobileNetV3-Large vehicle type classifier — Ambulance, Bicycle, Bus, Car, Motorcycle, Taxi, Truck, Van (val 89.4%)",
906
+ inputSize: {
907
+ width: 224,
908
+ height: 224
909
+ },
910
+ inputLayout: "nchw",
911
+ inputNormalization: "imagenet",
912
+ labels: [{
913
+ id: "vehicle-type",
914
+ name: "Vehicle Type"
915
+ }],
916
+ preprocessMode: "resize",
917
+ formats: {
918
+ onnx: {
919
+ url: hf("vehicleClassification/vehicle-type-v1/onnx/vehicle-type-v1.onnx"),
920
+ sizeMB: 17
921
+ },
922
+ coreml: {
923
+ url: hf("vehicleClassification/vehicle-type-v1/coreml/vehicle-type-v1.mlpackage"),
924
+ sizeMB: 8.1,
925
+ isDirectory: true,
926
+ files: [...MLPACKAGE_FILES],
927
+ runtimes: ["python"]
928
+ },
929
+ openvino: ovFormat(hf("vehicleClassification/vehicle-type-v1/openvino/vehicle-type-v1.xml"), 8.7)
930
+ },
931
+ extraFiles: [{
932
+ url: hf("vehicleClassification/vehicle-type-v1/onnx/vehicle-type-v1-labels.json"),
933
+ filename: "vehicle-type-v1-labels.json",
934
+ sizeMB: .01
935
+ }]
936
+ }, {
830
937
  id: "vehicle-type-efficientnet",
831
938
  name: "Vehicle Type (EfficientNet)",
832
939
  description: "EfficientNet-B4 vehicle make/model/year classifier — 8,949 classes from VMMRdb",
940
+ legacy: true,
833
941
  inputSize: {
834
942
  width: 380,
835
943
  height: 380
@@ -1105,6 +1213,16 @@ var ANIMAL_10_LABELS = [
1105
1213
  "sheep",
1106
1214
  "spider"
1107
1215
  ];
1216
+ var ANIMAL_CLASSIFIER_LABELS = [
1217
+ "bird",
1218
+ "cat",
1219
+ "cow",
1220
+ "deer",
1221
+ "dog",
1222
+ "horse",
1223
+ "sheep",
1224
+ "squirrel"
1225
+ ];
1108
1226
  var VGG_G2_CHARSET = [
1109
1227
  "",
1110
1228
  ..."0123456789".split(""),
@@ -1229,6 +1347,17 @@ var ObjectDetectionStep = class {
1229
1347
  step: .05,
1230
1348
  default: .5,
1231
1349
  showValue: true
1350
+ },
1351
+ {
1352
+ type: "slider",
1353
+ key: "nmsIouThreshold",
1354
+ label: "NMS IoU threshold",
1355
+ description: "Overlap above which two boxes of the same object are merged (deduplicated). Lower merges more aggressively; higher keeps more near-duplicate boxes.",
1356
+ min: .1,
1357
+ max: .9,
1358
+ step: .05,
1359
+ default: .45,
1360
+ showValue: true
1232
1361
  }
1233
1362
  ];
1234
1363
  }
@@ -1292,9 +1421,9 @@ var AnimalClassifierStep = class {
1292
1421
  inputClasses: ["animal"],
1293
1422
  outputClasses: ["animal-type"],
1294
1423
  models: [...ANIMAL_CLASSIFIER_MODELS],
1295
- defaultModelId: "animals-10",
1424
+ defaultModelId: "animal-classifier",
1296
1425
  enabledByDefault: false,
1297
- defaultConfidence: .2,
1426
+ defaultConfidence: .5,
1298
1427
  labels: [...ANIMAL_10_LABELS],
1299
1428
  cadence: {
1300
1429
  trigger: "once",
@@ -1308,7 +1437,7 @@ var AnimalClassifierStep = class {
1308
1437
  key: "enabledClasses",
1309
1438
  label: "Species",
1310
1439
  description: "Which animal species to keep (empty = all). The classifier still scores every label but only the selected classes are returned on the detection.",
1311
- options: ANIMAL_10_LABELS.map((l) => ({
1440
+ options: ANIMAL_CLASSIFIER_LABELS.map((l) => ({
1312
1441
  value: l,
1313
1442
  label: l
1314
1443
  })),
@@ -1454,9 +1583,10 @@ var STEP_BIRD_CLASSIFIER = new ClassifierWithMinConfidence({
1454
1583
  inputClasses: ["animal"],
1455
1584
  outputClasses: ["species"],
1456
1585
  models: [...BIRD_CLASSIFIER_MODELS],
1457
- defaultModelId: "bird-nabirds-404",
1586
+ defaultModelId: "bird-classifier",
1458
1587
  enabledByDefault: false,
1459
- defaultConfidence: .2,
1588
+ defaultConfidence: .6,
1589
+ rejectClasses: ["background"],
1460
1590
  cadence: {
1461
1591
  trigger: "once",
1462
1592
  maxPerTrack: 3,
@@ -1472,9 +1602,9 @@ var STEP_VEHICLE_CLASSIFIER = new ClassifierWithMinConfidence({
1472
1602
  inputClasses: ["vehicle"],
1473
1603
  outputClasses: ["vehicle-type"],
1474
1604
  models: [...VEHICLE_CLASSIFIER_MODELS],
1475
- defaultModelId: "vehicle-type-efficientnet",
1605
+ defaultModelId: "vehicle-type-v1",
1476
1606
  enabledByDefault: false,
1477
- defaultConfidence: .3,
1607
+ defaultConfidence: .5,
1478
1608
  cadence: {
1479
1609
  trigger: "once",
1480
1610
  maxPerTrack: 3,
@@ -1,4 +1,4 @@
1
- import { a as COCO_TO_MACRO, i as COCO_80_LABELS, r as AUDIO_MACRO_LABELS, w as hfModelUrl } from "./dist-BzVZ2I4P.mjs";
1
+ import { a as COCO_TO_MACRO, i as COCO_80_LABELS, r as AUDIO_MACRO_LABELS, w as hfModelUrl } from "./dist-BpWot5-w.mjs";
2
2
  //#region src/detection-pipeline/registry/model-catalogs.ts
3
3
  var HF_REPO = "camstack/camstack-models";
4
4
  var HF_SCRYPTED = "scrypted/plugin-models";
@@ -764,9 +764,45 @@ var PLATE_OCR_MODELS = [{
764
764
  }
765
765
  }];
766
766
  var ANIMAL_CLASSIFIER_MODELS = [{
767
+ id: "animal-classifier",
768
+ name: "Animal Classifier (8)",
769
+ description: "EfficientNet-Lite0 animal classifier — bird, cat, cow, deer, dog, horse, sheep, squirrel (Open Images V7, publicly distributable)",
770
+ inputSize: {
771
+ width: 224,
772
+ height: 224
773
+ },
774
+ inputLayout: "nchw",
775
+ inputNormalization: "imagenet",
776
+ license: "Apache-2.0",
777
+ labels: [{
778
+ id: "animal-type",
779
+ name: "Animal Type"
780
+ }],
781
+ preprocessMode: "resize",
782
+ formats: {
783
+ onnx: {
784
+ url: hf("animalClassification/animal-classifier/onnx/animal-classifier.onnx"),
785
+ sizeMB: 13
786
+ },
787
+ coreml: {
788
+ url: hf("animalClassification/animal-classifier/coreml/animal-classifier.mlpackage"),
789
+ sizeMB: 6.5,
790
+ isDirectory: true,
791
+ files: [...MLPACKAGE_FILES],
792
+ runtimes: ["python"]
793
+ },
794
+ openvino: ovFormat(hf("animalClassification/animal-classifier/openvino/animal-classifier.xml"), 6.9)
795
+ },
796
+ extraFiles: [{
797
+ url: hf("animalClassification/animal-classifier/onnx/animal-classifier-labels.json"),
798
+ filename: "animal-classifier-labels.json",
799
+ sizeMB: .01
800
+ }]
801
+ }, {
767
802
  id: "animals-10",
768
803
  name: "Animal Classifier (10)",
769
804
  description: "ViT-based animal type classifier — cat, cow, dog, dolphin, eagle, panda, horse, monkey, sheep, spider",
805
+ legacy: true,
770
806
  inputSize: {
771
807
  width: 224,
772
808
  height: 224
@@ -793,9 +829,46 @@ var ANIMAL_CLASSIFIER_MODELS = [{
793
829
  }
794
830
  }];
795
831
  var BIRD_CLASSIFIER_MODELS = [{
832
+ id: "bird-classifier",
833
+ name: "Bird Classifier (AIY 965)",
834
+ description: "Google AIY Birds V1 (MobileNetV2) — 964 species + background; publicly distributable (Apache-2.0)",
835
+ inputSize: {
836
+ width: 224,
837
+ height: 224
838
+ },
839
+ inputLayout: "nhwc",
840
+ inputNormalization: "none",
841
+ outputProbabilities: true,
842
+ license: "Apache-2.0",
843
+ labels: [{
844
+ id: "species",
845
+ name: "Bird Species"
846
+ }],
847
+ preprocessMode: "resize",
848
+ formats: {
849
+ onnx: {
850
+ url: hf("animalClassification/bird-classifier/onnx/bird-classifier.onnx"),
851
+ sizeMB: 14
852
+ },
853
+ coreml: {
854
+ url: hf("animalClassification/bird-classifier/coreml/bird-classifier.mlpackage"),
855
+ sizeMB: 6.8,
856
+ isDirectory: true,
857
+ files: [...MLPACKAGE_FILES],
858
+ runtimes: ["python"]
859
+ },
860
+ openvino: ovFormat(hf("animalClassification/bird-classifier/openvino/bird-classifier.xml"), 14)
861
+ },
862
+ extraFiles: [{
863
+ url: hf("animalClassification/bird-classifier/onnx/bird-classifier-labels.json"),
864
+ filename: "bird-classifier-labels.json",
865
+ sizeMB: .03
866
+ }]
867
+ }, {
796
868
  id: "bird-nabirds-404",
797
869
  name: "NABirds (404 species)",
798
870
  description: "ResNet50 trained on NABirds — 404 North American species",
871
+ legacy: true,
799
872
  inputSize: {
800
873
  width: 224,
801
874
  height: 224
@@ -827,9 +900,44 @@ var BIRD_CLASSIFIER_MODELS = [{
827
900
  }]
828
901
  }];
829
902
  var VEHICLE_CLASSIFIER_MODELS = [{
903
+ id: "vehicle-type-v1",
904
+ name: "Vehicle Type (8)",
905
+ description: "MobileNetV3-Large vehicle type classifier — Ambulance, Bicycle, Bus, Car, Motorcycle, Taxi, Truck, Van (val 89.4%)",
906
+ inputSize: {
907
+ width: 224,
908
+ height: 224
909
+ },
910
+ inputLayout: "nchw",
911
+ inputNormalization: "imagenet",
912
+ labels: [{
913
+ id: "vehicle-type",
914
+ name: "Vehicle Type"
915
+ }],
916
+ preprocessMode: "resize",
917
+ formats: {
918
+ onnx: {
919
+ url: hf("vehicleClassification/vehicle-type-v1/onnx/vehicle-type-v1.onnx"),
920
+ sizeMB: 17
921
+ },
922
+ coreml: {
923
+ url: hf("vehicleClassification/vehicle-type-v1/coreml/vehicle-type-v1.mlpackage"),
924
+ sizeMB: 8.1,
925
+ isDirectory: true,
926
+ files: [...MLPACKAGE_FILES],
927
+ runtimes: ["python"]
928
+ },
929
+ openvino: ovFormat(hf("vehicleClassification/vehicle-type-v1/openvino/vehicle-type-v1.xml"), 8.7)
930
+ },
931
+ extraFiles: [{
932
+ url: hf("vehicleClassification/vehicle-type-v1/onnx/vehicle-type-v1-labels.json"),
933
+ filename: "vehicle-type-v1-labels.json",
934
+ sizeMB: .01
935
+ }]
936
+ }, {
830
937
  id: "vehicle-type-efficientnet",
831
938
  name: "Vehicle Type (EfficientNet)",
832
939
  description: "EfficientNet-B4 vehicle make/model/year classifier — 8,949 classes from VMMRdb",
940
+ legacy: true,
833
941
  inputSize: {
834
942
  width: 380,
835
943
  height: 380
@@ -1105,6 +1213,16 @@ var ANIMAL_10_LABELS = [
1105
1213
  "sheep",
1106
1214
  "spider"
1107
1215
  ];
1216
+ var ANIMAL_CLASSIFIER_LABELS = [
1217
+ "bird",
1218
+ "cat",
1219
+ "cow",
1220
+ "deer",
1221
+ "dog",
1222
+ "horse",
1223
+ "sheep",
1224
+ "squirrel"
1225
+ ];
1108
1226
  var VGG_G2_CHARSET = [
1109
1227
  "",
1110
1228
  ..."0123456789".split(""),
@@ -1229,6 +1347,17 @@ var ObjectDetectionStep = class {
1229
1347
  step: .05,
1230
1348
  default: .5,
1231
1349
  showValue: true
1350
+ },
1351
+ {
1352
+ type: "slider",
1353
+ key: "nmsIouThreshold",
1354
+ label: "NMS IoU threshold",
1355
+ description: "Overlap above which two boxes of the same object are merged (deduplicated). Lower merges more aggressively; higher keeps more near-duplicate boxes.",
1356
+ min: .1,
1357
+ max: .9,
1358
+ step: .05,
1359
+ default: .45,
1360
+ showValue: true
1232
1361
  }
1233
1362
  ];
1234
1363
  }
@@ -1292,9 +1421,9 @@ var AnimalClassifierStep = class {
1292
1421
  inputClasses: ["animal"],
1293
1422
  outputClasses: ["animal-type"],
1294
1423
  models: [...ANIMAL_CLASSIFIER_MODELS],
1295
- defaultModelId: "animals-10",
1424
+ defaultModelId: "animal-classifier",
1296
1425
  enabledByDefault: false,
1297
- defaultConfidence: .2,
1426
+ defaultConfidence: .5,
1298
1427
  labels: [...ANIMAL_10_LABELS],
1299
1428
  cadence: {
1300
1429
  trigger: "once",
@@ -1308,7 +1437,7 @@ var AnimalClassifierStep = class {
1308
1437
  key: "enabledClasses",
1309
1438
  label: "Species",
1310
1439
  description: "Which animal species to keep (empty = all). The classifier still scores every label but only the selected classes are returned on the detection.",
1311
- options: ANIMAL_10_LABELS.map((l) => ({
1440
+ options: ANIMAL_CLASSIFIER_LABELS.map((l) => ({
1312
1441
  value: l,
1313
1442
  label: l
1314
1443
  })),
@@ -1454,9 +1583,10 @@ var STEP_BIRD_CLASSIFIER = new ClassifierWithMinConfidence({
1454
1583
  inputClasses: ["animal"],
1455
1584
  outputClasses: ["species"],
1456
1585
  models: [...BIRD_CLASSIFIER_MODELS],
1457
- defaultModelId: "bird-nabirds-404",
1586
+ defaultModelId: "bird-classifier",
1458
1587
  enabledByDefault: false,
1459
- defaultConfidence: .2,
1588
+ defaultConfidence: .6,
1589
+ rejectClasses: ["background"],
1460
1590
  cadence: {
1461
1591
  trigger: "once",
1462
1592
  maxPerTrack: 3,
@@ -1472,9 +1602,9 @@ var STEP_VEHICLE_CLASSIFIER = new ClassifierWithMinConfidence({
1472
1602
  inputClasses: ["vehicle"],
1473
1603
  outputClasses: ["vehicle-type"],
1474
1604
  models: [...VEHICLE_CLASSIFIER_MODELS],
1475
- defaultModelId: "vehicle-type-efficientnet",
1605
+ defaultModelId: "vehicle-type-v1",
1476
1606
  enabledByDefault: false,
1477
- defaultConfidence: .3,
1607
+ defaultConfidence: .5,
1478
1608
  cadence: {
1479
1609
  trigger: "once",
1480
1610
  maxPerTrack: 3,
@@ -1,8 +1,8 @@
1
- import { a as e, c as t, d as n, i as r, l as i, n as a, o, r as s, s as c, t as l, u } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CV2WbUaO.mjs";
1
+ import { a as e, c as t, d as n, i as r, l as i, n as a, o, r as s, s as c, t as l, u } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DlALQ_wp.mjs";
2
2
  import { a as d, i as f, n as p, o as m, r as h, t as g } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare__react__loadShare__.js-C9j-2lBe.mjs";
3
3
  import { n as _, r as v, t as y } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-XO0-Pyu6.mjs";
4
4
  import { n as b, t as x } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-BO7TIbJV.mjs";
5
- import { t as S } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-bAh96WNb.mjs";
5
+ import { t as S } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CPbLcza9.mjs";
6
6
  //#region ../../node_modules/lucide-react/dist/esm/shared/src/utils.js
7
7
  var C = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), w = (e) => e.replace(/^([A-Z])|[\s-_]+(\w)/g, (e, t, n) => n ? n.toUpperCase() : t.toLowerCase()), T = (e) => {
8
8
  let t = w(e);