Spaces:
Running
Running
| <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> |