@stream-io/video-filters-web 0.0.1

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 (37) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/LICENSE +202 -0
  3. package/README.md +50 -0
  4. package/dist/index.cjs.js +1558 -0
  5. package/dist/index.cjs.js.map +1 -0
  6. package/dist/index.d.ts +4 -0
  7. package/dist/index.es.js +1554 -0
  8. package/dist/index.es.js.map +1 -0
  9. package/dist/src/compatibility.d.ts +5 -0
  10. package/dist/src/createRenderer.d.ts +17 -0
  11. package/dist/src/helpers/webglHelper.d.ts +17 -0
  12. package/dist/src/segmentation.d.ts +9 -0
  13. package/dist/src/tflite.d.ts +19 -0
  14. package/dist/src/version.d.ts +2 -0
  15. package/dist/src/webgl2/backgroundBlurStage.d.ts +7 -0
  16. package/dist/src/webgl2/backgroundImageStage.d.ts +8 -0
  17. package/dist/src/webgl2/jointBilateralFilterStage.d.ts +7 -0
  18. package/dist/src/webgl2/resizingStage.d.ts +6 -0
  19. package/dist/src/webgl2/softmaxStage.d.ts +6 -0
  20. package/dist/src/webgl2/webgl2Pipeline.d.ts +8 -0
  21. package/index.ts +4 -0
  22. package/package.json +40 -0
  23. package/src/compatibility.ts +23 -0
  24. package/src/createRenderer.ts +63 -0
  25. package/src/helpers/webglHelper.ts +142 -0
  26. package/src/segmentation.ts +18 -0
  27. package/src/tflite-simd.js +874 -0
  28. package/src/tflite.ts +59 -0
  29. package/src/version.ts +3 -0
  30. package/src/webgl2/backgroundBlurStage.ts +307 -0
  31. package/src/webgl2/backgroundImageStage.ts +222 -0
  32. package/src/webgl2/jointBilateralFilterStage.ts +159 -0
  33. package/src/webgl2/resizingStage.ts +103 -0
  34. package/src/webgl2/softmaxStage.ts +103 -0
  35. package/src/webgl2/webgl2Pipeline.ts +200 -0
  36. package/tf/models/segm_full_v679.tflite +0 -0
  37. package/tf/tflite/tflite-simd.wasm +0 -0
