Spaces:
Running
Running
<html lang="fr"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Application de Reconnaissance Faciale</title> | |
<!-- Tailwind CSS --> | |
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet"> | |
<!-- Font Awesome (pour les icônes) --> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css"> | |
<style> | |
/* Styles personnalisés (si nécessaire) */ | |
.preview-image { | |
background-size: cover; | |
background-position: center; | |
min-height: 150px; | |
} | |
.github-link { | |
position: fixed; | |
bottom: 20px; | |
right: 20px; | |
padding: 10px; | |
background-color: #333; | |
color: white; | |
border-radius: 5px; | |
text-decoration: none; | |
display: flex; | |
align-items: center; | |
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); | |
} | |
.github-link i { | |
margin-right: 8px; | |
font-size: 20px; | |
} | |
.github-link:hover { | |
background-color: #555; | |
} | |
</style> | |
</head> | |
<body class="bg-gray-100 font-sans"> | |
<div class="container mx-auto p-4"> | |
<h1 class="text-4xl font-bold text-center text-blue-700 mb-8"> | |
<i class="fas fa-user-check text-5xl mr-3"></i> | |
Reconnaissance et Comparaison Faciale | |
</h1> | |
<div class="grid md:grid-cols-2 gap-8"> | |
<!-- Section de sélection d'images --> | |
<div class="bg-white p-6 rounded-lg shadow-lg"> | |
<h2 class="text-2xl font-semibold mb-4 text-gray-700"> | |
<i class="fas fa-images text-blue-500 mr-2"></i> | |
Sélectionner des images | |
</h2> | |
<input type="file" id="imageUpload" accept="image/*" class="mb-4 hidden"> | |
<label for="imageUpload" class="cursor-pointer bg-blue-500 hover:bg-blue-600 text-white font-bold py-3 px-6 rounded-lg inline-flex items-center transition duration-200 ease-in-out"> | |
<i class="fas fa-upload mr-2"></i> | |
Choisir un fichier | |
</label> | |
<button id="cameraButton" class="bg-green-500 hover:bg-green-600 text-white font-bold py-3 px-6 rounded-lg ml-4 inline-flex items-center transition duration-200 ease-in-out"> | |
<i class="fas fa-camera mr-2"></i> | |
Utiliser la caméra | |
</button> | |
<video id="videoFeed" width="320" height="240" autoplay class="hidden mt-4 rounded-lg"></video> | |
<canvas id="canvas" width="320" height="240" class="hidden"></canvas> | |
<!-- Prévisualisation des images --> | |
<div class="grid grid-cols-2 gap-4 mt-6"> | |
<div id="imagePreview1" class="w-full aspect-w-1 aspect-h-1 rounded-lg overflow-hidden preview-image"></div> | |
<div id="imagePreview2" class="w-full aspect-w-1 aspect-h-1 rounded-lg overflow-hidden preview-image"></div> | |
</div> | |
</div> | |
<!-- Section des résultats --> | |
<div class="bg-white p-6 rounded-lg shadow-lg"> | |
<h2 class="text-2xl font-semibold mb-4 text-gray-700"> | |
<i class="fas fa-poll-h text-blue-500 mr-2"></i> | |
Résultats de la comparaison | |
</h2> | |
<div id="results" class="text-center"> | |
<p class="text-gray-600">Veuillez sélectionner des images ou utiliser la caméra pour commencer.</p> | |
</div> | |
<button id="compareButton" class="hidden bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded mt-4"> | |
Comparer les visages | |
</button> | |
</div> | |
</div> | |
</div> | |
<!-- Lien GitHub --> | |
<a href="https://github.com/Yusufibin" target="_blank" class="github-link"> | |
<i class="fab fa-github"></i> | |
<span>Yusufibin</span> | |
</a> | |
<script> | |
const imageUpload = document.getElementById('imageUpload'); | |
const cameraButton = document.getElementById('cameraButton'); | |
const videoFeed = document.getElementById('videoFeed'); | |
const canvas = document.getElementById('canvas'); | |
const imagePreview1 = document.getElementById('imagePreview1'); | |
const imagePreview2 = document.getElementById('imagePreview2'); | |
const resultsDiv = document.getElementById('results'); | |
const compareButton = document.getElementById('compareButton'); | |
const API_BASE_URL = 'http://127.0.0.1:5000'; // URL de base de ton API Flask | |
let usingCamera = false; | |
let currentImage = 1; // 1 or 2 pour suivre quelle image prévisualiser | |
// Gestionnaire pour le téléchargement de fichiers | |
imageUpload.addEventListener('change', async (event) => { | |
const file = event.target.files[0]; | |
if (file) { | |
const reader = new FileReader(); | |
reader.onload = (e) => { | |
if (currentImage === 1) { | |
imagePreview1.style.backgroundImage = `url(${e.target.result})`; | |
currentImage = 2; | |
} else { | |
imagePreview2.style.backgroundImage = `url(${e.target.result})`; | |
currentImage = 1; | |
} | |
compareButton.classList.remove('hidden'); // Affiche le bouton de comparaison | |
}; | |
reader.readAsDataURL(file); | |
} | |
}); | |
// Gestionnaire pour le bouton de la caméra | |
cameraButton.addEventListener('click', async () => { | |
usingCamera = !usingCamera; | |
if (usingCamera) { | |
cameraButton.textContent = 'Arrêter la caméra'; | |
try { | |
const stream = await navigator.mediaDevices.getUserMedia({ video: true }); | |
videoFeed.srcObject = stream; | |
videoFeed.classList.remove('hidden'); | |
} catch (err) { | |
console.error("Erreur lors de l'accès à la caméra :", err); | |
} | |
} else { | |
cameraButton.textContent = 'Utiliser la caméra'; | |
const tracks = videoFeed.srcObject.getTracks(); | |
tracks.forEach(track => track.stop()); | |
videoFeed.classList.add('hidden'); | |
} | |
}); | |
// Capture d'image à partir de la caméra | |
function captureImageFromCamera() { | |
const context = canvas.getContext('2d'); | |
context.drawImage(videoFeed, 0, 0, canvas.width, canvas.height); | |
const imageDataURL = canvas.toDataURL('image/png'); | |
if (currentImage === 1) { | |
imagePreview1.style.backgroundImage = `url(${imageDataURL})`; | |
currentImage = 2; | |
} else { | |
imagePreview2.style.backgroundImage = `url(${imageDataURL})`; | |
currentImage = 1; | |
} | |
compareButton.classList.remove('hidden'); // Affiche le bouton de comparaison | |
} | |
// Gestionnaire pour le bouton de comparaison | |
compareButton.addEventListener('click', () => { | |
if (imagePreview1.style.backgroundImage && imagePreview2.style.backgroundImage) { | |
const formData = new FormData(); | |
const imageData1 = dataURLtoBlob(imagePreview1.style.backgroundImage.slice(5, -2)); | |
const imageData2 = dataURLtoBlob(imagePreview2.style.backgroundImage.slice(5, -2)); | |
formData.append('image1', imageData1, 'image1.png'); | |
formData.append('image2', imageData2, 'image2.png'); | |
fetch(`${API_BASE_URL}/verify`, { | |
method: 'POST', | |
body: formData, | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
displayResults(data); | |
}) | |
.catch(error => { | |
console.error('Erreur lors de la comparaison des visages:', error); | |
resultsDiv.innerHTML = `<p>Erreur lors de la comparaison.</p>`; | |
}); | |
} else { | |
alert('Veuillez télécharger deux images pour la comparaison.'); | |
} | |
}); | |
// Fonction pour convertir une URL de données en Blob | |
function dataURLtoBlob(dataURL) { | |
const parts = dataURL.split(';base64,'); | |
const contentType = parts[0].split(':')[1]; | |
const raw = window.atob(parts[1]); | |
const rawLength = raw.length; | |
const uInt8Array = new Uint8Array(rawLength); | |
for (let i = 0; i < rawLength; ++i) { | |
uInt8Array[i] = raw.charCodeAt(i); | |
} | |
return new Blob([uInt8Array], { type: contentType }); | |
} | |
// Fonction pour afficher les résultats de la comparaison | |
function displayResults(data) { | |
let resultHTML = ''; | |
if (data.error) { | |
resultHTML = `<p class="text-red-600">${data.error}</p>`; | |
} else { | |
const similarity = data.verified ? 'Visages similaires' : 'Visages différents'; | |
resultHTML += `<p class="text-lg ${data.verified ? 'text-green-600' : 'text-red-600'}">${similarity}</p>`; | |
if (data.distance !== undefined && data.threshold !== undefined) { | |
resultHTML += `<p class="mt-2">Distance : ${data.distance.toFixed(4)}</p>`; | |
resultHTML += `<p>Seuil de similarité : ${data.threshold.toFixed(4)}</p>`; | |
} | |
if (data.image1_url && data.image2_url) { | |
resultHTML += `<img src="${API_BASE_URL}/${data.image1_url}" alt="Image 1" class="w-32 h-32 mt-4 rounded-lg">`; | |
resultHTML += `<img src="${API_BASE_URL}/${data.image2_url}" alt="Image 2" class="w-32 h-32 mt-4 rounded-lg">`; | |
} | |
} | |
resultsDiv.innerHTML = resultHTML; | |
} | |
</script> | |
</body> | |
</html> |