manmohanai commited on
Commit
ef180d4
·
verified ·
1 Parent(s): 7608f4e

still not working. lets start capturing expression on live video as we were doing before. - Follow Up Deployment

Browse files
Files changed (1) hide show
  1. index.html +146 -91
index.html CHANGED
@@ -11,12 +11,10 @@
11
  position: relative;
12
  width: 100%;
13
  max-width: 640px;
14
- height: 480px;
15
  margin: 0 auto;
16
  border-radius: 12px;
17
  overflow: hidden;
18
  box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
19
- background-color: #000;
20
  }
21
 
22
  .loading-spinner {
@@ -29,21 +27,6 @@
29
  margin: 0 auto;
30
  }
31
 
32
- #cameraStatus {
33
- position: absolute;
34
- top: 0;
35
- left: 0;
36
- right: 0;
37
- bottom: 0;
38
- display: flex;
39
- flex-direction: column;
40
- justify-content: center;
41
- align-items: center;
42
- background-color: rgba(0, 0, 0, 0.5);
43
- z-index: 10;
44
- color: white;
45
- }
46
-
47
  @keyframes spin {
48
  0% { transform: rotate(0deg); }
49
  100% { transform: rotate(360deg); }
@@ -73,7 +56,7 @@
73
  </div>
74
 
75
  <div class="camera-container bg-gray-200 aspect-video relative" id="cameraView">
76
- <video id="video" autoplay muted playsinline class="w-full h-full object-cover transform scaleX(-1)"></video>
77
  <canvas id="canvas" class="absolute top-0 left-0 w-full h-full hidden"></canvas>
78
  </div>
79
 
@@ -166,16 +149,14 @@
166
  let stream = null;
167
  let currentFacingMode = 'user'; // 'user' for front camera, 'environment' for back
168
  let isCaptured = false;
 
 
169
 
170
  // Load models and start camera
171
  async function init() {
172
  statusElement.classList.remove('hidden');
173
 
174
  try {
175
- // Check camera permissions first
176
- if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
177
- throw new Error('Camera access not supported or blocked by browser');
178
- }
179
  await Promise.all([
180
  faceapi.nets.tinyFaceDetector.loadFromUri('https://justadudewhohacks.github.io/face-api.js/models'),
181
  faceapi.nets.faceLandmark68Net.loadFromUri('https://justadudewhohacks.github.io/face-api.js/models'),
@@ -201,66 +182,120 @@
201
  stream.getTracks().forEach(track => track.stop());
202
  }
203
 
204
- const cameraStatus = document.getElementById('cameraStatus');
205
- cameraStatus.classList.remove('hidden');
206
-
207
  try {
208
  stream = await navigator.mediaDevices.getUserMedia({
209
  video: {
210
  facingMode: currentFacingMode,
211
- width: { ideal: 640 },
212
- height: { ideal: 480 }
213
  },
214
  audio: false
215
  });
216
- cameraStatus.classList.add('hidden');
217
 
218
  video.srcObject = stream;
219
  video.onloadedmetadata = () => {
220
  video.play();
 
 
 
221
  };
222
-
223
- // Add error listener for the video element
224
- video.onerror = () => {
225
- console.error("Video playback error");
226
- alert("Error starting video stream. Please refresh the page.");
227
- };
228
-
229
  } catch (error) {
230
  console.error("Camera error:", error);
231
- cameraStatus.classList.add('hidden');
232
- statusElement.innerHTML = `
233
- <div class="bg-red-50 text-red-600 p-4 rounded-lg">
234
- <p>Camera error: ${error.message}</p>
235
- <p class="mt-2">Please check:</p>
236
- <ul class="list-disc pl-5 mt-1 space-y-1">
237
- <li>Camera permissions are allowed</li>
238
- <li>No other app is using the camera</li>
239
- <li>Your device has a working camera</li>
240
- </ul>
241
- <button onclick="window.location.reload()" class="mt-3 bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded-md text-sm">
242
- Try Again
243
- </button>
244
- </div>
245
- `;
246
  }
247
  }
248
 
249
-
250
- // Continuous face detection
251
- async function detectFaces() {
252
- if (video.paused || video.ended) return;
 
 
 
 
 
 
 
 
253
 
254
- const detections = await faceapi.detectAllFaces(video, new faceapi.TinyFaceDetectorOptions())
255
  .withFaceLandmarks()
256
  .withFaceExpressions();
257
 
258
- // Clear previous face boxes
259
- document.querySelectorAll('.face-box').forEach(el => el.remove());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
260
 
261
- if (detections.length > 0) {
262
- // Update results with first face detected
263
- const expressions = detections[0].expressions;
264
  const primaryEmotion = Object.entries(expressions).reduce((a, b) => a[1] > b[1] ? a : b);
265
 
266
  // Display results
@@ -288,30 +323,14 @@
288
  });
289
 
290
  resultsElement.classList.remove('hidden');
291
- updateEmotionSuggestions(expressions);
292
-
293
- // Draw face boxes on video
294
- const displaySize = { width: video.width, height: video.height };
295
- const resizedDetections = faceapi.resizeResults(detections, displaySize);
296
-
297
- resizedDetections.forEach(detection => {
298
- const box = detection.detection.box;
299
- const faceBox = document.createElement('div');
300
- faceBox.className = 'face-box';
301
- Object.assign(faceBox.style, {
302
- width: `${box.width}px`,
303
- height: `${box.height}px`,
304
- left: `${box.x}px`,
305
- top: `${box.y}px`
306
- });
307
- video.parentNode.appendChild(faceBox);
308
- });
309
  } else {
310
- resultsElement.classList.add('hidden');
 
311
  }
312
 
313
- requestAnimationFrame(detectFaces);
314
- }
315
 
