TDN-M commited on
Commit
d02188e
·
verified ·
1 Parent(s): 0981e2a

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +153 -61
index.html CHANGED
@@ -8,13 +8,14 @@
8
  <script src="https://cdn.jsdelivr.net/npm/@mediapipe/selfie_segmentation/selfie_segmentation.js" crossorigin="anonymous"></script>
9
  </head>
10
  <body>
11
- <div class="card">
12
  <h1>OBS AI Background Remover</h1>
13
  <div>
14
  <h2>Live Preview</h2>
15
  <video id="videoPreview" autoplay playsinline></video>
16
- <canvas id="canvasOutput" width="1920" height="1080" style="display: none;"></canvas>
17
  <p id="status">Loading AI Model...</p>
 
18
  </div>
19
 
20
  <div>
@@ -38,7 +39,7 @@
38
  <div>
39
  <input type="radio" name="bgType" id="bgCustomImage" value="customImage">
40
  <label for="bgCustomImage">Custom Image</label>
41
- <input type pigeonhole="file" id="customImageInput" accept="image/*">
42
  </div>
43
  <div>
44
  <input type="radio" name="bgType" id="bgCustomVideo" value="customVideo">
@@ -60,13 +61,33 @@
60
  </select>
61
  </div>
62
  <div>
63
- <input type="checkbox" id="enableBgRemoval">
64
  <label for="enableBgRemoval">Enable Background Removal</label>
65
  </div>
66
  <div>
67
  <input type="checkbox" id="showPreview" checked>
68
  <label for="showPreview">Show Preview</label>
69
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  </div>
71
 
72
  <div>
@@ -85,21 +106,36 @@
85
  </div>
86
  </div>
87
 
 
 
88
  <script>
 
89
  const videoElement = document.getElementById('videoPreview');
90
  const canvasElement = document.getElementById('canvasOutput');
91
  const canvasCtx = canvasElement.getContext('2d');
92
  const statusElement = document.getElementById('status');
 
93
  const videoSourceSelect = document.getElementById('videoSource');
94
  const enableBgRemoval = document.getElementById('enableBgRemoval');
95
  const showPreview = document.getElementById('showPreview');
 
96
  const processingTimeElement = document.getElementById('processingTime');
97
  const fpsElement = document.getElementById('fps');
98
  const copyUrlButton = document.getElementById('copyUrl');
 
 
 
 
 
 
 
 
99
  let selfieSegmentation;
100
- let lastFrameTime = 0;
 
 
101
  let frameCount = 0;
102
- let lastFpsUpdate = 0;
103
 
104
  // Initialize MediaPipe Selfie Segmentation
105
  async function initModel() {
@@ -107,7 +143,8 @@
107
  locateFile: (file) => `https://cdn.jsdelivr.net/npm/@mediapipe/selfie_segmentation/${file}`
108
  });
109
  selfieSegmentation.setOptions({
110
- modelSelection: 1, // 0 for general, 1 for landscape
 
111
  });
112
  selfieSegmentation.onResults(onResults);
113
  await selfieSegmentation.initialize();
@@ -116,42 +153,66 @@
116
 
117
  // Populate video sources
118
  async function populateVideoSources() {
119
- const devices = await navigator.mediaDevices.enumerateDevices();
120
- videoSourceSelect.innerHTML = '<option value="">Select a video source</option>';
121
- devices.forEach(device => {
122
- if (device.kind === 'videoinput') {
123
- const option = document.createElement('option');
124
- option.value = device.deviceId;
125
- option.text = device.label || `Camera ${videoSourceSelect.options.length + 1}`;
126
- videoSourceSelect.appendChild(option);
127
- }
128
- });
 
 
 
 
 
129
  }
130
 
131
  // Start video stream
132
  async function startVideo(deviceId) {
133
- const stream = await navigator.mediaDevices.getUserMedia({
134
- video: { deviceId: deviceId ? { exact: deviceId } : undefined }
135
- });
136
- videoElement.srcObject = stream;
137
- videoElement.play();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  }
139
 
140
  // Process video frames
