trannam1084 commited on
Commit
30fa9a3
·
verified ·
1 Parent(s): 423ca7e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +166 -87
app.py CHANGED
@@ -8,7 +8,7 @@ from ultralytics import YOLO
8
 
9
  DEFAULT_MAX_FRAME_SIZE = 640
10
  DEFAULT_DETECT_EVERY_N_FRAMES = 2
11
- DEFAULT_CONF_THRESHOLD = 0.3
12
 
13
  model = YOLO("yolov8n.pt")
14
  CLASS_NAMES_DICT = model.model.names
@@ -28,7 +28,7 @@ def process_video(
28
  max_frame_size: int = DEFAULT_MAX_FRAME_SIZE,
29
  detect_every_n: int = DEFAULT_DETECT_EVERY_N_FRAMES,
30
  line_orientation: str = "Ngang",
31
- performance_mode: str = "Cân bằng", # "Nhanh" | "Cân bằng" | "Đẹp" | "Tuỳ chỉnh"
32
  ):
33
  if video_path is None:
34
  return None
@@ -47,42 +47,25 @@ def process_video(
47
  )
48
  byte_tracker.reset()
49
 
50
- previous_positions = {}
51
  class_counts = {name: 0 for name in SELECTED_CLASS_NAMES}
52
- crossed_ids = set()
53
-
54
- # Cấu hình hiệu năng theo chế độ
55
- if performance_mode == "Nhanh":
56
- effective_max_size = 480
57
- detect_every = 4
58
- conf_threshold = 0.5
59
- enable_trace = False
60
- elif performance_mode == "Đẹp":
61
- effective_max_size = 800
62
- detect_every = 1
63
- conf_threshold = 0.3
64
- enable_trace = True
65
- elif performance_mode == "Cân bằng":
66
- effective_max_size = DEFAULT_MAX_FRAME_SIZE
67
- detect_every = DEFAULT_DETECT_EVERY_N_FRAMES
68
- conf_threshold = 0.4
69
- enable_trace = True
70
- else: # Tuỳ chỉnh
71
- effective_max_size = (
72
- int(max_frame_size) if max_frame_size and max_frame_size > 0 else DEFAULT_MAX_FRAME_SIZE
73
- )
74
- detect_every = (
75
- int(detect_every_n) if detect_every_n and detect_every_n >= 1 else 1
76
- )
77
- conf_threshold = DEFAULT_CONF_THRESHOLD
78
- enable_trace = True
79
 
80
  def callback(frame: np.ndarray, index: int) -> np.ndarray:
81
- nonlocal previous_positions, class_counts, crossed_ids
 
 
 
 
 
 
 
 
 
 
82
 
83
  fh_orig, fw_orig = frame.shape[:2]
84
  if use_resize:
85
- scale = min(1.0, effective_max_size / max(fh_orig, fw_orig))
86
  if scale < 1.0:
