ModuMLTECH commited on
Commit
5a8b195
·
verified ·
1 Parent(s): a9e1eed

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +196 -98
app.py CHANGED
@@ -1,111 +1,209 @@
1
- import streamlit as st
2
- import cv2
3
- import tempfile
4
- import os
5
- import time
6
- import numpy as np
7
- from ultralytics import YOLO
8
- import threading
9
- from PIL import Image
10
- import torch
11
- import queue
12
- from streamlit.runtime.scriptrunner import add_script_run_ctx
13
 
14
- # --- FONCTIONS UTILES ---
15
- def draw_text_with_background(image, text, position, font=cv2.FONT_HERSHEY_SIMPLEX,
16
- font_scale=1, font_thickness=2, text_color=(255, 255, 255), bg_color=(0, 0, 0), padding=5):
17
- """Ajoute du texte avec un fond sur une image OpenCV."""
18
- text_size = cv2.getTextSize(text, font, font_scale, font_thickness)[0]
19
- text_width, text_height = text_size
20
-
21
- x, y = position
22
- top_left = (x, y - text_height - padding)
23
- bottom_right = (x + text_width + padding * 2, y + padding)
24
-
25
- cv2.rectangle(image, top_left, bottom_right, bg_color, -1)
26
- cv2.putText(image, text, (x + padding, y), font, font_scale, text_color, font_thickness, cv2.LINE_AA)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
- # --- CLASSE YOLO OPTIMISÉE ---
29
- class YOLOVideoProcessor:
30
- def __init__(self, model_path, tracker_method="bot"):
31
- self.device = 'cuda' if torch.cuda.is_available() else 'cpu'
32
- self.frame_skip = 2
33
- self.downsample_factor = 0.5
34
- self.img_size = 640
35
- self.conf_threshold = 0.35
36
- self.model = YOLO(model_path, task="detect").to(self.device)
37
- self.tracker_method = tracker_method
38
- self.stop_processing = False
39
 
40
- def process_webcam(self, camera_id=0, display_placeholder=None):
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  cap = cv2.VideoCapture(camera_id)
 
 
 
 
 
 
 
 
 
42
  cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
43
  cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
44
- if not cap.isOpened():
45
- st.error("⚠️ Erreur : Impossible d'ouvrir la webcam.")
46
- return
47
- self.stop_processing = False
48
-
49
- while not self.stop_processing:
50
- success, frame = cap.read()
51
- if not success:
52
- break
53
- display_placeholder.image(frame, channels="BGR", use_column_width=True)
54
- cap.release()
55
- st.success("✅ Flux vidéo arrêté.")
56
-
57
- # --- INTERFACE STREAMLIT ---
58
- def main():
59
- st.set_page_config(page_title="Détecteur de Véhicules", page_icon="🚗", layout="wide")
60
- st.title("🚗 Détection et comptage de Véhicules sur l'Autoroute de l'Avenir")
61
 
62
- model_path = "best.pt"
63
- if not os.path.exists(model_path):
64
- st.error("❌ Modèle YOLO introuvable!")
65
- return
66
 
67
- tab1, tab2 = st.tabs(["🎥 Détection en Temps Réel", "📹 Analyse de Vidéo"])
 
 
 
68
 
69
- with st.sidebar:
70
- st.header("🔹 Paramètres")
71
- tracker_method = st.selectbox("Méthode de tracking", ["bot", "byte"], index=0)
72
- frame_skip = st.slider("Frames Skip", 1, 5, 2)
73
- downsample = st.slider("Facteur d'échelle", 0.3, 1.0, 0.5, 0.1)
74
- conf_threshold = st.slider("Seuil de confiance", 0.1, 0.9, 0.35, 0.05)
75
 
76
- with tab1:
77
- st.header("Détection en Temps Réel avec Webcam")
78
- camera_id = 0
79
- video_placeholder = st.empty()
 
 
 
 
 
 
80
 
81
- start_detection = st.button("▶️ Lancer la détection en temps réel")
82
- stop_detection = st.button("⏹️ Arrêter la détection")
 
 
83
 
84
- if start_detection:
85
- processor = YOLOVideoProcessor(model_path, tracker_method)
86
- processor.frame_skip = frame_skip
87
- processor.downsample_factor = downsample
88
- processor.conf_threshold = conf_threshold
89
- processing_thread = threading.Thread(
90
- target=processor.process_webcam, args=(camera_id, video_placeholder))
91
- processing_thread.daemon = True
92
- add_script_run_ctx(processing_thread)
93
- processing_thread.start()
94
- st.success("✅ Détection en cours...")
95
 
96
- if stop_detection:
97
- st.success("✅ Détection arrêtée.")
98
-
99
- with tab2:
100
- st.header("Analyse de Vidéo")
101
- uploaded_file = st.file_uploader("📂 Upload une vidéo", type=["mp4", "avi", "mov"])
102
- if uploaded_file is not None:
103
- temp_dir = tempfile.mkdtemp()
104
- input_video_path = os.path.join(temp_dir, "input_video.mp4")
105
- with open(input_video_path, "wb") as f:
106
- f.write(uploaded_file.getbuffer())
107
- st.video(input_video_path)
108
- st.success("Vidéo chargée avec succès.")
109
-
110
- if __name__ == "__main__":
111
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Modifier la partie de détection des caméras dans la fonction main() (onglet 2)
 
 
 
 
 
 
 
 
 
 
 
