trannam1084 commited on
Commit
9e9de80
·
verified ·
1 Parent(s): 982b91e

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -427
app.py DELETED
@@ -1,427 +0,0 @@
1
- import os
2
- import tempfile
3
- import cv2
4
- import numpy as np
5
- import gradio as gr
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
- DEFAULT_ZONE_MARGIN = 0.10
12
-
13
- model = YOLO("yolov8n.pt")
14
- CLASS_NAMES_DICT = model.model.names
15
-
16
- SELECTED_CLASS_NAMES = ['car', 'truck', 'bus', 'motorcycle', ]
17
- SELECTED_CLASS_IDS = [
18
- {value: key for key, value in CLASS_NAMES_DICT.items()}[name]
19
- for name in SELECTED_CLASS_NAMES
20
- ]
21
-
22
- box_annotator = sv.BoxAnnotator(thickness=4)
23
- label_annotator = sv.LabelAnnotator(text_thickness=2, text_scale=1.5, text_color=sv.Color.BLACK)
24
- trace_annotator = sv.TraceAnnotator(thickness=4, trace_length=50)
25
- def process_video(
26
- video_path,
27
- use_resize: bool = True,
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
35
-
36
- if isinstance(video_path, dict):
37
- video_path = video_path.get("path", video_path)
38
-
39
- video_info = sv.VideoInfo.from_video_path(video_path)
40
-
41
- byte_tracker = sv.ByteTrack(
42
- track_activation_threshold=0.25,
43
- lost_track_buffer=30,
44
- minimum_matching_threshold=0.8,
45
- frame_rate=video_info.fps or 30,
46
- minimum_consecutive_frames=3
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))
72
- )
73
- else:
74
- frame_infer = frame
75
- else:
76
- frame_infer = frame
77
-
78
- fh, fw = frame_infer.shape[:2]
79
- if line_orientation == "Dọc":
80
- line_pos = int(fw * 0.5)
81
- is_horizontal = False
82
- else:
83
- line_pos = int(fh * 0.5)
84
- is_horizontal = True
85
-
86
- if zone_margin is None or zone_margin <= 0:
87
- zm_ratio = DEFAULT_ZONE_MARGIN
88
- else:
89
- zm_ratio = max(0.01, min(0.5, float(zone_margin)))
90
-
91
- if is_horizontal:
92
- z_half = int(fh * zm_ratio)
93
- z_top = max(0, line_pos - z_half)
94
- z_bot = min(fh - 1, line_pos + z_half)
95
- else:
96
- z_half = int(fw * zm_ratio)
97
- z_left = max(0, line_pos - z_half)
98
- z_right = min(fw - 1, line_pos + z_half)
99
-
100
- if detect_every > 1 and index % detect_every != 0:
101
- annotator_frame = frame_infer.copy()
102
- overlay_zone = annotator_frame.copy()
103
- if is_horizontal:
104
- cv2.rectangle(
105
- overlay_zone,
106
- (0, z_top),
107
- (fw, z_bot),
108
- (0, 0, 200),
109
- -1,
110
- )
111
- else:
112
- cv2.rectangle(
113
- overlay_zone,
114
- (z_left, 0),
115
- (z_right, fh),
116
- (0, 0, 200),
117
- -1,
118
- )
119
- annotator_frame = cv2.addWeighted(
120
- overlay_zone, 0.18, annotator_frame, 0.82, 0
121
- )
122
-
123
- thickness_base = max(2, int(2 * (max(fw, fh) / 1920)))
124
- if is_horizontal:
125
- cv2.line(
126
- annotator_frame,
127
- (0, z_top),
128
- (fw, z_top),
129
- (0, 100, 255),
130
- thickness_base,
131
- )
132
- cv2.line(
133
- annotator_frame,
134
- (0, z_bot),
135
- (fw, z_bot),
136
- (0, 100, 255),
137
- thickness_base,
138
- )
139
- cv2.line(
140
- annotator_frame,
141
- (0, line_pos),
142
- (fw, line_pos),
143
- (0, 0, 255),
144
- max(3, thickness_base + 1),
145
- )
146
- else:
147
- cv2.line(
148
- annotator_frame,
149
- (z_left, 0),
150
- (z_left, fh),
151
- (0, 100, 255),
152
- thickness_base,
153
- )
154
- cv2.line(
155
- annotator_frame,
156
- (z_right, 0),
157
- (z_right, fh),
158
- (0, 100, 255),
159
- thickness_base,
160
- )
161
- cv2.line(
162
- annotator_frame,
163
- (line_pos, 0),
164
- (line_pos, fh),
165
- (0, 0, 255),
166
- max(3, thickness_base + 1),
167
- )
168
-
169
- box_w, box_h = 280, 50 + len(SELECTED_CLASS_NAMES) * 28
170
- x0, y0 = fw - box_w - 20, 20
171
-
172
- overlay = annotator_frame.copy()
173
- cv2.rectangle(overlay, (x0, y0), (x0 + box_w, y0 + box_h), (0, 0, 0), -1)
174
- annotator_frame = cv2.addWeighted(overlay, 0.6, annotator_frame, 0.4, 0)
175
-
176
- total = sum(class_counts.values())
177
- cv2.putText(
178
- annotator_frame,
179
- f'Total: {total}',
180
- (x0 + 10, y0 + 30),
181
- cv2.FONT_HERSHEY_SIMPLEX,
182
- 0.8,
183
- (0, 255, 0),
184
- 2,
185
- )
186
- for i, cls_name in enumerate(SELECTED_CLASS_NAMES):
187
- cnt = class_counts.get(cls_name, 0)
188
- cv2.putText(
189
- annotator_frame,
190
- f'{cls_name.capitalize()}: {cnt}',
191
- (x0 + 10, y0 + 60 + i * 28),
192
- cv2.FONT_HERSHEY_SIMPLEX,
193
- 0.7,
194
- (255, 255, 255),
195
- 2,
196
- )
197
-
198
- return annotator_frame
199
-
200
- results = model(frame_infer, verbose=False)[0]
201
- detections = sv.Detections.from_ultralytics(results)
202
- detections = detections[np.isin(detections.class_id, SELECTED_CLASS_IDS)]
203
- detections = byte_tracker.update_with_detections(detections)
204
-
205
- if detections.tracker_id is not None:
206
- xyxy = detections.xyxy
207
- for i in range(len(detections)):
208
- tid = int(detections.tracker_id[i])
209
- cls_id = int(detections.class_id[i])
210
- cls_name = CLASS_NAMES_DICT[cls_id]
211
- cx = (xyxy[i, 0] + xyxy[i, 2]) / 2
212
- cy = (xyxy[i, 1] + xyxy[i, 3]) / 2
213
-
214
- if cls_name in SELECTED_CLASS_NAMES and tid not in counted_ids:
215
- if is_horizontal and z_top <= cy <= z_bot:
216
- class_counts[cls_name] = class_counts.get(cls_name, 0) + 1
217
- counted_ids.add(tid)
218
- elif (not is_horizontal) and z_left <= cx <= z_right:
219
- class_counts[cls_name] = class_counts.get(cls_name, 0) + 1
220
- counted_ids.add(tid)
221
-
222
- labels = [
223
- f"#{tid} {CLASS_NAMES_DICT[cid]} {conf:0.2f}"
224
- for conf, cid, tid in zip(
225
- detections.confidence, detections.class_id, detections.tracker_id
226
- )
227
- ]
228
-
229
- annotator_frame = frame_infer.copy()
230
- annotator_frame = trace_annotator.annotate(scene=annotator_frame, detections=detections)
231
- annotator_frame = box_annotator.annotate(scene=annotator_frame, detections=detections)
232
- annotator_frame = label_annotator.annotate(
233
- scene=annotator_frame, detections=detections, labels=labels
234
- )
235
-
236
- overlay_zone = annotator_frame.copy()
237
- if is_horizontal:
238
- cv2.rectangle(
239
- overlay_zone,
240
- (0, z_top),
241
- (fw, z_bot),
242
- (0, 0, 200),
243
- -1,
244
- )
245
- else:
246
- cv2.rectangle(
247
- overlay_zone,
248
- (z_left, 0),
249
- (z_right, fh),
250
- (0, 0, 200),
251
- -1,
252
- )
253
- annotator_frame = cv2.addWeighted(
254
- overlay_zone, 0.18, annotator_frame, 0.82, 0
255
- )
256
-
257
- thickness_base = max(2, int(2 * (max(fw, fh) / 1920)))
258
- if is_horizontal:
259
- cv2.line(
260
- annotator_frame,
261
- (0, z_top),
262
- (fw, z_top),
263
- (0, 100, 255),
264
- thickness_base,
265
- )
266
- cv2.line(
267
- annotator_frame,
268
- (0, z_bot),
269
- (fw, z_bot),
270
- (0, 100, 255),
271
- thickness_base,
272
- )
273
- cv2.line(
274
- annotator_frame,
275
- (0, line_pos),
276
- (fw, line_pos),
277
- (0, 0, 255),
278
- max(3, thickness_base + 1),
279
- )
280
- else:
281
- cv2.line(
282
- annotator_frame,
283
- (z_left, 0),
284
- (z_left, fh),
285
- (0, 100, 255),
286
- thickness_base,
287
- )
288
- cv2.line(
289
- annotator_frame,
290
- (z_right, 0),
291
- (z_right, fh),
292
- (0, 100, 255),
293
- thickness_base,
294
- )
295
- cv2.line(
296
- annotator_frame,
297
- (line_pos, 0),
298
- (line_pos, fh),
299
- (0, 0, 255),
300
- max(3, thickness_base + 1),
301
- )
302
-
303
- box_w, box_h = 280, 50 + len(SELECTED_CLASS_NAMES) * 28
304
- x0, y0 = fw - box_w - 20, 20
305
-
306
- overlay = annotator_frame.copy()
307
- cv2.rectangle(overlay, (x0, y0), (x0 + box_w, y0 + box_h), (0, 0, 0), -1)
308
- annotator_frame = cv2.addWeighted(overlay, 0.6, annotator_frame, 0.4, 0)
309
-
310
- total = sum(class_counts.values())
311
- cv2.putText(
312
- annotator_frame,
313
- f'Total: {total}',
314
- (x0 + 10, y0 + 30),
315
- cv2.FONT_HERSHEY_SIMPLEX,
316
- 0.8,
317
- (0, 255, 0),
318
- 2,
319
- )
320
- for i, cls_name in enumerate(SELECTED_CLASS_NAMES):
321
- cnt = class_counts.get(cls_name, 0)
322
- cv2.putText(
323
- annotator_frame,
324
- f'{cls_name.capitalize()}: {cnt}',
325
- (x0 + 10, y0 + 60 + i * 28),
326
- cv2.FONT_HERSHEY_SIMPLEX,
327
- 0.7,
328
- (255, 255, 255),
329
- 2,
330
- )
331
-
332
- return annotator_frame
333
-
334
- output_path = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False).name
335
- sv.process_video(
336
- source_path=video_path,
337
- target_path=output_path,
338
- callback=callback
339
- )
340
- return output_path
341
-
342
-
343
- 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:
344
- with gr.Row():
345
- gr.Markdown(
346
- """
347
- <div style="display:flex;flex-direction:column;gap:4px;">
348
- <h1 style="margin-bottom:4px;">🚗 Nhận diện phương tiện giao thông (YOLOv8 + ByteTrack)</h1>
349
- <p style="margin:0;font-size:14px;color:#6b7280;">
350
- Thực hiện: <strong>Trần Hải Nam - 223332840</strong>
351
- </p>
352
- </div>
353
- """,
354
- elem_id="header",
355
- )
356
-
357
- with gr.Row():
358
- with gr.Column(scale=1):
359
- gr.Markdown(
360
- "### 🎥 Video đầu vào\n"
361
- "Upload video ngắn (ưu tiên &lt; 30s để xử lý nhanh hơn)."
362
- )
363
- video_input = gr.Video(label="Video đầu vào")
364
-
365
- with gr.Column(scale=1):
366
- gr.Markdown(
367
- "### ✅ Kết quả đã xử lý\n"
368
- "Hiển thị và thống kê số lượng theo lớp."
369
- )
370
- video_output = gr.Video(label="Video đã xử lý")
371
-
372
- # Tùy chọn cấu hình
373
- with gr.Accordion("⚙️ Tùy chọn nâng cao", open=False):
374
- with gr.Row():
375
- use_resize = gr.Checkbox(
376
- value=True, label="Giảm kích thước khung hình trước khi nhận dạng"
377
- )
378
- line_orientation = gr.Radio(
379
- choices=["Ngang", "Dọc"],
380
- value="Ngang",
381
- label="Hướng phương tiện di chuyển",
382
- )
383
- with gr.Row():
384
- max_frame_size = gr.Slider(
385
- minimum=320,
386
- maximum=1280,
387
- value=DEFAULT_MAX_FRAME_SIZE,
388
- step=64,
389
- label="Kích thước tối đa (px)",
390
- )
391
- detect_every_n = gr.Slider(
392
- minimum=1,
393
- maximum=5,
394
- value=DEFAULT_DETECT_EVERY_N_FRAMES,
395
- step=1,
396
- label="Detect mỗi N frame (1 = mọi frame)",
397
- )
398
- zone_margin = gr.Slider(
399
- minimum=0.02,
400
- maximum=0.30,
401
- value=DEFAULT_ZONE_MARGIN,
402
- step=0.01,
403
- label="Độ dày vùng đếm quanh line",
404
- )
405
-
406
- btn = gr.Button("▶️ Xử lý video", variant="primary")
407
- btn.click(
408
- fn=process_video,
409
- inputs=[video_input, use_resize, max_frame_size, detect_every_n, line_orientation, zone_margin],
410
- outputs=video_output,
411
- )
412
-
413
- gr.Markdown(
414
- """
415
- ---
416
- ### ℹ️ Gợi ý sử dụng
417
- - 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).
418
- - Có thể chuyển sang hướng **dọc** trong phần _"Tùy chọn nâng cao"_.
419
- - Vì sử dụng CPU, nên:
420
- - Dùng video **ngắn** (&lt; 30 giây).
421
- - Tăng `Detect mỗi N frame` nếu muốn xử lý nhanh hơn.
422
- - Model sử dụng: **YOLOv8n**.
423
- """
424
- )
425
-
426
- if __name__ == "__main__":
427
- demo.launch()