87
  frame_infer = cv2.resize(
88
  frame, (int(fw_orig * scale), int(fh_orig * scale))
@@ -93,6 +76,7 @@ def process_video(
93
  frame_infer = frame
94
 
95
  fh, fw = frame_infer.shape[:2]
 
96
  if line_orientation == "Dọc":
97
  line_pos = int(fw * 0.5)
98
  is_horizontal = False
@@ -100,24 +84,89 @@ def process_video(
100
  line_pos = int(fh * 0.5)
101
  is_horizontal = True
102
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  if detect_every > 1 and index % detect_every != 0:
104
  annotator_frame = frame_infer.copy()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
 
 
106
  if is_horizontal:
107
  cv2.line(
108
  annotator_frame,
109
- (int(fw * 0.05), line_pos),
110
- (int(fw * 0.95), line_pos),
111
- (0, 255, 255),
112
- 2,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  )
114
  else:
115
  cv2.line(
116
  annotator_frame,
117
- (line_pos, int(fh * 0.05)),
118
- (line_pos, int(fh * 0.95)),
119
- (0, 255, 255),
120
- 2,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  )
122
 
123
  box_w, box_h = 280, 50 + len(SELECTED_CLASS_NAMES) * 28
@@ -153,13 +202,10 @@ def process_video(
153
 
154
  results = model(frame_infer, verbose=False)[0]
155
  detections = sv.Detections.from_ultralytics(results)
156
- # Lọc theo class quan tâm
157
  detections = detections[np.isin(detections.class_id, SELECTED_CLASS_IDS)]
158
- # Lọc thêm theo confidence để giảm số lượng box
159
- if len(detections) > 0 and hasattr(detections, "confidence"):
160
- detections = detections[detections.confidence >= conf_threshold]
161
  detections = byte_tracker.update_with_detections(detections)
162
 
 
163
  if detections.tracker_id is not None:
164
  xyxy = detections.xyxy
165
  for i in range(len(detections)):
@@ -169,25 +215,14 @@ def process_video(
169
  cx = (xyxy[i, 0] + xyxy[i, 2]) / 2
170
  cy = (xyxy[i, 1] + xyxy[i, 3]) / 2
171
 
172
- curr_coord = cy if is_horizontal else cx
173
-
174
- if tid in previous_positions:
175
- prev_coord = previous_positions[tid]
176
- if (
177
- prev_coord < line_pos
178
- and curr_coord > line_pos
179
- and (tid, "out") not in crossed_ids
180
- ):
181
- crossed_ids.add((tid, "out"))
182
  class_counts[cls_name] = class_counts.get(cls_name, 0) + 1
183
- elif (
184
- prev_coord > line_pos
185
- and curr_coord < line_pos
186
- and (tid, "in") not in crossed_ids
187
- ):
188
- crossed_ids.add((tid, "in"))
189
  class_counts[cls_name] = class_counts.get(cls_name, 0) + 1
190
- previous_positions[tid] = curr_coord
191
 
192
  labels = [
193
  f"#{tid} {CLASS_NAMES_DICT[cid]} {conf:0.2f}"
@@ -197,28 +232,78 @@ def process_video(
197
  ]
198
 
199
  annotator_frame = frame_infer.copy()
200
- if enable_trace:
201
- annotator_frame = trace_annotator.annotate(scene=annotator_frame, detections=detections)
202
  annotator_frame = box_annotator.annotate(scene=annotator_frame, detections=detections)
203
  annotator_frame = label_annotator.annotate(
204
  scene=annotator_frame, detections=detections, labels=labels
205
  )
206
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  if is_horizontal:
208
  cv2.line(
209
  annotator_frame,
210
- (int(fw * 0.05), line_pos),
211
- (int(fw * 0.95), line_pos),
212
- (0, 255, 255),
213
- 2,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
  )
215
  else:
216
  cv2.line(
217
  annotator_frame,
218
- (line_pos, int(fh * 0.05)),
219
- (line_pos, int(fh * 0.95)),
220
- (0, 255, 255),
221
- 2,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
  )
223
 
224
  box_w, box_h = 280, 50 + len(SELECTED_CLASS_NAMES) * 28
@@ -301,39 +386,33 @@ with gr.Blocks(title="Nhận dạng phương tiện giao thông", theme=gr.theme
301
  value="Ngang",
302
  label="Hướng phương tiện di chuyển",
303
  )
304
- with gr.Row():
305
- performance_mode = gr.Radio(
306
- choices=["Nhanh", "Cân bằng", "Đẹp", "Tuỳ chỉnh"],
307
- value="Cân bằng",
308
- label="Chế độ hiệu năng",
309
- )
310
  with gr.Row():
311
  max_frame_size = gr.Slider(
312
  minimum=320,
313
  maximum=1280,
314
  value=DEFAULT_MAX_FRAME_SIZE,
315
  step=64,
316
- label="Kích thước tối đa (px) (chỉ dùng khi 'Tuỳ chỉnh')",
317
  )
318
  detect_every_n = gr.Slider(
319
  minimum=1,
320
  maximum=5,
321
  value=DEFAULT_DETECT_EVERY_N_FRAMES,
322
  step=1,
323
- label="Detect mỗi N frame (chỉ dùng khi 'Tuỳ chỉnh')",
 
 
 
 
 
 
 
324
  )
325
 
326
  btn = gr.Button("▶️ Xử lý video", variant="primary")
327
  btn.click(
328
  fn=process_video,
329
- inputs=[
330
- video_input,
331
- use_resize,
332
- max_frame_size,
333
- detect_every_n,
334
- line_orientation,
335
- performance_mode,
336
- ],
337
  outputs=video_output,
338
  )
339
 
 
8
 
9
  DEFAULT_MAX_FRAME_SIZE = 640
10
  DEFAULT_DETECT_EVERY_N_FRAMES = 2
11
+ DEFAULT_ZONE_MARGIN = 0.10 # Độ dày vùng đếm quanh line (tỉ lệ chiều cao/rộng)
12
 
13
  model = YOLO("yolov8n.pt")
14
  CLASS_NAMES_DICT = model.model.names
 
28
  max_frame_size: int = DEFAULT_MAX_FRAME_SIZE,
29
  detect_every_n: int = DEFAULT_DETECT_EVERY_N_FRAMES,
30
  line_orientation: str = "Ngang",
31
+ zone_margin: float = DEFAULT_ZONE_MARGIN,
32
  ):
33
  if video_path is None:
34
  return None
 
47
  )
48
  byte_tracker.reset()
49
 
 
50
  class_counts = {name: 0 for name in SELECTED_CLASS_NAMES}
51
+ counted_ids = set()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
  def callback(frame: np.ndarray, index: int) -> np.ndarray:
54
+ nonlocal class_counts, counted_ids
55
+
56
+ if max_frame_size is None or max_frame_size <= 0:
57
+ max_size = DEFAULT_MAX_FRAME_SIZE
58
+ else:
59
+ max_size = int(max_frame_size)
60
+
61
+ if detect_every_n is None or detect_every_n < 1:
62
+ detect_every = 1
63
+ else:
64
+ detect_every = int(detect_every_n)
65
 
66
  fh_orig, fw_orig = frame.shape[:2]
67
  if use_resize:
68
+ scale = min(1.0, max_size / max(fh_orig, fw_orig))
69
  if scale < 1.0:
70
  frame_infer = cv2.resize(
71
  frame, (int(fw_orig * scale), int(fh_orig * scale))
 
76
  frame_infer = frame
77
 
78
  fh, fw = frame_infer.shape[:2]
79
+ # Vị trí line theo hướng người dùng chọn
80
  if line_orientation == "Dọc":
81
  line_pos = int(fw * 0.5)
82
  is_horizontal = False
 
84
  line_pos = int(fh * 0.5)
85
  is_horizontal = True
86
 
87
+ # Vùng đếm (zone) quanh line, dày theo tỉ lệ zone_margin
88
+ if zone_margin is None or zone_margin <= 0:
89
+ zm_ratio = DEFAULT_ZONE_MARGIN
90
+ else:
91
+ zm_ratio = max(0.01, min(0.5, float(zone_margin)))
92
+
93
+ if is_horizontal:
94
+ z_half = int(fh * zm_ratio)
95
+ z_top = max(0, line_pos - z_half)
96
+ z_bot = min(fh - 1, line_pos + z_half)
97
+ else:
98
+ z_half = int(fw * zm_ratio)
99
+ z_left = max(0, line_pos - z_half)
100
+ z_right = min(fw - 1, line_pos + z_half)
101
+
102
  if detect_every > 1 and index % detect_every != 0:
103
  annotator_frame = frame_infer.copy()
104
+ # Vẽ vùng đếm (zone) + line trung tâm (không cập nhật đếm để tiết kiệm CPU)
105
+ overlay_zone = annotator_frame.copy()
106
+ if is_horizontal:
107
+ cv2.rectangle(
108
+ overlay_zone,
109
+ (0, z_top),
110
+ (fw, z_bot),
111
+ (0, 0, 200),
112
+ -1,
113
+ )
114
+ else:
115
+ cv2.rectangle(
116
+ overlay_zone,
117
+ (z_left, 0),
118
+ (z_right, fh),
119
+ (0, 0, 200),
120
+ -1,
121
+ )
122
+ annotator_frame = cv2.addWeighted(
123
+ overlay_zone, 0.18, annotator_frame, 0.82, 0
124
+ )
125
 
126
+ thickness_base = max(2, int(2 * (max(fw, fh) / 1920)))
127
  if is_horizontal:
128
  cv2.line(
129
  annotator_frame,
130
+ (0, z_top),
131
+ (fw, z_top),
132
+ (0, 100, 255),
133
+ thickness_base,
134
+ )
135
+ cv2.line(
136
+ annotator_frame,
137
+ (0, z_bot),
138
+ (fw, z_bot),
139
+ (0, 100, 255),
140
+ thickness_base,
141
+ )
142
+ cv2.line(
143
+ annotator_frame,
144
+ (0, line_pos),
145
+ (fw, line_pos),
146
+ (0, 0, 255),
147
+ max(3, thickness_base + 1),
148
  )
149
  else:
150
  cv2.line(
151
  annotator_frame,
152
+ (z_left, 0),
153
+ (z_left, fh),
154
+ (0, 100, 255),
155
+ thickness_base,
156
+ )
157
+ cv2.line(
158
+ annotator_frame,
159
+ (z_right, 0),
160
+ (z_right, fh),
161
+ (0, 100, 255),
162
+ thickness_base,
163
+ )
164
+ cv2.line(
165
+ annotator_frame,
166
+ (line_pos, 0),
167
+ (line_pos, fh),
168
+ (0, 0, 255),
169
+ max(3, thickness_base + 1),
170
  )
171
 
172
  box_w, box_h = 280, 50 + len(SELECTED_CLASS_NAMES) * 28
 
202
 
203
  results = model(frame_infer, verbose=False)[0]
204
  detections = sv.Detections.from_ultralytics(results)
 
205
  detections = detections[np.isin(detections.class_id, SELECTED_CLASS_IDS)]
 
 
 
206
  detections = byte_tracker.update_with_detections(detections)
207
 
208
+ # Đếm theo loại khi đi vào vùng đếm (zone) quanh line
209
  if detections.tracker_id is not None:
210
  xyxy = detections.xyxy
211
  for i in range(len(detections)):
 
215
  cx = (xyxy[i, 0] + xyxy[i, 2]) / 2
216
  cy = (xyxy[i, 1] + xyxy[i, 3]) / 2
217
 
218
+ # Đếm một lần khi ID lần đầu đi vào vùng đếm
219
+ if cls_name in SELECTED_CLASS_NAMES and tid not in counted_ids:
220
+ if is_horizontal and z_top <= cy <= z_bot:
 
 
 
 
 
 
 
221
  class_counts[cls_name] = class_counts.get(cls_name, 0) + 1
222
+ counted_ids.add(tid)
223
+ elif (not is_horizontal) and z_left <= cx <= z_right:
 
 
 
 
224
  class_counts[cls_name] = class_counts.get(cls_name, 0) + 1
225
+ counted_ids.add(tid)
226
 
227
  labels = [
228
  f"#{tid} {CLASS_NAMES_DICT[cid]} {conf:0.2f}"
 
232
  ]
233
 
234
  annotator_frame = frame_infer.copy()
235
+ annotator_frame = trace_annotator.annotate(scene=annotator_frame, detections=detections)
 
236
  annotator_frame = box_annotator.annotate(scene=annotator_frame, detections=detections)
237
  annotator_frame = label_annotator.annotate(
238
  scene=annotator_frame, detections=detections, labels=labels
239
  )
240
 
241
+ # Vẽ vùng đếm (zone) + line trung tâm
242
+ overlay_zone = annotator_frame.copy()
243
+ if is_horizontal:
244
+ cv2.rectangle(
245
+ overlay_zone,
246
+ (0, z_top),
247
+ (fw, z_bot),
248
+ (0, 0, 200),
249
+ -1,
250
+ )
251
+ else:
252
+ cv2.rectangle(
253
+ overlay_zone,
254
+ (z_left, 0),
255
+ (z_right, fh),
256
+ (0, 0, 200),
257
+ -1,
258
+ )
259
+ annotator_frame = cv2.addWeighted(
260
+ overlay_zone, 0.18, annotator_frame, 0.82, 0
261
+ )
262
+
263
+ thickness_base = max(2, int(2 * (max(fw, fh) / 1920)))
264
  if is_horizontal:
265
  cv2.line(
266
  annotator_frame,
267
+ (0, z_top),
268
+ (fw, z_top),
269
+ (0, 100, 255),
270
+ thickness_base,
271
+ )
272
+ cv2.line(
273
+ annotator_frame,
274
+ (0, z_bot),
275
+ (fw, z_bot),
276
+ (0, 100, 255),
277
+ thickness_base,
278
+ )
279
+ cv2.line(
280
+ annotator_frame,
281
+ (0, line_pos),
282
+ (fw, line_pos),
283
+ (0, 0, 255),
284
+ max(3, thickness_base + 1),
285
  )
286
  else:
287
  cv2.line(
288
  annotator_frame,
289
+ (z_left, 0),
290
+ (z_left, fh),
291
+ (0, 100, 255),
292
+ thickness_base,
293
+ )
294
+ cv2.line(
295
+ annotator_frame,
296
+ (z_right, 0),
297
+ (z_right, fh),
298
+ (0, 100, 255),
299
+ thickness_base,
300
+ )
301
+ cv2.line(
302
+ annotator_frame,
303
+ (line_pos, 0),
304
+ (line_pos, fh),
305
+ (0, 0, 255),
306
+ max(3, thickness_base + 1),
307
  )
308
 
309
  box_w, box_h = 280, 50 + len(SELECTED_CLASS_NAMES) * 28
 
386
  value="Ngang",
387
  label="Hướng phương tiện di chuyển",
388
  )
 
 
 
 
 
 
389
  with gr.Row():
390
  max_frame_size = gr.Slider(
391
  minimum=320,
392
  maximum=1280,
393
  value=DEFAULT_MAX_FRAME_SIZE,
394
  step=64,
395
+ label="Kích thước tối đa (px)",
396
  )
397
  detect_every_n = gr.Slider(
398
  minimum=1,
399
  maximum=5,
400
  value=DEFAULT_DETECT_EVERY_N_FRAMES,
401
  step=1,
402
+ label="Detect mỗi N frame (1 = mọi frame)",
403
+ )
404
+ zone_margin = gr.Slider(
405
+ minimum=0.02,
406
+ maximum=0.30,
407
+ value=DEFAULT_ZONE_MARGIN,
408
+ step=0.01,
409
+ label="Độ dày vùng đếm quanh line",
410
  )
411
 
412
  btn = gr.Button("▶️ Xử lý video", variant="primary")
413
  btn.click(
414
  fn=process_video,
415
+ inputs=[video_input, use_resize, max_frame_size, detect_every_n, line_orientation, zone_margin],
 
 
 
 
 
 
 
416
  outputs=video_output,
417
  )
418