Binta26 commited on
Commit
7425e34
Β·
verified Β·
1 Parent(s): 7d0e05b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +101 -44
app.py CHANGED
@@ -2,6 +2,7 @@
2
  Streamlit Web Interface – Traffic Detection
3
  Supports: YOLOv11 (tracking) | SSD MobileNetV3
4
  GPU forced automatically.
 
5
  """
6
 
7
  import streamlit as st
@@ -16,13 +17,20 @@ from ultralytics import YOLO
16
  from datetime import datetime
17
  from utils.yolo_tracker import normalize_class_name
18
 
 
19
  if 'global_unique_ids' not in st.session_state:
20
  st.session_state.global_unique_ids = {}
21
-
22
- def refine_class_by_shape(cls_name: str, x1: int, y1: int, x2: int, y2: int) -> str:
 
 
 
 
 
 
23
  w = max(1, x2 - x1)
24
  h = max(1, y2 - y1)
25
- if cls_name == "car" and (h / w) > 1.2:
26
  return "person"
27
  return cls_name
28
 
@@ -60,13 +68,13 @@ if model_choice == "YOLOv11 + ByteTrack (Base)":
60
  elif model_choice == "YOLOv11 + ByteTrack (Fine-tuned)":
61
  selected_yolo_path = YOLO_FINETUNED_PATH
62
  if os.path.exists(selected_yolo_path):
63
- st.sidebar.success("Weights: `models/best.pt` ")
64
  else:
65
  st.sidebar.error("`models/best.pt` not found.")
66
  else:
67
  selected_yolo_path = None
68
 
69
- selected_classes = st.sidebar.multiselect(
70
  "Classes to detect", TRAFFIC_CLASSES,
71
  default=['car', 'truck', 'bus', 'person', 'traffic light']
72
  )
@@ -76,33 +84,77 @@ min_track_hits = st.sidebar.number_input("Min track hits (YOLO only)", min
76
  shape_refine = st.sidebar.checkbox("Reclassify tall 'car' as person", value=True)
77
  person_car_ratio = st.sidebar.slider("Person/Car ratio (H/W)", 0.8, 2.0, 1.2, 0.05)
78
 
 
 
 
 
 
 
 
 
 
79
  uploaded_file = st.file_uploader(
80
- "πŸ“€ Upload a video (mp4, avi, mov)",
81
  type=["mp4", "avi", "mov"]
82
  )
83
 
84
- # ── Main ───────────────────────────────────────────────────────────────────────
85
- if not selected_classes:
86
- st.warning("Select at least one class in the sidebar.")
87
-
88
- elif uploaded_file:
89
- tfile = tempfile.NamedTemporaryFile(delete=False, suffix='.mp4')
90
- tfile.write(uploaded_file.read())
91
- tfile.flush()
92
-
93
- col_video, col_stats = st.columns([2, 1])
94
- with col_video:
95
- st.subheader("πŸ“Ή Annotated video")
96
- frame_display = st.empty()
97
- status_display = st.empty()
98
- with col_stats:
99
- st.subheader("πŸ”’ Unique object counters")
100
- counter_display = st.empty()
101
- progress_bar = st.progress(0)
102
-
103
- if st.button("▢️ Start Detection", type="primary"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  st.session_state.global_unique_ids = {}
105
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  # ── YOLOv11 ───────────────────────────────────────────────────────────
107
  if model_choice in ("YOLOv11 + ByteTrack (Base)",
108
  "YOLOv11 + ByteTrack (Fine-tuned)"):
@@ -111,11 +163,10 @@ elif uploaded_file:
111
  st.stop()
112
 
113
  model = YOLO(selected_yolo_path)
114
- cap = cv.VideoCapture(tfile.name)
115
  fps = cap.get(cv.CAP_PROP_FPS) or 25
116
  total = int(cap.get(cv.CAP_PROP_FRAME_COUNT))
117
  track_hits = {}
118
- logs = []
119
  frame_idx = 0
120
  log_path = f"logs/log_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
121
  name_to_id = {v: k for k, v in model.names.items()}
@@ -145,10 +196,8 @@ elif uploaded_file:
145
  x1, y1, x2, y2 = map(int, box.xyxy[0])
146
 
147
  if shape_refine:
148
- w = max(1, x2 - x1)
149
- h = max(1, y2 - y1)
150
- if cls_name == "car" and (h / w) > person_car_ratio:
151
- cls_name = "person"
152
 
153
  area = max(0, x2-x1) * max(0, y2-y1)
154
  if cls_name not in selected_classes: continue
@@ -176,7 +225,8 @@ elif uploaded_file:
176
 
177
  if no_object:
178
  cv.putText(annotated, "No selected object detected",
179
- (20, 50), cv.FONT_HERSHEY_SIMPLEX, 1.0, (0,0,255), 2)
 
180
  status_display.warning("⚠️ No selected object in this frame")
181
  else:
182
  status_display.empty()
@@ -208,7 +258,7 @@ elif uploaded_file:
208
  'class', 'x1', 'y1', 'x2', 'y2', 'confidence'])
209
  writer.writerows(logs)
210
 
211
- st.success(f"Done β€” {frame_idx} frames analyzed")
212
 
213
  # ── SSD ───────────────────────────────────────────────────────────────
214
  else:
@@ -241,15 +291,14 @@ elif uploaded_file:
241
  ssd_model.to(device)
242
  ssd_model.eval()
243
 
244
- cap = cv.VideoCapture(tfile.name)
245
  fps = cap.get(cv.CAP_PROP_FPS) or 25
246
  total = int(cap.get(cv.CAP_PROP_FRAME_COUNT))
247
- logs = []
248
  frame_idx = 0
249
  log_path = f"logs/ssd_log_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
250
  np.random.seed(42)
251
- COLORS = np.random.randint(0, 255, size=(len(COCO_CLASSES), 3),
252
- dtype="uint8")
253
  seen_classes = set()
254
 
255
  while cap.isOpened():
@@ -279,10 +328,8 @@ elif uploaded_file:
279
 
280
  x1, y1, x2, y2 = map(int, box)
281
  if shape_refine:
282
- w = max(1, x2 - x1)
283
- h = max(1, y2 - y1)
284
- if cls_name == "car" and (h / w) > person_car_ratio:
285
- cls_name = "person"
286
 
287
  area = max(0, x2-x1) * max(0, y2-y1)
288
  if cls_name not in selected_classes: continue
@@ -332,7 +379,9 @@ elif uploaded_file:
332
  'x1','y1','x2','y2','confidence'])
333
  writer.writerows(logs)
334
 
335
- st.success(f"Done β€” {frame_idx} frames analyzed")
 
 
336
 
337
  # ── Final Stats ────────────────────────────────────────────────────────
338
  if logs:
@@ -371,7 +420,15 @@ elif uploaded_file:
371
  mime="text/csv"
372
  )
373
 
374
- os.unlink(tfile.name)
 
 
 
 
 
375
 
376
  else:
 
 
 
377
  st.info("πŸ‘† Upload a video to start.")
 
2
  Streamlit Web Interface – Traffic Detection
3
  Supports: YOLOv11 (tracking) | SSD MobileNetV3
4
  GPU forced automatically.
5
+ Upload and processing are separated to avoid 403 errors.
6
  """
