@pexip/media-processor 20.3.5 → 21.0.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 +39 -0
- package/api-docs/README.mdx +33 -6
- package/api-docs/functions/createBenchmark.mdx +17 -5
- package/api-docs/functions/createWindowedStats.mdx +23 -0
- package/api-docs/functions/iou.mdx +19 -0
- package/api-docs/functions/labelComponents.mdx +20 -0
- package/api-docs/functions/nearestPowerOfTwo.mdx +21 -0
- package/api-docs/functions/playVideo.mdx +16 -0
- package/api-docs/functions/scoreComponent.mdx +22 -0
- package/api-docs/functions/setVideoElementSrc.mdx +14 -0
- package/api-docs/interfaces/Benchmark.mdx +12 -0
- package/api-docs/interfaces/RendererOptions.mdx +26 -9
- package/api-docs/interfaces/SegmentationParams.mdx +29 -12
- package/api-docs/interfaces/SelectionOptions.mdx +15 -0
- package/api-docs/interfaces/Stats.mdx +11 -0
- package/api-docs/interfaces/Weights.mdx +12 -0
- package/api-docs/type-aliases/OptionalKeys.mdx +10 -0
- package/api-docs/variables/BACKGROUND_THRESHOLD.mdx +3 -0
- package/api-docs/variables/DOWN_SAMPLE_FACTOR.mdx +3 -0
- package/api-docs/variables/EDGE_BLUR_AMOUNT.mdx +1 -1
- package/api-docs/variables/EXCLUDE_BYSTANDERS.mdx +3 -0
- package/api-docs/variables/FOREGROUND_THRESHOLD.mdx +1 -1
- package/api-docs/variables/LIGHT_WRAP_BLUR_AMOUNT.mdx +3 -0
- package/api-docs/variables/LIGHT_WRAP_EDGE_BAND.mdx +3 -0
- package/api-docs/variables/LIGHT_WRAP_INTENSITY.mdx +3 -0
- package/api-docs/variables/LIGHT_WRAP_TIGHTNESS.mdx +3 -0
- package/api-docs/variables/MASK_COMBINE_RATIO.mdx +1 -1
- package/api-docs/variables/MORPH_DILATE_RADIUS_PX.mdx +3 -0
- package/api-docs/variables/MORPH_ERODE_RADIUS_PX.mdx +3 -0
- package/api-docs/variables/MORPH_PASS.mdx +3 -0
- package/api-docs/variables/PERSON_CENTER.mdx +3 -0
- package/api-docs/variables/SIGMA_RANGE.mdx +3 -0
- package/api-docs/variables/SIGMA_SPACE.mdx +3 -0
- package/api-docs/variables/STRONG_EDGE_BLUR_AMOUNT.mdx +3 -0
- package/api-docs/variables/getCanvasRenderingContext2D.mdx +14 -0
- package/api-docs/variables/getImageSize.mdx +13 -0
- package/dist/common/backends/webgl/binaryMask.d.ts +8 -0
- package/dist/common/backends/webgl/binaryMask.js +95 -0
- package/dist/common/backends/webgl/blender.d.ts +2 -1
- package/dist/common/backends/webgl/blender.js +18 -15
- package/dist/common/backends/webgl/blur.d.ts +1 -1
- package/dist/common/backends/webgl/blur.js +23 -16
- package/dist/common/backends/webgl/canvas.js +9 -4
- package/dist/common/backends/webgl/diffAbsMask.d.ts +10 -0
- package/dist/common/backends/webgl/diffAbsMask.js +103 -0
- package/dist/common/backends/webgl/geodesicReconstruction.d.ts +13 -0
- package/dist/common/backends/webgl/geodesicReconstruction.js +176 -0
- package/dist/common/backends/webgl/jointBilateralFilter.d.ts +12 -0
- package/dist/common/backends/webgl/jointBilateralFilter.js +108 -0
- package/dist/common/backends/webgl/morphErode.d.ts +13 -0
- package/dist/common/backends/webgl/morphErode.js +121 -0
- package/dist/common/backends/webgl/renderer.js +194 -34
- package/dist/common/backends/webgl/replace.d.ts +17 -0
- package/dist/common/backends/webgl/replace.js +126 -0
- package/dist/common/backends/webgl/selectComponent.d.ts +15 -0
- package/dist/common/backends/webgl/selectComponent.js +102 -0
- package/dist/common/backends/webgl/smoothingMask.d.ts +3 -3
- package/dist/common/backends/webgl/smoothingMask.js +22 -21
- package/dist/common/backends/webgl/tentBlur.d.ts +9 -0
- package/dist/common/backends/webgl/tentBlur.js +118 -0
- package/dist/common/backends/webgl/texture.d.ts +1 -1
- package/dist/common/backends/webgl/texture.js +19 -17
- package/dist/common/backends/webgl/textureToTexture.d.ts +1 -1
- package/dist/common/backends/webgl/textureToTexture.js +19 -16
- package/dist/common/backends/webgl/webglUtils.d.ts +7 -1
- package/dist/common/backends/webgl/webglUtils.js +64 -6
- package/dist/common/backends/webgpu/blender.d.ts +6 -1
- package/dist/common/backends/webgpu/blender.js +96 -51
- package/dist/common/backends/webgpu/constants.js +1 -1
- package/dist/common/backends/webgpu/dualFilterBlur.d.ts +2 -3
- package/dist/common/backends/webgpu/dualFilterBlur.js +146 -140
- package/dist/common/backends/webgpu/jointBilateralFilter.d.ts +7 -0
- package/dist/common/backends/webgpu/jointBilateralFilter.js +162 -0
- package/dist/common/backends/webgpu/renderer.js +211 -39
- package/dist/common/backends/webgpu/replace.d.ts +11 -0
- package/dist/common/backends/webgpu/replace.js +175 -0
- package/dist/common/backends/webgpu/resize.d.ts +4 -0
- package/dist/common/backends/webgpu/resize.js +76 -0
- package/dist/common/backends/webgpu/smoothingMask.d.ts +2 -2
- package/dist/common/backends/webgpu/smoothingMask.js +74 -40
- package/dist/common/backends/webgpu/tentBlur.d.ts +5 -0
- package/dist/common/backends/webgpu/tentBlur.js +131 -0
- package/dist/common/backends/webgpu/texture.d.ts +2 -2
- package/dist/common/backends/webgpu/texture.js +24 -19
- package/dist/common/backends/webgpu/textureToTexture.d.ts +1 -1
- package/dist/common/backends/webgpu/textureToTexture.js +28 -15
- package/dist/common/backends/webgpu/types.d.ts +2 -0
- package/dist/common/backends/webgpu/webgpuUtils.d.ts +24 -4
- package/dist/common/backends/webgpu/webgpuUtils.js +60 -11
- package/dist/common/constants.d.ts +19 -5
- package/dist/common/constants.js +19 -5
- package/dist/common/index.d.ts +1 -0
- package/dist/common/index.js +1 -0
- package/dist/common/tsconfig.tsbuildinfo +1 -1
- package/dist/common/types/render.d.ts +142 -13
- package/dist/common/types/utils.d.ts +21 -0
- package/dist/common/utils.d.ts +16 -0
- package/dist/common/utils.js +152 -0
- package/dist/main/benchUtils.d.ts +33 -1
- package/dist/main/benchUtils.js +145 -11
- package/dist/main/tsconfig.tsbuildinfo +1 -1
- package/dist/main/video/canvasTransform.d.ts +1 -1
- package/dist/main/video/canvasTransform.js +135 -47
- package/dist/main/video/index.d.ts +1 -1
- package/dist/main/video/index.js +1 -1
- package/dist/workers/mediaWorker.js +35 -162
- package/dist/workers/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -7
- package/dist/common/backends/webgl/shaders/blendFragmentShader.d.ts +0 -1
- package/dist/common/backends/webgl/shaders/blendFragmentShader.js +0 -21
- package/dist/common/backends/webgl/shaders/dualFilterBlurFragmentShader.d.ts +0 -1
- package/dist/common/backends/webgl/shaders/dualFilterBlurFragmentShader.js +0 -45
- package/dist/common/backends/webgl/shaders/smoothingMaskFragmentShader.d.ts +0 -1
- package/dist/common/backends/webgl/shaders/smoothingMaskFragmentShader.js +0 -57
- package/dist/common/backends/webgl/shaders/textureFragmentShader.d.ts +0 -1
- package/dist/common/backends/webgl/shaders/textureFragmentShader.js +0 -16
- package/dist/common/backends/webgl/shaders/vertexShader.d.ts +0 -1
- package/dist/common/backends/webgl/shaders/vertexShader.js +0 -19
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { assert } from '@pexip/utils';
|
|
2
|
+
import { RENDERING_EVENTS } from '../../constants';
|
|
3
|
+
import { m3 } from '../../m3';
|
|
4
|
+
import { createGLProgram, createMaskTexture, createFrameBuffer, } from './webglUtils';
|
|
5
|
+
import vertexSrc from './shaders/matrix.vert';
|
|
6
|
+
import fragmentSrc from './shaders/jointBilateralFilter.frag';
|
|
7
|
+
/**
|
|
8
|
+
* Joint Bilateral Filter
|
|
9
|
+
*/
|
|
10
|
+
export const createJointBilateralFilterRenderer = (processor, width, height) => {
|
|
11
|
+
const { gl, program, attribLocations, uniformLocations, release } = createGLProgram(processor, {
|
|
12
|
+
attribs: {
|
|
13
|
+
position: 'a_position',
|
|
14
|
+
texCoord: 'a_texCoord',
|
|
15
|
+
},
|
|
16
|
+
uniforms: {
|
|
17
|
+
frameGuide: 'u_frame_guide',
|
|
18
|
+
frameInput: 'u_frame_input',
|
|
19
|
+
matrix: 'u_matrix',
|
|
20
|
+
texelSize: 'u_texel_size',
|
|
21
|
+
sigmaSpace: 'u_sigma_space',
|
|
22
|
+
sigmaRangeLo: 'u_sigma_range_lo',
|
|
23
|
+
sigmaRangeHi: 'u_sigma_range_hi',
|
|
24
|
+
foregroundThreshold: 'u_foreground_threshold',
|
|
25
|
+
backgroundThreshold: 'u_background_threshold',
|
|
26
|
+
},
|
|
27
|
+
vertexSrc,
|
|
28
|
+
fragmentSrc,
|
|
29
|
+
});
|
|
30
|
+
gl.enable(gl.BLEND);
|
|
31
|
+
const vao = gl.createVertexArray();
|
|
32
|
+
gl.bindVertexArray(vao);
|
|
33
|
+
// Position
|
|
34
|
+
const positionBuffer = gl.createBuffer();
|
|
35
|
+
gl.enableVertexAttribArray(attribLocations.position);
|
|
36
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
37
|
+
gl.vertexAttribPointer(attribLocations.position, 2, gl.FLOAT, false, 0, 0);
|
|
38
|
+
gl.bufferData(gl.ARRAY_BUFFER,
|
|
39
|
+
// prettier-ignore
|
|
40
|
+
new Float32Array([
|
|
41
|
+
0, 0,
|
|
42
|
+
0, 1,
|
|
43
|
+
1, 0,
|
|
44
|
+
1, 0,
|
|
45
|
+
0, 1,
|
|
46
|
+
1, 1,
|
|
47
|
+
]), gl.STATIC_DRAW);
|
|
48
|
+
// Texture coordinates for the rectangle
|
|
49
|
+
const texCoordBuffer = gl.createBuffer();
|
|
50
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
|
|
51
|
+
gl.bufferData(gl.ARRAY_BUFFER,
|
|
52
|
+
// prettier-ignore
|
|
53
|
+
new Float32Array([
|
|
54
|
+
0, 0,
|
|
55
|
+
0, 1,
|
|
56
|
+
1, 0,
|
|
57
|
+
1, 0,
|
|
58
|
+
0, 1,
|
|
59
|
+
1, 1,
|
|
60
|
+
]), gl.STATIC_DRAW);
|
|
61
|
+
gl.enableVertexAttribArray(attribLocations.texCoord);
|
|
62
|
+
gl.vertexAttribPointer(attribLocations.texCoord, 2, gl.FLOAT, false, 0, 0);
|
|
63
|
+
const outputTexture = createMaskTexture(gl, width, height);
|
|
64
|
+
const frameBuffer = createFrameBuffer(gl, outputTexture);
|
|
65
|
+
const scaleFactor = processor.width / width;
|
|
66
|
+
return {
|
|
67
|
+
render: ({ input, guide }, options) => {
|
|
68
|
+
assert(!gl.isContextLost(), RENDERING_EVENTS.ContextLost);
|
|
69
|
+
assert(options.foregroundThreshold >= options.backgroundThreshold, 'foregroundThreshold must be >= backgroundThreshold');
|
|
70
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
|
|
71
|
+
gl.viewport(0, 0, width, height);
|
|
72
|
+
gl.useProgram(program);
|
|
73
|
+
gl.bindVertexArray(vao);
|
|
74
|
+
// Textures
|
|
75
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
76
|
+
gl.uniform1i(uniformLocations.frameInput, 0);
|
|
77
|
+
gl.bindTexture(gl.TEXTURE_2D, input.texture);
|
|
78
|
+
gl.activeTexture(gl.TEXTURE1);
|
|
79
|
+
gl.uniform1i(uniformLocations.frameGuide, 1);
|
|
80
|
+
gl.bindTexture(gl.TEXTURE_2D, guide.texture);
|
|
81
|
+
gl.uniform2f(uniformLocations.texelSize, 1.0 / input.width, 1.0 / input.height);
|
|
82
|
+
gl.uniform1f(uniformLocations.sigmaSpace, options.sigmaSpace / scaleFactor);
|
|
83
|
+
gl.uniform1f(uniformLocations.sigmaRangeLo, options.sigmaRangeLo);
|
|
84
|
+
gl.uniform1f(uniformLocations.sigmaRangeHi, options.sigmaRangeHi);
|
|
85
|
+
gl.uniform1f(uniformLocations.backgroundThreshold, options.backgroundThreshold);
|
|
86
|
+
gl.uniform1f(uniformLocations.foregroundThreshold, options.foregroundThreshold);
|
|
87
|
+
// No Flip Y Coordinate for drawing on the frame buffer
|
|
88
|
+
// Project, position and scale to full-size of the canvas
|
|
89
|
+
const matrix = m3.projection(1, 1, 1);
|
|
90
|
+
gl.uniformMatrix3fv(uniformLocations.matrix, false, matrix);
|
|
91
|
+
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
92
|
+
// Cleanup
|
|
93
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
|
94
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
95
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
96
|
+
gl.bindVertexArray(null);
|
|
97
|
+
return { texture: outputTexture, width, height };
|
|
98
|
+
},
|
|
99
|
+
release: () => {
|
|
100
|
+
gl.deleteBuffer(positionBuffer);
|
|
101
|
+
gl.deleteBuffer(texCoordBuffer);
|
|
102
|
+
gl.deleteTexture(outputTexture);
|
|
103
|
+
gl.deleteFramebuffer(frameBuffer);
|
|
104
|
+
gl.deleteVertexArray(vao);
|
|
105
|
+
release();
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RendererOptions } from '../../types/render';
|
|
2
|
+
import type { TextureInfo } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Create a morphological erosion renderer that performs erosion of a mask with a
|
|
5
|
+
* given radius and number of passes.
|
|
6
|
+
* The erosion is implemented as a separable filter.
|
|
7
|
+
*/
|
|
8
|
+
export declare const createMorphErodeRenderer: (processor: OffscreenCanvas, width: number, height: number) => {
|
|
9
|
+
render: ({ mask }: {
|
|
10
|
+
mask: TextureInfo;
|
|
11
|
+
}, options: Pick<RendererOptions, "morphPass" | "morphErodeRadiusPx">) => TextureInfo;
|
|
12
|
+
release: () => void;
|
|
13
|
+
};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { assert } from '@pexip/utils';
|
|
2
|
+
import { m3 } from '../../m3';
|
|
3
|
+
import { RENDERING_EVENTS } from '../../constants';
|
|
4
|
+
import { clamping } from '../../utils';
|
|
5
|
+
import { createGLProgram, createMaskTexture, createFrameBuffer, } from './webglUtils';
|
|
6
|
+
import vertexSrc from './shaders/matrix.vert';
|
|
7
|
+
import fragmentSrc from './shaders/erode.frag';
|
|
8
|
+
const MAX_RADIUS_PX = 64;
|
|
9
|
+
const clampRadiusPx = clamping(0, MAX_RADIUS_PX);
|
|
10
|
+
/**
|
|
11
|
+
* Create a morphological erosion renderer that performs erosion of a mask with a
|
|
12
|
+
* given radius and number of passes.
|
|
13
|
+
* The erosion is implemented as a separable filter.
|
|
14
|
+
*/
|
|
15
|
+
export const createMorphErodeRenderer = (processor, width, height) => {
|
|
16
|
+
const { gl, program, attribLocations, uniformLocations, floatSingleChannelSupported, floatFullChannelSupported, release, } = createGLProgram(processor, {
|
|
17
|
+
attribs: {
|
|
18
|
+
position: 'a_position',
|
|
19
|
+
texCoord: 'a_texCoord',
|
|
20
|
+
},
|
|
21
|
+
uniforms: {
|
|
22
|
+
matrix: 'u_matrix',
|
|
23
|
+
mask: 'u_mask',
|
|
24
|
+
texelSize: 'u_texel_size',
|
|
25
|
+
radiusPx: 'u_radius_px',
|
|
26
|
+
direction: 'u_direction',
|
|
27
|
+
},
|
|
28
|
+
vertexSrc,
|
|
29
|
+
fragmentSrc,
|
|
30
|
+
});
|
|
31
|
+
const vao = gl.createVertexArray();
|
|
32
|
+
gl.bindVertexArray(vao);
|
|
33
|
+
// Position
|
|
34
|
+
const positionBuffer = gl.createBuffer();
|
|
35
|
+
gl.enableVertexAttribArray(attribLocations.position);
|
|
36
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
37
|
+
gl.vertexAttribPointer(attribLocations.position, 2, gl.FLOAT, false, 0, 0);
|
|
38
|
+
gl.bufferData(gl.ARRAY_BUFFER,
|
|
39
|
+
// prettier-ignore
|
|
40
|
+
new Float32Array([
|
|
41
|
+
0, 0,
|
|
42
|
+
0, 1,
|
|
43
|
+
1, 0,
|
|
44
|
+
1, 0,
|
|
45
|
+
0, 1,
|
|
46
|
+
1, 1,
|
|
47
|
+
]), gl.STATIC_DRAW);
|
|
48
|
+
// Texture coordinates for the rectangle
|
|
49
|
+
const texCoordBuffer = gl.createBuffer();
|
|
50
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
|
|
51
|
+
gl.bufferData(gl.ARRAY_BUFFER,
|
|
52
|
+
// prettier-ignore
|
|
53
|
+
new Float32Array([
|
|
54
|
+
0, 0,
|
|
55
|
+
0, 1,
|
|
56
|
+
1, 0,
|
|
57
|
+
1, 0,
|
|
58
|
+
0, 1,
|
|
59
|
+
1, 1,
|
|
60
|
+
]), gl.STATIC_DRAW);
|
|
61
|
+
gl.enableVertexAttribArray(attribLocations.texCoord);
|
|
62
|
+
gl.vertexAttribPointer(attribLocations.texCoord, 2, gl.FLOAT, false, 0, 0);
|
|
63
|
+
const outputTexturePing = createMaskTexture(gl, width, height, false, floatSingleChannelSupported, floatFullChannelSupported);
|
|
64
|
+
const frameBufferPing = createFrameBuffer(gl, outputTexturePing);
|
|
65
|
+
const outputTexturePong = createMaskTexture(gl, width, height, false, floatSingleChannelSupported, floatFullChannelSupported);
|
|
66
|
+
const frameBufferPong = createFrameBuffer(gl, outputTexturePong);
|
|
67
|
+
const scaleFactor = processor.width / width;
|
|
68
|
+
return {
|
|
69
|
+
render: ({ mask }, options) => {
|
|
70
|
+
assert(!gl.isContextLost(), RENDERING_EVENTS.ContextLost);
|
|
71
|
+
gl.clearColor(0, 0, 0, 0);
|
|
72
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
73
|
+
gl.useProgram(program);
|
|
74
|
+
gl.bindVertexArray(vao);
|
|
75
|
+
gl.uniform2f(uniformLocations.texelSize, 1 / mask.width, 1 / mask.height);
|
|
76
|
+
gl.uniform1i(uniformLocations.radiusPx, clampRadiusPx(Math.floor(options.morphErodeRadiusPx / scaleFactor)));
|
|
77
|
+
const matrix = m3.projection(1, 1, 1);
|
|
78
|
+
// No Flip Y Coordinate for drawing on the frame buffer
|
|
79
|
+
// Project, position and scale to full-size of the canvas
|
|
80
|
+
gl.uniformMatrix3fv(uniformLocations.matrix, false, matrix);
|
|
81
|
+
let write = frameBufferPing;
|
|
82
|
+
let read = mask.texture;
|
|
83
|
+
// Ping-pong framebuffers for multiple passes
|
|
84
|
+
for (let i = 0; i < options.morphPass; i++) {
|
|
85
|
+
// Separable
|
|
86
|
+
for (let j = 0; j < 2; j++) {
|
|
87
|
+
// Textures
|
|
88
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
89
|
+
gl.uniform1i(uniformLocations.mask, 0);
|
|
90
|
+
gl.bindTexture(gl.TEXTURE_2D, read);
|
|
91
|
+
// Horizontal then Vertical
|
|
92
|
+
gl.uniform2f(uniformLocations.direction, 1.0 - j, 0.0 + j);
|
|
93
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, write);
|
|
94
|
+
gl.viewport(0, 0, mask.width, mask.height);
|
|
95
|
+
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
96
|
+
// Swap
|
|
97
|
+
[read, write] =
|
|
98
|
+
write === frameBufferPing
|
|
99
|
+
? [outputTexturePing, frameBufferPong]
|
|
100
|
+
: [outputTexturePong, frameBufferPing];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Cleanup
|
|
104
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
|
105
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
106
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
107
|
+
gl.bindVertexArray(null);
|
|
108
|
+
return { texture: read, width, height };
|
|
109
|
+
},
|
|
110
|
+
release: () => {
|
|
111
|
+
gl.deleteBuffer(positionBuffer);
|
|
112
|
+
gl.deleteBuffer(texCoordBuffer);
|
|
113
|
+
gl.deleteTexture(outputTexturePing);
|
|
114
|
+
gl.deleteTexture(outputTexturePong);
|
|
115
|
+
gl.deleteFramebuffer(frameBufferPing);
|
|
116
|
+
gl.deleteFramebuffer(frameBufferPong);
|
|
117
|
+
gl.deleteVertexArray(vao);
|
|
118
|
+
release();
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
};
|
|
@@ -1,23 +1,59 @@
|
|
|
1
1
|
import { assert } from '@pexip/utils';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { BACKGROUND_BLUR_AMOUNT, PROCESSING_HEIGHT, PROCESSING_WIDTH, RENDERING_EVENTS, STRONG_EDGE_BLUR_AMOUNT, } from '../../constants';
|
|
3
|
+
import { createLazyProps, handleWebGLContextLoss } from '../../utils';
|
|
4
|
+
import { createBinaryMaskRenderer } from './binaryMask';
|
|
5
5
|
import { createBlendRenderer } from './blender';
|
|
6
|
+
import { createBlurRenderer } from './blur';
|
|
6
7
|
import { createCanvasRenderer } from './canvas';
|
|
7
|
-
import {
|
|
8
|
+
import { createComponentSelector } from './selectComponent';
|
|
9
|
+
import { createGeodesicReconstructionRenderer } from './geodesicReconstruction';
|
|
10
|
+
import { createJointBilateralFilterRenderer } from './jointBilateralFilter';
|
|
11
|
+
import { createMorphErodeRenderer } from './morphErode';
|
|
12
|
+
import { createReplaceRenderer } from './replace';
|
|
8
13
|
import { createSmoothingMaskRenderer } from './smoothingMask';
|
|
9
|
-
import {
|
|
14
|
+
import { createTentBlurRenderer } from './tentBlur';
|
|
15
|
+
import { createTextureRenderer } from './texture';
|
|
16
|
+
import { createTextureToTextureRenderer } from './textureToTexture';
|
|
10
17
|
export const createRenderer = (renderEventHandlers, canvas = new OffscreenCanvas(PROCESSING_WIDTH, PROCESSING_HEIGHT)) => {
|
|
18
|
+
const halfWidth = Math.trunc(canvas.width / 2);
|
|
19
|
+
const halfHeight = Math.trunc(canvas.height / 2);
|
|
20
|
+
const quarterWidth = Math.trunc(canvas.width / 4);
|
|
21
|
+
const quarterHeight = Math.trunc(canvas.height / 4);
|
|
22
|
+
// Width and Height used for the Center Component selection
|
|
23
|
+
const cclWidth = 32;
|
|
24
|
+
const cclHeight = 18;
|
|
11
25
|
const rendererCreator = {
|
|
12
|
-
|
|
13
|
-
maskRenderer: () => createTextureRenderer(canvas),
|
|
14
|
-
canvasRenderer: () => createCanvasRenderer(canvas),
|
|
15
|
-
backgroundImageRenderer: () => createTextureRenderer(canvas),
|
|
26
|
+
backgroundImageRenderer: () => createTextureRenderer(canvas, false),
|
|
16
27
|
blendRenderer: () => createBlendRenderer(canvas),
|
|
17
28
|
blurRenderer: () => createBlurRenderer(canvas, BACKGROUND_BLUR_AMOUNT),
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
29
|
+
canvasRenderer: () => createCanvasRenderer(canvas),
|
|
30
|
+
frameDownsampler2: () => createTextureToTextureRenderer(canvas, false, true, halfWidth, halfHeight),
|
|
31
|
+
frameDownsampler4: () => createTextureToTextureRenderer(canvas, false, true, quarterWidth, quarterHeight),
|
|
32
|
+
geodesicReconstructionRenderer2: () => createGeodesicReconstructionRenderer(canvas, halfWidth, halfHeight),
|
|
33
|
+
geodesicReconstructionRenderer4: () => createGeodesicReconstructionRenderer(canvas, quarterWidth, quarterHeight),
|
|
34
|
+
jointBilateralFilterRenderer2: () => createJointBilateralFilterRenderer(canvas, halfWidth, halfHeight),
|
|
35
|
+
jointBilateralFilterRenderer4: () => createJointBilateralFilterRenderer(canvas, quarterWidth, quarterHeight),
|
|
36
|
+
jointBilateralFilterRenderer: () => createJointBilateralFilterRenderer(canvas, canvas.width, canvas.height),
|
|
37
|
+
lightWrapBlurRenderer: () => createBlurRenderer(canvas, BACKGROUND_BLUR_AMOUNT),
|
|
38
|
+
maskBlurRenderer: () => createTentBlurRenderer(canvas, canvas.width, canvas.height),
|
|
39
|
+
strongMaskBlurRenderer: () => createBlurRenderer(canvas, STRONG_EDGE_BLUR_AMOUNT),
|
|
40
|
+
maskDownsampler2: () => createTextureToTextureRenderer(canvas, true, false, halfWidth, halfHeight),
|
|
41
|
+
maskDownsampler4: () => createTextureToTextureRenderer(canvas, true, false, quarterWidth, quarterHeight),
|
|
42
|
+
maskRenderer: () => createTextureRenderer(canvas, true),
|
|
43
|
+
maskUpsampler2: () => createTextureToTextureRenderer(canvas, true, false, halfWidth, halfHeight),
|
|
44
|
+
maskUpsampler4: () => createTextureToTextureRenderer(canvas, true, false, quarterWidth, quarterHeight),
|
|
45
|
+
maskUpsampler: () => createTextureToTextureRenderer(canvas, true, false, canvas.width, canvas.height),
|
|
46
|
+
morphErodeRenderer: () => createMorphErodeRenderer(canvas, halfWidth, halfHeight),
|
|
47
|
+
morphErodeRenderer4: () => createMorphErodeRenderer(canvas, quarterWidth, quarterHeight),
|
|
48
|
+
prevMaskRenderer2: () => createTextureToTextureRenderer(canvas, true, false, halfWidth, halfHeight),
|
|
49
|
+
prevMaskRenderer4: () => createTextureToTextureRenderer(canvas, true, false, quarterWidth, quarterHeight),
|
|
50
|
+
replaceRenderer: () => createReplaceRenderer(canvas),
|
|
51
|
+
selectCenterComponent: () => createComponentSelector(canvas, cclWidth, cclHeight),
|
|
52
|
+
smoothingMaskRenderer4: () => createSmoothingMaskRenderer(canvas, quarterWidth, quarterHeight),
|
|
53
|
+
smoothingMaskRenderer2: () => createSmoothingMaskRenderer(canvas, halfWidth, halfHeight),
|
|
54
|
+
binaryMaskRenderer2: () => createBinaryMaskRenderer(canvas, halfWidth, halfHeight),
|
|
55
|
+
binaryMaskRenderer4: () => createBinaryMaskRenderer(canvas, quarterWidth, quarterHeight),
|
|
56
|
+
videoFrameRenderer: () => createTextureRenderer(canvas, false),
|
|
21
57
|
release: (states) => () => {
|
|
22
58
|
for (const key in states) {
|
|
23
59
|
if (key !== 'release') {
|
|
@@ -35,6 +71,15 @@ export const createRenderer = (renderEventHandlers, canvas = new OffscreenCanvas
|
|
|
35
71
|
get videoFrameRenderer() {
|
|
36
72
|
return lazyProps.videoFrameRenderer;
|
|
37
73
|
},
|
|
74
|
+
get jointBilateralFilterRenderer() {
|
|
75
|
+
return lazyProps.jointBilateralFilterRenderer;
|
|
76
|
+
},
|
|
77
|
+
get jointBilateralFilterRenderer2() {
|
|
78
|
+
return lazyProps.jointBilateralFilterRenderer2;
|
|
79
|
+
},
|
|
80
|
+
get jointBilateralFilterRenderer4() {
|
|
81
|
+
return lazyProps.jointBilateralFilterRenderer4;
|
|
82
|
+
},
|
|
38
83
|
get maskRenderer() {
|
|
39
84
|
return lazyProps.maskRenderer;
|
|
40
85
|
},
|
|
@@ -47,18 +92,74 @@ export const createRenderer = (renderEventHandlers, canvas = new OffscreenCanvas
|
|
|
47
92
|
get blendRenderer() {
|
|
48
93
|
return lazyProps.blendRenderer;
|
|
49
94
|
},
|
|
95
|
+
get replaceRenderer() {
|
|
96
|
+
return lazyProps.replaceRenderer;
|
|
97
|
+
},
|
|
50
98
|
get blurRenderer() {
|
|
51
99
|
return lazyProps.blurRenderer;
|
|
52
100
|
},
|
|
53
|
-
|
|
101
|
+
get lightWrapBlurRenderer() {
|
|
102
|
+
return lazyProps.lightWrapBlurRenderer;
|
|
103
|
+
},
|
|
104
|
+
get prevMaskRenderer2() {
|
|
105
|
+
return lazyProps.prevMaskRenderer2;
|
|
106
|
+
},
|
|
107
|
+
get prevMaskRenderer4() {
|
|
108
|
+
return lazyProps.prevMaskRenderer4;
|
|
109
|
+
},
|
|
110
|
+
get selectCenterComponent() {
|
|
111
|
+
return lazyProps.selectCenterComponent;
|
|
112
|
+
},
|
|
113
|
+
get smoothingMaskRenderer2() {
|
|
114
|
+
return lazyProps.smoothingMaskRenderer2;
|
|
115
|
+
},
|
|
116
|
+
get smoothingMaskRenderer4() {
|
|
117
|
+
return lazyProps.smoothingMaskRenderer4;
|
|
118
|
+
},
|
|
119
|
+
get binaryMaskRenderer2() {
|
|
120
|
+
return lazyProps.binaryMaskRenderer2;
|
|
121
|
+
},
|
|
122
|
+
get binaryMaskRenderer4() {
|
|
123
|
+
return lazyProps.binaryMaskRenderer4;
|
|
124
|
+
},
|
|
125
|
+
get morphErodeRenderer() {
|
|
126
|
+
return lazyProps.morphErodeRenderer;
|
|
127
|
+
},
|
|
128
|
+
get morphErodeRenderer4() {
|
|
129
|
+
return lazyProps.morphErodeRenderer4;
|
|
130
|
+
},
|
|
54
131
|
get maskBlurRenderer() {
|
|
55
132
|
return lazyProps.maskBlurRenderer;
|
|
56
133
|
},
|
|
57
|
-
get
|
|
58
|
-
return lazyProps.
|
|
134
|
+
get strongMaskBlurRenderer() {
|
|
135
|
+
return lazyProps.strongMaskBlurRenderer;
|
|
136
|
+
},
|
|
137
|
+
get maskDownsampler2() {
|
|
138
|
+
return lazyProps.maskDownsampler2;
|
|
139
|
+
},
|
|
140
|
+
get maskDownsampler4() {
|
|
141
|
+
return lazyProps.maskDownsampler4;
|
|
142
|
+
},
|
|
143
|
+
get maskUpsampler() {
|
|
144
|
+
return lazyProps.maskUpsampler;
|
|
145
|
+
},
|
|
146
|
+
get maskUpsampler2() {
|
|
147
|
+
return lazyProps.maskUpsampler2;
|
|
148
|
+
},
|
|
149
|
+
get maskUpsampler4() {
|
|
150
|
+
return lazyProps.maskUpsampler4;
|
|
151
|
+
},
|
|
152
|
+
get frameDownsampler2() {
|
|
153
|
+
return lazyProps.frameDownsampler2;
|
|
154
|
+
},
|
|
155
|
+
get frameDownsampler4() {
|
|
156
|
+
return lazyProps.frameDownsampler4;
|
|
59
157
|
},
|
|
60
|
-
get
|
|
61
|
-
return lazyProps.
|
|
158
|
+
get geodesicReconstructionRenderer2() {
|
|
159
|
+
return lazyProps.geodesicReconstructionRenderer2;
|
|
160
|
+
},
|
|
161
|
+
get geodesicReconstructionRenderer4() {
|
|
162
|
+
return lazyProps.geodesicReconstructionRenderer4;
|
|
62
163
|
},
|
|
63
164
|
};
|
|
64
165
|
const init = () => {
|
|
@@ -75,43 +176,102 @@ export const createRenderer = (renderEventHandlers, canvas = new OffscreenCanvas
|
|
|
75
176
|
};
|
|
76
177
|
const render = (mask, frame, options) => {
|
|
77
178
|
assert(!props.gl?.isContextLost(), RENDERING_EVENTS.ContextLost);
|
|
179
|
+
// 1. Upload current frame and raw mask
|
|
180
|
+
const frameTexture = props.videoFrameRenderer.render(frame);
|
|
78
181
|
const maskTexture = props.maskRenderer.render({
|
|
79
182
|
frame: mask,
|
|
80
183
|
width: mask.width,
|
|
81
184
|
height: mask.height,
|
|
82
185
|
timestamp: frame.timestamp,
|
|
83
186
|
});
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
187
|
+
// 2. Joint Bilateral Filter
|
|
188
|
+
let smoothedMask;
|
|
189
|
+
let compositeMaskInput;
|
|
190
|
+
switch (options.downSampleFactor) {
|
|
191
|
+
case 4: {
|
|
192
|
+
const maskLowRes = props.maskDownsampler4.render(maskTexture);
|
|
193
|
+
const frameLowRes = props.frameDownsampler4.render(frameTexture);
|
|
194
|
+
const filteredMaskLowRes = props.jointBilateralFilterRenderer4.render({ input: maskLowRes, guide: frameLowRes }, options);
|
|
195
|
+
smoothedMask = props.smoothingMaskRenderer4.render({
|
|
196
|
+
mask: filteredMaskLowRes,
|
|
197
|
+
prevMask: props.prevMask4 ?? filteredMaskLowRes,
|
|
198
|
+
}, options);
|
|
199
|
+
if (options.excludeBystanders) {
|
|
200
|
+
const binary = props.binaryMaskRenderer4.render(smoothedMask, options.foregroundThreshold);
|
|
201
|
+
const eroded = props.morphErodeRenderer4.render({ mask: binary }, options);
|
|
202
|
+
const selected = props.selectCenterComponent.render(eroded, options);
|
|
203
|
+
const filted = props.maskUpsampler4.render(selected);
|
|
204
|
+
smoothedMask = props.geodesicReconstructionRenderer4.render({ mask: filted, maskRef: smoothedMask }, options);
|
|
205
|
+
}
|
|
206
|
+
// Upsample to original size
|
|
207
|
+
const result = props.maskUpsampler.render(smoothedMask);
|
|
208
|
+
compositeMaskInput = props.strongMaskBlurRenderer.render(result, STRONG_EDGE_BLUR_AMOUNT);
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
default: {
|
|
212
|
+
const maskLowRes = props.maskDownsampler2.render(maskTexture);
|
|
213
|
+
const frameLowRes = props.frameDownsampler2.render(frameTexture);
|
|
214
|
+
const filteredMaskLowRes = props.jointBilateralFilterRenderer2.render({ input: maskLowRes, guide: frameLowRes }, options);
|
|
215
|
+
smoothedMask = props.smoothingMaskRenderer2.render({
|
|
216
|
+
mask: filteredMaskLowRes,
|
|
217
|
+
prevMask: props.prevMask2 ?? filteredMaskLowRes,
|
|
218
|
+
}, options);
|
|
219
|
+
if (options.excludeBystanders) {
|
|
220
|
+
const binary = props.binaryMaskRenderer2.render(smoothedMask, options.foregroundThreshold);
|
|
221
|
+
const eroded = props.morphErodeRenderer.render({ mask: binary }, options);
|
|
222
|
+
const selected = props.selectCenterComponent.render(eroded, options);
|
|
223
|
+
const filted = props.maskUpsampler2.render(selected);
|
|
224
|
+
smoothedMask = props.geodesicReconstructionRenderer2.render({ mask: filted, maskRef: smoothedMask }, options);
|
|
225
|
+
}
|
|
226
|
+
// Upsample to original size
|
|
227
|
+
const result = props.maskUpsampler.render(smoothedMask);
|
|
228
|
+
compositeMaskInput = props.maskBlurRenderer.render(result, options.edgeBlurAmount);
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// 5. Composite final output
|
|
233
|
+
let outputFrameTexture = frameTexture;
|
|
90
234
|
if (options.effects === 'blur' && options.backgroundBlurAmount) {
|
|
91
|
-
background = props.blurRenderer.render(frameTexture, options.backgroundBlurAmount);
|
|
235
|
+
const background = props.blurRenderer.render(frameTexture, options.backgroundBlurAmount);
|
|
236
|
+
outputFrameTexture = props.blendRenderer.render({
|
|
237
|
+
mask: compositeMaskInput,
|
|
238
|
+
foreground: frameTexture,
|
|
239
|
+
background,
|
|
240
|
+
}, options);
|
|
92
241
|
}
|
|
93
242
|
else if (options?.effects === 'overlay' && options.backgroundImage) {
|
|
94
|
-
background = props.backgroundImageRenderer.render({
|
|
243
|
+
const background = props.backgroundImageRenderer.render({
|
|
95
244
|
frame: options.backgroundImage,
|
|
96
245
|
timestamp: performance.now(),
|
|
97
246
|
width: options.backgroundImage.width,
|
|
98
247
|
height: options.backgroundImage.height,
|
|
99
248
|
});
|
|
249
|
+
outputFrameTexture = props.replaceRenderer.render({
|
|
250
|
+
mask: compositeMaskInput,
|
|
251
|
+
foreground: frameTexture,
|
|
252
|
+
background,
|
|
253
|
+
blurredBackground: props.lightWrapBlurRenderer.render(background, options.lightWrapBlurAmount),
|
|
254
|
+
timestamp: frame.timestamp,
|
|
255
|
+
}, options);
|
|
256
|
+
}
|
|
257
|
+
// 6. Output to present
|
|
258
|
+
props.canvasRenderer.render(outputFrameTexture);
|
|
259
|
+
// 7. Cloned the current smoothedMask & frame so it can be fed back to the next round
|
|
260
|
+
switch (options.downSampleFactor) {
|
|
261
|
+
case 4:
|
|
262
|
+
props.prevMask4 = props.prevMaskRenderer4.render(smoothedMask);
|
|
263
|
+
break;
|
|
264
|
+
default:
|
|
265
|
+
props.prevMask2 = props.prevMaskRenderer2.render(smoothedMask);
|
|
266
|
+
break;
|
|
100
267
|
}
|
|
101
|
-
const blendedFrameTexture = props.blendRenderer.render({
|
|
102
|
-
mask: smoothedMask,
|
|
103
|
-
foreground: frameTexture,
|
|
104
|
-
background,
|
|
105
|
-
});
|
|
106
|
-
props.canvasRenderer.render(blendedFrameTexture);
|
|
107
|
-
// Cloned the current smoothedMask so it can be fed back to the next round
|
|
108
|
-
props.prevMask = props.textureRenderer.render(smoothedMask);
|
|
109
268
|
};
|
|
110
269
|
const cleanup = () => {
|
|
111
270
|
props.canvasContextEventsCleanup();
|
|
112
271
|
lazyProps.release();
|
|
113
272
|
lazyProps = createLazyProps(rendererCreator);
|
|
114
|
-
props.
|
|
273
|
+
props.prevMask2 = undefined;
|
|
274
|
+
props.prevMask4 = undefined;
|
|
115
275
|
};
|
|
116
276
|
const release = () => {
|
|
117
277
|
cleanup();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { TextureInfo } from './types';
|
|
2
|
+
import type { RendererOptions } from '../../types/render';
|
|
3
|
+
/**
|
|
4
|
+
* Create a background replacement renderer that composites
|
|
5
|
+
* the foreground and background as well as adding light wrap
|
|
6
|
+
* effect based on the mask and blurred background.
|
|
7
|
+
*/
|
|
8
|
+
export declare const createReplaceRenderer: (processor: OffscreenCanvas) => {
|
|
9
|
+
render: ({ mask, foreground, background, blurredBackground, timestamp, }: {
|
|
10
|
+
mask: TextureInfo;
|
|
11
|
+
foreground: TextureInfo;
|
|
12
|
+
background: TextureInfo;
|
|
13
|
+
blurredBackground: TextureInfo;
|
|
14
|
+
timestamp: number;
|
|
15
|
+
}, options: RendererOptions) => TextureInfo;
|
|
16
|
+
release: () => void;
|
|
17
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { assert } from '@pexip/utils';
|
|
2
|
+
import { RENDERING_EVENTS } from '../../constants';
|
|
3
|
+
import { m3 } from '../../m3';
|
|
4
|
+
import { createGLProgram, createRGBAFrameTexture, createFrameBuffer, } from './webglUtils';
|
|
5
|
+
import vertexSrc from './shaders/matrix.vert';
|
|
6
|
+
import fragmentSrc from './shaders/replace.frag';
|
|
7
|
+
/**
|
|
8
|
+
* Create a background replacement renderer that composites
|
|
9
|
+
* the foreground and background as well as adding light wrap
|
|
10
|
+
* effect based on the mask and blurred background.
|
|
11
|
+
*/
|
|
12
|
+
export const createReplaceRenderer = (processor) => {
|
|
13
|
+
const { gl, program, attribLocations, floatFullChannelSupported, uniformLocations, release, } = createGLProgram(processor, {
|
|
14
|
+
attribs: {
|
|
15
|
+
position: 'a_position',
|
|
16
|
+
texCoord: 'a_texCoord',
|
|
17
|
+
},
|
|
18
|
+
uniforms: {
|
|
19
|
+
matrix: 'u_matrix',
|
|
20
|
+
frameBackground: 'u_frame_background',
|
|
21
|
+
frameForeground: 'u_frame_foreground',
|
|
22
|
+
blurBackground: 'u_blur_background',
|
|
23
|
+
mask: 'u_mask',
|
|
24
|
+
confidenceThresholds: 'u_confidence_thresholds',
|
|
25
|
+
lightWrap: 'u_light_wrap',
|
|
26
|
+
lightWrapTightness: 'u_light_wrap_tightness',
|
|
27
|
+
lightWrapEdgeBand: 'u_edge_band',
|
|
28
|
+
time: 'u_time',
|
|
29
|
+
parity: 'u_parity',
|
|
30
|
+
frameSize: 'u_frame_size',
|
|
31
|
+
},
|
|
32
|
+
vertexSrc,
|
|
33
|
+
fragmentSrc,
|
|
34
|
+
});
|
|
35
|
+
gl.enable(gl.BLEND);
|
|
36
|
+
const vao = gl.createVertexArray();
|
|
37
|
+
gl.bindVertexArray(vao);
|
|
38
|
+
// Position
|
|
39
|
+
const positionBuffer = gl.createBuffer();
|
|
40
|
+
gl.enableVertexAttribArray(attribLocations.position);
|
|
41
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
42
|
+
gl.vertexAttribPointer(attribLocations.position, 2, gl.FLOAT, false, 0, 0);
|
|
43
|
+
gl.bufferData(gl.ARRAY_BUFFER,
|
|
44
|
+
// prettier-ignore
|
|
45
|
+
new Float32Array([
|
|
46
|
+
0, 0,
|
|
47
|
+
0, 1,
|
|
48
|
+
1, 0,
|
|
49
|
+
1, 0,
|
|
50
|
+
0, 1,
|
|
51
|
+
1, 1,
|
|
52
|
+
]), gl.STATIC_DRAW);
|
|
53
|
+
// Texture coordinates for the rectangle
|
|
54
|
+
const texCoordBuffer = gl.createBuffer();
|
|
55
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
|
|
56
|
+
gl.bufferData(gl.ARRAY_BUFFER,
|
|
57
|
+
// prettier-ignore
|
|
58
|
+
new Float32Array([
|
|
59
|
+
0, 0,
|
|
60
|
+
0, 1,
|
|
61
|
+
1, 0,
|
|
62
|
+
1, 0,
|
|
63
|
+
0, 1,
|
|
64
|
+
1, 1,
|
|
65
|
+
]), gl.STATIC_DRAW);
|
|
66
|
+
gl.enableVertexAttribArray(attribLocations.texCoord);
|
|
67
|
+
gl.vertexAttribPointer(attribLocations.texCoord, 2, gl.FLOAT, false, 0, 0);
|
|
68
|
+
const outputTexture = createRGBAFrameTexture(gl, processor.width, processor.height, true, floatFullChannelSupported);
|
|
69
|
+
const frameBuffer = createFrameBuffer(gl, outputTexture);
|
|
70
|
+
let parity = -1;
|
|
71
|
+
return {
|
|
72
|
+
render: ({ mask, foreground, background, blurredBackground, timestamp, }, options) => {
|
|
73
|
+
assert(!gl.isContextLost(), RENDERING_EVENTS.ContextLost);
|
|
74
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
|
|
75
|
+
gl.viewport(0, 0, processor.width, processor.height);
|
|
76
|
+
gl.useProgram(program);
|
|
77
|
+
gl.bindVertexArray(vao);
|
|
78
|
+
// Textures
|
|
79
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
80
|
+
gl.uniform1i(uniformLocations.frameForeground, 0);
|
|
81
|
+
gl.bindTexture(gl.TEXTURE_2D, foreground.texture);
|
|
82
|
+
gl.activeTexture(gl.TEXTURE1);
|
|
83
|
+
gl.uniform1i(uniformLocations.frameBackground, 1);
|
|
84
|
+
gl.bindTexture(gl.TEXTURE_2D, background.texture);
|
|
85
|
+
gl.activeTexture(gl.TEXTURE2);
|
|
86
|
+
gl.uniform1i(uniformLocations.mask, 2);
|
|
87
|
+
gl.bindTexture(gl.TEXTURE_2D, mask.texture);
|
|
88
|
+
gl.activeTexture(gl.TEXTURE3);
|
|
89
|
+
gl.uniform1i(uniformLocations.blurBackground, 3);
|
|
90
|
+
gl.bindTexture(gl.TEXTURE_2D, blurredBackground.texture);
|
|
91
|
+
gl.uniform2f(uniformLocations.confidenceThresholds, options.backgroundThreshold, options.foregroundThreshold);
|
|
92
|
+
gl.uniform1f(uniformLocations.lightWrap, options.lightWrapIntensity);
|
|
93
|
+
gl.uniform1f(uniformLocations.lightWrapTightness, options.lightWrapTightness);
|
|
94
|
+
gl.uniform1f(uniformLocations.lightWrapEdgeBand, options.lightWrapEdgeBand);
|
|
95
|
+
parity = -parity;
|
|
96
|
+
gl.uniform1f(uniformLocations.parity, parity);
|
|
97
|
+
gl.uniform1f(uniformLocations.time,
|
|
98
|
+
// Use seconds and wrap to a modest range to avoid mediump precision loss
|
|
99
|
+
(timestamp * 1e-6) % 1024.0);
|
|
100
|
+
gl.uniform2f(uniformLocations.frameSize, processor.width, processor.height);
|
|
101
|
+
// No Flip Y Coordinate for drawing on the frame buffer
|
|
102
|
+
// Project, position and scale to full-size of the canvas
|
|
103
|
+
const matrix = m3.projection(1, 1, 1);
|
|
104
|
+
gl.uniformMatrix3fv(uniformLocations.matrix, false, matrix);
|
|
105
|
+
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
106
|
+
// Cleanup
|
|
107
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
|
108
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
109
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
110
|
+
gl.bindVertexArray(null);
|
|
111
|
+
return {
|
|
112
|
+
texture: outputTexture,
|
|
113
|
+
width: processor.width,
|
|
114
|
+
height: processor.height,
|
|
115
|
+
};
|
|
116
|
+
},
|
|
117
|
+
release: () => {
|
|
118
|
+
gl.deleteBuffer(positionBuffer);
|
|
119
|
+
gl.deleteBuffer(texCoordBuffer);
|
|
120
|
+
gl.deleteTexture(outputTexture);
|
|
121
|
+
gl.deleteFramebuffer(frameBuffer);
|
|
122
|
+
gl.deleteVertexArray(vao);
|
|
123
|
+
release();
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
};
|