2
 
3
+ # Dans l'onglet 2: Détection en Temps Réel, remplacer le code de sélection de caméra par:
4
+ with tab2:
5
+ st.header("Détection en Temps Réel avec Webcam")
6
+
7
+ # Simplified camera selection - focusing on reliability
8
+ camera_options = {"Webcam par défaut (0)": 0}
9
+ # Ajout de quelques options supplémentaires sans test préalable
10
+ for i in range(1, 4):
11
+ camera_options[f"Caméra alternative ({i})"] = i
12
+
13
+ selected_camera = st.selectbox("Sélectionnez la source vidéo", list(camera_options.keys()))
14
+ camera_id = camera_options[selected_camera]
15
+
16
+ # Paramètres d'affichage
17
+ display_quality = st.select_slider(
18
+ "Qualité d'affichage",
19
+ options=["Basse", "Moyenne", "Haute"],
20
+ value="Moyenne"
21
+ )
22
+
23
+ # Affichage des placeholders
24
+ video_container = st.container()
25
+ video_placeholder = video_container.empty()
26
+
27
+ # Crée une ligne pour les compteurs
28
+ count_col1, count_col2 = st.columns(2)
29
+ count_placeholders = [count_col1.empty(), count_col2.empty()]
30
+
31
+ # Afficher les infos sur les performances
32
+ st.info("ℹ️ **Optimisations appliquées:** Multi-threading, redimensionnement des images, et utilisation de CUDA si disponible")
33
+
34
+ # Boutons pour démarrer/arrêter la webcam
35
+ col_start, col_stop = st.columns(2)
36
+
37
+ if col_start.button("▶️ Démarrer la détection en direct"):
38
+ if not valid_polygons:
39
+ st.error("❌ Les coordonnées des polygones doivent contenir **exactement 4 points**.")
40
+ elif st.session_state.webcam_active:
41
+ st.warning("⚠️ La webcam est déjà active !")
42
+ else:
43
+ # Tester l'ouverture de la webcam avant de démarrer le traitement
44
+ test_cap = cv2.VideoCapture(camera_id)
45
+ if not test_cap.isOpened():
46
+ st.error(f"⚠️ Impossible d'ouvrir la caméra {camera_id}. Essayez une autre source vidéo.")
47
+ test_cap.release()
48
+ else:
49
+ test_cap.release() # Libérer la caméra pour le traitement réel
50
+
51
+ # Créer le processeur YOLO avec les paramètres d'optimisation
52
+ processor = YOLOVideoProcessor(model_path, poly1, poly2, tracker_method)
53
+ processor.frame_skip = frame_skip
54
+ processor.downsample_factor = downsample
55
+ processor.conf_threshold = conf_threshold
56
+
57
+ st.session_state.processor = processor
58
+ st.session_state.webcam_active = True
59
+
60
+ # Démarrer le traitement dans un thread séparé
61
+ processing_thread = threading.Thread(
62
+ target=st.session_state.processor.process_webcam,
63
+ args=(camera_id, video_placeholder, count_placeholders)
64
+ )
65
+ processing_thread.daemon = True
66
+
67
+ # Ajouter le contexte Streamlit au thread pour éviter les erreurs
68
+ add_script_run_ctx(processing_thread)
69
+
70
+ try:
71
+ processing_thread.start()
72
+ st.session_state.processing_thread = processing_thread
73
+ st.success("✅ Webcam démarrée avec succès!")
74
+ except Exception as e:
75
+ st.error(f"Erreur au démarrage du thread: {e}")
76
+ st.session_state.webcam_active = False
77
 
78
+ # Maintenant, modifions la méthode process_webcam dans la classe YOLOVideoProcessor pour améliorer la gestion de la webcam:
 
 
 
 
 
 
 
 
 
 
79
 
80
+ def process_webcam(self, camera_id=0, display_placeholder=None, count_placeholders=None):
81
+ """Traite la vidéo en temps réel depuis une webcam avec multi-threading"""
82
+ # Tentative d'ouverture avec plusieurs backends dans OpenCV
83
+ for backend_pref in [cv2.CAP_ANY, cv2.CAP_DSHOW, cv2.CAP_MSMF, cv2.CAP_V4L2]:
84
+ try:
85
+ # Essayer différents backends de capture
86
+ cap = cv2.VideoCapture(camera_id, backend_pref)
87
+ if cap.isOpened():
88
+ break
89
+ except:
90
+ continue
91
+
92
+ # Si aucun backend n'a fonctionné, essayer une dernière fois avec le backend par défaut
93
+ if not cap.isOpened():
94
  cap = cv2.VideoCapture(camera_id)
