|
|
<!DOCTYPE html> |
|
|
<html lang="fr"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>🧪 Test YOLOv5 - Détection Simple</title> |
|
|
<script src="https://cdn.tailwindcss.com"></script> |
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"> |
|
|
</head> |
|
|
<body class="bg-gray-100 p-8"> |
|
|
<div class="max-w-4xl mx-auto"> |
|
|
<div class="bg-white rounded-lg shadow-lg p-6"> |
|
|
<h1 class="text-3xl font-bold mb-4 text-center text-blue-600"> |
|
|
<i class="fas fa-vial mr-2"></i> Test YOLOv5 - Détection Simple |
|
|
</h1> |
|
|
|
|
|
<div class="mb-6 p-4 bg-yellow-50 border border-yellow-200 rounded-lg"> |
|
|
<p class="text-sm font-medium text-yellow-800"> |
|
|
<i class="fas fa-info-circle mr-2"></i> |
|
|
<strong>Important :</strong> YOLOv5 détecte uniquement des objets physiques (personnes, voitures, animaux, objets du quotidien). |
|
|
</p> |
|
|
<p class="text-xs text-yellow-700 mt-2"> |
|
|
❌ Ne fonctionne PAS avec : documents, texte, visages spécifiques<br> |
|
|
✅ Fonctionne avec : personnes, véhicules, animaux, téléphones, ordinateurs, etc. |
|
|
</p> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="mb-6"> |
|
|
<label class="block text-gray-700 font-medium mb-2"> |
|
|
<i class="fas fa-upload mr-2"></i> Sélectionnez une image : |
|
|
</label> |
|
|
<input type="file" id="imageInput" accept="image/*" |
|
|
class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100"> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="grid grid-cols-2 gap-4 mb-6"> |
|
|
<div> |
|
|
<label class="block text-gray-700 text-sm font-medium mb-2">Modèle :</label> |
|
|
<select id="modelSelect" class="w-full px-3 py-2 border border-gray-300 rounded-lg"> |
|
|
<option value="yolov5n">YOLOv5n (très rapide)</option> |
|
|
<option value="yolov5s" selected>YOLOv5s (rapide)</option> |
|
|
<option value="yolov5m">YOLOv5m (moyen)</option> |
|
|
<option value="yolov5l">YOLOv5l (précis)</option> |
|
|
</select> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-gray-700 text-sm font-medium mb-2"> |
|
|
Confiance : <span id="confidenceValue">0.25</span> |
|
|
</label> |
|
|
<input type="range" id="confidenceSlider" min="0.1" max="0.9" step="0.05" value="0.25" |
|
|
class="w-full"> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<button id="detectBtn" |
|
|
class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded-lg transition-colors disabled:bg-gray-400 disabled:cursor-not-allowed"> |
|
|
<i class="fas fa-search mr-2"></i> Détecter les objets |
|
|
</button> |
|
|
|
|
|
|
|
|
<div id="results" class="mt-6 hidden"> |
|
|
<h2 class="text-xl font-bold mb-4 text-gray-800"> |
|
|
<i class="fas fa-chart-bar mr-2"></i> Résultats : |
|
|
</h2> |
|
|
<div id="resultContent"></div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="mt-6 bg-blue-50 border border-blue-200 rounded-lg p-4"> |
|
|
<h3 class="font-bold text-blue-800 mb-2"> |
|
|
<i class="fas fa-lightbulb mr-2"></i> Testez avec ces objets : |
|
|
</h3> |
|
|
<div class="grid grid-cols-2 gap-2 text-sm text-blue-700"> |
|
|
<div>✅ Photo de vous-même</div> |
|
|
<div>✅ Photo de voiture</div> |
|
|
<div>✅ Votre téléphone</div> |
|
|
<div>✅ Une bouteille/tasse</div> |
|
|
<div>✅ Un animal (chat/chien)</div> |
|
|
<div>✅ Un ordinateur portable</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
const imageInput = document.getElementById('imageInput'); |
|
|
const detectBtn = document.getElementById('detectBtn'); |
|
|
const modelSelect = document.getElementById('modelSelect'); |
|
|
const confidenceSlider = document.getElementById('confidenceSlider'); |
|
|
const confidenceValue = document.getElementById('confidenceValue'); |
|
|
const results = document.getElementById('results'); |
|
|
const resultContent = document.getElementById('resultContent'); |
|
|
|
|
|
let selectedFile = null; |
|
|
|
|
|
// Mise à jour de l'affichage de la confiance |
|
|
confidenceSlider.addEventListener('input', (e) => { |
|
|
confidenceValue.textContent = e.target.value; |
|
|
}); |
|
|
|
|
|
// Sélection de fichier |
|
|
imageInput.addEventListener('change', (e) => { |
|
|
selectedFile = e.target.files[0]; |
|
|
detectBtn.disabled = !selectedFile; |
|
|
}); |
|
|
|
|
|
// Détection |
|
|
detectBtn.addEventListener('click', async () => { |
|
|
if (!selectedFile) return; |
|
|
|
|
|
detectBtn.disabled = true; |
|
|
detectBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Analyse en cours...'; |
|
|
|
|
|
const formData = new FormData(); |
|
|
formData.append('files', selectedFile); |
|
|
|
|
|
const model = modelSelect.value; |
|
|
const confidence = confidenceSlider.value; |
|
|
|
|
|
try { |
|
|
const response = await fetch(`http://localhost:8001/detect/batch?model=${model}&confidence=${confidence}`, { |
|
|
method: 'POST', |
|
|
body: formData |
|
|
}); |
|
|
|
|
|
if (!response.ok) { |
|
|
throw new Error(`Erreur HTTP: ${response.status}`); |
|
|
} |
|
|
|
|
|
const data = await response.json(); |
|
|
displayResults(data); |
|
|
|
|
|
} catch (error) { |
|
|
console.error('Erreur:', error); |
|
|
resultContent.innerHTML = ` |
|
|
<div class="bg-red-50 border border-red-200 rounded-lg p-4"> |
|
|
<p class="text-red-800 font-medium"> |
|
|
<i class="fas fa-exclamation-circle mr-2"></i> Erreur de connexion |
|
|
</p> |
|
|
<p class="text-sm text-red-600 mt-2"> |
|
|
${error.message}<br> |
|
|
Vérifiez que le serveur est bien lancé sur http://localhost:8001 |
|
|
</p> |
|
|
</div> |
|
|
`; |
|
|
results.classList.remove('hidden'); |
|
|
} finally { |
|
|
detectBtn.disabled = false; |
|
|
detectBtn.innerHTML = '<i class="fas fa-search mr-2"></i> Détecter les objets'; |
|
|
} |
|
|
}); |
|
|
|
|
|
function displayResults(data) { |
|
|
if (!data.results || data.results.length === 0) { |
|
|
resultContent.innerHTML = ` |
|
|
<div class="bg-gray-50 border border-gray-200 rounded-lg p-4"> |
|
|
<p class="text-gray-800">Aucun résultat reçu</p> |
|
|
</div> |
|
|
`; |
|
|
results.classList.remove('hidden'); |
|
|
return; |
|
|
} |
|
|
|
|
|
const result = data.results[0]; |
|
|
const hasDetections = result.detections && result.detections.length > 0; |
|
|
|
|
|
let html = ''; |
|
|
|
|
|
// Affichage de l'image |
|
|
html += ` |
|
|
<div class="mb-4"> |
|
|
<img src="data:image/jpeg;base64,${result.image_base64}" |
|
|
class="w-full rounded-lg shadow-md" |
|
|
alt="Image analysée"> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
if (hasDetections) { |
|
|
// Objets détectés |
|
|
html += ` |
|
|
<div class="bg-green-50 border border-green-200 rounded-lg p-4 mb-4"> |
|
|
<p class="text-green-800 font-bold text-lg mb-2"> |
|
|
<i class="fas fa-check-circle mr-2"></i> |
|
|
${result.detections.length} objet(s) détecté(s) ! |
|
|
</p> |
|
|
</div> |
|
|
<div class="space-y-2"> |
|
|
`; |
|
|
|
|
|
result.detections.forEach((det, idx) => { |
|
|
html += ` |
|
|
<div class="flex items-center justify-between p-3 bg-white border border-gray-200 rounded-lg hover:shadow-md transition-shadow"> |
|
|
<div class="flex items-center space-x-3"> |
|
|
<div class="w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center text-blue-600 font-bold"> |
|
|
${idx + 1} |
|
|
</div> |
|
|
<div> |
|
|
<p class="font-bold text-gray-800">${det.class}</p> |
|
|
<p class="text-xs text-gray-500">Position: (${det.bbox.join(', ')})</p> |
|
|
</div> |
|
|
</div> |
|
|
<div class="text-right"> |
|
|
<div class="text-2xl font-bold text-blue-600">${(det.confidence * 100).toFixed(1)}%</div> |
|
|
<div class="text-xs text-gray-500">confiance</div> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
}); |
|
|
|
|
|
html += `</div>`; |
|
|
|
|
|
} else { |
|
|
// Aucun objet détecté |
|
|
html += ` |
|
|
<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-6 text-center"> |
|
|
<i class="fas fa-exclamation-triangle text-5xl text-yellow-500 mb-3"></i> |
|
|
<p class="text-yellow-800 font-bold text-lg mb-2">Aucun objet détecté</p> |
|
|
<p class="text-sm text-yellow-700 mb-3"> |
|
|
YOLOv5 ne détecte que 80 types d'objets physiques spécifiques |
|
|
</p> |
|
|
|
|
|
<div class="bg-white rounded-lg p-4 text-left"> |
|
|
<p class="font-semibold text-gray-800 mb-2"> |
|
|
<i class="fas fa-question-circle mr-2"></i> Pourquoi aucune détection ? |
|
|
</p> |
|
|
<ul class="text-sm text-gray-700 space-y-1 ml-5"> |
|
|
<li>❌ Image de document ou texte</li> |
|
|
<li>❌ Objets trop petits ou flous</li> |
|
|
<li>❌ Mauvais éclairage</li> |
|
|
<li>❌ Objets non inclus dans les 80 classes COCO</li> |
|
|
</ul> |
|
|
|
|
|
<div class="mt-3 pt-3 border-t border-gray-200"> |
|
|
<p class="font-semibold text-gray-800 mb-1"> |
|
|
<i class="fas fa-lightbulb mr-2"></i> Essayez avec : |
|
|
</p> |
|
|
<p class="text-sm text-gray-600"> |
|
|
Une photo de <strong>personne, voiture, téléphone, ordinateur, bouteille, animal</strong> |
|
|
</p> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="mt-4"> |
|
|
<p class="text-xs text-yellow-600"> |
|
|
💡 Conseil : Baissez le seuil de confiance à 0.15 pour détecter plus d'objets |
|
|
</p> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
|
|
|
// Statistiques |
|
|
html += ` |
|
|
<div class="mt-4 grid grid-cols-3 gap-2 text-center"> |
|
|
<div class="bg-gray-50 rounded-lg p-2"> |
|
|
<p class="text-xs text-gray-600">Modèle</p> |
|
|
<p class="text-sm font-bold text-gray-800">${modelSelect.value}</p> |
|
|
</div> |
|
|
<div class="bg-gray-50 rounded-lg p-2"> |
|
|
<p class="text-xs text-gray-600">Confiance min.</p> |
|
|
<p class="text-sm font-bold text-gray-800">${(confidence * 100).toFixed(0)}%</p> |
|
|
</div> |
|
|
<div class="bg-gray-50 rounded-lg p-2"> |
|
|
<p class="text-xs text-gray-600">Temps</p> |
|
|
<p class="text-sm font-bold text-gray-800">${result.processing_time.toFixed(2)}s</p> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
resultContent.innerHTML = html; |
|
|
results.classList.remove('hidden'); |
|
|
} |
|
|
</script> |
|
|
</body> |
|
|
</html> |
|
|
|