7
 
8
  import streamlit as st
 
17
  from datetime import datetime
18
  from utils.yolo_tracker import normalize_class_name
19
 
20
+ # ── Session state init ─────────────────────────────────────────────────────────
21
  if 'global_unique_ids' not in st.session_state:
22
  st.session_state.global_unique_ids = {}
23
+ if 'video_ready' not in st.session_state:
24
+ st.session_state.video_ready = False
25
+ if 'tmp_path' not in st.session_state:
26
+ st.session_state.tmp_path = None
27
+ if 'processing_done' not in st.session_state:
28
+ st.session_state.processing_done = False
29
+
30
+ def refine_class_by_shape(cls_name, x1, y1, x2, y2, ratio=1.2):
31
  w = max(1, x2 - x1)
32
  h = max(1, y2 - y1)
33
+ if cls_name == "car" and (h / w) > ratio:
34
  return "person"
35
  return cls_name
36
 
 
68
  elif model_choice == "YOLOv11 + ByteTrack (Fine-tuned)":
69
  selected_yolo_path = YOLO_FINETUNED_PATH
70
  if os.path.exists(selected_yolo_path):
71
+ st.sidebar.success("Weights: `models/best.pt` βœ…")
72
  else:
73
  st.sidebar.error("`models/best.pt` not found.")
