// YOLOv5 Web App - Main JavaScript // Dynamic API URL detection for both local and Hugging Face deployment const API_URL = window.location.protocol + '//' + window.location.host; const WS_URL = (window.location.protocol === 'https:' ? 'wss://' : 'ws://') + window.location.host + '/ws'; let ws = null; let webcamStream = null; let isWebcamActive = false; let animationFrame = null; let stats = { fps: 0, latency: 0, objectCount: 0, avgConf: 0 }; let objectsChart = null; let performanceChart = null; let uploadedFiles = []; // Initialize app document.addEventListener('DOMContentLoaded', () => { initializeTabs(); initializeTheme(); initializeWebcam(); initializeUpload(); initializeDashboard(); loadStatistics(); loadHistory(); // Auto-refresh statistics every 5 seconds setInterval(loadStatistics, 5000); }); // Tab Management function initializeTabs() { const tabBtns = document.querySelectorAll('.tab-btn'); const tabContents = document.querySelectorAll('.tab-content'); tabBtns.forEach(btn => { btn.addEventListener('click', () => { const tabName = btn.dataset.tab; // Update active states tabBtns.forEach(b => b.classList.remove('active', 'text-blue-600', 'border-b-2', 'border-blue-600')); tabContents.forEach(c => c.classList.add('hidden')); btn.classList.add('active', 'text-blue-600', 'border-b-2', 'border-blue-600'); document.getElementById(`${tabName}-tab`).classList.remove('hidden'); // Load data when switching tabs if (tabName === 'dashboard') loadStatistics(); if (tabName === 'history') loadHistory(); }); }); // Set initial active state document.querySelector('.tab-btn').classList.add('text-blue-600', 'border-b-2', 'border-blue-600'); } // Theme Management function initializeTheme() { const themeToggle = document.getElementById('themeToggle'); const html = document.documentElement; // Load saved theme const savedTheme = localStorage.getItem('theme') || 'light'; if (savedTheme === 'dark') { html.classList.add('dark'); } themeToggle.addEventListener('click', () => { html.classList.toggle('dark'); const newTheme = html.classList.contains('dark') ? 'dark' : 'light'; localStorage.setItem('theme', newTheme); }); } // Webcam Management function initializeWebcam() { const startBtn = document.getElementById('startWebcam'); const stopBtn = document.getElementById('stopWebcam'); const confThreshold = document.getElementById('confThreshold'); const confValue = document.getElementById('confValue'); startBtn.addEventListener('click', startWebcamDetection); stopBtn.addEventListener('click', stopWebcamDetection); confThreshold.addEventListener('input', (e) => { confValue.textContent = e.target.value; }); } async function startWebcamDetection() { try { const video = document.getElementById('webcam'); const canvas = document.getElementById('webcamCanvas'); const placeholder = document.getElementById('webcamPlaceholder'); const stats = document.getElementById('webcamStats'); const startBtn = document.getElementById('startWebcam'); const stopBtn = document.getElementById('stopWebcam'); // Get webcam stream webcamStream = await navigator.mediaDevices.getUserMedia({ video: { width: 1280, height: 720 } }); video.srcObject = webcamStream; // Show video video.classList.remove('hidden'); canvas.classList.remove('hidden'); placeholder.classList.add('hidden'); stats.classList.remove('hidden'); startBtn.classList.add('hidden'); stopBtn.classList.remove('hidden'); isWebcamActive = true; console.log('đ„ Webcam dĂ©marrĂ©e, connexion WebSocket...'); // Connect WebSocket connectWebSocket(); } catch (error) { console.error('Error accessing webcam:', error); alert('Erreur: Impossible d\'accĂ©der Ă la webcam. VĂ©rifiez les permissions.'); } } function stopWebcamDetection() { isWebcamActive = false; if (webcamStream) { webcamStream.getTracks().forEach(track => track.stop()); webcamStream = null; } if (ws) { ws.close(); ws = null; } if (animationFrame) { cancelAnimationFrame(animationFrame); } const video = document.getElementById('webcam'); const canvas = document.getElementById('webcamCanvas'); const placeholder = document.getElementById('webcamPlaceholder'); const stats = document.getElementById('webcamStats'); const startBtn = document.getElementById('startWebcam'); const stopBtn = document.getElementById('stopWebcam'); video.classList.add('hidden'); canvas.classList.add('hidden'); placeholder.classList.remove('hidden'); stats.classList.add('hidden'); startBtn.classList.remove('hidden'); stopBtn.classList.add('hidden'); document.getElementById('liveDetections').innerHTML = '
Aucune détection
'; } function connectWebSocket() { console.log('đ Connexion WebSocket Ă :', WS_URL); ws = new WebSocket(WS_URL); ws.onopen = () => { console.log('â WebSocket connectĂ© avec succĂšs!'); // Attendre un peu que la vidĂ©o soit prĂȘte, puis commencer Ă envoyer setTimeout(() => { console.log('DĂ©marrage de l\'envoi de frames...'); sendWebcamFrame(); }, 500); }; ws.onmessage = (event) => { console.log('Frame reçue du serveur'); const data = JSON.parse(event.data); displayWebcamDetections(data); updateWebcamStats(data); }; ws.onerror = (error) => { console.error('â WebSocket error:', error); alert('Erreur WebSocket. VĂ©rifiez la connexion au serveur.'); }; ws.onclose = () => { console.log('â ïž WebSocket dĂ©connectĂ©'); if (isWebcamActive) { // RĂ©essayer la connexion si la webcam est toujours active setTimeout(() => { console.log('Tentative de reconnexion...'); connectWebSocket(); }, 2000); } }; } function sendWebcamFrame() { if (!isWebcamActive || !ws || ws.readyState !== WebSocket.OPEN) { console.log('Cannot send frame:', { isWebcamActive, wsState: ws?.readyState }); return; } const video = document.getElementById('webcam'); const canvas = document.getElementById('webcamCanvas'); // VĂ©rifier que la vidĂ©o est prĂȘte if (!video.videoWidth || !video.videoHeight) { console.log('Video not ready, retrying...'); setTimeout(() => sendWebcamFrame(), 100); return; } const ctx = canvas.getContext('2d'); canvas.width = video.videoWidth; canvas.height = video.videoHeight; ctx.drawImage(video, 0, 0); const frameData = canvas.toDataURL('image/jpeg', 0.8); const model = document.getElementById('modelSelect').value; const confidence = parseFloat(document.getElementById('confThreshold').value); const startTime = Date.now(); try { ws.send(JSON.stringify({ frame: frameData, model: model, confidence: confidence, timestamp: startTime })); console.log('Frame sent successfully'); } catch (error) { console.error('Error sending frame:', error); } // Send next frame after 500ms (2 FPS) pour meilleure stabilitĂ© if (isWebcamActive) { setTimeout(() => sendWebcamFrame(), 500); } } function displayWebcamDetections(data) { const canvas = document.getElementById('webcamCanvas'); const img = new Image(); img.onload = () => { const ctx = canvas.getContext('2d'); canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img, 0, 0); }; img.src = data.image; // Update detections list with better display const liveDetections = document.getElementById('liveDetections'); if (data.detections.length === 0) { liveDetections.innerHTML = `Aucun objet dĂ©tectĂ©
Montrez des personnes, animaux, ou objets à la caméra
Erreur lors du traitement
'; } finally { processBtn.disabled = false; processBtn.innerHTML = 'Analyser les images'; } } function displayUploadResults(results) { const resultsDiv = document.getElementById('uploadResults'); resultsDiv.innerHTML = results.map(result => `Aucun objet détecté
YOLOv5 détecte : personnes, animaux, véhicules, objets du quotidien
đĄ Essayez avec une photo contenant des personnes, voitures, ou objets physiques
Aucun historique
'; return; } historyList.innerHTML = data.history.reverse().map(item => `