moustaphasidibe's picture
<!DOCTYPE html> <html lang="fr" class="scroll-smooth"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>Reconnaissance Faciale en Direct</title> <script src="https://cdn.tailwindcss.com"></script> <script> tailwind.config = { darkMode: 'class', theme: { extend: { fontFamily: { sans: ['Inter', 'sans-serif'], }, animation: { 'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite', 'bounce-slow': 'bounce 2s infinite', }, colors: { primary: { 50: '#eff6ff', 100: '#dbeafe', 200: '#bfdbfe', 300: '#93c5fd', 400: '#60a5fa', 500: '#3b82f6', 600: '#2563eb', 700: '#1d4ed8', 800: '#1e40af', 900: '#1e3a8a', }, } } } } </script> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> <style> @keyframes float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-10px); } } .float-animation { animation: float 3s ease-in-out infinite; } .btn-hover-effect { transition: all 0.3s ease; transform: translateY(0); } .btn-hover-effect:hover { transform: translateY(-2px); box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); } .dark .gradient-bg { background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%); } .light .gradient-bg { background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%); } .result-container { transition: all 0.5s ease; } .result-container.show { opacity: 1; transform: scale(1); } .result-container.hide { opacity: 0; transform: scale(0.95); } </style> </head> <body class="bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 min-h-screen font-sans transition-colors duration-300"> <!-- Navigation --> <nav class="bg-white dark:bg-gray-800 shadow-sm sticky top-0 z-50"> <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div class="flex justify-between h-16"> <div class="flex items-center"> <div class="flex-shrink-0 flex items-center"> <svg class="h-8 w-8 text-primary-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.828 14.828a4 4 0 01-5.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> </svg> <span class="ml-2 text-xl font-semibold dark:text-white">FaceID</span> </div> </div> <div class="flex items-center space-x-4"> <a href="#home" class="px-3 py-2 rounded-md text-sm font-medium hover:bg-gray-100 dark:hover:bg-gray-700 transition">Accueil</a> <a href="#about" class="px-3 py-2 rounded-md text-sm font-medium hover:bg-gray-100 dark:hover:bg-gray-700 transition">À propos</a> <button id="themeToggle" class="p-2 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition"> <svg id="sunIcon" class="w-5 h-5 text-yellow-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"> <path fill-rule="evenodd" d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z" clip-rule="evenodd" /> </svg> <svg id="moonIcon" class="w-5 h-5 text-indigo-400 hidden" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"> <path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" /> </svg> </button> </div> </div> </div> </nav> <!-- Main Content --> <main> <!-- Home Page --> <section id="home" class="py-12 px-4 sm:px-6 lg:px-8 max-w-7xl mx-auto"> <div class="text-center mb-12"> <h1 class="text-4xl font-bold text-primary-600 dark:text-primary-400 mb-4">🎥 Reconnaissance Faciale en Direct</h1> <p class="text-lg text-gray-600 dark:text-gray-300 max-w-2xl mx-auto">Une solution avancée de reconnaissance faciale en temps réel avec apprentissage automatique</p> </div> <div class="flex flex-col lg:flex-row gap-8 items-center justify-center"> <!-- Webcam Preview --> <div class="w-full lg:w-1/2 max-w-xl"> <div class="bg-white dark:bg-gray-800 rounded-xl shadow-xl overflow-hidden transition-all duration-300 hover:shadow-2xl"> <div class="p-4 bg-primary-500 text-white font-medium">Vue de la webcam</div> <video id="video" class="w-full h-auto" autoplay muted playsinline></video> </div> </div> <!-- Controls and Results --> <div class="w-full lg:w-1/2 max-w-xl space-y-6"> <div class="bg-white dark:bg-gray-800 rounded-xl shadow-xl p-6 transition-all duration-300"> <h3 class="text-xl font-semibold mb-4 text-primary-600 dark:text-primary-400">Contrôles</h3> <div class="mb-4"> <label for="nameInput" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Nom pour ajout rapide</label> <input type="text" id="nameInput" placeholder="Entrez un nom" class="w-full px-4 py-2 rounded-lg border border-gray-300 dark:border-gray-600 focus:ring-2 focus:ring-primary-500 focus:border-primary-500 dark:bg-gray-700 dark:text-white transition"> </div> <button id="addFaceBtn" class="w-full btn-hover-effect bg-primary-600 hover:bg-primary-700 text-white font-medium py-3 px-6 rounded-lg shadow-md transition flex items-center justify-center space-x-2"> <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> <path fill-rule="evenodd" d="M4 5a2 2 0 00-2 2v8a2 2 0 002 2h12a2 2 0 002-2V7a2 2 0 00-2-2h-1.586a1 1 0 01-.707-.293l-1.121-1.121A2 2 0 0011.172 3H8.828a2 2 0 00-1.414.586L6.293 4.707A1 1 0 015.586 5H4zm6 9a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd" /> </svg> <span>📸 Ajouter ce visage</span> </button> </div> <!-- Loader --> <div id="loader" class="hidden bg-white dark:bg-gray-800 rounded-xl shadow-xl p-6 text-center"> <div class="flex flex-col items-center justify-center space-y-3"> <div class="w-12 h-12 border-4 border-primary-500 border-t-transparent rounded-full animate-spin"></div> <p class="text-gray-700 dark:text-gray-300 font-medium">Traitement en cours...</p> </div> </div> <!-- Results --> <div id="resultContainer" class="result-container hide bg-white dark:bg-gray-800 rounded-xl shadow-xl overflow-hidden transition-all duration-300"> <div class="p-4 bg-primary-500 text-white font-medium">Résultat de la reconnaissance</div> <img id="result" class="w-full h-auto" src="" alt="Résultat"> </div> </div> </div> </section> <!-- About Page --> <section id="about" class="py-16 px-4 sm:px-6 lg:px-8 max-w-7xl mx-auto bg-gray-50 dark:bg-gray-800 rounded-2xl my-12"> <div class="text-center mb-12"> <h2 class="text-3xl font-bold text-primary-600 dark:text-primary-400 mb-2">À propos de cette application</h2> <div class="w-24 h-1 bg-primary-500 mx-auto"></div> </div> <div class="grid md:grid-cols-2 gap-8 items-center"> <div class="space-y-6"> <div class="bg-white dark:bg-gray-700 p-6 rounded-xl shadow-lg"> <h3 class="text-xl font-semibold mb-3 text-primary-600 dark:text-primary-400">Qu'est-ce que cette application ?</h3> <p class="text-gray-600 dark:text-gray-300"> Cette application utilise la technologie de reconnaissance faciale en temps réel pour identifier ou enregistrer des visages. Elle combine la puissance de l'apprentissage automatique avec une interface simple pour une expérience utilisateur fluide. </p> </div> <div class="bg-white dark:bg-gray-700 p-6 rounded-xl shadow-lg"> <h3 class="text-xl font-semibold mb-3 text-primary-600 dark:text-primary-400">Comment ça marche ?</h3> <p class="text-gray-600 dark:text-gray-300"> L'application capture des images de votre webcam, les analyse en temps réel et compare les caractéristiques faciales avec une base de données. Elle peut également apprendre de nouveaux visages lorsque vous les enregistrez avec un nom. </p> </div> </div> <div class="bg-white dark:bg-gray-700 p-6 rounded-xl shadow-lg"> <h3 class="text-xl font-semibold mb-4 text-primary-600 dark:text-primary-400">Guide d'utilisation</h3> <div class="space-y-4"> <div class="flex items-start space-x-4"> <div class="flex-shrink-0 bg-primary-100 dark:bg-primary-900 rounded-full p-2"> <span class="text-primary-600 dark:text-primary-300 font-bold">1</span> </div> <div> <h4 class="font-medium text-gray-800 dark:text-gray-200">Autorisez l'accès à la webcam</h4> <p class="text-gray-600 dark:text-gray-400 text-sm">Lorsque vous ouvrez l'application, cliquez sur "Autoriser" pour donner l'accès à votre webcam.</p> </div> </div> <div class="flex items-start space-x-4"> <div class="flex-shrink-0 bg-primary-100 dark:bg-primary-900 rounded-full p-2"> <span class="text-primary-600 dark:text-primary-300 font-bold">2</span> </div> <div> <h4 class="font-medium text-gray-800 dark:text-gray-200">Positionnez votre visage</h4> <p class="text-gray-600 dark:text-gray-400 text-sm">Assurez-vous que votre visage est bien visible dans le cadre de la webcam avec un éclairage adéquat.</p> </div> </div> <div class="flex items-start space-x-4"> <div class="flex-shrink-0 bg-primary-100 dark:bg-primary-900 rounded-full p-2"> <span class="text-primary-600 dark:text-primary-300 font-bold">3</span> </div> <div> <h4 class="font-medium text-gray-800 dark:text-gray-200">Ajoutez un nouveau visage</h4> <p class="text-gray-600 dark:text-gray-400 text-sm">Entrez un nom dans le champ prévu et cliquez sur "Ajouter ce visage" pour enregistrer votre visage.</p> </div> </div> <div class="flex items-start space-x-4"> <div class="flex-shrink-0 bg-primary-100 dark:bg-primary-900 rounded-full p-2"> <span class="text-primary-600 dark:text-primary-300 font-bold">4</span> </div> <div> <h4 class="font-medium text-gray-800 dark:text-gray-200">Voir les résultats</h4> <p class="text-gray-600 dark:text-gray-400 text-sm">L'application affichera automatiquement les résultats de reconnaissance dans la section dédiée.</p> </div> </div> </div> </div> </div> <div class="mt-12 text-center"> <a href="#home" class="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-md shadow-sm text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 transition btn-hover-effect"> Essayer maintenant <svg xmlns="http://www.w3.org/2000/svg" class="ml-2 h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-8.707l-3-3a1 1 0 00-1.414 0l-3 3a1 1 0 001.414 1.414L9 9.414V13a1 1 0 102 0V9.414l1.293 1.293a1 1 0 001.414-1.414z" clip-rule="evenodd" /> </svg> </a> </div> </section> </main> <!-- Footer --> <footer class="bg-white dark:bg-gray-800 shadow-inner mt-12"> <div class="max-w-7xl mx-auto py-8 px-4 sm:px-6 lg:px-8"> <div class="flex flex-col md:flex-row justify-between items-center"> <div class="flex items-center space-x-2 mb-4 md:mb-0"> <svg class="h-6 w-6 text-primary-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.828 14.828a4 4 0 01-5.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> </svg> <span class="text-lg font-semibold">FaceID</span> </div> <div class="flex space-x-6"> <a href="#home" class="text-gray-500 hover:text-primary-600 dark:hover:text-primary-400 transition">Accueil</a> <a href="#about" class="text-gray-500 hover:text-primary-600 dark:hover:text-primary-400 transition">À propos</a> <a href="#" class="text-gray-500 hover:text-primary-600 dark:hover:text-primary-400 transition">Confidentialité</a> <a href="#" class="text-gray-500 hover:text-primary-600 dark:hover:text-primary-400 transition">Conditions</a> </div> </div> <div class="mt-8 border-t border-gray-200 dark:border-gray-700 pt-8 flex flex-col md:flex-row justify-between items-center"> <p class="text-gray-500 text-sm">© 2023 FaceID. Tous droits réservés.</p> <div class="flex space-x-6 mt-4 md:mt-0"> <a href="#" class="text-gray-400 hover:text-primary-600 dark:hover:text-primary-400 transition"> <span class="sr-only">Twitter</span> <svg class="h-6 w-6" fill="currentColor" viewBox="0 0 24 24"> <path d="M8.29 20.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0022 5.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.072 4.072 0 012.8 9.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 012 18.407a11.616 11.616 0 006.29 1.84" /> </svg> </a> <a href="#" class="text-gray-400 hover:text-primary-600 dark:hover:text-primary-400 transition"> <span class="sr-only">GitHub</span> <svg class="h-6 w-6" fill="currentColor" viewBox="0 0 24 24"> <path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd" /> </svg> </a> </div> </div> </div> </footer> <script> // Theme toggle const themeToggle = document.getElementById('themeToggle'); const sunIcon = document.getElementById('sunIcon'); const moonIcon = document.getElementById('moonIcon'); if (localStorage.getItem('color-theme') === 'dark' || (!('color-theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) { document.documentElement.classList.add('dark'); sunIcon.classList.add('hidden'); moonIcon.classList.remove('hidden'); } else { document.documentElement.classList.remove('dark'); sunIcon.classList.remove('hidden'); moonIcon.classList.add('hidden'); } themeToggle.addEventListener('click', function() { if (document.documentElement.classList.contains('dark')) { document.documentElement.classList.remove('dark'); localStorage.setItem('color-theme', 'light'); sunIcon.classList.remove('hidden'); moonIcon.classList.add('hidden'); } else { document.documentElement.classList.add('dark'); localStorage.setItem('color-theme', 'dark'); sunIcon.classList.add('hidden'); moonIcon.classList.remove('hidden'); } }); // Original JavaScript logic (unchanged) const video = document.getElementById('video'); const resultImg = document.getElementById('result'); const addFaceBtn = document.getElementById('addFaceBtn'); const nameInput = document.getElementById('nameInput'); const loader = document.getElementById('loader'); const resultContainer = document.getElementById('resultContainer'); async function startWebcam() { try { const stream = await navigator.mediaDevices.getUserMedia({ video: true }); video.srcObject = stream; } catch (err) { alert("Erreur d'accès à la webcam : " + err.message); } } function canvasToBlob(canvas) { return new Promise(resolve => canvas.toBlob(resolve, 'image/jpeg')); } async function captureAndSend() { const canvas = document.createElement('canvas'); canvas.width = video.videoWidth; canvas.height = video.videoHeight; const ctx = canvas.getContext('2d'); ctx.drawImage(video, 0, 0, canvas.width, canvas.height); const blob = await canvasToBlob(canvas); const formData = new FormData(); formData.append('file', blob, 'frame.jpg'); loader.style.display = 'block'; try { const response = await fetch('/detect', { method: 'POST', body: formData }); const data = await response.json(); resultImg.src = data.image; resultContainer.classList.remove('hide'); resultContainer.classList.add('show'); } catch (err) { console.error(err); } finally { loader.style.display = 'none'; } } async function addFace() { const name = nameInput.value.trim(); if (!name) { alert("Veuillez entrer un nom avant d'ajouter."); return; } const canvas = document.createElement('canvas'); canvas.width = video.videoWidth; canvas.height = video.videoHeight; const ctx = canvas.getContext('2d'); ctx.drawImage(video, 0, 0, canvas.width, canvas.height); const blob = await canvasToBlob(canvas); const formData = new FormData(); formData.append('file', blob, 'face.jpg'); formData.append('name', name); loader.style.display = 'block'; try { const response = await fetch('/add_face', { method: 'POST', body: formData }); const data = await response.json(); alert(data.status); } catch (err) { console.error(err); } finally { loader.style.display = 'none'; } } addFaceBtn.addEventListener('click', addFace); setInterval(captureAndSend, 1500); startWebcam(); </script> <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=moustaphasidibe/face-id" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> </html> Do not change anything in this code I just sent you. Only adjust the two video blocks so that they are at the same level, and place the new face registration form just below the two video blocks. - Initial Deployment
8a646c0 verified