141
  function onResults(results) {
 
 
 
 
142
  if (!enableBgRemoval.checked) {
143
  canvasCtx.drawImage(videoElement, 0, 0, canvasElement.width, canvasElement.height);
144
- updatePerformance();
 
145
  return;
146
  }
147
 
148
- canvasCtx.save();
149
- canvasCtx.clearRect(0, 0, canvasElement.width, canvasElement.height);
150
-
151
- // Draw segmented background
152
  const bgType = document.querySelector('input[name="bgType"]:checked').value;
 
153
  if (bgType === 'transparent') {
154
- canvasCtx.globalCompositeOperation = 'destination-over';
155
  canvasCtx.fillStyle = 'rgba(0, 0, 0, 0)';
156
  canvasCtx.fillRect(0, 0, canvasElement.width, canvasElement.height);
157
  } else if (bgType === 'black') {
@@ -163,41 +224,33 @@
163
  } else if (bgType === 'green') {
164
  canvasCtx.fillStyle = 'green';
165
  canvasCtx.fillRect(0, 0, canvasElement.width, canvasElement.height);
166
- } else if (bgType === 'customImage') {
167
- const img = document.getElementById('customImageInput').files[0];
168
- if (img) {
169
- const image = new Image();
170
- image.src = URL.createObjectURL(img);
171
- canvasCtx.drawImage(image, 0, 0, canvasElement.width, canvasElement.height);
172
- }
173
- } else if (bgType === 'customVideo') {
174
- const vid = document.getElementById('customVideoInput').files[0];
175
- if (vid) {
176
- const video = document.createElement('video');
177
- video.src = URL.createObjectURL(vid);
178
- video.loop = true;
179
- video.play();
180
- canvasCtx.drawImage(video, 0, 0, canvasElement.width, canvasElement.height);
181
- }
182
  } else if (bgType === 'blur') {
183
- canvasCtx.filter = 'blur(10px)';
184
  canvasCtx.drawImage(videoElement, 0, 0, canvasElement.width, canvasElement.height);
185
  canvasCtx.filter = 'none';
186
  }
187
 
188
- // Draw foreground (person)
189
  canvasCtx.globalCompositeOperation = 'destination-atop';
 
190
  canvasCtx.drawImage(results.segmentationMask, 0, 0, canvasElement.width, canvasElement.height);
 
 
191
  canvasCtx.globalCompositeOperation = 'source-over';
 
192
  canvasCtx.drawImage(videoElement, 0, 0, canvasElement.width, canvasElement.height);
 
193
  canvasCtx.restore();
194
 
195
- updatePerformance();
196
  }
197
 
198
  // Update performance metrics