95
+
96
+ # Vérifier si la caméra est ouverte
97
+ if not cap.isOpened():
98
+ if display_placeholder:
99
+ display_placeholder.error("⚠️ Erreur : Impossible d'ouvrir la webcam. Essayez de redémarrer l'application ou utiliser une autre source vidéo.")
100
+ return
101
+
102
+ # Configuration de la webcam pour de meilleures performances (avec gestion d'erreurs)
103
+ try:
104
  cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
105
  cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
106
+ cap.set(cv2.CAP_PROP_FPS, 30)
107
+ except:
108
+ # En cas d'échec, continuer sans erreur
109
+ if display_placeholder:
110
+ display_placeholder.warning("⚠️ Impossible de configurer certains paramètres de la webcam. Utilisation des paramètres par défaut.")
 
 
 
 
 
 
 
 
 
 
 
 
111
 
112
+ # Réinitialiser les compteurs pour la nouvelle session
113
+ self.reset_counts()
114
+ self.stop_processing = False
115
+ frame_count = 0
116
 
117
+ # Démarrer le thread de traitement des frames
118
+ processing_thread = threading.Thread(target=self.process_webcam_frames)
119
+ processing_thread.daemon = True
120
+ processing_thread.start()
121
 
122
+ # Horodatage pour limiter la fréquence de rafraîchissement de l'interface
123
+ last_ui_update_time = time.time()
124
+ ui_update_interval = 0.03 # ~30 FPS pour l'interface
 
 
 
125
 
126
+ try:
127
+ # Attendre un peu pour que la webcam s'initialise
128
+ time.sleep(0.5)
129
+
130
+ # Essayer de lire le premier frame pour s'assurer que la caméra fonctionne
131
+ success, first_frame = cap.read()
132
+ if not success:
133
+ if display_placeholder:
134
+ display_placeholder.error("⚠️ Impossible de lire les images de la webcam. Vérifiez votre caméra et ses permissions.")
135
+ return
136
 
137
+ # Afficher ce premier frame pour montrer que la connexion fonctionne
138
+ if display_placeholder:
139
+ first_frame_rgb = cv2.cvtColor(first_frame, cv2.COLOR_BGR2RGB)
140
+ display_placeholder.image(first_frame_rgb, channels="RGB", use_column_width=True, caption="Webcam connectée avec succès!")
141
 
142
+ # Boucle principale de capture
143
+ while not self.stop_processing:
144
+ success, frame = cap.read()
145
+ if not success:
146
+ # Essayer de reconnecter en cas d'erreur
147
+ time.sleep(0.1)
148
+ continue
 
 
 
 
149
 
150
+ # Ne traiter qu'une image sur N (frame_skip)
151
+ if frame_count % self.frame_skip == 0:
152
+ # Vider la file si elle est pleine pour éviter le retard
153
+ if self.frame_queue.full():
154
+ try:
155
+ self.frame_queue.get_nowait()
156
+ self.frame_queue.task_done()
157
+ except:
158
+ pass
159
+
160
+ # Mettre le frame dans la file pour traitement
161
+ try:
162
+ self.frame_queue.put(frame, block=False)
163
+ except queue.Full:
164
+ pass # Ignorer si la file est pleine
165
+
166
+ # Mise à jour de l'interface à fréquence limitée
167
+ current_time = time.time()
168
+ if current_time - last_ui_update_time >= ui_update_interval:
169
+ try:
170
+ # Récupérer le dernier résultat disponible sans bloquer
171
+ if not self.result_queue.empty():
172
+ processed_frame, count1, count2 = self.result_queue.get_nowait()
173
+
174
+ # Convertir l'image OpenCV en format compatible avec Streamlit
175
+ processed_frame_rgb = cv2.cvtColor(processed_frame, cv2.COLOR_BGR2RGB)
176
+ img = Image.fromarray(processed_frame_rgb)
177
+
178
+ # Afficher l'image dans le placeholder Streamlit
179
+ if display_placeholder:
180
+ display_placeholder.image(img, channels="RGB", use_column_width=True)
181
+
182
+ # Mettre à jour les compteurs
183
+ if count_placeholders and len(count_placeholders) >= 2:
184
+ count_placeholders[0].metric("Véhicules Sens 1 (Vert)", count1)
185
+ count_placeholders[1].metric("Véhicules Sens 2 (Rouge)", count2)
186
+
187
+ last_ui_update_time = current_time
188
+ except queue.Empty:
189
+ pass
190
+ except Exception as e:
191
+ if display_placeholder:
192
+ display_placeholder.warning(f"Erreur lors de l'affichage: {e}")
193
+
194
+ frame_count += 1
195
+
196
+ # Pause légère pour éviter d'utiliser 100% du CPU
197
+ time.sleep(0.001)
198
+
199
+ except Exception as e:
200
+ if display_placeholder:
201
+ display_placeholder.error(f"Erreur dans la boucle principale: {e}")
202
+ finally:
203
+ # Nettoyage
204
+ self.stop_processing = True
205
+ cap.release()
206
+ # Attendre que le thread de traitement se termine
207
+ processing_thread.join(timeout=1.0)
208
+ if display_placeholder:
209
+ display_placeholder.success("✅ Flux vidéo arrêté.")