ModuMLTECH commited on
Commit
d1c42bc
·
verified ·
1 Parent(s): 35b76d7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +371 -55
app.py CHANGED
@@ -4,9 +4,10 @@ import tempfile
4
  import os
5
  import time
6
  import numpy as np
7
- import pandas as pd
8
- from collections import defaultdict
9
  from ultralytics import YOLO
 
 
 
10
 
11
  # --- FONCTIONS UTILES ---
12
  def draw_text_with_background(image, text, position, font=cv2.FONT_HERSHEY_SIMPLEX,
@@ -22,25 +23,47 @@ def draw_text_with_background(image, text, position, font=cv2.FONT_HERSHEY_SIMPL
22
  cv2.rectangle(image, top_left, bottom_right, bg_color, -1)
23
  cv2.putText(image, text, (x + padding, y), font, font_scale, text_color, font_thickness, cv2.LINE_AA)
24
 
25
- # --- CLASSE YOLO ---
26
  class YOLOVideoProcessor:
27
- def __init__(self, model_path, video_path, output_path, poly1, poly2, tracker_method="bot"):
 
 
 
 
 
 
 
 
 
 
28
  self.model = YOLO(model_path, task="detect")
 
 
 
29
  self.tracker_method = tracker_method
30
- self.video_path = video_path
31
- self.output_path = output_path
32
-
33
  self.unique_region1_ids = set()
34
  self.unique_region2_ids = set()
35
  self.poly1 = poly1
36
  self.poly2 = poly2
 
 
 
 
 
 
37
 
38
  def is_in_region(self, center, poly):
39
  poly_np = np.array(poly, dtype=np.int32)
40
  return cv2.pointPolygonTest(poly_np, center, False) >= 0
41
 
42
- def process_video(self, progress_bar=None):
43
- cap = cv2.VideoCapture(self.video_path)
 
 
 
 
 
 
44
  if not cap.isOpened():
45
  st.error("⚠️ Erreur : Impossible d'ouvrir la vidéo.")
46
  return
@@ -54,10 +77,11 @@ class YOLOVideoProcessor:
54
 
55
  # Utiliser XVID qui est généralement mieux supporté
56
  fourcc = cv2.VideoWriter_fourcc(*'XVID')
57
- out = cv2.VideoWriter(self.output_path, fourcc, fps, (frame_width, frame_height))
58
 
59
  processed_frames = 0
60
  total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
 
61
 
62
  while cap.isOpened():
63
  success, frame = cap.read()
@@ -68,35 +92,17 @@ class YOLOVideoProcessor:
68
  if progress_bar is not None:
69
  progress_bar.progress(processed_frames / total_frames)
70
 
71
- tracker = "botsort.yaml" if self.tracker_method.lower() == "bot" else "bytetrack.yaml"
72
- results = self.model.track(frame, persist=True, tracker=tracker, conf=0.25)
73
-
74
- track_ids = []
75
- if results and len(results) > 0 and len(results[0].boxes) > 0:
76
- try:
77
- track_ids = results[0].boxes.id.int().cpu().tolist()
78
- except AttributeError:
79
- track_ids = [i for i in range(len(results[0].boxes.xywh.cpu().numpy()))]
80
-
81
- # Dessiner les polygones
82
- cv2.polylines(frame, [np.array(self.poly1, np.int32)], isClosed=True, color=(0, 255, 0), thickness=2)
83
- cv2.polylines(frame, [np.array(self.poly2, np.int32)], isClosed=True, color=(255, 0, 0), thickness=2)
84
-
85
- for box, track_id in zip(results[0].boxes.xywh.cpu().numpy(), track_ids):
86
- x, y, w, h = box
87
- center_point = (int(x), int(y))
88
-
89
- if self.is_in_region(center_point, self.poly1):
90
- self.unique_region1_ids.add(track_id)
91
- if self.is_in_region(center_point, self.poly2):
92
- self.unique_region2_ids.add(track_id)
93
-
94
- # Affichage du comptage des véhicules
95
- draw_text_with_background(frame, f'Total Sens 1: {len(self.unique_region1_ids)}', (10, frame_height - 50))
96
- draw_text_with_background(frame, f'Total Sens 2: {len(self.unique_region2_ids)}', (frame_width - 300, frame_height - 50))
97
-
98
- out.write(frame)
99
  processed_frames += 1
 
100
 
101
  cap.release()
102
  out.release()
@@ -107,6 +113,191 @@ class YOLOVideoProcessor:
107
 
108
  return len(self.unique_region1_ids), len(self.unique_region2_ids)
109
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
111
  # --- INTERFACE STREAMLIT ---
112
  def main():
@@ -118,6 +309,16 @@ def main():
118
 
119
  st.title("🚗 Détection et comptage de Véhicules sur l'Autoroute de l'Avenir")
120
 
 
 
 
 
 
 
 
 
 
 
121
  # Vérifier si le modèle existe déjà ou doit être téléchargé
122
  model_path = "best.pt"
123
  if not os.path.exists(model_path):
@@ -133,10 +334,11 @@ def main():
133
  st.warning("⚠️ Utilisation du modèle YOLO standard à la place")
134
  model_path = "yolov8n.pt"
135
 
136
- # Colonnes pour l'organisation de l'interface
137
- col1, col2 = st.columns([3, 1])
138
 
139
- with col2:
 
140
  st.header("🔹 Paramètres")
141
 
142
  # Entrée utilisateur pour les polygones
@@ -147,18 +349,28 @@ def main():
147
  poly2_input = st.text_area("Entrez 4 points (x,y) séparés par des espaces", "678,350 815,350 1203,630 743,630")
148
 
149
  tracker_method = st.selectbox("Méthode de tracking", ["bot", "byte"], index=0)
150
-
151
- with col1:
152
- uploaded_file = st.file_uploader("📂 Upload une vidéo", type=["mp4", "avi", "mov"])
153
 
154
- def parse_polygon(input_text):
155
- try:
156
- return [tuple(map(int, point.split(','))) for point in input_text.split()]
157
- except:
158
- return []
159
 
160
- poly1 = parse_polygon(poly1_input)
161
- poly2 = parse_polygon(poly2_input)
 
 
 
 
 
 
 
 
 
 
 
 
 
162
 
163
  if uploaded_file is not None:
164
  # Créer un dossier temporaire si nécessaire
@@ -173,17 +385,20 @@ def main():
173
  st.video(input_video_path) # Afficher la vidéo d'entrée
174
 
175
  if st.button("▶️ Lancer la détection"):
176
- if len(poly1) == 4 and len(poly2) == 4:
177
  # Afficher la barre de progression
178
  progress_text = "🔄 Traitement de la vidéo en cours..."
179
  progress_bar = st.progress(0)
180
 
181
- # Traitement de la vidéo
182
- processor = YOLOVideoProcessor(model_path, input_video_path, output_video_path, poly1, poly2, tracker_method)
 
 
 
183
 
184
  # Démarrer le traitement
185
  start_time = time.time()
186
- count1, count2 = processor.process_video(progress_bar=progress_bar)
187
  end_time = time.time()
188
 
189
  # Calcul du temps de traitement
@@ -214,5 +429,106 @@ def main():
214
  else:
215
  st.error("❌ Les coordonnées des polygones doivent contenir **exactement 4 points**.")
216
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  if __name__ == "__main__":
218
  main()
 
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
 
12
  # --- FONCTIONS UTILES ---
13
  def draw_text_with_background(image, text, position, font=cv2.FONT_HERSHEY_SIMPLEX,
 
23
  cv2.rectangle(image, top_left, bottom_right, bg_color, -1)
24
  cv2.putText(image, text, (x + padding, y), font, font_scale, text_color, font_thickness, cv2.LINE_AA)
25
 
26
+ # --- CLASSE YOLO OPTIMISÉE ---
27
  class YOLOVideoProcessor:
28
+ def __init__(self, model_path, poly1, poly2, tracker_method="bot"):
29
+ # Déterminer le meilleur device disponible
30
+ self.device = 'cuda' if torch.cuda.is_available() else 'cpu'
31
+
32
+ # Paramètres d'optimisation
33
+ self.frame_skip = 2 # Traiter une image sur N
34
+ self.downsample_factor = 0.5 # Réduire la taille des images de 50%
35
+ self.img_size = 640 # Taille d'entrée fixe pour YOLO
36
+ self.conf_threshold = 0.35 # Seuil de confiance plus élevé
37
+
38
+ # Charger le modèle une seule fois et avec les bons paramètres
39
  self.model = YOLO(model_path, task="detect")
40
+ self.model.to(self.device)
41
+
42
+ # Autres paramètres
43
  self.tracker_method = tracker_method
 
 
 
44
  self.unique_region1_ids = set()
45
  self.unique_region2_ids = set()
46
  self.poly1 = poly1
47
  self.poly2 = poly2
48
+ self.stop_processing = False
49
+ self.last_processed_frame = None
50
+ self.current_frame = 0
51
+
52
+ # Préparer le tracker une seule fois
53
+ self.tracker_config = "botsort.yaml" if self.tracker_method.lower() == "bot" else "bytetrack.yaml"
54
 
55
  def is_in_region(self, center, poly):
56
  poly_np = np.array(poly, dtype=np.int32)
57
  return cv2.pointPolygonTest(poly_np, center, False) >= 0
58
 
59
+ def reset_counts(self):
60
+ """Réinitialiser les compteurs"""
61
+ self.unique_region1_ids = set()
62
+ self.unique_region2_ids = set()
63
+
64
+ def process_video(self, video_path, output_path, progress_bar=None):
65
+ """Traite une vidéo enregistrée avec optimisations"""
66
+ cap = cv2.VideoCapture(video_path)
67
  if not cap.isOpened():
68
  st.error("⚠️ Erreur : Impossible d'ouvrir la vidéo.")
69
  return
 
77
 
78
  # Utiliser XVID qui est généralement mieux supporté
79
  fourcc = cv2.VideoWriter_fourcc(*'XVID')
80
+ out = cv2.VideoWriter(output_path, fourcc, fps, (frame_width, frame_height))
81
 
82
  processed_frames = 0
83
  total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
84
+ frame_count = 0
85
 
86
  while cap.isOpened():
87
  success, frame = cap.read()
 
92
  if progress_bar is not None:
93
  progress_bar.progress(processed_frames / total_frames)
94
 
95
+ # Ne traiter qu'une image sur N pour accélérer le traitement
96
+ if frame_count % self.frame_skip == 0:
97
+ processed_frame = self.process_frame(frame)
98
+ self.last_processed_frame = processed_frame
99
+ else:
100
+ # Réutiliser le dernier frame traité, mais mettre à jour juste les compteurs
101
+ processed_frame = self.last_processed_frame if self.last_processed_frame is not None else frame
102
+
103
+ out.write(processed_frame)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  processed_frames += 1
105
+ frame_count += 1
106
 
107
  cap.release()
108
  out.release()
 
113
 
114
  return len(self.unique_region1_ids), len(self.unique_region2_ids)
115
 
116
+ def process_frame(self, frame):
117
+ """Traite une image individuelle avec YOLO et le tracking, avec optimisations"""
118
+ if frame is None:
119
+ return None
120
+
121
+ # Redimensionner l'image pour accélérer le traitement
122
+ orig_height, orig_width = frame.shape[:2]
123
+ if self.downsample_factor < 1.0:
124
+ resized_width = int(orig_width * self.downsample_factor)
125
+ resized_height = int(orig_height * self.downsample_factor)
126
+ resized_frame = cv2.resize(frame, (resized_width, resized_height), interpolation=cv2.INTER_AREA)
127
+ else:
128
+ resized_frame = frame
129
+
130
+ # Processus de détection avec YOLO
131
+ with torch.no_grad(): # Désactiver le calcul des gradients pour économiser de la mémoire
132
+ results = self.model.track(
133
+ resized_frame,
134
+ persist=True,
135
+ tracker=self.tracker_config,
136
+ conf=self.conf_threshold,
137
+ imgsz=self.img_size
138
+ )
139
+
140
+ # Créer une copie du frame original pour l'affichage
141
+ display_frame = frame.copy()
142
+ frame_height, frame_width = display_frame.shape[:2]
143
+
144
+ # Dessiner les polygones
145
+ cv2.polylines(display_frame, [np.array(self.poly1, np.int32)], isClosed=True, color=(0, 255, 0), thickness=2)
146
+ cv2.polylines(display_frame, [np.array(self.poly2, np.int32)], isClosed=True, color=(255, 0, 0), thickness=2)
147
+
148
+ # Facteur d'échelle pour ajuster les coordonnées si on a redimensionné l'image
149
+ scale_x = orig_width / resized_width if self.downsample_factor < 1.0 else 1.0
150
+ scale_y = orig_height / resized_height if self.downsample_factor < 1.0 else 1.0
151
+
152
+ track_ids = []
153
+ if results and len(results) > 0 and len(results[0].boxes) > 0:
154
+ try:
155
+ boxes = results[0].boxes.xywh.cpu().numpy()
156
+ track_ids = results[0].boxes.id.int().cpu().tolist()
157
+
158
+ # Dessiner les détections et mettre à jour les compteurs
159
+ for i, (box, track_id) in enumerate(zip(boxes, track_ids)):
160
+ x, y, w, h = box
161
+ # Ajuster les coordonnées au frame original
162
+ center_x = int(x * scale_x)
163
+ center_y = int(y * scale_y)
164
+ center_point = (center_x, center_y)
165
+
166
+ # Vérifier les régions
167
+ if self.is_in_region(center_point, self.poly1):
168
+ self.unique_region1_ids.add(track_id)
169
+ if self.is_in_region(center_point, self.poly2):
170
+ self.unique_region2_ids.add(track_id)
171
+
172
+ # Optionnel: dessiner les boîtes de détection
173
+ width = int(w * scale_x)
174
+ height = int(h * scale_y)
175
+ top_left = (center_x - width // 2, center_y - height // 2)
176
+ bottom_right = (center_x + width // 2, center_y + height // 2)
177
+ cv2.rectangle(display_frame, top_left, bottom_right, (0, 255, 0), 2)
178
+
179
+ except AttributeError:
180
+ pass
181
+
182
+ # Affichage du comptage des véhicules
183
+ draw_text_with_background(display_frame, f'Total Sens 1: {len(self.unique_region1_ids)}', (10, frame_height - 50))
184
+ draw_text_with_background(display_frame, f'Total Sens 2: {len(self.unique_region2_ids)}', (frame_width - 300, frame_height - 50))
185
+
186
+ return display_frame
187
+
188
+ def process_webcam(self, camera_id=0, display_placeholder=None, count_placeholders=None):
189
+ """Traite la vidéo en temps réel depuis une webcam, optimisé pour la vitesse"""
190
+ cap = cv2.VideoCapture(camera_id)
191
+
192
+ # Configuration de la webcam - résolution plus petite pour plus de FPS
193
+ cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
194
+ cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
195
+ cap.set(cv2.CAP_PROP_FPS, 30) # Essayer d'obtenir 30 FPS
196
+
197
+ if not cap.isOpened():
198
+ st.error("⚠️ Erreur : Impossible d'ouvrir la webcam.")
199
+ return
200
+
201
+ # Réinitialiser les compteurs pour la nouvelle session
202
+ self.reset_counts()
203
+ self.stop_processing = False
204
+ frame_count = 0
205
+ fps_start_time = time.time()
206
+ fps = 0
207
+
208
+ while not self.stop_processing:
209
+ success, frame = cap.read()
210
+ if not success:
211
+ st.error("⚠️ Erreur lors de la lecture du flux vidéo.")
212
+ break
213
+
214
+ # Ne traiter qu'une image sur N (frame_skip)
215
+ if frame_count % self.frame_skip == 0:
216
+ # Mesurer le temps de traitement
217
+ start_time = time.time()
218
+
219
+ processed_frame = self.process_frame(frame)
220
+ self.last_processed_frame = processed_frame
221
+
222
+ # Calculer et afficher le FPS
223
+ frame_time = time.time() - start_time
224
+ fps = 1 / (time.time() - fps_start_time)
225
+ fps_start_time = time.time()
226
+
227
+ # Ajouter info FPS
228
+ if processed_frame is not None:
229
+ fps_text = f"FPS: {fps:.1f}"
230
+ draw_text_with_background(processed_frame, fps_text, (10, 30))
231
+ else:
232
+ # Utiliser le dernier frame traité pour économiser du temps de calcul
233
+ processed_frame = self.last_processed_frame if self.last_processed_frame is not None else frame
234
+
235
+ # Convertir l'image OpenCV en format compatible avec Streamlit
236
+ if processed_frame is not None:
237
+ processed_frame_rgb = cv2.cvtColor(processed_frame, cv2.COLOR_BGR2RGB)
238
+ img = Image.fromarray(processed_frame_rgb)
239
+
240
+ # Afficher l'image dans le placeholder Streamlit
241
+ if display_placeholder:
242
+ display_placeholder.image(img, channels="RGB", use_column_width=True)
243
+
244
+ # Mettre à jour les compteurs
245
+ if count_placeholders and len(count_placeholders) >= 2:
246
+ count_placeholders[0].metric("Véhicules Sens 1 (Vert)", len(self.unique_region1_ids))
247
+ count_placeholders[1].metric("Véhicules Sens 2 (Rouge)", len(self.unique_region2_ids))
248
+
249
+ frame_count += 1
250
+
251
+ # Attendre un peu moins pour augmenter le FPS
252
+ time.sleep(0.01)
253
+
254
+ cap.release()
255
+ st.success("✅ Flux vidéo arrêté.")
256
+
257
+
258
+ # --- NOUVELLE FONCTION POUR AFFICHER PRÉVISUALISATION DE LA WEBCAM ---
259
+ def display_webcam_preview(camera_id, display_placeholder, poly1=None, poly2=None):
260
+ """Affiche une prévisualisation simple de la webcam sans détection"""
261
+ cap = cv2.VideoCapture(camera_id)
262
+
263
+ # Configuration de la webcam pour une bonne performance
264
+ cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
265
+ cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
266
+ cap.set(cv2.CAP_PROP_FPS, 30)
267
+
268
+ if not cap.isOpened():
269
+ st.error("⚠️ Erreur : Impossible d'ouvrir la webcam.")
270
+ return False
271
+
272
+ stop_preview = False
273
+ st.session_state.preview_active = True
274
+
275
+ while st.session_state.preview_active and not stop_preview:
276
+ success, frame = cap.read()
277
+ if not success:
278
+ st.error("⚠️ Erreur lors de la lecture du flux vidéo.")
279
+ break
280
+
281
+ # Dessiner les polygones si disponibles
282
+ if poly1 and len(poly1) >= 3:
283
+ cv2.polylines(frame, [np.array(poly1, np.int32)], isClosed=True, color=(0, 255, 0), thickness=2)
284
+ if poly2 and len(poly2) >= 3:
285
+ cv2.polylines(frame, [np.array(poly2, np.int32)], isClosed=True, color=(255, 0, 0), thickness=2)
286
+
287
+ # Ajouter un texte explicatif
288
+ draw_text_with_background(frame, "Prévisualisation (sans détection)", (10, 30))
289
+
290
+ # Convertir et afficher l'image
291
+ frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
292
+ img = Image.fromarray(frame_rgb)
293
+ display_placeholder.image(img, channels="RGB", use_column_width=True)
294
+
295
+ # Petit délai pour ne pas surcharger l'interface
296
+ time.sleep(0.03)
297
+
298
+ cap.release()
299
+ return True
300
+
301
 
302
  # --- INTERFACE STREAMLIT ---
303
  def main():
 
309
 
310
  st.title("🚗 Détection et comptage de Véhicules sur l'Autoroute de l'Avenir")
311
 
312
+ # Session state pour gérer l'état de la webcam et de la prévisualisation
313
+ if 'webcam_active' not in st.session_state:
314
+ st.session_state.webcam_active = False
315
+ if 'preview_active' not in st.session_state:
316
+ st.session_state.preview_active = False
317
+ if 'processor' not in st.session_state:
318
+ st.session_state.processor = None
319
+ if 'preview_thread' not in st.session_state:
320
+ st.session_state.preview_thread = None
321
+
322
  # Vérifier si le modèle existe déjà ou doit être téléchargé
323
  model_path = "best.pt"
324
  if not os.path.exists(model_path):
 
334
  st.warning("⚠️ Utilisation du modèle YOLO standard à la place")
335
  model_path = "yolov8n.pt"
336
 
337
+ # Onglets pour séparer les modes vidéo et webcam
338
+ tab1, tab2 = st.tabs(["📹 Analyse de Vidéo", "🎥 Détection en Temps Réel"])
339
 
340
+ # Paramètres communs entre les onglets
341
+ with st.sidebar:
342
  st.header("🔹 Paramètres")
343
 
344
  # Entrée utilisateur pour les polygones
 
349
  poly2_input = st.text_area("Entrez 4 points (x,y) séparés par des espaces", "678,350 815,350 1203,630 743,630")
350
 
351
  tracker_method = st.selectbox("Méthode de tracking", ["bot", "byte"], index=0)
 
 
 
352
 
353
+ # Paramètres d'optimisation
354
+ st.subheader("🚀 Paramètres d'optimisation")
355
+ frame_skip = st.slider("Skip de frames (plus élevé = plus rapide)", 1, 5, 2)
356
+ downsample = st.slider("Facteur d'échelle (plus petit = plus rapide)", 0.3, 1.0, 0.5, 0.1)
357
+ conf_threshold = st.slider("Seuil de confiance", 0.1, 0.9, 0.35, 0.05)
358
 
359
+ def parse_polygon(input_text):
360
+ try:
361
+ return [tuple(map(int, point.split(','))) for point in input_text.split()]
362
+ except:
363
+ return []
364
+
365
+ poly1 = parse_polygon(poly1_input)
366
+ poly2 = parse_polygon(poly2_input)
367
+
368
+ # Vérifier que les polygones sont valides
369
+ valid_polygons = len(poly1) == 4 and len(poly2) == 4
370
+
371
+ # Onglet 1: Analyse de Vidéo
372
+ with tab1:
373
+ uploaded_file = st.file_uploader("📂 Upload une vidéo", type=["mp4", "avi", "mov"])
374
 
375
  if uploaded_file is not None:
376
  # Créer un dossier temporaire si nécessaire
 
385
  st.video(input_video_path) # Afficher la vidéo d'entrée
386
 
387
  if st.button("▶️ Lancer la détection"):
388
+ if valid_polygons:
389
  # Afficher la barre de progression
390
  progress_text = "🔄 Traitement de la vidéo en cours..."
391
  progress_bar = st.progress(0)
392
 
393
+ # Traitement de la vidéo avec les paramètres d'optimisation
394
+ processor = YOLOVideoProcessor(model_path, poly1, poly2, tracker_method)
395
+ processor.frame_skip = frame_skip
396
+ processor.downsample_factor = downsample
397
+ processor.conf_threshold = conf_threshold
398
 
399
  # Démarrer le traitement
400
  start_time = time.time()
401
+ count1, count2 = processor.process_video(input_video_path, output_video_path, progress_bar=progress_bar)
402
  end_time = time.time()
403
 
404
  # Calcul du temps de traitement
 
429
  else:
430
  st.error("❌ Les coordonnées des polygones doivent contenir **exactement 4 points**.")
431
 
432
+ # Onglet 2: Détection en Temps Réel
433
+ with tab2:
434
+ st.header("Détection en Temps Réel avec Webcam")
435
+
436
+ # Sélectionner la source vidéo
437
+ camera_options = {"Webcam par défaut": 0}
438
+ # Ajouter des options pour d'autres caméras si disponibles
439
+ for i in range(1, 5): # Essayer de détecter jusqu'à 5 caméras
440
+ try:
441
+ cap = cv2.VideoCapture(i)
442
+ if cap.isOpened():
443
+ camera_options[f"Caméra {i}"] = i
444
+ cap.release()
445
+ except:
446
+ pass
447
+
448
+ selected_camera = st.selectbox("Sélectionnez la source vidéo", list(camera_options.keys()))
449
+ camera_id = camera_options[selected_camera]
450
+
451
+ # Affichage des placeholders
452
+ video_placeholder = st.empty()
453
+ col1, col2 = st.columns(2)
454
+ count_placeholders = [col1.empty(), col2.empty()]
455
+
456
+ # Nouvelle section pour la prévisualisation
457
+ st.subheader("1️⃣ Prévisualisation de la vidéo")
458
+ preview_col1, preview_col2 = st.columns(2)
459
+
460
+ with preview_col1:
461
+ if st.button("👁️ Afficher la prévisualisation"):
462
+ # Arrêter la détection si elle est active
463
+ if st.session_state.webcam_active:
464
+ st.session_state.processor.stop_processing = True
465
+ st.session_state.webcam_active = False
466
+ time.sleep(0.5) # Attendre que le thread se termine
467
+
468
+ # Démarrer la prévisualisation
469
+ if not st.session_state.preview_active:
470
+ st.session_state.preview_active = True
471
+ # Démarrer la prévisualisation dans un thread séparé
472
+ st.session_state.preview_thread = threading.Thread(
473
+ target=display_webcam_preview,
474
+ args=(camera_id, video_placeholder, poly1, poly2)
475
+ )
476
+ st.session_state.preview_thread.daemon = True
477
+ st.session_state.preview_thread.start()
478
+
479
+ with preview_col2:
480
+ if st.button("⏹️ Arrêter la prévisualisation"):
481
+ st.session_state.preview_active = False
482
+ time.sleep(0.5) # Attendre que le thread se termine
483
+ video_placeholder.empty() # Effacer l'affichage vidéo
484
+
485
+ # Section de détection
486
+ st.subheader("2️⃣ Détection des véhicules")
487
+
488
+ # Afficher les infos sur les performances
489
+ st.info("ℹ️ **Optimisations appliquées:** Redimensionnement des images, skip de frames, et utilisation de CUDA si disponible")
490
+
491
+ # Boutons pour démarrer/arrêter la webcam
492
+ detect_col1, detect_col2 = st.columns(2)
493
+
494
+ with detect_col1:
495
+ if st.button("▶️ Démarrer la détection"):
496
+ if not valid_polygons:
497
+ st.error("❌ Les coordonnées des polygones doivent contenir **exactement 4 points**.")
498
+ elif st.session_state.webcam_active:
499
+ st.warning("⚠️ La détection est déjà active !")
500
+ else:
501
+ # Arrêter la prévisualisation si elle est active
502
+ if st.session_state.preview_active:
503
+ st.session_state.preview_active = False
504
+ time.sleep(0.5) # Attendre que le thread se termine
505
+
506
+ # Créer le processeur YOLO avec les paramètres d'optimisation
507
+ processor = YOLOVideoProcessor(model_path, poly1, poly2, tracker_method)
508
+ processor.frame_skip = frame_skip
509
+ processor.downsample_factor = downsample
510
+ processor.conf_threshold = conf_threshold
511
+
512
+ st.session_state.processor = processor
513
+ st.session_state.webcam_active = True
514
+
515
+ # Démarrer le traitement dans un thread séparé
516
+ processing_thread = threading.Thread(
517
+ target=st.session_state.processor.process_webcam,
518
+ args=(camera_id, video_placeholder, count_placeholders)
519
+ )
520
+ processing_thread.daemon = True
521
+ processing_thread.start()
522
+
523
+ with detect_col2:
524
+ if st.button("⏹️ Arrêter la détection"):
525
+ if st.session_state.webcam_active and st.session_state.processor:
526
+ st.session_state.processor.stop_processing = True
527
+ st.session_state.webcam_active = False
528
+ time.sleep(0.5) # Attendre que le thread se termine
529
+ video_placeholder.empty() # Effacer l'affichage vidéo
530
+ else:
531
+ st.warning("⚠️ Aucune détection en cours !")
532
+
533
  if __name__ == "__main__":
534
  main()