199
- function updatePerformance() {
200
- const now = performance.now();
201
  frameCount++;
202
  if (now - lastFpsUpdate > 1000) {
203
  const fps = frameCount / ((now - lastFpsUpdate) / 1000);
@@ -209,13 +262,13 @@
209
  lastFrameTime = now;
210
  }
211
 
212
- // Copy URL
213
- copyUrlButton.addEventListener('click', () => {
214
- const url = window.location.href;
215
- navigator.clipboard.writeText(url).then(() => {
216
- alert('URL copied to clipboard!');
217
- });
218
- });
219
 
220
  // Event listeners
221
  videoSourceSelect.addEventListener('change', () => {
@@ -223,17 +276,53 @@
223
  });
224
 
225
  enableBgRemoval.addEventListener('change', () => {
 
226
  canvasElement.style.display = enableBgRemoval.checked ? 'block' : 'none';
227
- videoElement.style.display = !enableBgRemoval.checked ? 'block' : 'none';
228
  });
229
 
230
  showPreview.addEventListener('change', () => {
231
- document.querySelector('.card').style.display = showPreview.checked ? 'block' : 'none';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
  });
233
 
234
  // Process video frames
235
  async function processFrame() {
236
- if (enableBgRemoval.checked) {
237
  await selfieSegmentation.send({ image: videoElement });
238
  }
239
  requestAnimationFrame(processFrame);
@@ -243,11 +332,14 @@
243
  async function init() {
244
  await initModel();
245
  await populateVideoSources();
246
- await startVideo();
247
  processFrame();
248
  }
249
 
250
- init();
 
 
 
251
  </script>
252
  </body>
253
  </html>
 
8
  <script src="https://cdn.jsdelivr.net/npm/@mediapipe/selfie_segmentation/selfie_segmentation.js" crossorigin="anonymous"></script>
9
  </head>
10
  <body>
11
+ <div class="card" id="controlPanel">
12
  <h1>OBS AI Background Remover</h1>
13
  <div>
14
  <h2>Live Preview</h2>
15
  <video id="videoPreview" autoplay playsinline></video>
16
+ <canvas id="canvasOutput" width="1920" height="1080"></canvas>
17
  <p id="status">Loading AI Model...</p>
18
+ <p id="noVideo" style="display: none;">No video source selected</p>
19
  </div>
20
 
21
  <div>
 
39
  <div>
40
  <input type="radio" name="bgType" id="bgCustomImage" value="customImage">
41
  <label for="bgCustomImage">Custom Image</label>
42
+ <input type="file" id="customImageInput" accept="image/*">
43
  </div>
44
  <div>
45
  <input type="radio" name="bgType" id="bgCustomVideo" value="customVideo">
 
61
  </select>
62
  </div>
63
  <div>
64
+ <input type="checkbox" id="enableBgRemoval" checked>
65
  <label for="enableBgRemoval">Enable Background Removal</label>
66
  </div>
67
  <div>
68
  <input type="checkbox" id="showPreview" checked>
69
  <label for="showPreview">Show Preview</label>
70
  </div>
71
+ <div>
72
+ <label for="modelQuality">AI Model Quality</label>
73
+ <input type="range" id="modelQuality" min="0" max="1" value="1" step="1">
74
+ <span id="modelQualityLabel">High Quality (Slow)</span>
75
+ </div>
76
+ <div>
77
+ <label for="edgeSmoothness">Edge Smoothness</label>
78
+ <input type="range" id="edgeSmoothness" min="0" max="1" value="0.5" step="0.1">
79
+ <span id="edgeSmoothnessLabel">Medium</span>
80
+ </div>
81
+ <div>
82
+ <label for="bgBlur">Background Blur</label>
83
+ <input type="range" id="bgBlur" min="0" max="20" value="0" step="1">
84
+ <span id="bgBlurLabel">Off</span>
85
+ </div>
86
+ <div>
87
+ <label for="fgBrightness">Foreground Brightness</label>
88
+ <input type="range" id="fgBrightness" min="0.5" max="1.5" value="1" step="0.1">
89
+ <span id="fgBrightnessLabel">Normal</span>
90
+ </div>
91
  </div>
92
 
93
  <div>
 
106
  </div>
107
  </div>
108
 
109
+ <p>Made with DeepSite - 🧬 Remix</p>
110
+
111
  <script>
112
+ // Initialize elements
113
  const videoElement = document.getElementById('videoPreview');
114
  const canvasElement = document.getElementById('canvasOutput');
115
  const canvasCtx = canvasElement.getContext('2d');
116
  const statusElement = document.getElementById('status');
117
+ const noVideoElement = document.getElementById('noVideo');
118
  const videoSourceSelect = document.getElementById('videoSource');
119
  const enableBgRemoval = document.getElementById('enableBgRemoval');
120
  const showPreview = document.getElementById('showPreview');
121
+ const controlPanel = document.getElementById('controlPanel');
122
  const processingTimeElement = document.getElementById('processingTime');
123
  const fpsElement = document.getElementById('fps');
124
  const copyUrlButton = document.getElementById('copyUrl');
125
+ const modelQuality = document.getElementById('modelQuality');
126
+ const edgeSmoothness = document.getElementById('edgeSmoothness');
127
+ const bgBlur = document.getElementById('bgBlur');
128
+ const fgBrightness = document.getElementById('fgBrightness');
129
+ const modelQualityLabel = document.getElementById('modelQualityLabel');
130
+ const edgeSmoothnessLabel = document.getElementById('edgeSmoothnessLabel');
131
+ const bgBlurLabel = document.getElementById('bgBlurLabel');
132
+ const fgBrightnessLabel = document.getElementById('fgBrightnessLabel');
133
  let selfieSegmentation;
134
+ let customImage = null;
135
+ let customVideo = null;
136
+ let lastFrameTime = performance.now();
137
  let frameCount = 0;
138
+ let lastFpsUpdate = performance.now();
139
 
140
  // Initialize MediaPipe Selfie Segmentation
141
  async function initModel() {
 
143
  locateFile: (file) => `https://cdn.jsdelivr.net/npm/@mediapipe/selfie_segmentation/${file}`
144
  });
145
  selfieSegmentation.setOptions({
146
+ modelSelection: parseInt(modelQuality.value), // 0: General, 1: Landscape
147
+ selfieMode: true
148
  });
149
  selfieSegmentation.onResults(onResults);
150
  await selfieSegmentation.initialize();
 
153
 
154
  // Populate video sources
155
  async function populateVideoSources() {
156
+ try {
157
+ const devices = await navigator.mediaDevices.enumerateDevices();
158
+ videoSourceSelect.innerHTML = '<option value="">Select a video source</option>';
159
+ devices.forEach(device => {
160
+ if (device.kind === 'videoinput') {
161
+ const option = document.createElement('option');
162
+ option.value = device.deviceId;
163
+ option.text = device.label || `Camera ${videoSourceSelect.options.length + 1}`;
164
+ videoSourceSelect.appendChild(option);
165
+ }
166
+ });
167
+ } catch (err) {
168
+ statusElement.textContent = 'Error accessing video devices';
169
+ console.error(err);
170
+ }
171
  }
172
 
173
  // Start video stream
174
  async function startVideo(deviceId) {
175
+ try {
176
+ if (videoElement.srcObject) {
177
+ videoElement.srcObject.getTracks().forEach(track => track.stop());
178
+ }
179
+ if (!deviceId) {
180
+ noVideoElement.style.display = 'block';
181
+ videoElement.style.display = 'none';
182
+ canvasElement.style.display = 'none';
183
+ return;
184
+ }
185
+ const stream = await navigator.mediaDevices.getUserMedia({
186
+ video: { deviceId: deviceId ? { exact: deviceId } : undefined }
187
+ });
188
+ videoElement.srcObject = stream;
189
+ videoElement.play();
190
+ noVideoElement.style.display = 'none';
191
+ videoElement.style.display = enableBgRemoval.checked ? 'none' : 'block';
192
+ canvasElement.style.display = enableBgRemoval.checked ? 'block' : 'none';
193
+ } catch (err) {
194
+ statusElement.textContent = 'Error starting video stream';
195
+ console.error(err);
196
+ }
197
  }
198
 
199
  // Process video frames
200
  function onResults(results) {
201
+ const now = performance.now();
202
+ canvasCtx.save();
203
+ canvasCtx.clearRect(0, 0, canvasElement.width, canvasElement.height);
204
+
205
  if (!enableBgRemoval.checked) {
206
  canvasCtx.drawImage(videoElement, 0, 0, canvasElement.width, canvasElement.height);
207
+ canvasCtx.restore();
208
+ updatePerformance(now);
209
  return;
210
  }
211
 
212
+ // Draw background
 
 
 
213
  const bgType = document.querySelector('input[name="bgType"]:checked').value;
214
+ canvasCtx.globalCompositeOperation = 'destination-over';
215
  if (bgType === 'transparent') {
 
216
  canvasCtx.fillStyle = 'rgba(0, 0, 0, 0)';
217
  canvasCtx.fillRect(0, 0, canvasElement.width, canvasElement.height);
218
  } else if (bgType === 'black') {
 
224
  } else if (bgType === 'green') {
225
  canvasCtx.fillStyle = 'green';
226
  canvasCtx.fillRect(0, 0, canvasElement.width, canvasElement.height);
227
+ } else if (bgType === 'customImage' && customImage) {
228
+ canvasCtx.drawImage(customImage, 0, 0, canvasElement.width, canvasElement.height);
229
+ } else if (bgType === 'customVideo' && customVideo) {
230
+ canvasCtx.drawImage(customVideo, 0, 0, canvasElement.width, canvasElement.height);
 
 
 
 
 
 
 
 
 
 
 
 
231
  } else if (bgType === 'blur') {
232
+ canvasCtx.filter = `blur(${bgBlur.value}px)`;
233
  canvasCtx.drawImage(videoElement, 0, 0, canvasElement.width, canvasElement.height);
234
  canvasCtx.filter = 'none';
235
  }
236
 
237
+ // Apply edge smoothness (simulated via alpha threshold)
238
  canvasCtx.globalCompositeOperation = 'destination-atop';
239
+ canvasCtx.globalAlpha = parseFloat(edgeSmoothness.value);
240
  canvasCtx.drawImage(results.segmentationMask, 0, 0, canvasElement.width, canvasElement.height);
241
+
242
+ // Draw foreground with brightness adjustment
243
  canvasCtx.globalCompositeOperation = 'source-over';
244
+ canvasCtx.filter = `brightness(${fgBrightness.value})`;
245
  canvasCtx.drawImage(videoElement, 0, 0, canvasElement.width, canvasElement.height);
246
+ canvasCtx.filter = 'none';
247
  canvasCtx.restore();
248
 
249
+ updatePerformance(now);
250
  }
251
 
252
  // Update performance metrics
253
+ function updatePerformance(now) {
 
254
  frameCount++;
255
  if (now - lastFpsUpdate > 1000) {
256
  const fps = frameCount / ((now - lastFpsUpdate) / 1000);
 
262
  lastFrameTime = now;
263
  }
264
 
265
+ // Update slider labels
266
+ function updateSliderLabels() {
267
+ modelQualityLabel.textContent = modelQuality.value === '0' ? 'Fast (Low Quality)' : 'High Quality (Slow)';
268
+ edgeSmoothnessLabel.textContent = edgeSmoothness.value <= 0.3 ? 'Low' : edgeSmoothness.value <= 0.7 ? 'Medium' : 'High';
269
+ bgBlurLabel.textContent = bgBlur.value === '0' ? 'Off' : `${bgBlur.value}px`;
270
+ fgBrightnessLabel.textContent = fgBrightness.value < 1 ? 'Darker' : fgBrightness.value > 1 ? 'Brighter' : 'Normal';
271
+ }
272
 
273
  // Event listeners
274
  videoSourceSelect.addEventListener('change', () => {
 
276
  });
277
 
278
  enableBgRemoval.addEventListener('change', () => {
279
+ videoElement.style.display = enableBgRemoval.checked ? 'none' : 'block';
280
  canvasElement.style.display = enableBgRemoval.checked ? 'block' : 'none';
 
281
  });
282
 
283
  showPreview.addEventListener('change', () => {
284
+ controlPanel.style.display = showPreview.checked ? 'block' : 'none';
285
+ });
286
+
287
+ modelQuality.addEventListener('input', () => {
288
+ updateSliderLabels();
289
+ selfieSegmentation.setOptions({ modelSelection: parseInt(modelQuality.value) });
290
+ });
291
+
292
+ edgeSmoothness.addEventListener('input', updateSliderLabels);
293
+ bgBlur.addEventListener('input', updateSliderLabels);
294
+ fgBrightness.addEventListener('input', updateSliderLabels);
295
+
296
+ document.getElementById('customImageInput').addEventListener('change', (e) => {
297
+ if (e.target.files[0]) {
298
+ customImage = new Image();
299
+ customImage.src = URL.createObjectURL(e.target.files[0]);
300
+ document.getElementById('bgCustomImage').checked = true;
301
+ }
302
+ });
303
+
304
+ document.getElementById('customVideoInput').addEventListener('change', (e) => {
305
+ if (e.target.files[0]) {
306
+ customVideo = document.createElement('video');
307
+ customVideo.src = URL.createObjectURL(e.target.files[0]);
308
+ customVideo.loop = true;
309
+ customVideo.play();
310
+ document.getElementById('bgCustomVideo').checked = true;
311
+ }
312
+ });
313
+
314
+ copyUrlButton.addEventListener('click', () => {
315
+ const url = window.location.href;
316
+ navigator.clipboard.writeText(url).then(() => {
317
+ alert('URL copied to clipboard!');
318
+ }).catch(err => {
319
+ console.error('Failed to copy URL:', err);
320
+ });
321
  });
322
 
323
  // Process video frames
324
  async function processFrame() {
325
+ if (videoElement.srcObject && enableBgRemoval.checked) {
326
  await selfieSegmentation.send({ image: videoElement });
327
  }
328
  requestAnimationFrame(processFrame);
 
332
  async function init() {
333
  await initModel();
334
  await populateVideoSources();
335
+ updateSliderLabels();
336
  processFrame();
337
  }
338
 
339
+ init().catch(err => {
340
+ statusElement.textContent = 'Initialization failed';
341
+ console.error(err);
342
+ });
343
  </script>
344
  </body>
345
  </html>