74
  else:
75
  selected_yolo_path = None
76
 
77
+ selected_classes = st.sidebar.multiselect(
78
  "Classes to detect", TRAFFIC_CLASSES,
79
  default=['car', 'truck', 'bus', 'person', 'traffic light']
80
  )
 
84
  shape_refine = st.sidebar.checkbox("Reclassify tall 'car' as person", value=True)
85
  person_car_ratio = st.sidebar.slider("Person/Car ratio (H/W)", 0.8, 2.0, 1.2, 0.05)
86
 
87
+ if not selected_classes:
88
+ st.warning("Select at least one class in the sidebar.")
89
+ st.stop()
90
+
91
+ # ══════════════════════════════════════════════════════════════════════════════
92
+ # Γ‰TAPE 1 β€” UPLOAD
93
+ # ══════════════════════════════════════════════════════════════════════════════
94
+ st.subheader("πŸ“€ Step 1 β€” Upload your video")
95
+
96
  uploaded_file = st.file_uploader(
97
+ "Choose a video file (mp4, avi, mov)",
98
  type=["mp4", "avi", "mov"]
99
  )
100
 
101
+ if uploaded_file is not None:
102
+ # Sauvegarde du fichier en session pour ne pas le reperdre
103
+ if not st.session_state.video_ready:
104
+ with st.spinner("Saving video..."):
105
+ tfile = tempfile.NamedTemporaryFile(delete=False, suffix='.mp4')
106
+ tfile.write(uploaded_file.read())
107
+ tfile.flush()
108
+ tfile.close()
109
+ st.session_state.tmp_path = tfile.name
110
+ st.session_state.video_ready = True
111
+ st.session_state.processing_done = False
112
+ st.session_state.global_unique_ids = {}
113
+
114
+ st.success(f"βœ… Video ready : **{uploaded_file.name}** "
115
+ f"({uploaded_file.size / 1e6:.1f} MB)")
116
+
117
+ # AperΓ§u info vidΓ©o
118
+ cap_info = cv.VideoCapture(st.session_state.tmp_path)
119
+ fps_info = cap_info.get(cv.CAP_PROP_FPS)
120
+ w_info = int(cap_info.get(cv.CAP_PROP_FRAME_WIDTH))
121
+ h_info = int(cap_info.get(cv.CAP_PROP_FRAME_HEIGHT))
122
+ total_info = int(cap_info.get(cv.CAP_PROP_FRAME_COUNT))
123
+ cap_info.release()
124
+
125
+ col1, col2, col3, col4 = st.columns(4)
126
+ col1.metric("Resolution", f"{w_info}x{h_info}")
127
+ col2.metric("FPS", f"{fps_info:.1f}")
128
+ col3.metric("Frames", total_info)
129
+ col4.metric("Duration", f"{total_info/fps_info:.1f}s" if fps_info > 0 else "N/A")
130
+
131
+ st.divider()
132
+
133
+ # ══════════════════════════════════════════════════════════════════════════
134
+ # Γ‰TAPE 2 β€” TRAITEMENT
135
+ # ══════════════════════════════════════════════════════════════════════════
136
+ st.subheader("▢️ Step 2 β€” Run Detection")
137
+
138
+ start = st.button("▢️ Start Detection", type="primary",
139
+ disabled=st.session_state.processing_done)
140
+
141
+ if start:
142
+ st.session_state.processing_done = False
143
  st.session_state.global_unique_ids = {}
144
 
