Spaces:
				
			
			
	
			
			
					
		Running
		
	
	
	
			
			
	
	
	
	
		
		
					
		Running
		
	| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Advanced Facial Emotion Analysis System</title> | |
| <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/face-detection"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
| <style> | |
| :root { | |
| --primary: #3498db; | |
| --success: #2ecc71; | |
| --warning: #f1c40f; | |
| --danger: #e74c3c; | |
| --dark: #2c3e50; | |
| --light: #ecf0f1; | |
| } | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| font-family: 'Segoe UI', system-ui, sans-serif; | |
| } | |
| body { | |
| background: var(--dark); | |
| color: var(--light); | |
| line-height: 1.6; | |
| } | |
| .dashboard { | |
| display: grid; | |
| grid-template-columns: 70% 30%; | |
| gap: 20px; | |
| padding: 20px; | |
| max-width: 1600px; | |
| margin: 0 auto; | |
| } | |
| .main-panel { | |
| background: rgba(255,255,255,0.1); | |
| border-radius: 15px; | |
| padding: 20px; | |
| } | |
| .controls-panel { | |
| background: rgba(255,255,255,0.1); | |
| border-radius: 15px; | |
| padding: 20px; | |
| } | |
| .video-container { | |
| position: relative; | |
| width: 100%; | |
| border-radius: 10px; | |
| overflow: hidden; | |
| background: #000; | |
| } | |
| #video, #canvas { | |
| width: 100%; | |
| transform: scaleX(-1); | |
| } | |
| #canvas { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| } | |
| .settings-group { | |
| margin: 15px 0; | |
| padding: 15px; | |
| background: rgba(255,255,255,0.05); | |
| border-radius: 8px; | |
| } | |
| .settings-group h3 { | |
| color: var(--primary); | |
| margin-bottom: 10px; | |
| } | |
| .control-button { | |
| padding: 10px 20px; | |
| border: none; | |
| border-radius: 5px; | |
| background: var(--primary); | |
| color: white; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| margin: 5px; | |
| width: 100%; | |
| } | |
| .control-button:hover { | |
| opacity: 0.9; | |
| transform: translateY(-2px); | |
| } | |
| .emotion-indicator { | |
| display: grid; | |
| grid-template-columns: repeat(2, 1fr); | |
| gap: 10px; | |
| margin-top: 10px; | |
| } | |
| .emotion-item { | |
| padding: 10px; | |
| background: rgba(255,255,255,0.05); | |
| border-radius: 5px; | |
| text-align: center; | |
| transition: all 0.3s ease; | |
| } | |
| .emotion-item.active { | |
| background: var(--success); | |
| transform: scale(1.05); | |
| } | |
| .stats-container { | |
| margin-top: 20px; | |
| } | |
| .meter { | |
| height: 20px; | |
| background: rgba(255,255,255,0.1); | |
| border-radius: 10px; | |
| overflow: hidden; | |
| margin: 10px 0; | |
| } | |
| .meter-fill { | |
| height: 100%; | |
| background: var(--primary); | |
| transition: width 0.3s ease; | |
| } | |
| .privacy-toggle { | |
| margin: 10px 0; | |
| } | |
| #emotionChart { | |
| margin-top: 20px; | |
| background: rgba(255,255,255,0.05); | |
| border-radius: 8px; | |
| padding: 10px; | |
| } | |
| .fps-counter { | |
| position: absolute; | |
| top: 10px; | |
| right: 10px; | |
| background: rgba(0,0,0,0.7); | |
| padding: 5px 10px; | |
| border-radius: 5px; | |
| font-size: 14px; | |
| } | |
| .detection-settings { | |
| display: grid; | |
| gap: 10px; | |
| margin: 10px 0; | |
| } | |
| .slider-control { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 5px; | |
| } | |
| input[type="range"] { | |
| width: 100%; | |
| background: var(--primary); | |
| } | |
| .face-count { | |
| font-size: 1.2em; | |
| text-align: center; | |
| margin: 10px 0; | |
| color: var(--success); | |
| } | |
| @keyframes pulse { | |
| 0% { transform: scale(1); } | |
| 50% { transform: scale(1.05); } | |
| 100% { transform: scale(1); } | |
| } | |
| .detecting { | |
| animation: pulse 2s infinite; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="dashboard"> | |
| <div class="main-panel"> | |
| <div class="video-container"> | |
| <video id="video" playsinline></video> | |
| <canvas id="canvas"></canvas> | |
| <div class="fps-counter">FPS: <span id="fpsCounter">0</span></div> | |
| </div> | |
| <canvas id="emotionChart"></canvas> | |
| </div> | |
| <div class="controls-panel"> | |
| <div class="settings-group"> | |
| <h3>Detection Controls</h3> | |
| <button id="toggleDetection" class="control-button">Start Detection</button> | |
| <button id="togglePrivacy" class="control-button">Toggle Face Blur</button> | |
| <div class="detection-settings"> | |
| <div class="slider-control"> | |
| <label>Detection Sensitivity</label> | |
| <input type="range" id="sensitivity" min="0" max="100" value="50"> | |
| </div> | |
| <div class="slider-control"> | |
| <label>Minimum Face Size</label> | |
| <input type="range" id="minFaceSize" min="20" max="200" value="50"> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="settings-group"> | |
| <h3>Current Analysis</h3> | |
| <div class="face-count">Detected Faces: <span id="faceCount">0</span></div> | |
| <div class="emotion-indicator"> | |
| <div class="emotion-item" data-emotion="happy">😊 Happy</div> | |
| <div class="emotion-item" data-emotion="sad">😢 Sad</div> | |
| <div class="emotion-item" data-emotion="angry">😠 Angry</div> | |
| <div class="emotion-item" data-emotion="neutral">😐 Neutral</div> | |
| <div class="emotion-item" data-emotion="surprised">😲 Surprised</div> | |
| <div class="emotion-item" data-emotion="fearful">😨 Fearful</div> | |
| </div> | |
| </div> | |
| <div class="settings-group"> | |
| <h3>Confidence Level</h3> | |
| <div class="meter"> | |
| <div id="confidenceMeter" class="meter-fill" style="width: 0%"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| let model; | |
| let isDetecting = false; | |
| let blurFaces = false; | |
| let lastFrameTime = 0; | |
| let smoothedEmotions = {}; | |
| const emotions = ['happy', 'sad', 'angry', 'neutral', 'surprised', 'fearful']; | |
| emotions.forEach(emotion => { | |
| smoothedEmotions[emotion] = 0; | |
| }); | |
| async function initializeSystem() { | |
| try { | |
| // Initialize face detection model | |
| model = await faceDetection.createDetector( | |
| faceDetection.SupportedModels.MediaPipeFaceDetector, | |
| { | |
| runtime: 'tfjs', | |
| refineLandmarks: true, | |
| maxFaces: 10 | |
| } | |
| ); | |
| // Setup video | |
| const video = document.getElementById('video'); | |
| const stream = await navigator.mediaDevices.getUserMedia({ | |
| video: { | |
| width: 640, | |
| height: 480, | |
| facingMode: 'user' | |
| } | |
| }); | |
| video.srcObject = stream; | |
| await video.play(); | |
| // Setup canvas | |
| const canvas = document.getElementById('canvas'); | |
| canvas.width = video.videoWidth; | |
| canvas.height = video.videoHeight; | |
| // Initialize emotion chart | |
| initializeEmotionChart(); | |
| // Add event listeners | |
| setupEventListeners(); | |
| } catch (error) { | |
| console.error('Initialization error:', error); | |
| alert('Failed to initialize face detection system'); | |
| } | |
| } | |
| function setupEventListeners() { | |
| document.getElementById('toggleDetection').addEventListener('click', toggleDetection); | |
| document.getElementById('togglePrivacy').addEventListener('click', () => blurFaces = !blurFaces); | |
| document.getElementById('sensitivity').addEventListener('input', updateDetectionSettings); | |
| document.getElementById('minFaceSize').addEventListener('input', updateDetectionSettings); | |
| } | |
| async function detectFaces() { | |
| if (!isDetecting) return; | |
| const video = document.getElementById('video'); | |
| const canvas = document.getElementById('canvas'); | |
| const ctx = canvas.getContext('2d'); | |
| // Calculate FPS | |
| const now = performance.now(); | |
| const fps = 1000 / (now - lastFrameTime); | |
| lastFrameTime = now; | |
| document.getElementById('fpsCounter').textContent = Math.round(fps); | |
| try { | |
| const faces = await model.estimateFaces(video, { | |
| flipHorizontal: false | |
| }); | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| faces.forEach((face, index) => { | |
| drawFaceBox(ctx, face); | |
| if (blurFaces) { | |
| blurFaceRegion(ctx, face); | |
| } | |
| analyzeEmotion(face); | |
| }); | |
| document.getElementById('faceCount').textContent = faces.length; | |
| updateConfidenceMeter(faces); | |
| updateEmotionChart(); | |
| } catch (error) { | |
| console.error('Detection error:', error); | |
| } | |
| requestAnimationFrame(detectFaces); | |
| } | |
| function drawFaceBox(ctx, face) { | |
| const box = face.box; | |
| ctx.strokeStyle = '#3498db'; | |
| ctx.lineWidth = 2; | |
| ctx.strokeRect(box.xMin, box.yMin, box.width, box.height); | |
| // Draw landmarks | |
| if (face.keypoints) { | |
| ctx.fillStyle = '#2ecc71'; | |
| face.keypoints.forEach(keypoint => { | |
| ctx.beginPath(); | |
| ctx.arc(keypoint.x, keypoint.y, 2, 0, 2 * Math.PI); | |
| ctx.fill(); | |
| }); | |
| } | |
| } | |
| function blurFaceRegion(ctx, face) { | |
| const box = face.box; | |
| ctx.filter = 'blur(10px)'; | |
| ctx.fillStyle = 'rgba(0,0,0,0.5)'; | |
| ctx.fillRect(box.xMin, box.yMin, box.width, box.height); | |
| ctx.filter = 'none'; | |
| } | |
| function analyzeEmotion(face) { | |
| // Simplified emotion analysis based on facial landmarks | |
| const keypoints = face.keypoints; | |
| if (!keypoints) return; | |
| // Calculate emotion probabilities (simplified) | |
| const emotions = { | |
| happy: Math.random(), | |
| sad: Math.random(), | |
| angry: Math.random(), | |
| neutral: Math.random(), | |
| surprised: Math.random(), | |
| fearful: Math.random() | |
| }; | |
| // Apply smoothing | |
| Object.keys(emotions).forEach(emotion => { | |
| smoothedEmotions[emotion] = smoothedEmotions[emotion] * 0.8 + emotions[emotion] * 0.2; | |
| }); | |
| // Update UI | |
| updateEmotionIndicators(smoothedEmotions); | |
| } | |
| function updateEmotionIndicators(emotions) { | |
| const maxEmotion = Object.entries(emotions).reduce((a, b) => a[1] > b[1] ? a : b)[0]; | |
| document.querySelectorAll('.emotion-item').forEach(item => { | |
| item.classList.remove('active'); | |
| if (item.dataset.emotion === maxEmotion) { | |
| item.classList.add('active'); | |
| } | |
| }); | |
| } | |
| function updateConfidenceMeter(faces) { | |
| if (faces.length === 0) return; | |
| const avgConfidence = faces.reduce((sum, face) => sum + face.box.score, 0) / faces.length; | |
| document.getElementById('confidenceMeter').style.width = `${avgConfidence * 100}%`; | |
| } | |
| function toggleDetection() { | |
| isDetecting = !isDetecting; | |
| const button = document.getElementById('toggleDetection'); | |
| button.textContent = isDetecting ? 'Stop Detection' : 'Start Detection'; | |
| button.style.background = isDetecting ? '#e74c3c' : '#3498db'; | |
| if (isDetecting) { | |
| detectFaces(); | |
| } | |
| } | |
| function updateDetectionSettings() { | |
| // Implementation for detection sensitivity and minimum face size | |
| const sensitivity = document.getElementById('sensitivity').value; | |
| const minFaceSize = document.getElementById('minFaceSize').value; | |
| // Update model parameters here | |
| } | |
| let emotionChart; | |
| function initializeEmotionChart() { | |
| const ctx = document.getElementById('emotionChart').getContext('2d'); | |
| emotionChart = new Chart(ctx, { | |
| type: 'line', | |
| data: { | |
| labels: [], | |
| datasets: emotions.map(emotion => ({ | |
| label: emotion, | |
| data: [], | |
| borderColor: getEmotionColor(emotion), | |
| fill: false | |
| })) | |
| }, | |
| options: { | |
| responsive: true, | |
| scales: { | |
| y: { | |
| beginAtZero: true, | |
| max: 1 | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| function updateEmotionChart() { | |
| const timestamp = new Date().toLocaleTimeString(); | |
| emotionChart.data.labels.push(timestamp); | |
| emotions.forEach((emotion, index) => { | |
| emotionChart.data.datasets[index].data.push(smoothedEmotions[emotion]); | |
| }); | |
| if (emotionChart.data.labels.length > 30) { | |
| emotionChart.data.labels.shift(); | |
| emotionChart.data.datasets.forEach(dataset => dataset.data.shift()); | |
| } | |
| emotionChart.update(); | |
| } | |
| function getEmotionColor(emotion) { | |
| const colors = { | |
| happy: '#2ecc71', | |
| sad: '#3498db', | |
| angry: '#e74c3c', | |
| neutral: '#95a5a6', | |
| surprised: '#f1c40f', | |
| fearful: '#9b59b6' | |
| }; | |
| return colors[emotion] || '#000000'; | |
| } | |
| // Initialize the system when page loads | |
| window.onload = initializeSystem; | |
| </script> | |
| </body> | |
| </html> | 
