-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathblur-worker.js
More file actions
121 lines (105 loc) · 4.43 KB
/
Copy pathblur-worker.js
File metadata and controls
121 lines (105 loc) · 4.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// Copyright (C) <2025> Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
// This script runs in a dedicated worker.
// All state is managed within the worker, not via global scope from the main thread.
let isRunning = false;
let appBlurRenderer = null;
let segmenter = null;
// Import scripts that are also needed in the worker.
// These scripts must not access `document` or `window`.
import { createWebGL2BlurRenderer } from './webgl-renderer.js';
import { getWebGPUDevice, createWebGPUBlurRenderer } from './webgpu-renderer.js';
import { TriangleFakeSegmenter } from './blur4/triangle-fake-segmenter.js';
import { MediaPipeSegmenter } from "./blur4/mediapipe-segmenter";
import { WebNNSegmenter } from './blur4/webnn-segmenter.js';
// Since the worker can't access the main thread's 'bodySegmentation' object directly,
// we need to load the scripts that provide it.
async function initializeSegmenter(segmenterType, webGpuDevice) {
try {
switch (segmenterType) {
case 'triangle':
segmenter = new TriangleFakeSegmenter();
console.log('Worker: Using Triangle Fake Segmenter');
break;
case 'mediapipe':
// Dynamically import the necessary scripts for MediaPipe.
// These scripts register themselves on the global `self` scope.
await import('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-core/dist/tf-core.js');
await import('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-converter/dist/tf-converter.js');
await import('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-cpu/dist/tf-backend-cpu.js');
await import('https://cdn.jsdelivr.net/npm/@tensorflow-models/body-segmentation/dist/body-segmentation.js');
await import('https://cdn.jsdelivr.net/npm/@mediapipe/selfie_segmentation/selfie_segmentation.js');
segmenter = new MediaPipeSegmenter();
console.log('Worker: Using CPU (MediaPipe) for segmentation');
break;
case 'webnn-gpu':
segmenter = new WebNNSegmenter({ deviceType: 'gpu', webGpuDevice });
console.log('Worker: Using WebNN GPU for segmentation');
break;
case 'webnn-npu':
segmenter = new WebNNSegmenter({ deviceType: 'npu' });
console.log('Worker: Using WebNN NPU for segmentation');
break;
default:
throw new Error(`Unknown segmenter in worker: ${segmenterType}`);
}
} catch (error) {
console.error('Worker: Failed to initialize segmenter:', error);
throw error; // Propagate error to the main thread
}
}
async function initializeBlurRenderer(options) {
const { webGpuDevice, segmenterType, zeroCopy, directOutput } = options;
await initializeSegmenter(segmenterType, webGpuDevice);
try {
if (webGpuDevice) {
console.log('Instantiate WebGPU renderer')
appBlurRenderer = await createWebGPUBlurRenderer(webGpuDevice, segmenter, zeroCopy, directOutput);
} else {
console.log('Instantiate WebGL renderer')
appBlurRenderer = await createWebGL2BlurRenderer(segmenter);
}
} catch (error) {
console.error('Worker: Failed to initialize renderer', error);
// Attempt to fallback to WebGL2 if WebGPU fails
appBlurRenderer = await createWebGL2BlurRenderer(segmenter);
}
}
async function processOneFrame(videoFrame) {
if (!appBlurRenderer) return videoFrame;
return await appBlurRenderer.render(videoFrame);
}
self.onmessage = async (event) => {
const { type, readable, writable, options } = event.data;
if (type === 'start') {
isRunning = true;
options.webGpuDevice = await getWebGPUDevice();
await initializeBlurRenderer(options);
const reader = readable.getReader();
const writer = writable.getWriter();
let frameCount = 0;
let lastFpsTime = performance.now();
self.postMessage({ type: 'ready' });
while (isRunning) {
const { done, value: frame } = await reader.read();
if (done) break;
const processedFrame = await processOneFrame(frame);
await writer.write(processedFrame);
processedFrame.close();
frame.close();
frameCount++;
const currentTime = performance.now();
if (currentTime - lastFpsTime >= 1000) {
const fps = (frameCount * 1000) / (currentTime - lastFpsTime);
self.postMessage({ type: 'fpsUpdate', fps: fps.toFixed(1) });
frameCount = 0;
lastFpsTime = currentTime;
}
}
reader.releaseLock();
writer.close();
} else if (type === 'stop') {
isRunning = false;
}
};