145
+ col_video, col_stats = st.columns([2, 1])
146
+ with col_video:
147
+ st.subheader("πŸ“Ή Annotated video")
148
+ frame_display = st.empty()
149
+ status_display = st.empty()
150
+ with col_stats:
151
+ st.subheader("πŸ”’ Unique counters")
152
+ counter_display = st.empty()
153
+ progress_bar = st.progress(0)
154
+
155
+ tmp_path = st.session_state.tmp_path
156
+ logs = []
157
+
158
  # ── YOLOv11 ───────────────────────────────────────────────────────────
159
  if model_choice in ("YOLOv11 + ByteTrack (Base)",
160
  "YOLOv11 + ByteTrack (Fine-tuned)"):
 
163
  st.stop()
164
 
165
  model = YOLO(selected_yolo_path)
166
+ cap = cv.VideoCapture(tmp_path)
167
  fps = cap.get(cv.CAP_PROP_FPS) or 25
168
  total = int(cap.get(cv.CAP_PROP_FRAME_COUNT))
169
  track_hits = {}
 
170
  frame_idx = 0
171
  log_path = f"logs/log_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
172
  name_to_id = {v: k for k, v in model.names.items()}
 
196
  x1, y1, x2, y2 = map(int, box.xyxy[0])
197
 
198
  if shape_refine:
199
+ cls_name = refine_class_by_shape(
200
+ cls_name, x1, y1, x2, y2, person_car_ratio)
 
 
201
 
202
  area = max(0, x2-x1) * max(0, y2-y1)
203
  if cls_name not in selected_classes: continue
 
225
 
226
  if no_object:
227
  cv.putText(annotated, "No selected object detected",
228
+ (20, 50), cv.FONT_HERSHEY_SIMPLEX,
229
+ 1.0, (0, 0, 255), 2)
230
  status_display.warning("⚠️ No selected object in this frame")
231
  else:
232
  status_display.empty()
 
258
  'class', 'x1', 'y1', 'x2', 'y2', 'confidence'])
259
  writer.writerows(logs)
260
 
261
+ st.success(f"βœ… Done β€” {frame_idx} frames analyzed")
262
 
263
  # ── SSD ───────────────────────────────────────────────────────────────
264
  else:
 
291
  ssd_model.to(device)
292
  ssd_model.eval()
293
 
294
+ cap = cv.VideoCapture(tmp_path)
295
  fps = cap.get(cv.CAP_PROP_FPS) or 25
296
  total = int(cap.get(cv.CAP_PROP_FRAME_COUNT))
 
297
  frame_idx = 0
298
  log_path = f"logs/ssd_log_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
299
  np.random.seed(42)
300
+ COLORS = np.random.randint(0, 255, size=(len(COCO_CLASSES), 3),
301
+ dtype="uint8")
302
  seen_classes = set()
303
 
304
  while cap.isOpened():
 
328
 
329
  x1, y1, x2, y2 = map(int, box)
330
  if shape_refine:
331
+ cls_name = refine_class_by_shape(
332
+ cls_name, x1, y1, x2, y2, person_car_ratio)
 
 
333
 
334
  area = max(0, x2-x1) * max(0, y2-y1)
335
  if cls_name not in selected_classes: continue
 
379
  'x1','y1','x2','y2','confidence'])
380
  writer.writerows(logs)
381
 
382
+ st.success(f"βœ… Done β€” {frame_idx} frames analyzed")
383
+
384
+ st.session_state.processing_done = True
385
 
386
  # ── Final Stats ────────────────────────────────────────────────────────
387
  if logs:
 
420
  mime="text/csv"
421
  )
422
 
423
+ if st.button("πŸ”„ Process another video"):
424
+ st.session_state.video_ready = False
425
+ st.session_state.tmp_path = None
426
+ st.session_state.processing_done = False
427
+ st.session_state.global_unique_ids = {}
428
+ st.rerun()
429
 
430
  else:
431
+ st.session_state.video_ready = False
432
+ st.session_state.tmp_path = None
433
+ st.session_state.processing_done = False
434
  st.info("πŸ‘† Upload a video to start.")