| <!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> |
|
|