@@ -0,0 +1,159 @@
1
+ import {
2
+ compileShader,
3
+ createPipelineStageProgram,
4
+ glsl,
5
+ } from '../helpers/webglHelper';
6
+ import { SegmentationParams } from '../segmentation';
7
+
8
+ export function buildJointBilateralFilterStage(
9
+ gl: WebGL2RenderingContext,
10
+ vertexShader: WebGLShader,
11
+ positionBuffer: WebGLBuffer,
12
+ texCoordBuffer: WebGLBuffer,
13
+ inputTexture: WebGLTexture,
14
+ outputTexture: WebGLTexture,
15
+ canvas: HTMLCanvasElement,
16
+ segmentationConfig: SegmentationParams,
17
+ ) {
18
+ const fragmentShaderSource = glsl`#version 300 es
19
+
20
+ precision highp float;
21
+
22
+ uniform sampler2D u_inputFrame;
23
+ uniform sampler2D u_segmentationMask;
24
+ uniform vec2 u_texelSize;
25
+ uniform float u_step;
26
+ uniform float u_radius;
27
+ uniform float u_offset;
28
+ uniform float u_sigmaTexel;
29
+ uniform float u_sigmaColor;
30
+
31
+ in vec2 v_texCoord;
32
+ out vec4 outColor;
33
+
34
+ float gaussian(float x, float sigma) {
35
+ float coeff = -0.5 / (sigma * sigma * 4.0 + 1.0e-6);
36
+ return exp((x * x) * coeff);
37
+ }
38
+
39
+ void main() {
40
+ vec2 centerCoord = v_texCoord;
41
+ vec3 centerColor = texture(u_inputFrame, centerCoord).rgb;
42
+ float newVal = 0.0;
43
+
44
+ float spaceWeight = 0.0;
45
+ float colorWeight = 0.0;
46
+ float totalWeight = 0.0;
47
+
48
+ // Subsample kernel space.
49
+ for (float i = -u_radius + u_offset; i <= u_radius; i += u_step) {
50
+ for (float j = -u_radius + u_offset; j <= u_radius; j += u_step) {
51
+ vec2 shift = vec2(j, i) * u_texelSize;
52
+ vec2 coord = vec2(centerCoord + shift);
53
+ vec3 frameColor = texture(u_inputFrame, coord).rgb;
54
+ float outVal = texture(u_segmentationMask, coord).a;
55
+
56
+ spaceWeight = gaussian(distance(centerCoord, coord), u_sigmaTexel);
57
+ colorWeight = gaussian(distance(centerColor, frameColor), u_sigmaColor);
58
+ totalWeight += spaceWeight * colorWeight;
59
+
60
+ newVal += spaceWeight * colorWeight * outVal;
61
+ }
62
+ }
63
+ newVal /= totalWeight;
64
+
65
+ outColor = vec4(vec3(0.0), newVal);
66
+ }
67
+ `;
68
+
69
+ const { width: segmentationWidth, height: segmentationHeight } =
70
+ segmentationConfig;
71
+ const { width: outputWidth, height: outputHeight } = canvas;
72
+ const texelWidth = 1 / outputWidth;
73
+ const texelHeight = 1 / outputHeight;
74
+
75
+ const fragmentShader = compileShader(
76
+ gl,
77
+ gl.FRAGMENT_SHADER,
78
+ fragmentShaderSource,
79
+ );
80
+ const program = createPipelineStageProgram(
81
+ gl,
82
+ vertexShader,
83
+ fragmentShader,
84
+ positionBuffer,
85
+ texCoordBuffer,
86
+ );
87
+ const inputFrameLocation = gl.getUniformLocation(program, 'u_inputFrame');
88
+ const segmentationMaskLocation = gl.getUniformLocation(
89
+ program,
90
+ 'u_segmentationMask',
91
+ );
92
+ const texelSizeLocation = gl.getUniformLocation(program, 'u_texelSize');
93
+ const stepLocation = gl.getUniformLocation(program, 'u_step');
94
+ const radiusLocation = gl.getUniformLocation(program, 'u_radius');
95
+ const offsetLocation = gl.getUniformLocation(program, 'u_offset');
96
+ const sigmaTexelLocation = gl.getUniformLocation(program, 'u_sigmaTexel');
97
+ const sigmaColorLocation = gl.getUniformLocation(program, 'u_sigmaColor');
98
+
99
+ const frameBuffer = gl.createFramebuffer();
100
+ gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
101
+ gl.framebufferTexture2D(
102
+ gl.FRAMEBUFFER,
103
+ gl.COLOR_ATTACHMENT0,
104
+ gl.TEXTURE_2D,
105
+ outputTexture,
106
+ 0,
107
+ );
108
+
109
+ gl.useProgram(program);
110
+ gl.uniform1i(inputFrameLocation, 0);
111
+ gl.uniform1i(segmentationMaskLocation, 1);
112
+ gl.uniform2f(texelSizeLocation, texelWidth, texelHeight);
113
+
114
+ // Ensures default values are configured to prevent infinite
115
+ // loop in fragment shader
116
+ updateSigmaSpace(0);
117
+ updateSigmaColor(0);
118
+
119
+ function render() {
120
+ gl.viewport(0, 0, outputWidth, outputHeight);
121
+ gl.useProgram(program);
122
+ gl.activeTexture(gl.TEXTURE1);
123
+ gl.bindTexture(gl.TEXTURE_2D, inputTexture);
124
+ gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
125
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
126
+ }
127
+
128
+ function updateSigmaSpace(sigmaSpace: number) {
129
+ sigmaSpace *= Math.max(
130
+ outputWidth / segmentationWidth,
131
+ outputHeight / segmentationHeight,
132
+ );
133
+
134
+ const kSparsityFactor = 0.66; // Higher is sparser.
135
+ const step = Math.max(1, Math.sqrt(sigmaSpace) * kSparsityFactor);
136
+ const radius = sigmaSpace;
137
+ const offset = step > 1 ? step * 0.5 : 0;
138
+ const sigmaTexel = Math.max(texelWidth, texelHeight) * sigmaSpace;
139
+
140
+ gl.useProgram(program);
141
+ gl.uniform1f(stepLocation, step);
142
+ gl.uniform1f(radiusLocation, radius);
143
+ gl.uniform1f(offsetLocation, offset);
144
+ gl.uniform1f(sigmaTexelLocation, sigmaTexel);
145
+ }
146
+
147
+ function updateSigmaColor(sigmaColor: number) {
148
+ gl.useProgram(program);
149
+ gl.uniform1f(sigmaColorLocation, sigmaColor);
150
+ }
151
+
152
+ function cleanUp() {
153
+ gl.deleteFramebuffer(frameBuffer);
154
+ gl.deleteProgram(program);
155
+ gl.deleteShader(fragmentShader);
156
+ }
157
+
158
+ return { render, updateSigmaSpace, updateSigmaColor, cleanUp };
159
+ }
@@ -0,0 +1,103 @@
1
+ import { TFLite } from '../tflite';
2
+ import {
3
+ compileShader,
4
+ createPipelineStageProgram,
5
+ createTexture,
6
+ glsl,
7
+ readPixelsAsync,
8
+ } from '../helpers/webglHelper';
9
+ import { SegmentationParams } from '../segmentation';
10
+
11
+ export function buildResizingStage(
12
+ gl: WebGL2RenderingContext,
13
+ vertexShader: WebGLShader,
14
+ positionBuffer: WebGLBuffer,
15
+ texCoordBuffer: WebGLBuffer,
16
+ tflite: TFLite,
17
+ segmentationConfig: SegmentationParams,
18
+ ) {
19
+ const fragmentShaderSource = glsl`#version 300 es
20
+
21
+ precision highp float;
22
+ uniform sampler2D u_inputFrame;
23
+ in vec2 v_texCoord;
24
+ out vec4 outColor;
25
+
26
+ void main() {
27
+ outColor = texture(u_inputFrame, v_texCoord);
28
+ }
29
+ `;
30
+
31
+ // TFLite memory will be accessed as float32
32
+ const tfliteInputMemoryOffset = tflite._getInputMemoryOffset() / 4;
33
+
34
+ const { width: outputWidth, height: outputHeight } = segmentationConfig;
35
+ const outputPixelCount = outputWidth * outputHeight;
36
+
37
+ const fragmentShader = compileShader(
38
+ gl,
39
+ gl.FRAGMENT_SHADER,
40
+ fragmentShaderSource,
41
+ );
42
+ const program = createPipelineStageProgram(
43
+ gl,
44
+ vertexShader,
45
+ fragmentShader,
46
+ positionBuffer,
47
+ texCoordBuffer,
48
+ );
49
+ const inputFrameLocation = gl.getUniformLocation(program, 'u_inputFrame');
50
+ const outputTexture = createTexture(gl, gl.RGBA8, outputWidth, outputHeight);
51
+
52
+ const frameBuffer = gl.createFramebuffer();
53
+ gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
54
+ gl.framebufferTexture2D(
55
+ gl.FRAMEBUFFER,
56
+ gl.COLOR_ATTACHMENT0,
57
+ gl.TEXTURE_2D,
58
+ outputTexture,
59
+ 0,
60
+ );
61
+ const outputPixels = new Uint8Array(outputPixelCount * 4);
62
+
63
+ gl.useProgram(program);
64
+ gl.uniform1i(inputFrameLocation, 0);
65
+
66
+ function render() {
67
+ gl.viewport(0, 0, outputWidth, outputHeight);
68
+ gl.useProgram(program);
69
+ gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
70
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
71
+
72
+ // Downloads pixels asynchronously from GPU while rendering the current frame.
73
+ // The pixels will be available in the next frame render which results
74
+ // in offsets in the segmentation output but increases the frame rate.
75
+ readPixelsAsync(
76
+ gl,
77
+ 0,
78
+ 0,
79
+ outputWidth,
80
+ outputHeight,
81
+ gl.RGBA,
82
+ gl.UNSIGNED_BYTE,
83
+ outputPixels,
84
+ );
85
+
86
+ for (let i = 0; i < outputPixelCount; i++) {
87
+ const tfliteIndex = tfliteInputMemoryOffset + i * 3;
88
+ const outputIndex = i * 4;
89
+ tflite.HEAPF32[tfliteIndex] = outputPixels[outputIndex] / 255;
90
+ tflite.HEAPF32[tfliteIndex + 1] = outputPixels[outputIndex + 1] / 255;
91
+ tflite.HEAPF32[tfliteIndex + 2] = outputPixels[outputIndex + 2] / 255;
92
+ }
93
+ }
94
+
95
+ function cleanUp() {
96
+ gl.deleteFramebuffer(frameBuffer);
97
+ gl.deleteTexture(outputTexture);
98
+ gl.deleteProgram(program);
99
+ gl.deleteShader(fragmentShader);
100
+ }
101
+
102
+ return { render, cleanUp };
103
+ }
@@ -0,0 +1,103 @@
1
+ import { TFLite } from '../tflite';
2
+ import {
3
+ compileShader,
4
+ createPipelineStageProgram,
5
+ createTexture,
6
+ glsl,
7
+ } from '../helpers/webglHelper';
8
+ import { SegmentationParams } from '../segmentation';
9
+
10
+ export function buildSoftmaxStage(
11
+ gl: WebGL2RenderingContext,
12
+ vertexShader: WebGLShader,
13
+ positionBuffer: WebGLBuffer,
14
+ texCoordBuffer: WebGLBuffer,
15
+ tflite: TFLite,
16
+ outputTexture: WebGLTexture,
17
+ segmentationConfig: SegmentationParams,
18
+ ) {
19
+ const fragmentShaderSource = glsl`#version 300 es
20
+
21
+ precision highp float;
22
+
23
+ uniform sampler2D u_inputSegmentation;
24
+ in vec2 v_texCoord;
25
+ out vec4 outColor;
26
+
27
+ void main() {
28
+ vec2 segmentation = texture(u_inputSegmentation, v_texCoord).rg;
29
+ float shift = max(segmentation.r, segmentation.g);
30
+ float backgroundExp = exp(segmentation.r - shift);
31
+ float personExp = exp(segmentation.g - shift);
32
+ outColor = vec4(vec3(0.0), personExp / (backgroundExp + personExp));
33
+ }
34
+ `;
35
+
36
+ // TFLite memory will be accessed as float32
37
+ const tfliteOutputMemoryOffset = tflite._getOutputMemoryOffset() / 4;
38
+ const { width: segmentationWidth, height: segmentationHeight } =
39
+ segmentationConfig;
40
+
41
+ const fragmentShader = compileShader(
42
+ gl,
43
+ gl.FRAGMENT_SHADER,
44
+ fragmentShaderSource,
45
+ );
46
+ const program = createPipelineStageProgram(
47
+ gl,
48
+ vertexShader,
49
+ fragmentShader,
50
+ positionBuffer,
51
+ texCoordBuffer,
52
+ );
53
+ const inputLocation = gl.getUniformLocation(program, 'u_inputSegmentation');
54
+ const inputTexture = createTexture(
55
+ gl,
56
+ gl.RG32F,
57
+ segmentationWidth,
58
+ segmentationHeight,
59
+ );
60
+
61
+ const frameBuffer = gl.createFramebuffer();
62
+ gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
63
+ gl.framebufferTexture2D(
64
+ gl.FRAMEBUFFER,
65
+ gl.COLOR_ATTACHMENT0,
66
+ gl.TEXTURE_2D,
67
+ outputTexture,
68
+ 0,
69
+ );
70
+
71
+ gl.useProgram(program);
72
+ gl.uniform1i(inputLocation, 1);
73
+
74
+ function render() {
75
+ gl.viewport(0, 0, segmentationWidth, segmentationHeight);
76
+ gl.useProgram(program);
77
+ gl.activeTexture(gl.TEXTURE1);
78
+ gl.bindTexture(gl.TEXTURE_2D, inputTexture);
79
+ gl.texSubImage2D(
80
+ gl.TEXTURE_2D,
81
+ 0,
82
+ 0,
83
+ 0,
84
+ segmentationWidth,
85
+ segmentationHeight,
86
+ gl.RG,
87
+ gl.FLOAT,
88
+ tflite.HEAPF32,
89
+ tfliteOutputMemoryOffset,
90
+ );
91
+ gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
92
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
93
+ }
94
+
95
+ function cleanUp() {
96
+ gl.deleteFramebuffer(frameBuffer);
97
+ gl.deleteTexture(inputTexture);
98
+ gl.deleteProgram(program);
99
+ gl.deleteShader(fragmentShader);
100
+ }
101
+
102
+ return { render, cleanUp };
103
+ }
@@ -0,0 +1,200 @@
1
+ import { TFLite } from '../tflite';
2
+ import { compileShader, createTexture, glsl } from '../helpers/webglHelper';
3
+ import {
4
+ BackgroundBlurStage,
5
+ buildBackgroundBlurStage,
6
+ } from './backgroundBlurStage';
7
+ import {
8
+ BackgroundImageStage,
9
+ buildBackgroundImageStage,
10
+ } from './backgroundImageStage';
11
+ import { buildJointBilateralFilterStage } from './jointBilateralFilterStage';
12
+ import { buildResizingStage } from './resizingStage';
13
+ import { buildSoftmaxStage } from './softmaxStage';
14
+ import { BackgroundBlurLevel, BackgroundFilter } from '../createRenderer';
15
+ import { SegmentationParams } from '../segmentation';
16
+
17
+ export function buildWebGL2Pipeline(
18
+ videoSource: HTMLVideoElement,
19
+ backgroundImage: HTMLImageElement | undefined,
20
+ blurLevel: BackgroundBlurLevel | undefined,
21
+ backgroundFilter: BackgroundFilter,
22
+ canvas: HTMLCanvasElement,
23
+ tflite: TFLite,
24
+ segmentationConfig: SegmentationParams,
25
+ ) {
26
+ const gl = canvas.getContext('webgl2')!;
27
+ if (!gl) throw new Error('WebGL2 is not supported');
28
+
29
+ const { width: frameWidth, height: frameHeight } = videoSource;
30
+ const { width: segmentationWidth, height: segmentationHeight } =
31
+ segmentationConfig;
32
+
33
+ const vertexShaderSource = glsl`#version 300 es
34
+
35
+ in vec2 a_position;
36
+ in vec2 a_texCoord;
37
+ out vec2 v_texCoord;
38
+
39
+ void main() {
40
+ gl_Position = vec4(a_position, 0.0, 1.0);
41
+ v_texCoord = a_texCoord;
42
+ }
43
+ `;
44
+ const vertexShader = compileShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
45
+
46
+ const vertexArray = gl.createVertexArray();
47
+ gl.bindVertexArray(vertexArray);
48
+
49
+ const positionBuffer = gl.createBuffer()!;
50
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
51
+ gl.bufferData(
52
+ gl.ARRAY_BUFFER,
53
+ new Float32Array([-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0]),
54
+ gl.STATIC_DRAW,
55
+ );
56
+
57
+ const texCoordBuffer = gl.createBuffer()!;
58
+ gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
59
+ gl.bufferData(
60
+ gl.ARRAY_BUFFER,
61
+ new Float32Array([0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0]),
62
+ gl.STATIC_DRAW,
63
+ );
64
+
65
+ // We don't use texStorage2D here because texImage2D seems faster
66
+ // to upload video texture than texSubImage2D even though the latter
67
+ // is supposed to be the recommended way:
68
+ // https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices#use_texstorage_to_create_textures
69
+ const inputFrameTexture = gl.createTexture();
70
+ gl.bindTexture(gl.TEXTURE_2D, inputFrameTexture);
71
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
72
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
73
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
74
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
75
+
76
+ // TODO Rename segmentation and person mask to be more specific
77
+ const segmentationTexture = createTexture(
78
+ gl,
79
+ gl.RGBA8,
80
+ segmentationWidth,
81
+ segmentationHeight,
82
+ )!;
83
+ const personMaskTexture = createTexture(
84
+ gl,
85
+ gl.RGBA8,
86
+ frameWidth,
87
+ frameHeight,
88
+ )!;
89
+
90
+ const resizingStage = buildResizingStage(
91
+ gl,
92
+ vertexShader,
93
+ positionBuffer,
94
+ texCoordBuffer,
95
+ tflite,
96
+ segmentationConfig,
97
+ );
98
+ const loadSegmentationStage = buildSoftmaxStage(
99
+ gl,
100
+ vertexShader,
101
+ positionBuffer,
102
+ texCoordBuffer,
103
+ tflite,
104
+ segmentationTexture,
105
+ segmentationConfig,
106
+ );
107
+
108
+ const jointBilateralFilterStage = buildJointBilateralFilterStage(
109
+ gl,
110
+ vertexShader,
111
+ positionBuffer,
112
+ texCoordBuffer,
113
+ segmentationTexture,
114
+ personMaskTexture,
115
+ canvas,
116
+ segmentationConfig,
117
+ );
118
+ const backgroundStage =
119
+ backgroundFilter === 'blur'
120
+ ? buildBackgroundBlurStage(
121
+ gl,
122
+ vertexShader,
123
+ positionBuffer,
124
+ texCoordBuffer,
125
+ personMaskTexture,
126
+ canvas,
127
+ blurLevel || 'high',
128
+ )
129
+ : buildBackgroundImageStage(
130
+ gl,
131
+ positionBuffer,
132
+ texCoordBuffer,
133
+ personMaskTexture,
134
+ backgroundImage,
135
+ canvas,
136
+ );
137
+
138
+ function render() {
139
+ gl.activeTexture(gl.TEXTURE0);
140
+ gl.bindTexture(gl.TEXTURE_2D, inputFrameTexture);
141
+
142
+ // texImage2D seems faster than texSubImage2D to upload
143
+ // video texture
144
+ gl.texImage2D(
145
+ gl.TEXTURE_2D,
146
+ 0,
147
+ gl.RGBA,
148
+ gl.RGBA,
149
+ gl.UNSIGNED_BYTE,
150
+ videoSource,
151
+ );
152
+
153
+ gl.bindVertexArray(vertexArray);
154
+
155
+ resizingStage.render();
156
+
157
+ tflite._runInference();
158
+
159
+ loadSegmentationStage.render();
160
+ jointBilateralFilterStage.render();
161
+ backgroundStage.render();
162
+ }
163
+
164
+ function updatePostProcessingConfig() {
165
+ jointBilateralFilterStage.updateSigmaSpace(1);
166
+ jointBilateralFilterStage.updateSigmaColor(0.1);
167
+
168
+ if (backgroundFilter === 'image') {
169
+ const backgroundImageStage = backgroundStage as BackgroundImageStage;
170
+ backgroundImageStage.updateCoverage([0.5, 0.75]);
171
+ backgroundImageStage.updateLightWrapping(0.3);
172
+ backgroundImageStage.updateBlendMode('screen');
173
+ } else if (backgroundFilter === 'blur') {
174
+ const backgroundBlurStage = backgroundStage as BackgroundBlurStage;
175
+ backgroundBlurStage.updateCoverage([0.5, 0.75]);
176
+ } else {
177
+ // TODO Handle no background in a separate pipeline path
178
+ const backgroundImageStage = backgroundStage as BackgroundImageStage;
179
+ backgroundImageStage.updateCoverage([0, 0.9999]);
180
+ backgroundImageStage.updateLightWrapping(0);
181
+ }
182
+ }
183
+
184
+ function cleanUp() {
185
+ backgroundStage.cleanUp();
186
+ jointBilateralFilterStage.cleanUp();
187
+ loadSegmentationStage.cleanUp();
188
+ resizingStage.cleanUp();
189
+
190
+ gl.deleteTexture(personMaskTexture);
191
+ gl.deleteTexture(segmentationTexture);
192
+ gl.deleteTexture(inputFrameTexture);
193
+ gl.deleteBuffer(texCoordBuffer);
194
+ gl.deleteBuffer(positionBuffer);
195
+ gl.deleteVertexArray(vertexArray);
196
+ gl.deleteShader(vertexShader);
197
+ }
198
+
199
+ return { render, updatePostProcessingConfig, cleanUp };
200
+ }
Binary file
Binary file