trannam1084 commited on
Commit
9da31f5
·
verified ·
1 Parent(s): bf1f29f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +13 -45
app.py CHANGED
@@ -1,6 +1,3 @@
1
- """
2
- Đếm xe qua line - Deploy lên Hugging Face Spaces với Gradio
3
- """
4
  import os
5
  import tempfile
6
  import cv2
@@ -9,21 +6,18 @@ import gradio as gr
9
  import supervision as sv
10
  from ultralytics import YOLO
11
 
12
- # Cấu hình mặc định cho tối ưu
13
- DEFAULT_MAX_FRAME_SIZE = 640 # Giới hạn kích thước khung hình đưa vào YOLO
14
- DEFAULT_DETECT_EVERY_N_FRAMES = 2 # Chỉ detect mỗi N frame để giảm tải CPU
15
 
16
- # Load model (yolov8n nhẹ cho CPU miễn phí)
17
  model = YOLO("yolov8n.pt")
18
  CLASS_NAMES_DICT = model.model.names
19
 
20
- SELECTED_CLASS_NAMES = ['person', 'bus', 'motorcycle', 'car', 'truck']
21
  SELECTED_CLASS_IDS = [
22
  {value: key for key, value in CLASS_NAMES_DICT.items()}[name]
23
  for name in SELECTED_CLASS_NAMES
24
  ]
25
 
26
- # Annotators (tạo 1 lần, dùng lại)
27
  box_annotator = sv.BoxAnnotator(thickness=4)
28
  label_annotator = sv.LabelAnnotator(text_thickness=2, text_scale=1.5, text_color=sv.Color.BLACK)
29
  trace_annotator = sv.TraceAnnotator(thickness=4, trace_length=50)
@@ -32,24 +26,14 @@ def process_video(
32
  use_resize: bool = True,
33
  max_frame_size: int = DEFAULT_MAX_FRAME_SIZE,
34
  detect_every_n: int = DEFAULT_DETECT_EVERY_N_FRAMES,
35
- line_orientation: str = "Ngang", # "Ngang" | "Dọc"
36
  ):
37
- """Xử lý video: đếm xe qua line và trả về video đã annotate.
38
-
39
- Các tham số tối ưu và cấu hình được truyền từ UI:
40
- - use_resize: có resize khung hình trước khi detect hay không
41
- - max_frame_size: cạnh dài tối đa sau resize
42
- - detect_every_n: chỉ detect mỗi N frame (1 = detect mọi frame)
43
- - line_orientation: "Ngang" hoặc "Dọc"
44
- """
45
  if video_path is None:
46
  return None
47
 
48
- # Gradio Video có thể trả về dict với key "path"
49
  if isinstance(video_path, dict):
50
  video_path = video_path.get("path", video_path)
51
 
52
- # Lấy thông tin video (dùng cho tracker)
53
  video_info = sv.VideoInfo.from_video_path(video_path)
54
 
