rtrm HF staff commited on
Commit
36cb1b4
·
1 Parent(s): 6a50751

first commit

Browse files
Files changed (4) hide show
  1. css/style.css +48 -0
  2. images/background.webp +0 -0
  3. index.html +29 -18
  4. js/index.js +160 -0
css/style.css ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body {
2
+ margin: 0;
3
+ padding: 0;
4
+ display: flex;
5
+ flex-direction: column;
6
+ align-items: center;
7
+ justify-content: center;
8
+ height: 100vh;
9
+ background-color: #f0f0f0;
10
+ }
11
+
12
+ #videoContainer {
13
+ width: 100%;
14
+ max-width: 600px; /* Adjust the maximum width as needed */
15
+ position: relative;
16
+ }
17
+
18
+ video, canvas {
19
+ width: 100%;
20
+ height: auto;
21
+ border-radius: 1rem;
22
+ }
23
+
24
+ img {
25
+ max-width: 100%;
26
+ height: auto;
27
+ }
28
+
29
+ button {
30
+ margin: 10px;
31
+ padding: 10px 20px;
32
+ font-size: 18px;
33
+ border-radius: 1rem;
34
+ border: none;
35
+ cursor: pointer;
36
+ background-color: #e3e3e3;
37
+ }
38
+
39
+ button:hover {
40
+ background-color: #1D2026;
41
+ color: #FFF;
42
+ }
43
+
44
+ #errorText {
45
+ width: 50%;
46
+ color: red;
47
+ font-weight: bold;
48
+ }
images/background.webp ADDED
index.html CHANGED
@@ -1,19 +1,30 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Virtual Background</title>
7
+
8
+ <link rel="stylesheet" href="css/style.css">
9
+ <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs/dist/tf.min.js"></script>
10
+ <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/body-pix/dist/body-pix.min.js"></script>
11
+ </head>
12
+ <body>
13
+ <p id="errorText"></p>
14
+
15
+ <div id="videoContainer">
16
+ <!-- Create a video element for the webcam feed -->
17
+ <video id="videoElement" playsinline></video>
18
+
19
+ <!-- Create a canvas element for the background -->
20
+ <canvas id="backgroundCanvas" playsinline hidden></canvas>
21
+ </div>
22
+
23
+ <img id="yourBackgroundImage" src="https://i.postimg.cc/t9PJw5P7/forest.jpg" style="display: none;">
24
+
25
+ <button id="startButton">Start Virtual Background</button>
26
+ <button id="stopButton" style="display: none;">Stop Virtual Background</button>
27
+
28
+ <script src="js/index.js"></script>
29
+ </body>
30
  </html>
