@camstack/addon-pipeline 1.1.3 → 1.1.5
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.
- package/dist/audio-analyzer/index.js +17 -19
- package/dist/audio-analyzer/index.mjs +17 -19
- package/dist/audio-codec-nodeav/index.js +1 -1
- package/dist/audio-codec-nodeav/index.mjs +1 -1
- package/dist/decoder-nodeav/index.js +1 -1
- package/dist/decoder-nodeav/index.mjs +1 -1
- package/dist/detection-pipeline/index.js +195 -65
- package/dist/detection-pipeline/index.mjs +195 -65
- package/dist/{dist-DnD2tm7T.js → dist-BhpOMzzH.js} +44 -11
- package/dist/{dist-BWc-HYQz.mjs → dist-GFzs5acw.mjs} +44 -11
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +1 -1
- package/dist/pipeline-runner/index.mjs +1 -1
- package/dist/recorder/index.js +1 -1
- package/dist/recorder/index.mjs +1 -1
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DmOHs-vv.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DQYrh0ie.mjs} +3 -3
- package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-FRD2eBuz.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-S6ETjGtN.mjs} +1 -1
- package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CHcXI1Wf.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-5vetl3CL.mjs} +1 -1
- package/dist/stream-broker/{hostInit-CqjZ-6gj.mjs → hostInit-DvWdWFSe.mjs} +3 -3
- package/dist/stream-broker/index.js +1 -1
- package/dist/stream-broker/index.mjs +1 -1
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-5UPreLSr.js → MaskShapeCanvas-DI4BY7W2-Bulflbu5.js} +1 -1
- package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-Bxqs-CpZ.js → MotionZonesSettings-NcxxQN8r-DJ9keq4q.js} +1 -1
- package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-BDMPeMJd.js → PrivacyMaskSettings-APgPLF7p-CRsjkTB1.js} +1 -1
- package/embed-dist/assets/index-DBB2e2P1.js +80 -0
- package/embed-dist/index.html +1 -1
- package/package.json +1 -9
- package/python/postprocessors/__init__.py +2 -0
- package/python/postprocessors/clip.py +35 -0
- package/python/postprocessors/test_clip.py +33 -0
- package/embed-dist/assets/index-BgGwqHYl.js +0 -80
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_model_download_service_C_IHWnXx = require("../model-download-service-C-IHWnXx-BnQ_awK4.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
6
|
+
const require_dist = require("../dist-BhpOMzzH.js");
|
|
7
7
|
let node_fs = require("node:fs");
|
|
8
8
|
node_fs = require_model_download_service_C_IHWnXx.__toESM(node_fs);
|
|
9
9
|
let node_path = require("node:path");
|
|
@@ -719,14 +719,14 @@ var AudioAnalyzerAddon = class extends require_dist.BaseAddon {
|
|
|
719
719
|
* preview mode (operator typed but didn't save yet) — same
|
|
720
720
|
* semantics detection-pipeline relies on.
|
|
721
721
|
*/
|
|
722
|
-
async getGlobalSettings(overlay, nodeId) {
|
|
722
|
+
async getGlobalSettings(overlay, _cap, nodeId) {
|
|
723
723
|
const ctx = this.ctxIfReady;
|
|
724
724
|
const stored = ctx?.settings ? await ctx.settings.readAddonStore() ?? {} : {};
|
|
725
725
|
const merged = overlay ? {
|
|
726
726
|
...stored,
|
|
727
727
|
...overlay
|
|
728
728
|
} : stored;
|
|
729
|
-
const isDarwin = await this.
|
|
729
|
+
const isDarwin = await this.resolveNodePlatform(nodeId) === "darwin";
|
|
730
730
|
const backendOptions = require_dist.AUDIO_BACKEND_CHOICES.filter((o) => o.value !== "apple-soundanalysis" || isDarwin);
|
|
731
731
|
const operatorChoiceRaw = typeof merged.audioBackend === "string" ? merged.audioBackend : require_dist.DEFAULT_AUDIO_ANALYZER_CONFIG.audioBackend;
|
|
732
732
|
const operatorChoice = backendOptions.some((o) => o.value === operatorChoiceRaw) ? operatorChoiceRaw : require_dist.DEFAULT_AUDIO_ANALYZER_CONFIG.audioBackend;
|
|
@@ -765,21 +765,22 @@ var AudioAnalyzerAddon = class extends require_dist.BaseAddon {
|
|
|
765
765
|
}, raw);
|
|
766
766
|
}
|
|
767
767
|
/**
|
|
768
|
-
*
|
|
769
|
-
*
|
|
770
|
-
*
|
|
771
|
-
*
|
|
772
|
-
*
|
|
768
|
+
* Resolve the OS platform of the target node WITHOUT the flaky platform-probe
|
|
769
|
+
* "hardware" cap. For the local node (or no nodeId) the running process is the
|
|
770
|
+
* authority. For a remote node — the hub-centric settings form serves every
|
|
771
|
+
* node from the hub child — read the node's registration-reported platform
|
|
772
|
+
* from the in-memory cluster topology (`nodes.topology`): a hub-local read, no
|
|
773
|
+
* remote round-trip, no probe. Falls back to this host's `process.platform`
|
|
774
|
+
* when topology is unavailable so the form stays usable.
|
|
773
775
|
*/
|
|
774
|
-
async
|
|
775
|
-
const api = this.ctxIfReady?.api;
|
|
776
|
-
if (!api) return process.platform;
|
|
776
|
+
async resolveNodePlatform(nodeId) {
|
|
777
777
|
const localNode = this.ctxIfReady?.kernel?.localNodeId;
|
|
778
|
+
if (!nodeId || nodeId === localNode) return process.platform;
|
|
778
779
|
try {
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
780
|
+
const entry = (await this.ctxIfReady?.api?.nodes?.topology.query())?.find((n) => n.id === nodeId);
|
|
781
|
+
if (entry?.platform) return entry.platform;
|
|
782
|
+
} catch {}
|
|
783
|
+
return process.platform;
|
|
783
784
|
}
|
|
784
785
|
/**
|
|
785
786
|
* Re-run the platform probe and persist the detected backend into
|
|
@@ -852,10 +853,7 @@ var AudioAnalyzerAddon = class extends require_dist.BaseAddon {
|
|
|
852
853
|
*/
|
|
853
854
|
async buildPipeline(backend) {
|
|
854
855
|
const logger = this.ctx.logger;
|
|
855
|
-
const modelsDir = await this.
|
|
856
|
-
location: "models",
|
|
857
|
-
relativePath: ""
|
|
858
|
-
}).catch(() => "camstack-data/models");
|
|
856
|
+
const modelsDir = await this.resolveModelsDir();
|
|
859
857
|
logger.info("audio-analyzer: resolving pipeline", { meta: {
|
|
860
858
|
operatorChoice: this.config.audioBackend,
|
|
861
859
|
effectiveBackend: backend,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __require } from "../chunk-BdkLduGY.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { B as BaseAddon, E as mapAudioLabelToMacro, J as hydrateSchema, g as audioAnalyzerCapability, h as audioAnalysisCapability, l as HF_BASE_URL, n as AUDIO_BACKEND_CHOICES, o as DEFAULT_AUDIO_ANALYZER_CONFIG, z as errMsg } from "../dist-GFzs5acw.mjs";
|
|
3
3
|
import { r as downloadFile } from "../model-download-service-C-IHWnXx-3Mmeob3l.mjs";
|
|
4
4
|
import * as fs from "node:fs";
|
|
5
5
|
import * as path$1 from "node:path";
|
|
@@ -714,14 +714,14 @@ var AudioAnalyzerAddon = class extends BaseAddon {
|
|
|
714
714
|
* preview mode (operator typed but didn't save yet) — same
|
|
715
715
|
* semantics detection-pipeline relies on.
|
|
716
716
|
*/
|
|
717
|
-
async getGlobalSettings(overlay, nodeId) {
|
|
717
|
+
async getGlobalSettings(overlay, _cap, nodeId) {
|
|
718
718
|
const ctx = this.ctxIfReady;
|
|
719
719
|
const stored = ctx?.settings ? await ctx.settings.readAddonStore() ?? {} : {};
|
|
720
720
|
const merged = overlay ? {
|
|
721
721
|
...stored,
|
|
722
722
|
...overlay
|
|
723
723
|
} : stored;
|
|
724
|
-
const isDarwin = await this.
|
|
724
|
+
const isDarwin = await this.resolveNodePlatform(nodeId) === "darwin";
|
|
725
725
|
const backendOptions = AUDIO_BACKEND_CHOICES.filter((o) => o.value !== "apple-soundanalysis" || isDarwin);
|
|
726
726
|
const operatorChoiceRaw = typeof merged.audioBackend === "string" ? merged.audioBackend : DEFAULT_AUDIO_ANALYZER_CONFIG.audioBackend;
|
|
727
727
|
const operatorChoice = backendOptions.some((o) => o.value === operatorChoiceRaw) ? operatorChoiceRaw : DEFAULT_AUDIO_ANALYZER_CONFIG.audioBackend;
|
|
@@ -760,21 +760,22 @@ var AudioAnalyzerAddon = class extends BaseAddon {
|
|
|
760
760
|
}, raw);
|
|
761
761
|
}
|
|
762
762
|
/**
|
|
763
|
-
*
|
|
764
|
-
*
|
|
765
|
-
*
|
|
766
|
-
*
|
|
767
|
-
*
|
|
763
|
+
* Resolve the OS platform of the target node WITHOUT the flaky platform-probe
|
|
764
|
+
* "hardware" cap. For the local node (or no nodeId) the running process is the
|
|
765
|
+
* authority. For a remote node — the hub-centric settings form serves every
|
|
766
|
+
* node from the hub child — read the node's registration-reported platform
|
|
767
|
+
* from the in-memory cluster topology (`nodes.topology`): a hub-local read, no
|
|
768
|
+
* remote round-trip, no probe. Falls back to this host's `process.platform`
|
|
769
|
+
* when topology is unavailable so the form stays usable.
|
|
768
770
|
*/
|
|
769
|
-
async
|
|
770
|
-
const api = this.ctxIfReady?.api;
|
|
771
|
-
if (!api) return process.platform;
|
|
771
|
+
async resolveNodePlatform(nodeId) {
|
|
772
772
|
const localNode = this.ctxIfReady?.kernel?.localNodeId;
|
|
773
|
+
if (!nodeId || nodeId === localNode) return process.platform;
|
|
773
774
|
try {
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
775
|
+
const entry = (await this.ctxIfReady?.api?.nodes?.topology.query())?.find((n) => n.id === nodeId);
|
|
776
|
+
if (entry?.platform) return entry.platform;
|
|
777
|
+
} catch {}
|
|
778
|
+
return process.platform;
|
|
778
779
|
}
|
|
779
780
|
/**
|
|
780
781
|
* Re-run the platform probe and persist the detected backend into
|
|
@@ -847,10 +848,7 @@ var AudioAnalyzerAddon = class extends BaseAddon {
|
|
|
847
848
|
*/
|
|
848
849
|
async buildPipeline(backend) {
|
|
849
850
|
const logger = this.ctx.logger;
|
|
850
|
-
const modelsDir = await this.
|
|
851
|
-
location: "models",
|
|
852
|
-
relativePath: ""
|
|
853
|
-
}).catch(() => "camstack-data/models");
|
|
851
|
+
const modelsDir = await this.resolveModelsDir();
|
|
854
852
|
logger.info("audio-analyzer: resolving pipeline", { meta: {
|
|
855
853
|
operatorChoice: this.config.audioBackend,
|
|
856
854
|
effectiveBackend: backend,
|
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
5
|
+
const require_dist = require("../dist-BhpOMzzH.js");
|
|
6
6
|
const require_codec_runtime = require("../codec-runtime-BOk-13PN.js");
|
|
7
7
|
let node_crypto = require("node:crypto");
|
|
8
8
|
//#region src/audio-codec-nodeav/addon/index.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as BaseAddon, _ as audioCodecCapability, z as errMsg } from "../dist-
|
|
1
|
+
import { B as BaseAddon, _ as audioCodecCapability, z as errMsg } from "../dist-GFzs5acw.mjs";
|
|
2
2
|
import { t as DecodeRuntime } from "../codec-runtime-BsqlEjPi.mjs";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
//#region src/audio-codec-nodeav/addon/index.ts
|
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
5
|
+
const require_dist = require("../dist-BhpOMzzH.js");
|
|
6
6
|
let _camstack_shm_ring = require("@camstack/shm-ring");
|
|
7
7
|
let node_crypto = require("node:crypto");
|
|
8
8
|
//#region src/decoder-nodeav/frame-ring-sink.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as BaseAddon, b as decoderCapability, f as RingBuffer, s as DEFAULT_DECODER_HWACCEL_CONFIG, u as HWACCEL_OPTIONS, z as errMsg } from "../dist-
|
|
1
|
+
import { B as BaseAddon, b as decoderCapability, f as RingBuffer, s as DEFAULT_DECODER_HWACCEL_CONFIG, u as HWACCEL_OPTIONS, z as errMsg } from "../dist-GFzs5acw.mjs";
|
|
2
2
|
import { FrameRingReaderCache, FrameRingWriter, MIN_RING_SLOTS, computeSegmentSize, computeSlotByteLength, createSegment, deriveSlotCount } from "@camstack/shm-ring";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
//#region src/decoder-nodeav/frame-ring-sink.ts
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_model_download_service_C_IHWnXx = require("../model-download-service-C-IHWnXx-BnQ_awK4.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
6
|
+
const require_dist = require("../dist-BhpOMzzH.js");
|
|
7
7
|
let node_fs = require("node:fs");
|
|
8
8
|
node_fs = require_model_download_service_C_IHWnXx.__toESM(node_fs);
|
|
9
9
|
let node_path = require("node:path");
|
|
@@ -1753,6 +1753,95 @@ var INSTANCE_SEGMENTATION_MODELS = [
|
|
|
1753
1753
|
}
|
|
1754
1754
|
}
|
|
1755
1755
|
];
|
|
1756
|
+
var CLIP_EMBEDDING_MODELS = [
|
|
1757
|
+
{
|
|
1758
|
+
id: "mobileclip-s0",
|
|
1759
|
+
name: "MobileCLIP S0",
|
|
1760
|
+
description: "MobileCLIP S0 — Apple ultra-fast CLIP vision encoder, 512-dim, 256×256 (40 MB ONNX)",
|
|
1761
|
+
inputSize: {
|
|
1762
|
+
width: 256,
|
|
1763
|
+
height: 256
|
|
1764
|
+
},
|
|
1765
|
+
labels: [{
|
|
1766
|
+
id: "embedding",
|
|
1767
|
+
name: "CLIP Embedding"
|
|
1768
|
+
}],
|
|
1769
|
+
preprocessMode: "resize",
|
|
1770
|
+
inputNormalization: "none",
|
|
1771
|
+
formats: {
|
|
1772
|
+
onnx: {
|
|
1773
|
+
url: hf("clip/mobileclip-s0/onnx/camstack-mobileclip-s0-vision.onnx"),
|
|
1774
|
+
sizeMB: 40
|
|
1775
|
+
},
|
|
1776
|
+
openvino: ovFormat(hf("clip/mobileclip-s0/openvino/camstack-mobileclip-s0-vision.xml"), 25),
|
|
1777
|
+
coreml: {
|
|
1778
|
+
url: hf("clip/mobileclip-s0/coreml/camstack-mobileclip-s0-vision.mlpackage"),
|
|
1779
|
+
sizeMB: 30,
|
|
1780
|
+
isDirectory: true,
|
|
1781
|
+
files: [...MLPACKAGE_FILES],
|
|
1782
|
+
runtimes: ["python"]
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
},
|
|
1786
|
+
{
|
|
1787
|
+
id: "mobileclip-s1",
|
|
1788
|
+
name: "MobileCLIP S1",
|
|
1789
|
+
description: "MobileCLIP S1 — Apple balanced CLIP vision encoder, 512-dim, 256×256 (90 MB ONNX)",
|
|
1790
|
+
inputSize: {
|
|
1791
|
+
width: 256,
|
|
1792
|
+
height: 256
|
|
1793
|
+
},
|
|
1794
|
+
labels: [{
|
|
1795
|
+
id: "embedding",
|
|
1796
|
+
name: "CLIP Embedding"
|
|
1797
|
+
}],
|
|
1798
|
+
preprocessMode: "resize",
|
|
1799
|
+
inputNormalization: "none",
|
|
1800
|
+
formats: {
|
|
1801
|
+
onnx: {
|
|
1802
|
+
url: hf("clip/mobileclip-s1/onnx/camstack-mobileclip-s1-vision.onnx"),
|
|
1803
|
+
sizeMB: 90
|
|
1804
|
+
},
|
|
1805
|
+
openvino: ovFormat(hf("clip/mobileclip-s1/openvino/camstack-mobileclip-s1-vision.xml"), 55),
|
|
1806
|
+
coreml: {
|
|
1807
|
+
url: hf("clip/mobileclip-s1/coreml/camstack-mobileclip-s1-vision.mlpackage"),
|
|
1808
|
+
sizeMB: 65,
|
|
1809
|
+
isDirectory: true,
|
|
1810
|
+
files: [...MLPACKAGE_FILES],
|
|
1811
|
+
runtimes: ["python"]
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
},
|
|
1815
|
+
{
|
|
1816
|
+
id: "mobileclip-s2",
|
|
1817
|
+
name: "MobileCLIP S2",
|
|
1818
|
+
description: "MobileCLIP S2 — Apple high-accuracy CLIP vision encoder, 512-dim, 256×256 (150 MB ONNX)",
|
|
1819
|
+
inputSize: {
|
|
1820
|
+
width: 256,
|
|
1821
|
+
height: 256
|
|
1822
|
+
},
|
|
1823
|
+
labels: [{
|
|
1824
|
+
id: "embedding",
|
|
1825
|
+
name: "CLIP Embedding"
|
|
1826
|
+
}],
|
|
1827
|
+
preprocessMode: "resize",
|
|
1828
|
+
inputNormalization: "none",
|
|
1829
|
+
formats: {
|
|
1830
|
+
onnx: {
|
|
1831
|
+
url: hf("clip/mobileclip-s2/onnx/camstack-mobileclip-s2-vision.onnx"),
|
|
1832
|
+
sizeMB: 150
|
|
1833
|
+
},
|
|
1834
|
+
openvino: ovFormat(hf("clip/mobileclip-s2/openvino/camstack-mobileclip-s2-vision.xml"), 90),
|
|
1835
|
+
coreml: {
|
|
1836
|
+
url: hf("clip/mobileclip-s2/coreml/camstack-mobileclip-s2-vision.mlpackage"),
|
|
1837
|
+
sizeMB: 110,
|
|
1838
|
+
isDirectory: true,
|
|
1839
|
+
files: [...MLPACKAGE_FILES],
|
|
1840
|
+
runtimes: ["python"]
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
];
|
|
1756
1845
|
var AUDIO_CLASSIFIER_MODELS = [{
|
|
1757
1846
|
id: "yamnet-onnx",
|
|
1758
1847
|
name: "YAMNet",
|
|
@@ -2024,6 +2113,21 @@ var STEP_FACE_EMBEDDING = new PipelineStepBase({
|
|
|
2024
2113
|
defaultModelId: "arcface-r100",
|
|
2025
2114
|
defaultConfidence: 0
|
|
2026
2115
|
});
|
|
2116
|
+
var STEP_CLIP_EMBEDDING = new PipelineStepBase({
|
|
2117
|
+
id: "clip-embedding",
|
|
2118
|
+
name: "CLIP Embedding",
|
|
2119
|
+
slot: "classifier",
|
|
2120
|
+
postprocessor: "clip",
|
|
2121
|
+
extractMode: "crop-roi",
|
|
2122
|
+
inputClasses: null,
|
|
2123
|
+
outputClasses: ["embedding"],
|
|
2124
|
+
models: [...CLIP_EMBEDDING_MODELS],
|
|
2125
|
+
defaultModelId: "mobileclip-s1",
|
|
2126
|
+
enabledByDefault: false,
|
|
2127
|
+
defaultConfidence: 0,
|
|
2128
|
+
defaultMinParentScore: .4,
|
|
2129
|
+
group: "CLIP"
|
|
2130
|
+
});
|
|
2027
2131
|
var STEP_PLATE_DETECTION = new PipelineStepBase({
|
|
2028
2132
|
id: "plate-detection",
|
|
2029
2133
|
name: "Plate Detection",
|
|
@@ -2113,6 +2217,7 @@ var ALL_PIPELINE_STEPS = [
|
|
|
2113
2217
|
STEP_OBJECT_DETECTION_INSTANCE,
|
|
2114
2218
|
STEP_FACE_DETECTION,
|
|
2115
2219
|
STEP_FACE_EMBEDDING,
|
|
2220
|
+
STEP_CLIP_EMBEDDING,
|
|
2116
2221
|
STEP_PLATE_DETECTION,
|
|
2117
2222
|
STEP_PLATE_OCR,
|
|
2118
2223
|
new AnimalClassifierStep(),
|
|
@@ -2663,6 +2768,22 @@ function postprocessArcface(output, _stepDef) {
|
|
|
2663
2768
|
embeddingNorm: normalized
|
|
2664
2769
|
};
|
|
2665
2770
|
}
|
|
2771
|
+
function postprocessClip(output, _stepDef) {
|
|
2772
|
+
const structured = tryStructured(output);
|
|
2773
|
+
if (structured) return structured;
|
|
2774
|
+
const tensor = output.tensor;
|
|
2775
|
+
if (!tensor) throw new Error("CLIP postprocessor: no tensor in engine output");
|
|
2776
|
+
let sumSq = 0;
|
|
2777
|
+
for (let i = 0; i < tensor.length; i++) sumSq += tensor[i] * tensor[i];
|
|
2778
|
+
const norm = Math.sqrt(sumSq);
|
|
2779
|
+
const normalized = Array.from({ length: tensor.length });
|
|
2780
|
+
for (let i = 0; i < tensor.length; i++) normalized[i] = norm === 0 ? 0 : tensor[i] / norm;
|
|
2781
|
+
return {
|
|
2782
|
+
kind: "embedding",
|
|
2783
|
+
embedding: Array.from(tensor),
|
|
2784
|
+
embeddingNorm: normalized
|
|
2785
|
+
};
|
|
2786
|
+
}
|
|
2666
2787
|
function postprocessSoftmax(output, stepDef) {
|
|
2667
2788
|
const structured = tryStructured(output);
|
|
2668
2789
|
if (structured) return structured;
|
|
@@ -2722,6 +2843,67 @@ function postprocessCtc(output, stepDef) {
|
|
|
2722
2843
|
confidence: seqLen > 0 ? totalScore / seqLen : 0
|
|
2723
2844
|
};
|
|
2724
2845
|
}
|
|
2846
|
+
/**
|
|
2847
|
+
* Bounding box [x, y, w, h] (mask-pixel space) of the largest 4-connected
|
|
2848
|
+
* foreground component. Port of `_largest_component_bbox` from
|
|
2849
|
+
* `python/postprocessors/saliency.py`.
|
|
2850
|
+
*
|
|
2851
|
+
* Returns null when there are no foreground pixels.
|
|
2852
|
+
*/
|
|
2853
|
+
function largestComponentBbox(binary, w, h) {
|
|
2854
|
+
const seeds = [];
|
|
2855
|
+
for (let row = 0; row < h; row++) for (let col = 0; col < w; col++) if ((binary[row * w + col] ?? 0) > 0) seeds.push([row, col]);
|
|
2856
|
+
if (seeds.length === 0) return null;
|
|
2857
|
+
const visited = new Uint8Array(w * h);
|
|
2858
|
+
let best = null;
|
|
2859
|
+
for (const [sy, sx] of seeds) {
|
|
2860
|
+
const seedIdx = sy * w + sx;
|
|
2861
|
+
if (visited[seedIdx] === 1) continue;
|
|
2862
|
+
const stack = [[sy, sx]];
|
|
2863
|
+
visited[seedIdx] = 1;
|
|
2864
|
+
let minX = sx;
|
|
2865
|
+
let maxX = sx;
|
|
2866
|
+
let minY = sy;
|
|
2867
|
+
let maxY = sy;
|
|
2868
|
+
let area = 0;
|
|
2869
|
+
while (stack.length > 0) {
|
|
2870
|
+
const [cy, cx] = stack.pop();
|
|
2871
|
+
area += 1;
|
|
2872
|
+
if (cx < minX) minX = cx;
|
|
2873
|
+
if (cx > maxX) maxX = cx;
|
|
2874
|
+
if (cy < minY) minY = cy;
|
|
2875
|
+
if (cy > maxY) maxY = cy;
|
|
2876
|
+
const neighbours = [
|
|
2877
|
+
[cy - 1, cx],
|
|
2878
|
+
[cy + 1, cx],
|
|
2879
|
+
[cy, cx - 1],
|
|
2880
|
+
[cy, cx + 1]
|
|
2881
|
+
];
|
|
2882
|
+
for (const [ny, nx] of neighbours) {
|
|
2883
|
+
if (ny < 0 || ny >= h || nx < 0 || nx >= w) continue;
|
|
2884
|
+
const nIdx = ny * w + nx;
|
|
2885
|
+
if ((binary[nIdx] ?? 0) > 0 && visited[nIdx] !== 1) {
|
|
2886
|
+
visited[nIdx] = 1;
|
|
2887
|
+
stack.push([ny, nx]);
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
}
|
|
2891
|
+
if (best === null || area > best.area) best = {
|
|
2892
|
+
area,
|
|
2893
|
+
minX,
|
|
2894
|
+
minY,
|
|
2895
|
+
maxX,
|
|
2896
|
+
maxY
|
|
2897
|
+
};
|
|
2898
|
+
}
|
|
2899
|
+
if (best === null) return null;
|
|
2900
|
+
return [
|
|
2901
|
+
best.minX,
|
|
2902
|
+
best.minY,
|
|
2903
|
+
best.maxX - best.minX + 1,
|
|
2904
|
+
best.maxY - best.minY + 1
|
|
2905
|
+
];
|
|
2906
|
+
}
|
|
2725
2907
|
function postprocessSaliency(output, _stepDef) {
|
|
2726
2908
|
const structured = tryStructured(output);
|
|
2727
2909
|
if (structured) return structured;
|
|
@@ -2730,9 +2912,18 @@ function postprocessSaliency(output, _stepDef) {
|
|
|
2730
2912
|
const binary = new Uint8Array(tensor.length);
|
|
2731
2913
|
for (let i = 0; i < tensor.length; i++) binary[i] = 1 / (1 + Math.exp(-tensor[i])) > .5 ? 255 : 0;
|
|
2732
2914
|
const side = Math.round(Math.sqrt(tensor.length));
|
|
2915
|
+
const mask = Buffer.from(binary).toString("base64");
|
|
2916
|
+
const maskBbox = largestComponentBbox(binary, side, side);
|
|
2917
|
+
if (maskBbox !== null) return {
|
|
2918
|
+
kind: "mask",
|
|
2919
|
+
mask,
|
|
2920
|
+
maskWidth: side,
|
|
2921
|
+
maskHeight: side,
|
|
2922
|
+
maskBbox
|
|
2923
|
+
};
|
|
2733
2924
|
return {
|
|
2734
2925
|
kind: "mask",
|
|
2735
|
-
mask
|
|
2926
|
+
mask,
|
|
2736
2927
|
maskWidth: side,
|
|
2737
2928
|
maskHeight: side
|
|
2738
2929
|
};
|
|
@@ -2879,6 +3070,7 @@ var DISPATCH = {
|
|
|
2879
3070
|
"yolo-seg": postprocessYoloSeg,
|
|
2880
3071
|
scrfd: postprocessScrfd,
|
|
2881
3072
|
arcface: postprocessArcface,
|
|
3073
|
+
clip: postprocessClip,
|
|
2882
3074
|
softmax: postprocessSoftmax,
|
|
2883
3075
|
ctc: postprocessCtc,
|
|
2884
3076
|
saliency: postprocessSaliency,
|
|
@@ -5950,41 +6142,6 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5950
6142
|
return readStored(raw, isEngineChoice);
|
|
5951
6143
|
}
|
|
5952
6144
|
/**
|
|
5953
|
-
* Synchronous availability probe for a Python inference backend. Runs a
|
|
5954
|
-
* short `<python> -c "import <mod>"` with a 3s timeout. Used at
|
|
5955
|
-
* `loadEngine` time to reject a persisted backend choice the Python
|
|
5956
|
-
* interpreter on this host can't actually import — without this the
|
|
5957
|
-
* detection-pipeline child process exits with code 1 the moment
|
|
5958
|
-
* `inference_pool.py` hits its `from openvino.runtime import Core`.
|
|
5959
|
-
*
|
|
5960
|
-
* MUST probe the EMBEDDED portable interpreter (`pythonPath`, resolved via
|
|
5961
|
-
* `ctx.deps.ensurePython()`) — that's where `installPythonRequirements`
|
|
5962
|
-
* puts the backend packages and what `SharedInferencePool` actually spawns.
|
|
5963
|
-
* Probing the system `python3` checks the wrong site-packages (and on a
|
|
5964
|
-
* slim container there is no system python3 at all → every backend would
|
|
5965
|
-
* be reported unavailable, silently disabling ML). Falls back to `python3`
|
|
5966
|
-
* only when no embedded interpreter is resolved.
|
|
5967
|
-
*/
|
|
5968
|
-
static isPythonBackendAvailable(backend, pythonPath) {
|
|
5969
|
-
const mod = {
|
|
5970
|
-
coreml: "coremltools",
|
|
5971
|
-
openvino: "openvino.runtime",
|
|
5972
|
-
onnx: "onnxruntime"
|
|
5973
|
-
}[backend];
|
|
5974
|
-
if (mod === null) return true;
|
|
5975
|
-
if (!mod) return false;
|
|
5976
|
-
try {
|
|
5977
|
-
const { execFileSync } = require("node:child_process");
|
|
5978
|
-
execFileSync(pythonPath || "python3", ["-c", `import ${mod}`], {
|
|
5979
|
-
timeout: 3e3,
|
|
5980
|
-
stdio: "pipe"
|
|
5981
|
-
});
|
|
5982
|
-
return true;
|
|
5983
|
-
} catch {
|
|
5984
|
-
return false;
|
|
5985
|
-
}
|
|
5986
|
-
}
|
|
5987
|
-
/**
|
|
5988
6145
|
* Re-run the platform probe for the inference engine and persist the
|
|
5989
6146
|
* result into the addon's own global-settings keys. Because those
|
|
5990
6147
|
* fields are declared `requiresRestart: true`, the
|
|
@@ -7037,33 +7194,6 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
|
|
|
7037
7194
|
numWorkers: num(t["numWorkers"], 1)
|
|
7038
7195
|
};
|
|
7039
7196
|
}
|
|
7040
|
-
/**
|
|
7041
|
-
* Resolve the directory models are downloaded into, resilient across nodes.
|
|
7042
|
-
* The `models` storage location is GLOBAL (hub-seeded) and can resolve to a
|
|
7043
|
-
* path that exists only on the hub — e.g. Docker's `/data/models`, which an
|
|
7044
|
-
* agent on a different filesystem (a Mac) cannot create (`ENOENT mkdir
|
|
7045
|
-
* /data/models`). Verify the resolved dir is creatable; otherwise fall back to
|
|
7046
|
-
* this node's LOCAL addon data-dir so models always land on writable disk.
|
|
7047
|
-
*/
|
|
7048
|
-
async resolveModelsDir() {
|
|
7049
|
-
const fallback = node_path.join(this.ctx.dataDir, "models");
|
|
7050
|
-
const candidate = await this.ctx.api.storage.resolve.query({
|
|
7051
|
-
location: "models",
|
|
7052
|
-
relativePath: ""
|
|
7053
|
-
}).catch(() => null) ?? fallback;
|
|
7054
|
-
try {
|
|
7055
|
-
await node_fs.promises.mkdir(candidate, { recursive: true });
|
|
7056
|
-
return candidate;
|
|
7057
|
-
} catch (err) {
|
|
7058
|
-
this.ctx.logger.warn("models dir not creatable on this node — using local data-dir", { meta: {
|
|
7059
|
-
resolved: candidate,
|
|
7060
|
-
fallback,
|
|
7061
|
-
error: err instanceof Error ? err.message : String(err)
|
|
7062
|
-
} });
|
|
7063
|
-
await node_fs.promises.mkdir(fallback, { recursive: true });
|
|
7064
|
-
return fallback;
|
|
7065
|
-
}
|
|
7066
|
-
}
|
|
7067
7197
|
async onInitialize() {
|
|
7068
7198
|
const modelsDir = await this.resolveModelsDir();
|
|
7069
7199
|
if (!this.ctx.settings) throw new Error("DetectionPipelineAddon: ctx.settings not available");
|
|
@@ -7097,7 +7227,7 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
|
|
|
7097
7227
|
});
|
|
7098
7228
|
this.probePhaseUnsub = this.ctx.eventBus?.subscribe({ category: require_dist.EventCategory.PlatformProbePhase }, (event) => {
|
|
7099
7229
|
if (event.data?.phase !== "done") return;
|
|
7100
|
-
this.provider
|
|
7230
|
+
this.provider?.repickEngineOnProbeReady().catch((err) => {
|
|
7101
7231
|
this.ctx.logger.warn("repick on platform-probe done failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
7102
7232
|
});
|
|
7103
7233
|
}) ?? null;
|