316
  // Switch between front and back camera
317
  switchCameraBtn.addEventListener('click', () => {
@@ -319,6 +338,24 @@
319
  startCamera();
320
  });
321
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
322
  // Speech synthesis
323
  function speak(text) {
324
  if ('speechSynthesis' in window && !isMuted) {
@@ -342,6 +379,10 @@
342
  return "Very Low";
343
  }
344
 
 
 
 
 
345
 
346
  // Emotion-based suggestions
347
  const emotionTips = {
@@ -384,12 +425,35 @@
384
 
385
  function updateEmotionSuggestions(expressions) {
386
  const primaryEmotion = Object.entries(expressions).reduce((a, b) => a[1] > b[1] ? a : b)[0];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
387
  const suggestionsContainer = document.getElementById('emotionSuggestions');
388
  const tips = emotionTips[primaryEmotion] || [
389
  "Your emotions are valid. Take a moment to acknowledge how you feel."
390
  ];
391
 
392
  const randomTip = tips[Math.floor(Math.random() * tips.length)];
 
393
 
394
  suggestionsContainer.innerHTML = `
395
  <div class="bg-indigo-50 rounded-lg p-4">
@@ -453,16 +517,7 @@
453
  });
454
 
455
  // Initialize the app
456
- document.addEventListener('DOMContentLoaded', async () => {
457
- await init();
458
- // Start face detection loop once video is playing
459
- video.addEventListener('play', () => {
460
- // Set video dimensions for face detection
461
- video.width = video.videoWidth;
462
- video.height = video.videoHeight;
463
- detectFaces();
464
- });
465
- });
466
  </script>
467
  <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=manmohanai/faceimotion" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
468
  </html>
 
11
  position: relative;
12
  width: 100%;
13
  max-width: 640px;
 
14
  margin: 0 auto;
15
  border-radius: 12px;
16
  overflow: hidden;
17
  box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
 
18
  }
19
 
20
  .loading-spinner {
 
27
  margin: 0 auto;
28
  }
29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  @keyframes spin {
31
  0% { transform: rotate(0deg); }
32
  100% { transform: rotate(360deg); }
 
56
  </div>
57
 
58
  <div class="camera-container bg-gray-200 aspect-video relative" id="cameraView">
59
+ <video id="video" autoplay muted playsinline class="w-full h-full object-cover"></video>
60
  <canvas id="canvas" class="absolute top-0 left-0 w-full h-full hidden"></canvas>
61
  </div>
62
 
 
149
  let stream = null;
150
  let currentFacingMode = 'user'; // 'user' for front camera, 'environment' for back
151
  let isCaptured = false;
152
+ let isCooldown = false;
153
+ let cooldownEndTime = 0;
154
 
155
  // Load models and start camera
156
  async function init() {
157
  statusElement.classList.remove('hidden');
158
 
159
  try {
 
 
 
 
160
  await Promise.all([
161
  faceapi.nets.tinyFaceDetector.loadFromUri('https://justadudewhohacks.github.io/face-api.js/models'),
162
  faceapi.nets.faceLandmark68Net.loadFromUri('https://justadudewhohacks.github.io/face-api.js/models'),
 
182
  stream.getTracks().forEach(track => track.stop());
183
  }
184
 
 
 
 
185
  try {
186
  stream = await navigator.mediaDevices.getUserMedia({
187
  video: {
188
  facingMode: currentFacingMode,
189
+ width: { ideal: 1280 },
190
+ height: { ideal: 720 }
191
  },
192
  audio: false
193
  });
 
194
 
195
  video.srcObject = stream;
196
  video.onloadedmetadata = () => {
197
  video.play();
198
+ if (!isCaptured) {
199
+ detectEmotions();
200
+ }
201
  };
 
 
 
 
 
 
 
202
  } catch (error) {
203
  console.error("Camera error:", error);
204
+ alert("Could not access the camera. Please make sure you've granted camera permissions.");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  }
206
  }
207
 
208
+ // Detect emotions in real-time
209
+ async function detectEmotions() {
210
+ if (isCaptured || isCooldown) {
211
+ // Show remaining cooldown time if active
212
+ if (isCooldown) {
213
+ const remaining = Math.ceil((cooldownEndTime - Date.now()) / 1000);
214
+ if (remaining > 0) {
215
+ console.log(`Cooldown: ${remaining}s remaining`);
216
+ }
217
+ }
218
+ return;
219
+ }
220
 
221
+ const detection = await faceapi.detectSingleFace(video, new faceapi.TinyFaceDetectorOptions())
222
  .withFaceLandmarks()
223
  .withFaceExpressions();
224
 
225
+ // Clear previous drawings
226
+ const canvas = faceapi.createCanvasFromMedia(video);
227
+ const context = canvas.getContext('2d');
228
+ context.clearRect(0, 0, canvas.width, canvas.height);
229
+
230
+ // Resize canvas to match video dimensions
231
+ faceapi.matchDimensions(canvas, {
232
+ width: video.clientWidth,
233
+ height: video.clientHeight
234
+ });
235
+
236
+ if (detection) {
237
+ // Draw face detection box
238
+ const resizedDetections = faceapi.resizeResults(detection, {
239
+ width: video.clientWidth,
240
+ height: video.clientHeight
241
+ });
242
+
243
+ // Draw face box
244
+ const box = resizedDetections.detection.box;
245
+ const drawBox = new faceapi.draw.DrawBox(box, {
246
+ label: `Confidence: ${Math.round(detection.detection.score * 100)}%`
247
+ });
248
+ drawBox.draw(canvas);
249
+
250
+ // Draw face landmarks
251
+ faceapi.draw.drawFaceLandmarks(canvas, resizedDetections);
252
+
253
+ // Get dominant emotion
254
+ const expressions = detection.expressions;
255
+ const primaryEmotion = Object.entries(expressions).reduce((a, b) => a[1] > b[1] ? a : b);
256
+
257
+ }
258
+
259
+ // Update emotion suggestions
260
+ if (detection) {
261
+ updateEmotionSuggestions(detection.expressions);
262
+ // Start cooldown period
263
+ isCooldown = true;
264
+ cooldownEndTime = Date.now() + 10000; // 10 seconds from now
265
+ setTimeout(() => {
266
+ isCooldown = false;
267
+ detectEmotions();
268
+ }, 10000);
269
+ } else {
270
+ // Schedule next detection normally if no face detected
271
+ setTimeout(() => detectEmotions(), 300);
272
+ }
273
+ }
274
+
275
+ // Capture photo and analyze emotions
276
+ captureBtn.addEventListener('click', async () => {
277
+ if (isCaptured) return;
278
+
279
+ isCaptured = true;
280
+ captureBtn.disabled = true;
281
+
282
+ // Create canvas from video
283
+ canvas.width = video.clientWidth;
284
+ canvas.height = video.clientHeight;
285
+ const context = canvas.getContext('2d');
286
+ context.drawImage(video, 0, 0, canvas.width, canvas.height);
287
+
288
+ // Hide video and show canvas
289
+ video.classList.add('hidden');
290
+ canvas.classList.remove('hidden');
291
+
292
+ // Analyze the captured image
293
+ const detection = await faceapi.detectSingleFace(canvas, new faceapi.TinyFaceDetectorOptions())
294
+ .withFaceLandmarks()
295
+ .withFaceExpressions();
296
 
297
+ if (detection) {
298
+ const expressions = detection.expressions;
 
299
  const primaryEmotion = Object.entries(expressions).reduce((a, b) => a[1] > b[1] ? a : b);
300
 
301
  // Display results
 
323
  });
324
 
325
  resultsElement.classList.remove('hidden');
326
+ resetBtn.classList.remove('hidden');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
327
  } else {
328
+ alert("No face detected in the captured image. Please try again.");
329
+ resetApp();
330
  }
331
 
332
+ captureBtn.disabled = false;
333
+ });
334
 
335
  // Switch between front and back camera
336
  switchCameraBtn.addEventListener('click', () => {
 
338
  startCamera();
339
  });
340
 
341
+ // Reset the app to initial state
342
+ resetBtn.addEventListener('click', resetApp);
343
+
344
+ function resetApp() {
345
+ isCaptured = false;
346
+ video.classList.remove('hidden');
347
+ canvas.classList.add('hidden');
348
+ resultsElement.classList.add('hidden');
349
+ resetBtn.classList.add('hidden');
350
+
351
+ // Clear canvas
352
+ const context = canvas.getContext('2d');
353
+ context.clearRect(0, 0, canvas.width, canvas.height);
354
+
355
+ // Restart emotion detection
356
+ detectEmotions();
357
+ }
358
+
359
  // Speech synthesis
360
  function speak(text) {
361
  if ('speechSynthesis' in window && !isMuted) {
 
379
  return "Very Low";
380
  }
381
 
382
+ // Track last emotion and values
383
+ let lastEmotion = null;
384
+ let lastEmotionValues = {};
385
+ const EMOTION_CHANGE_THRESHOLD = 0.2; // Minimum change to consider significant
386
 
387
  // Emotion-based suggestions
388
  const emotionTips = {
 
425
 
426
  function updateEmotionSuggestions(expressions) {
427
  const primaryEmotion = Object.entries(expressions).reduce((a, b) => a[1] > b[1] ? a : b)[0];
428
+
429
+ // Check if emotion values have changed significantly
430
+ let hasSignificantChange = false;
431
+ if (lastEmotionValues) {
432
+ for (const [emotion, value] of Object.entries(expressions)) {
433
+ if (Math.abs(value - (lastEmotionValues[emotion] || 0)) > EMOTION_CHANGE_THRESHOLD) {
434
+ hasSignificantChange = true;
435
+ break;
436
+ }
437
+ }
438
+ } else {
439
+ hasSignificantChange = true; // First detection
440
+ }
441
+
442
+ // Only update if emotion changed significantly
443
+ if (!hasSignificantChange && primaryEmotion === lastEmotion) {
444
+ return;
445
+ }
446
+
447
+ lastEmotion = primaryEmotion;
448
+ lastEmotionValues = {...expressions};
449
+
450
  const suggestionsContainer = document.getElementById('emotionSuggestions');
451
  const tips = emotionTips[primaryEmotion] || [
452
  "Your emotions are valid. Take a moment to acknowledge how you feel."
453
  ];
454
 
455
  const randomTip = tips[Math.floor(Math.random() * tips.length)];
456
+ speak(`You seem ${primaryEmotion}. ${randomTip}`);
457
 
458
  suggestionsContainer.innerHTML = `
459
  <div class="bg-indigo-50 rounded-lg p-4">
 
517
  });
518
 
519
  // Initialize the app
520
+ document.addEventListener('DOMContentLoaded', init);
 
 
 
 
 
 
 
 
 
521
  </script>
522
  <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=manmohanai/faceimotion" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
523
  </html>