55
  byte_tracker = sv.ByteTrack(
@@ -68,7 +52,6 @@ def process_video(
68
  def callback(frame: np.ndarray, index: int) -> np.ndarray:
69
  nonlocal previous_positions, class_counts, crossed_ids
70
 
71
- # Đảm bảo giá trị hợp lệ
72
  if max_frame_size is None or max_frame_size <= 0:
73
  max_size = DEFAULT_MAX_FRAME_SIZE
74
  else:
@@ -79,7 +62,6 @@ def process_video(
79
  else:
80
  detect_every = int(detect_every_n)
81
 
82
- # Resize khung hình trước khi đưa vào YOLO để giảm tải (nếu bật)
83
  fh_orig, fw_orig = frame.shape[:2]
84
  if use_resize:
85
  scale = min(1.0, max_size / max(fh_orig, fw_orig))
@@ -92,9 +74,7 @@ def process_video(
92
  else:
93
  frame_infer = frame
94
 
95
- # Tính lại kích thước sau khi resize
96
  fh, fw = frame_infer.shape[:2]
97
- # Vị trí line theo hướng người dùng chọn
98
  if line_orientation == "Dọc":
99
  line_pos = int(fw * 0.5)
100
  is_horizontal = False
@@ -102,11 +82,9 @@ def process_video(
102
  line_pos = int(fh * 0.5)
103
  is_horizontal = True
104
 
105
- # Bỏ qua một số frame để giảm số lần detect
106
  if detect_every > 1 and index % detect_every != 0:
107
  annotator_frame = frame_infer.copy()
108
 
109
- # Vẽ line đếm theo cấu hình
110
  if is_horizontal:
111
  cv2.line(
112
  annotator_frame,
@@ -124,7 +102,6 @@ def process_video(
124
  2,
125
  )
126
 
127
- # Khung tổng đếm (dùng class_counts hiện tại)
128
  box_w, box_h = 280, 50 + len(SELECTED_CLASS_NAMES) * 28
129
  x0, y0 = fw - box_w - 20, 20
130
 
@@ -161,7 +138,6 @@ def process_video(
161
  detections = detections[np.isin(detections.class_id, SELECTED_CLASS_IDS)]
162
  detections = byte_tracker.update_with_detections(detections)
163
 
164
- # Đếm theo loại khi qua line
165
  if detections.tracker_id is not None:
166
  xyxy = detections.xyxy
167
  for i in range(len(detections)):
@@ -171,7 +147,6 @@ def process_video(
171
  cx = (xyxy[i, 0] + xyxy[i, 2]) / 2
172
  cy = (xyxy[i, 1] + xyxy[i, 3]) / 2
173
 
174
- # Toạ độ 1D dùng để kiểm tra qua line (y nếu line ngang, x nếu line dọc)
175
  curr_coord = cy if is_horizontal else cx
176
 
177
  if tid in previous_positions:
@@ -206,7 +181,6 @@ def process_video(
206
  scene=annotator_frame, detections=detections, labels=labels
207
  )
208
 
209
- # Vẽ line đếm theo cấu hình
210
  if is_horizontal:
211
  cv2.line(
212
  annotator_frame,
@@ -224,7 +198,6 @@ def process_video(
224
  2,
225
  )
226
 
227
- # Khung tổng đếm
228
  box_w, box_h = 280, 50 + len(SELECTED_CLASS_NAMES) * 28
229
  x0, y0 = fw - box_w - 20, 20
230
 
@@ -265,17 +238,12 @@ def process_video(
265
  return output_path
266
 
267
 
268
- # Gradio UI
269
- with gr.Blocks(title="Đếm xe qua line", theme=gr.themes.Soft(primary_hue="blue", secondary_hue="gray")) as demo:
270
- # Header đẹp hơn + thông tin sinh viên
271
  with gr.Row():
272
  gr.Markdown(
273
  """
274
  <div style="display:flex;flex-direction:column;gap:4px;">
275
- <h1 style="margin-bottom:4px;">🚗 Đếm xe qua line (YOLOv8 + ByteTrack)</h1>
276
- <p style="margin:0;font-size:16px;color:#4b5563;">
277
- Dự án: Đếm đối tượng qua line (Horizonal / Vertical) dùng YOLOv8 + ByteTrack.
278
- </p>
279
  <p style="margin:0;font-size:14px;color:#6b7280;">
280
  Thực hiện: <strong>Trần Hải Nam - 223332840</strong>
281
  </p>
@@ -295,7 +263,7 @@ with gr.Blocks(title="Đếm xe qua line", theme=gr.themes.Soft(primary_hue="blu
295
  with gr.Column(scale=1):
296
  gr.Markdown(
297
  "### ✅ Kết quả đã xử lý\n"
298
- "Hiển thị tracking, line đếm và thống kê số lượng theo lớp."
299
  )
300
  video_output = gr.Video(label="Video đã xử lý")
301
 
@@ -303,12 +271,12 @@ with gr.Blocks(title="Đếm xe qua line", theme=gr.themes.Soft(primary_hue="blu
303
  with gr.Accordion("⚙️ Tùy chọn nâng cao", open=False):
304
  with gr.Row():
305
  use_resize = gr.Checkbox(
306
- value=True, label="Giảm kích thước khung hình trước khi detect"
307
  )
308
  line_orientation = gr.Radio(
309
  choices=["Ngang", "Dọc"],
310
  value="Ngang",
311
- label="Hướng line đếm",
312
  )
313
  with gr.Row():
314
  max_frame_size = gr.Slider(
@@ -337,12 +305,12 @@ with gr.Blocks(title="Đếm xe qua line", theme=gr.themes.Soft(primary_hue="blu
337
  """
338
  ---
339
  ### ℹ️ Gợi ý sử dụng
340
- - Mặc định line đếm nằm **ngang** ở giữa khung hình (50% chiều cao).
341
- - Có thể chuyển sang line **dọc** trong phần _"Tùy chọn nâng cao"_.
342
- - Với CPU miễn phí (Hugging Face Spaces), nên:
343
  - Dùng video **ngắn** (&lt; 30 giây).
344
  - Tăng `Detect mỗi N frame` nếu muốn xử lý nhanh hơn.
345
- - Model sử dụng: **YOLOv8n** (phiên bản nhẹ, phù hợp CPU).
346
  """
347
  )
348
 
 
 
 
 
1
  import os
2
  import tempfile
3
  import cv2
 
6
  import supervision as sv
7
  from ultralytics import YOLO
8
 
9
+ DEFAULT_MAX_FRAME_SIZE = 640
10
+ DEFAULT_DETECT_EVERY_N_FRAMES = 2
 
11
 
 
12
  model = YOLO("yolov8n.pt")
13
  CLASS_NAMES_DICT = model.model.names
14
 
15
+ SELECTED_CLASS_NAMES = ['car', 'truck', 'bus', 'motorcycle', ]
16
  SELECTED_CLASS_IDS = [
17
  {value: key for key, value in CLASS_NAMES_DICT.items()}[name]
18
  for name in SELECTED_CLASS_NAMES
19
  ]
20
 
 
21
  box_annotator = sv.BoxAnnotator(thickness=4)
22
  label_annotator = sv.LabelAnnotator(text_thickness=2, text_scale=1.5, text_color=sv.Color.BLACK)
23
  trace_annotator = sv.TraceAnnotator(thickness=4, trace_length=50)
 
26
  use_resize: bool = True,
27
  max_frame_size: int = DEFAULT_MAX_FRAME_SIZE,
28
  detect_every_n: int = DEFAULT_DETECT_EVERY_N_FRAMES,
29
+ line_orientation: str = "Ngang",
30
  ):
 
 
 
 
 
 
 
 
31
  if video_path is None:
32
  return None
33
 
 
34
  if isinstance(video_path, dict):
35
  video_path = video_path.get("path", video_path)
36
 
 
37
  video_info = sv.VideoInfo.from_video_path(video_path)
38
 
39
  byte_tracker = sv.ByteTrack(
 
52
  def callback(frame: np.ndarray, index: int) -> np.ndarray:
53
  nonlocal previous_positions, class_counts, crossed_ids
54
 
 
55
  if max_frame_size is None or max_frame_size <= 0:
56
  max_size = DEFAULT_MAX_FRAME_SIZE
57
  else:
 
62
  else:
63
  detect_every = int(detect_every_n)
64
 
 
65
  fh_orig, fw_orig = frame.shape[:2]
66
  if use_resize:
67
  scale = min(1.0, max_size / max(fh_orig, fw_orig))
 
74
  else:
75
  frame_infer = frame
76
 
 
77
  fh, fw = frame_infer.shape[:2]
 
78
  if line_orientation == "Dọc":
79
  line_pos = int(fw * 0.5)
80
  is_horizontal = False
 
82
  line_pos = int(fh * 0.5)
83
  is_horizontal = True
84
 
 
85
  if detect_every > 1 and index % detect_every != 0:
86
  annotator_frame = frame_infer.copy()
87
 
 
88
  if is_horizontal:
89
  cv2.line(
90
  annotator_frame,
 
102
  2,
103
  )
104
 
 
105
  box_w, box_h = 280, 50 + len(SELECTED_CLASS_NAMES) * 28
106
  x0, y0 = fw - box_w - 20, 20
107
 
 
138
  detections = detections[np.isin(detections.class_id, SELECTED_CLASS_IDS)]
139
  detections = byte_tracker.update_with_detections(detections)
140
 
 
141
  if detections.tracker_id is not None:
142
  xyxy = detections.xyxy
143
  for i in range(len(detections)):
 
147
  cx = (xyxy[i, 0] + xyxy[i, 2]) / 2
148
  cy = (xyxy[i, 1] + xyxy[i, 3]) / 2
149
 
 
150
  curr_coord = cy if is_horizontal else cx
151
 
152
  if tid in previous_positions:
 
181
  scene=annotator_frame, detections=detections, labels=labels
182
  )
183
 
 
184
  if is_horizontal:
185
  cv2.line(
186
  annotator_frame,
 
198
  2,
199
  )
200
 
 
201
  box_w, box_h = 280, 50 + len(SELECTED_CLASS_NAMES) * 28
202
  x0, y0 = fw - box_w - 20, 20
203
 
 
238
  return output_path
239
 
240
 
241
+ with gr.Blocks(title="Nhận dạng phương tiện giao thông", theme=gr.themes.Soft(primary_hue="blue", secondary_hue="gray")) as demo:
 
 
242
  with gr.Row():
243
  gr.Markdown(
244
  """
245
  <div style="display:flex;flex-direction:column;gap:4px;">
246
+ <h1 style="margin-bottom:4px;">🚗 Nhận diện phương tiện giao thông (YOLOv8 + ByteTrack)</h1>
 
 
 
247
  <p style="margin:0;font-size:14px;color:#6b7280;">
248
  Thực hiện: <strong>Trần Hải Nam - 223332840</strong>
249
  </p>
 
263
  with gr.Column(scale=1):
264
  gr.Markdown(
265
  "### ✅ Kết quả đã xử lý\n"
266
+ "Hiển thị và thống kê số lượng theo lớp."
267
  )
268
  video_output = gr.Video(label="Video đã xử lý")
269
 
 
271
  with gr.Accordion("⚙️ Tùy chọn nâng cao", open=False):
272
  with gr.Row():
273
  use_resize = gr.Checkbox(
274
+ value=True, label="Giảm kích thước khung hình trước khi nhận dạng"
275
  )
276
  line_orientation = gr.Radio(
277
  choices=["Ngang", "Dọc"],
278
  value="Ngang",
279
+ label="Hướng phương tiện di chuyển",
280
  )
281
  with gr.Row():
282
  max_frame_size = gr.Slider(
 
305
  """
306
  ---
307
  ### ℹ️ Gợi ý sử dụng
308
+ - Mặc định hướng phương tiện di chuyển để nhận dạng là nằm **ngang** ở giữa khung hình (50% chiều cao).
309
+ - Có thể chuyển sang hướng **dọc** trong phần _"Tùy chọn nâng cao"_.
310
+ - sử dụng CPU, nên:
311
  - Dùng video **ngắn** (&lt; 30 giây).
312
  - Tăng `Detect mỗi N frame` nếu muốn xử lý nhanh hơn.
313
+ - Model sử dụng: **YOLOv8n**.
314
  """
315
  )
316