js/index.js ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Initialize variables
2
+ let isVirtual = false;
3
+
4
+ // DOM elements
5
+ const videoContainer = document.getElementById('videoContainer');
6
+ const videoElement = document.getElementById('videoElement');
7
+ const canvasElement = document.getElementById('backgroundCanvas');
8
+ const backgroundImage = document.getElementById('yourBackgroundImage');
9
+ const ctx = canvasElement.getContext('2d');
10
+ const startButton = document.getElementById('startButton');
11
+ const stopButton = document.getElementById('stopButton');
12
+ const errorText = document.getElementById('errorText');
13
+
14
+ // MobileNetV1 or ResNet50
15
+ const BodyPixModel = 'MobileNetV1';
16
+
17
+ async function startWebCamStream() {
18
+ try {
19
+ // Start the webcam stream
20
+ const stream = await navigator.mediaDevices.getUserMedia({ video: true });
21
+ videoElement.srcObject = stream;
22
+
23
+ // Wait for the video to play
24
+ await videoElement.play();
25
+ } catch (error) {
26
+ displayError(error)
27
+ }
28
+ }
29
+
30
+ // Function to start the virtual background
31
+ async function startVirtualBackground() {
32
+ try {
33
+ // Set canvas dimensions to match video dimensions
34
+ canvasElement.width = videoElement.videoWidth;
35
+ canvasElement.height = videoElement.videoHeight;
36
+
37
+ // Load the BodyPix model:
38
+ let net = null;
39
+
40
+ switch (BodyPixModel) {
41
+ case 'MobileNetV1':
42
+ /*
43
+ This is a lightweight architecture that is suitable for real-time applications and has lower computational requirements.
44
+ It provides good performance for most use cases.
45
+ */
46
+ net = await bodyPix.load({
47
+ architecture: 'MobileNetV1',
48
+ outputStride: 16, // Output stride (16 or 32). 16 is faster, 32 is more accurate.
49
+ multiplier: 0.75, // The model's depth multiplier. Options: 0.50, 0.75, or 1.0.
50
+ quantBytes: 2, // The number of bytes to use for quantization (4 or 2).
51
+ });
52
+ break;
53
+ case 'ResNet50':
54
+ /*
55
+ This is a deeper and more accurate architecture compared to MobileNetV1.
56
+ It may provide better segmentation accuracy, but it requires more computational resources and can be slower.
57
+ */
58
+ net = await bodyPix.load({
59
+ architecture: 'ResNet50',
60
+ outputStride: 16, // Output stride (16 or 32). 16 is faster, 32 is more accurate.
61
+ quantBytes: 4, // The number of bytes to use for quantization (4 or 2).
62
+ });
63
+ break;
64
+ default:
65
+ break;
66
+ }
67
+
68
+ // Start the virtual background loop
69
+ isVirtual = true;
70
+ videoElement.hidden = true;
71
+ canvasElement.hidden = false;
72
+ display(canvasElement, 'block');
73
+
74
+ // Show the stop button and hide the start button
75
+ startButton.style.display = 'none';
76
+ stopButton.style.display = 'block';
77
+
78
+ async function updateCanvas() {
79
+ if (isVirtual) {
80
+ // 1. Segmentation Calculation
81
+ const segmentation = await net.segmentPerson(videoElement, {
82
+ flipHorizontal: false, // Whether to flip the input video horizontally
83
+ internalResolution: 'medium', // The resolution for internal processing (options: 'low', 'medium', 'high')
84
+ segmentationThreshold: 0.7, // Segmentation confidence threshold (0.0 - 1.0)
85
+ maxDetections: 10, // Maximum number of detections to return
86
+ scoreThreshold: 0.2, // Confidence score threshold for detections (0.0 - 1.0)
87
+ nmsRadius: 20, // Non-Maximum Suppression (NMS) radius for de-duplication
88
+ minKeypointScore: 0.3, // Minimum keypoint detection score (0.0 - 1.0)
89
+ refineSteps: 10, // Number of refinement steps for segmentation
90
+ });
91
+
92
+ // 2. Creating a Background Mask
93
+ const background = { r: 0, g: 0, b: 0, a: 0 };
94
+ const mask = bodyPix.toMask(segmentation, background, { r: 0, g: 0, b: 0, a: 255 });
95
+
96
+ if (mask) {
97
+ ctx.putImageData(mask, 0, 0);
98
+ ctx.globalCompositeOperation = 'source-in';
99
+
100
+ // 3. Drawing the Background
101
+ if (backgroundImage.complete) {
102
+ ctx.drawImage(backgroundImage, 0, 0, canvasElement.width, canvasElement.height);
103
+ } else {
104
+ // If the image is not loaded yet, wait for it to load and then draw
105
+ backgroundImage.onload = () => {
106
+ ctx.drawImage(backgroundImage, 0, 0, canvasElement.width, canvasElement.height);
107
+ };
108
+ }
109
+
110
+ // Draw the mask (segmentation)
111
+ ctx.globalCompositeOperation = 'destination-over';
112
+ ctx.drawImage(videoElement, 0, 0, canvasElement.width, canvasElement.height);
113
+ ctx.globalCompositeOperation = 'source-over';
114
+
115
+ // Add a delay to control the frame rate (adjust as needed) less CPU intensive
116
+ // await new Promise((resolve) => setTimeout(resolve, 100));
117
+
118
+ // Continue updating the canvas
119
+ requestAnimationFrame(updateCanvas);
120
+ }
121
+ }
122
+ }
123
+ // Start update canvas
124
+ updateCanvas();
125
+ } catch (error) {
126
+ stopVirtualBackground();
127
+ displayError(error);
128
+ }
129
+ }
130
+
131
+ // Function to stop the virtual background
132
+ function stopVirtualBackground() {
133
+ isVirtual = false;
134
+ videoElement.hidden = false;
135
+ canvasElement.hidden = true;
136
+ display(canvasElement, 'none');
137
+
138
+ // Hide the stop button and show the start button
139
+ startButton.style.display = 'block';
140
+ stopButton.style.display = 'none';
141
+ }
142
+
143
+ // Helper function to set the display style of an element
144
+ function display(element, style) {
145
+ element.style.display = style;
146
+ }
147
+
148
+ // Helper function to display errors
149
+ function displayError(error){
150
+ console.error(error);
151
+ // Display error message in the <p> element
152
+ errorText.textContent = 'An error occurred: ' + error.message;
153
+ }
154
+
155
+ // Add click event listeners to the buttons
156
+ startButton.addEventListener('click', startVirtualBackground);
157
+ stopButton.addEventListener('click', stopVirtualBackground);
158
+
159
+ // Start video stream
160
+ startWebCamStream();