@webex/ladon-ts 5.7.0 → 5.8.0
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/CHANGELOG.md +14 -0
- package/build/module/index.d.ts +1 -0
- package/build/module/lib/pipeline-types.d.ts +34 -0
- package/build/module/lib/pipeline.d.ts +5 -18
- package/build/module/lib/pipeline.js +39 -24
- package/build/module/lib/pipeline.js.map +1 -1
- package/build/module/lib/version.d.ts +1 -1
- package/build/module/lib/version.js +1 -1
- package/build/module/lib/version.js.map +1 -1
- package/build/worker/segmentation.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## 5.8.0 (2025-07-01)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* add inference stats to frame result ([#73](https://github.com/beyonddunbar/ladon-ts/issues/73)) ([5b02f51](https://github.com/beyonddunbar/ladon-ts/commit/5b02f51e92fe5dc305e5fa3ca9036fb718a2a1f7))
|
|
11
|
+
|
|
12
|
+
### 5.7.1 (2025-06-17)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* use live video element when skipping inference ([#71](https://github.com/beyonddunbar/ladon-ts/issues/71)) ([b916af0](https://github.com/beyonddunbar/ladon-ts/commit/b916af01cd66fd6e54030c95a19156d3b5656bcc))
|
|
18
|
+
|
|
5
19
|
## 5.7.0 (2025-05-22)
|
|
6
20
|
|
|
7
21
|
|
package/build/module/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from './lib/config-builder';
|
|
|
3
3
|
export * from './lib/config-types';
|
|
4
4
|
export * from './lib/generator';
|
|
5
5
|
export * from './lib/pipeline';
|
|
6
|
+
export * from './lib/pipeline-types';
|
|
6
7
|
export * from './lib/preload';
|
|
7
8
|
export * from './lib/renderer';
|
|
8
9
|
export * from './lib/resource-loader';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { FacesAndLandmarks } from './generator-types';
|
|
2
|
+
export interface InferenceStats {
|
|
3
|
+
/** Total time from frame input to output (ms) */
|
|
4
|
+
totalFrameTime: number;
|
|
5
|
+
/** Time spent running the actual ML model inference (ms) */
|
|
6
|
+
modelInferenceTime: number;
|
|
7
|
+
/** Time spent on pre-processing (resizing, normalizing, etc.) (ms) */
|
|
8
|
+
preprocessingTime?: number;
|
|
9
|
+
/** Time spent on post-processing (masking, blending, etc.) (ms) */
|
|
10
|
+
postprocessingTime?: number;
|
|
11
|
+
/** GPU/CPU queue time if measurable (ms) */
|
|
12
|
+
queueTime?: number;
|
|
13
|
+
/** Frame timestamp when processing started */
|
|
14
|
+
frameStartTime: number;
|
|
15
|
+
/** Frame timestamp when processing completed */
|
|
16
|
+
frameEndTime: number;
|
|
17
|
+
/** Whether inference was skipped (reused previous result) */
|
|
18
|
+
inferenceSkipped?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface IFrameInferenceResult {
|
|
21
|
+
facesAndLandmarks: FacesAndLandmarks;
|
|
22
|
+
gesture: Float32Array;
|
|
23
|
+
motion: number;
|
|
24
|
+
skipped: boolean;
|
|
25
|
+
timestamp: number;
|
|
26
|
+
stats?: InferenceStats;
|
|
27
|
+
}
|
|
28
|
+
export interface IPipeline {
|
|
29
|
+
onFrame(timestamp: number, maskNeeded?: boolean): Promise<IFrameInferenceResult>;
|
|
30
|
+
isLoaded(): boolean;
|
|
31
|
+
load(): Promise<void>;
|
|
32
|
+
destroy(): Promise<void>;
|
|
33
|
+
reset(): void;
|
|
34
|
+
}
|
|
@@ -1,19 +1,5 @@
|
|
|
1
1
|
import { PipelineConfig } from './config-types';
|
|
2
|
-
import {
|
|
3
|
-
export interface IFrameInferenceResult {
|
|
4
|
-
facesAndLandmarks: FacesAndLandmarks;
|
|
5
|
-
gesture: Float32Array;
|
|
6
|
-
motion: number;
|
|
7
|
-
skipped: boolean;
|
|
8
|
-
timestamp: number;
|
|
9
|
-
}
|
|
10
|
-
export interface IPipeline {
|
|
11
|
-
onFrame(timestamp: number, maskNeeded?: boolean): Promise<IFrameInferenceResult>;
|
|
12
|
-
isLoaded(): boolean;
|
|
13
|
-
load(): Promise<void>;
|
|
14
|
-
destroy(): Promise<void>;
|
|
15
|
-
reset(): void;
|
|
16
|
-
}
|
|
2
|
+
import { IFrameInferenceResult, IPipeline } from './pipeline-types';
|
|
17
3
|
export declare class Pipeline implements IPipeline {
|
|
18
4
|
private inputCanvas;
|
|
19
5
|
private outputCanvas;
|
|
@@ -32,6 +18,7 @@ export declare class Pipeline implements IPipeline {
|
|
|
32
18
|
*
|
|
33
19
|
* @param timestamp - The timestamp of the frame.
|
|
34
20
|
* @param performInference - Flag to perform mask inference.
|
|
21
|
+
* @param frameData - Optional frame data to process.
|
|
35
22
|
* @returns The result of the frame processing.
|
|
36
23
|
* @throws If the pipeline (generator or renderer) is not loaded.
|
|
37
24
|
*/
|
|
@@ -54,9 +41,9 @@ export declare class Pipeline implements IPipeline {
|
|
|
54
41
|
/**
|
|
55
42
|
* Creates the inference result object.
|
|
56
43
|
*
|
|
57
|
-
* @param
|
|
58
|
-
* @param
|
|
59
|
-
* @returns
|
|
44
|
+
* @param timestamp - The timestamp of the frame.
|
|
45
|
+
* @param performedInference - Indicates if the inference was performed.
|
|
46
|
+
* @returns The result object containing inference details.
|
|
60
47
|
*/
|
|
61
48
|
private createInferenceResult;
|
|
62
49
|
/**
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { WorkerGenerator as
|
|
2
|
-
import { BlurRenderer as
|
|
3
|
-
import { PassthroughRenderer as
|
|
4
|
-
import { ReplacementRenderer as
|
|
5
|
-
import { resizeImage as
|
|
6
|
-
class
|
|
1
|
+
import { WorkerGenerator as w } from "./worker-generator.js";
|
|
2
|
+
import { BlurRenderer as k } from "./renderer/blur-renderer.js";
|
|
3
|
+
import { PassthroughRenderer as R } from "./renderer/passthrough-renderer.js";
|
|
4
|
+
import { ReplacementRenderer as v } from "./renderer/replacement-renderer.js";
|
|
5
|
+
import { resizeImage as C } from "./util.js";
|
|
6
|
+
class A {
|
|
7
7
|
constructor(t, e, a, s) {
|
|
8
8
|
this.inputCanvas = t, this.outputCanvas = e, this.config = a, this.videoElement = s, this.loaded = !1;
|
|
9
9
|
const r = this.inputCanvas.getContext("2d", {
|
|
@@ -20,22 +20,35 @@ class k {
|
|
|
20
20
|
*
|
|
21
21
|
* @param timestamp - The timestamp of the frame.
|
|
22
22
|
* @param performInference - Flag to perform mask inference.
|
|
23
|
+
* @param frameData - Optional frame data to process.
|
|
23
24
|
* @returns The result of the frame processing.
|
|
24
25
|
* @throws If the pipeline (generator or renderer) is not loaded.
|
|
25
26
|
*/
|
|
26
27
|
async onFrame(t, e = !0) {
|
|
28
|
+
const a = performance.now();
|
|
27
29
|
if (!this.generator || !this.renderer)
|
|
28
30
|
throw new Error("[ladon-ts] Pipeline is not loaded.");
|
|
29
|
-
const
|
|
30
|
-
let
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
const s = performance.now(), r = this.getInputFrameData(e);
|
|
32
|
+
let n;
|
|
33
|
+
r instanceof Uint8ClampedArray ? ((!this.frameDataBuffer || this.frameDataBuffer.length !== r.length) && (this.frameDataBuffer = new Uint8ClampedArray(r.length)), this.frameDataBuffer.set(r), n = this.frameDataBuffer) : n = r;
|
|
34
|
+
const d = performance.now() - s;
|
|
35
|
+
let i = 0;
|
|
33
36
|
if (e || !this.lastMaskResult) {
|
|
34
|
-
const
|
|
35
|
-
await this.generator.postRender(
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
const m = performance.now(), p = r, g = this.getMaskImageData(p);
|
|
38
|
+
await this.generator.postRender(g, t), this.lastMaskResult = await this.generator.getLadonOutputForTimestamp(t), i = performance.now() - m;
|
|
39
|
+
}
|
|
40
|
+
const u = performance.now(), l = this.videoElement || n;
|
|
41
|
+
await this.renderer.render(l, this.lastMaskResult);
|
|
42
|
+
const f = performance.now() - u, o = performance.now(), c = o - a, h = this.createInferenceResult(t, e);
|
|
43
|
+
return h.stats = {
|
|
44
|
+
totalFrameTime: c,
|
|
45
|
+
modelInferenceTime: i,
|
|
46
|
+
preprocessingTime: d,
|
|
47
|
+
postprocessingTime: f,
|
|
48
|
+
frameStartTime: a,
|
|
49
|
+
frameEndTime: o,
|
|
50
|
+
inferenceSkipped: !e
|
|
51
|
+
}, h;
|
|
39
52
|
}
|
|
40
53
|
/**
|
|
41
54
|
* Gets the raw image data from the input canvas context.
|
|
@@ -60,7 +73,7 @@ class k {
|
|
|
60
73
|
*/
|
|
61
74
|
getMaskImageData(t) {
|
|
62
75
|
const e = this.config.mask.inputSize, a = this.config.input;
|
|
63
|
-
return e.width === a.width && e.height === a.height ? t :
|
|
76
|
+
return e.width === a.width && e.height === a.height ? t : C(
|
|
64
77
|
t,
|
|
65
78
|
a.width,
|
|
66
79
|
a.height,
|
|
@@ -71,9 +84,9 @@ class k {
|
|
|
71
84
|
/**
|
|
72
85
|
* Creates the inference result object.
|
|
73
86
|
*
|
|
74
|
-
* @param
|
|
75
|
-
* @param
|
|
76
|
-
* @returns
|
|
87
|
+
* @param timestamp - The timestamp of the frame.
|
|
88
|
+
* @param performedInference - Indicates if the inference was performed.
|
|
89
|
+
* @returns The result object containing inference details.
|
|
77
90
|
*/
|
|
78
91
|
createInferenceResult(t, e) {
|
|
79
92
|
return this.lastMaskResult ? {
|
|
@@ -82,12 +95,14 @@ class k {
|
|
|
82
95
|
motion: this.lastMaskResult.motion,
|
|
83
96
|
skipped: !e,
|
|
84
97
|
timestamp: t
|
|
98
|
+
// Stats will be added by the caller if needed
|
|
85
99
|
} : {
|
|
86
100
|
facesAndLandmarks: { data: new Float32Array(0), dims: [] },
|
|
87
101
|
gesture: new Float32Array(0),
|
|
88
102
|
motion: 0,
|
|
89
103
|
skipped: !0,
|
|
90
104
|
timestamp: t
|
|
105
|
+
// Stats will be added by the caller if needed
|
|
91
106
|
};
|
|
92
107
|
}
|
|
93
108
|
/**
|
|
@@ -104,18 +119,18 @@ class k {
|
|
|
104
119
|
async load() {
|
|
105
120
|
switch (this.config.render.type) {
|
|
106
121
|
case "blur":
|
|
107
|
-
this.renderer = new
|
|
122
|
+
this.renderer = new k(this.outputCanvas, this.config);
|
|
108
123
|
break;
|
|
109
124
|
case "passthrough":
|
|
110
|
-
this.renderer = new
|
|
125
|
+
this.renderer = new R(this.outputCanvas, this.config);
|
|
111
126
|
break;
|
|
112
127
|
case "replacement":
|
|
113
|
-
this.renderer = new
|
|
128
|
+
this.renderer = new v(this.outputCanvas, this.config);
|
|
114
129
|
break;
|
|
115
130
|
default:
|
|
116
131
|
throw new Error("[ladon-ts] Renderer not implemented.");
|
|
117
132
|
}
|
|
118
|
-
await this.renderer.load(), this.generator = new
|
|
133
|
+
await this.renderer.load(), this.generator = new w(this.config), await this.generator.load(), this.loaded = !0;
|
|
119
134
|
}
|
|
120
135
|
reset() {
|
|
121
136
|
this.lastMaskResult = void 0;
|
|
@@ -129,6 +144,6 @@ class k {
|
|
|
129
144
|
}
|
|
130
145
|
}
|
|
131
146
|
export {
|
|
132
|
-
|
|
147
|
+
A as Pipeline
|
|
133
148
|
};
|
|
134
149
|
//# sourceMappingURL=pipeline.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.js","sources":["../../../src/lib/pipeline.ts"],"sourcesContent":["import { PipelineConfig } from './config-types';\nimport {\n FacesAndLandmarks,\n FrameData,\n LadonOutput,\n MaskGenerator,\n WorkerGenerator,\n} from './generator';\nimport { BlurRenderer, LadonRenderer, PassthroughRenderer, ReplacementRenderer } from './renderer';\nimport { resizeImage } from './util';\n\nexport interface IFrameInferenceResult {\n facesAndLandmarks: FacesAndLandmarks;\n gesture: Float32Array;\n motion: number;\n skipped: boolean;\n timestamp: number;\n}\n\nexport interface IPipeline {\n onFrame(timestamp: number, maskNeeded?: boolean): Promise<IFrameInferenceResult>;\n isLoaded(): boolean;\n load(): Promise<void>;\n destroy(): Promise<void>;\n reset(): void;\n}\n\nexport class Pipeline implements IPipeline {\n private generator?: MaskGenerator;\n private renderer?: LadonRenderer;\n private loaded = false;\n\n private inputCanvasCtx: CanvasRenderingContext2D;\n private lastMaskResult?: LadonOutput;\n\n private frameDataBuffer?: Uint8ClampedArray;\n\n constructor(\n private inputCanvas: HTMLCanvasElement,\n private outputCanvas: HTMLCanvasElement,\n public readonly config: PipelineConfig,\n private readonly videoElement?: HTMLVideoElement\n ) {\n const inputCanvasCtx = this.inputCanvas.getContext('2d', {\n alpha: false,\n willReadFrequently: true,\n });\n\n if (!inputCanvasCtx) {\n throw new Error('[ladon-ts] Error getting input context 2d');\n }\n this.inputCanvasCtx = inputCanvasCtx;\n }\n\n /**\n * Processes a single frame and returns the inference result.\n * This method handles the frame data, performs inference if needed, and renders the output.\n *\n * @param timestamp - The timestamp of the frame.\n * @param performInference - Flag to perform mask inference.\n * @returns The result of the frame processing.\n * @throws If the pipeline (generator or renderer) is not loaded.\n */\n public async onFrame(\n timestamp: number,\n performInference = true\n ): Promise<IFrameInferenceResult> {\n if (!this.generator || !this.renderer) {\n throw new Error('[ladon-ts] Pipeline is not loaded.');\n }\n\n // Always get the latest frame data\n const frameData = this.getInputFrameData(true);\n\n // Reuse or create buffer for frame data copies, which is needed to ensure the data is not modified\n // or garbage collected before the inference is done.\n let frameDataCopy: FrameData;\n if (frameData instanceof Uint8ClampedArray) {\n if (!this.frameDataBuffer || this.frameDataBuffer.length !== frameData.length) {\n this.frameDataBuffer = new Uint8ClampedArray(frameData.length);\n }\n this.frameDataBuffer.set(frameData);\n frameDataCopy = this.frameDataBuffer;\n } else {\n frameDataCopy = frameData;\n }\n\n let maskResult: LadonOutput | undefined;\n\n if (performInference || !this.lastMaskResult) {\n // Send frame for inference and wait for the mask for this timestamp\n const rawImageData = frameData as Uint8ClampedArray;\n const maskImageData = this.getMaskImageData(rawImageData);\n await this.generator.postRender(maskImageData, timestamp);\n maskResult = await this.generator.getLadonOutputForTimestamp(timestamp);\n this.lastMaskResult = maskResult;\n } else {\n // Use the last mask result if skipping inference\n maskResult = this.lastMaskResult;\n }\n\n // Render the frame and mask together (mask may be stale if skipping inference)\n await this.renderer.render(frameDataCopy, maskResult);\n\n return this.createInferenceResult(timestamp, performInference);\n }\n\n /**\n * Gets the raw image data from the input canvas context.\n *\n * @param needFreshImageData - Flag to indicate if fresh image data is needed.\n *\n * @returns The raw image data.\n */\n private getInputFrameData(needFreshImageData: boolean): FrameData {\n if (needFreshImageData || !this.videoElement) {\n return this.inputCanvasCtx.getImageData(\n 0,\n 0,\n this.config.input.width,\n this.config.input.height\n ).data;\n }\n\n return this.videoElement;\n }\n\n /**\n * Resizes the image data if the model expects a fixed size input.\n *\n * @param {Uint8ClampedArray} rawImageData - The raw image data.\n * @returns {Uint8ClampedArray} - The processed mask image data.\n */\n private getMaskImageData(rawImageData: Uint8ClampedArray): Uint8ClampedArray {\n const modelInput = this.config.mask.inputSize;\n const inputImage = this.config.input;\n\n // Check if resizing is needed based on configuration.\n if (modelInput.width === inputImage.width && modelInput.height === inputImage.height) {\n return rawImageData;\n }\n\n // Resize the image data.\n return resizeImage(\n rawImageData,\n inputImage.width,\n inputImage.height,\n modelInput.width,\n modelInput.height\n );\n }\n\n /**\n * Creates the inference result object.\n *\n * @param {number} timestamp - The timestamp of the frame.\n * @param {boolean} performedInference - Indicates if the inference was performed.\n * @returns {IFrameInferenceResult} - The result object containing inference details.\n */\n private createInferenceResult(\n timestamp: number,\n performedInference: boolean\n ): IFrameInferenceResult {\n if (!this.lastMaskResult) {\n return {\n facesAndLandmarks: { data: new Float32Array(0), dims: [] },\n gesture: new Float32Array(0),\n motion: 0,\n skipped: true,\n timestamp,\n };\n }\n\n return {\n facesAndLandmarks: this.lastMaskResult.facesAndLandmarks,\n gesture: this.lastMaskResult.gesture,\n motion: this.lastMaskResult.motion,\n skipped: !performedInference,\n timestamp,\n };\n }\n\n /**\n * Checks if the pipeline is loaded.\n *\n * @returns True if the pipeline is loaded, false otherwise.\n */\n public isLoaded(): boolean {\n return this.loaded;\n }\n\n /**\n * Loads the pipeline components such as the generator and renderer.\n */\n public async load(): Promise<void> {\n switch (this.config.render.type) {\n case 'blur':\n this.renderer = new BlurRenderer(this.outputCanvas, this.config);\n break;\n case 'passthrough':\n this.renderer = new PassthroughRenderer(this.outputCanvas, this.config);\n break;\n case 'replacement':\n this.renderer = new ReplacementRenderer(this.outputCanvas, this.config);\n break;\n default:\n throw new Error('[ladon-ts] Renderer not implemented.');\n }\n\n await this.renderer.load();\n\n this.generator = new WorkerGenerator(this.config);\n await this.generator.load();\n\n this.loaded = true;\n }\n\n public reset(): void {\n this.lastMaskResult = undefined;\n }\n\n /**\n * Destroys the pipeline and cleans up resources.\n */\n public async destroy(): Promise<void> {\n this.reset();\n this.renderer?.destroy();\n await this.generator?.destroy();\n this.frameDataBuffer = undefined;\n this.loaded = false;\n }\n}\n"],"names":["Pipeline","inputCanvas","outputCanvas","config","videoElement","inputCanvasCtx","timestamp","performInference","frameData","frameDataCopy","maskResult","rawImageData","maskImageData","needFreshImageData","modelInput","inputImage","resizeImage","performedInference","BlurRenderer","PassthroughRenderer","ReplacementRenderer","WorkerGenerator","_a","_b"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pipeline.js","sources":["../../../src/lib/pipeline.ts"],"sourcesContent":["import { PipelineConfig } from './config-types';\nimport { FrameData, LadonOutput, MaskGenerator, WorkerGenerator } from './generator';\nimport { IFrameInferenceResult, IPipeline } from './pipeline-types';\nimport { BlurRenderer, LadonRenderer, PassthroughRenderer, ReplacementRenderer } from './renderer';\nimport { resizeImage } from './util';\n\nexport class Pipeline implements IPipeline {\n private generator?: MaskGenerator;\n private renderer?: LadonRenderer;\n private loaded = false;\n\n private inputCanvasCtx: CanvasRenderingContext2D;\n private lastMaskResult?: LadonOutput;\n\n private frameDataBuffer?: Uint8ClampedArray;\n\n constructor(\n private inputCanvas: HTMLCanvasElement,\n private outputCanvas: HTMLCanvasElement,\n public readonly config: PipelineConfig,\n private readonly videoElement?: HTMLVideoElement\n ) {\n const inputCanvasCtx = this.inputCanvas.getContext('2d', {\n alpha: false,\n willReadFrequently: true,\n });\n\n if (!inputCanvasCtx) {\n throw new Error('[ladon-ts] Error getting input context 2d');\n }\n this.inputCanvasCtx = inputCanvasCtx;\n }\n\n /**\n * Processes a single frame and returns the inference result.\n * This method handles the frame data, performs inference if needed, and renders the output.\n *\n * @param timestamp - The timestamp of the frame.\n * @param performInference - Flag to perform mask inference.\n * @param frameData - Optional frame data to process.\n * @returns The result of the frame processing.\n * @throws If the pipeline (generator or renderer) is not loaded.\n */\n public async onFrame(\n timestamp: number,\n performInference = true\n ): Promise<IFrameInferenceResult> {\n const frameStartTime = performance.now();\n\n if (!this.generator || !this.renderer) {\n throw new Error('[ladon-ts] Pipeline is not loaded.');\n }\n\n // Pre-processing timing\n const preprocessStart = performance.now();\n\n // Get the current frame (raw image data or video element). Use the video element if skipping\n // inference to avoid unnecessary processing when calling canvas getImageData.\n const frameData = this.getInputFrameData(performInference);\n\n // Reuse or create buffer for frame data copies, which is needed to ensure the data is not modified\n // or garbage collected before the inference is done.\n let frameDataCopy: FrameData;\n if (frameData instanceof Uint8ClampedArray) {\n if (!this.frameDataBuffer || this.frameDataBuffer.length !== frameData.length) {\n this.frameDataBuffer = new Uint8ClampedArray(frameData.length);\n }\n this.frameDataBuffer.set(frameData);\n frameDataCopy = this.frameDataBuffer;\n } else {\n frameDataCopy = frameData;\n }\n\n const preprocessingTime = performance.now() - preprocessStart;\n\n let modelInferenceTime = 0;\n\n if (performInference || !this.lastMaskResult) {\n // Model inference timing\n const inferenceStart = performance.now();\n\n // Send frame for inference and wait for the mask for this timestamp\n const rawImageData = frameData as Uint8ClampedArray;\n const maskImageData = this.getMaskImageData(rawImageData);\n await this.generator.postRender(maskImageData, timestamp);\n this.lastMaskResult = await this.generator.getLadonOutputForTimestamp(timestamp);\n\n modelInferenceTime = performance.now() - inferenceStart;\n }\n\n // Post-processing timing (rendering)\n const postprocessStart = performance.now();\n\n // Render the frame and mask together (mask may be stale if skipping inference)\n const videoInputImage = this.videoElement || frameDataCopy;\n await this.renderer.render(videoInputImage, this.lastMaskResult);\n\n const postprocessingTime = performance.now() - postprocessStart;\n\n const frameEndTime = performance.now();\n const totalFrameTime = frameEndTime - frameStartTime;\n\n const result = this.createInferenceResult(timestamp, performInference);\n\n // Add timing stats to the result\n result.stats = {\n totalFrameTime,\n modelInferenceTime,\n preprocessingTime,\n postprocessingTime,\n frameStartTime,\n frameEndTime,\n inferenceSkipped: !performInference,\n };\n\n return result;\n }\n\n /**\n * Gets the raw image data from the input canvas context.\n *\n * @param needFreshImageData - Flag to indicate if fresh image data is needed.\n *\n * @returns The raw image data.\n */\n private getInputFrameData(needFreshImageData: boolean): FrameData {\n if (needFreshImageData || !this.videoElement) {\n return this.inputCanvasCtx.getImageData(\n 0,\n 0,\n this.config.input.width,\n this.config.input.height\n ).data;\n }\n\n return this.videoElement;\n }\n\n /**\n * Resizes the image data if the model expects a fixed size input.\n *\n * @param {Uint8ClampedArray} rawImageData - The raw image data.\n * @returns {Uint8ClampedArray} - The processed mask image data.\n */\n private getMaskImageData(rawImageData: Uint8ClampedArray): Uint8ClampedArray {\n const modelInput = this.config.mask.inputSize;\n const inputImage = this.config.input;\n\n // Check if resizing is needed based on configuration.\n if (modelInput.width === inputImage.width && modelInput.height === inputImage.height) {\n return rawImageData;\n }\n\n // Resize the image data.\n return resizeImage(\n rawImageData,\n inputImage.width,\n inputImage.height,\n modelInput.width,\n modelInput.height\n );\n }\n\n /**\n * Creates the inference result object.\n *\n * @param timestamp - The timestamp of the frame.\n * @param performedInference - Indicates if the inference was performed.\n * @returns The result object containing inference details.\n */\n private createInferenceResult(\n timestamp: number,\n performedInference: boolean\n ): IFrameInferenceResult {\n if (!this.lastMaskResult) {\n return {\n facesAndLandmarks: { data: new Float32Array(0), dims: [] },\n gesture: new Float32Array(0),\n motion: 0,\n skipped: true,\n timestamp,\n // Stats will be added by the caller if needed\n };\n }\n\n return {\n facesAndLandmarks: this.lastMaskResult.facesAndLandmarks,\n gesture: this.lastMaskResult.gesture,\n motion: this.lastMaskResult.motion,\n skipped: !performedInference,\n timestamp,\n // Stats will be added by the caller if needed\n };\n }\n\n /**\n * Checks if the pipeline is loaded.\n *\n * @returns True if the pipeline is loaded, false otherwise.\n */\n public isLoaded(): boolean {\n return this.loaded;\n }\n\n /**\n * Loads the pipeline components such as the generator and renderer.\n */\n public async load(): Promise<void> {\n switch (this.config.render.type) {\n case 'blur':\n this.renderer = new BlurRenderer(this.outputCanvas, this.config);\n break;\n case 'passthrough':\n this.renderer = new PassthroughRenderer(this.outputCanvas, this.config);\n break;\n case 'replacement':\n this.renderer = new ReplacementRenderer(this.outputCanvas, this.config);\n break;\n default:\n throw new Error('[ladon-ts] Renderer not implemented.');\n }\n\n await this.renderer.load();\n\n this.generator = new WorkerGenerator(this.config);\n await this.generator.load();\n\n this.loaded = true;\n }\n\n public reset(): void {\n this.lastMaskResult = undefined;\n }\n\n /**\n * Destroys the pipeline and cleans up resources.\n */\n public async destroy(): Promise<void> {\n this.reset();\n this.renderer?.destroy();\n await this.generator?.destroy();\n this.frameDataBuffer = undefined;\n this.loaded = false;\n }\n}\n"],"names":["Pipeline","inputCanvas","outputCanvas","config","videoElement","inputCanvasCtx","timestamp","performInference","frameStartTime","preprocessStart","frameData","frameDataCopy","preprocessingTime","modelInferenceTime","inferenceStart","rawImageData","maskImageData","postprocessStart","videoInputImage","postprocessingTime","frameEndTime","totalFrameTime","result","needFreshImageData","modelInput","inputImage","resizeImage","performedInference","BlurRenderer","PassthroughRenderer","ReplacementRenderer","WorkerGenerator","_a","_b"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const LIB_VERSION = "5.
|
|
1
|
+
export declare const LIB_VERSION = "5.8.0";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sources":["../../../src/lib/version.ts"],"sourcesContent":["export const LIB_VERSION = '5.
|
|
1
|
+
{"version":3,"file":"version.js","sources":["../../../src/lib/version.ts"],"sourcesContent":["export const LIB_VERSION = '5.8.0';\n"],"names":["LIB_VERSION"],"mappings":"AAAO,MAAMA,IAAc;"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Cisco Ladon-TS
|
|
3
3
|
* Copyright (c) Cisco Systems, Inc. All rights reserved.
|
|
4
|
-
* Build timestamp:
|
|
4
|
+
* Build timestamp: July 1, 2025 at 12:11:01 AM UTC
|
|
5
5
|
*/
|
|
6
6
|
(function(){"use strict";var _documentCurrentScript=typeof document<"u"?document.currentScript:null;self.document={baseURI:location.origin};const originalConsoleLog=console.log,originalConsoleWarn=console.warn,originalConsoleError=console.error;console.log=(...e)=>{postMessage({type:"log",message:e.join(" "),severity:"log"}),originalConsoleLog(...e)},console.warn=(...e)=>{postMessage({type:"log",message:e.join(" "),severity:"warn"}),originalConsoleWarn(...e)},console.error=(...e)=>{postMessage({type:"log",message:e.join(" "),severity:"error"}),originalConsoleError(...e)};/*!
|
|
7
7
|
* ONNX Runtime Web v1.20.1
|