MrSimple01 commited on
Commit
67310f6
Β·
verified Β·
1 Parent(s): aedd790

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +237 -0
app.py ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Videodagi odamlar sonini hisoblash β€” People Counter
3
+ YOLOv8 + ByteTrack/SORT tracker yordamida
4
+ """
5
+
6
+ import gradio as gr
7
+ import cv2
8
+ import numpy as np
9
+ import tempfile
10
+ import os
11
+ from collections import defaultdict
12
+ from ultralytics import YOLO
13
+
14
+ # ── Model yuklash ──────────────────────────────────────────────
15
+ model = YOLO("yolov8n.pt") # nano β€” tez, yengil, yetarli aniqlik
16
+ PERSON_CLASS = 0 # COCO dataset: 0 = person
17
+
18
+ # ── Tracker state (har bir video uchun yangilanadi) ────────────
19
+ class SimpleTracker:
20
+ """
21
+ IoU-asosidagi sodda tracker.
22
+ Har bir frame'dagi detection'larni oldingi track'lar bilan solishtiradi.
23
+ Yangi track_id berib, unique odamlar sonini hisoblaydi.
24
+ """
25
+ def __init__(self, iou_threshold=0.3, max_lost=30):
26
+ self.tracks = {} # track_id -> {'bbox': ..., 'lost': 0}
27
+ self.next_id = 1
28
+ self.unique_ids = set()
29
+ self.iou_thr = iou_threshold
30
+ self.max_lost = max_lost
31
+
32
+ def _iou(self, a, b):
33
+ ax1, ay1, ax2, ay2 = a
34
+ bx1, by1, bx2, by2 = b
35
+ ix1, iy1 = max(ax1, bx1), max(ay1, by1)
36
+ ix2, iy2 = min(ax2, bx2), min(ay2, by2)
37
+ inter = max(0, ix2 - ix1) * max(0, iy2 - iy1)
38
+ if inter == 0:
39
+ return 0.0
40
+ ua = (ax2-ax1)*(ay2-ay1) + (bx2-bx1)*(by2-by1) - inter
41
+ return inter / ua if ua > 0 else 0.0
42
+
43
+ def update(self, detections):
44
+ """detections: list of [x1,y1,x2,y2]"""
45
+ # ── 1. Mavjud track'larni detectionlar bilan moslashtir
46
+ matched_track_ids = set()
47
+ matched_det_idxs = set()
48
+
49
+ track_ids = list(self.tracks.keys())
50
+ for det_idx, det_bbox in enumerate(detections):
51
+ best_iou, best_tid = 0, None
52
+ for tid in track_ids:
53
+ if tid in matched_track_ids:
54
+ continue
55
+ iou = self._iou(det_bbox, self.tracks[tid]['bbox'])
56
+ if iou > best_iou:
57
+ best_iou, best_tid = iou, tid
58
+ if best_iou >= self.iou_thr and best_tid is not None:
59
+ self.tracks[best_tid]['bbox'] = det_bbox
60
+ self.tracks[best_tid]['lost'] = 0
61
+ matched_track_ids.add(best_tid)
62
+ matched_det_idxs.add(det_idx)
63
+
64
+ # ── 2. Mos kelmagan detectionlar β†’ yangi track
65
+ for det_idx, det_bbox in enumerate(detections):
66
+ if det_idx not in matched_det_idxs:
67
+ tid = self.next_id
68
+ self.next_id += 1
69
+ self.tracks[tid] = {'bbox': det_bbox, 'lost': 0}
70
+ self.unique_ids.add(tid)
71
+
72
+ # ── 3. Mos kelmagan track'lar β†’ lost++
73
+ for tid in track_ids:
74
+ if tid not in matched_track_ids:
75
+ self.tracks[tid]['lost'] += 1
76
+
77
+ # ── 4. Ko'p yo'qolgan track'larni o'chir
78
+ self.tracks = {
79
+ tid: v for tid, v in self.tracks.items()
80
+ if v['lost'] < self.max_lost
81
+ }
82
+
83
+ return {
84
+ tid: v['bbox']
85
+ for tid, v in self.tracks.items()
86
+ if v['lost'] == 0
87
+ }
88
+
89
+
90
+ # ── Asosiy hisoblash funksiyasi ────────────────────────────────
91
+ def count_people(video_path, conf_threshold=0.4, progress=gr.Progress()):
92
+ if video_path is None:
93
+ return None, "❌ Video yuklanmadi."
94
+
95
+ cap = cv2.VideoCapture(video_path)
96
+ if not cap.isOpened():
97
+ return None, "❌ Video ochilmadi."
98
+
99
+ # Video parametrlari
100
+ total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
101
+ fps = cap.get(cv2.CAP_PROP_FPS) or 25
102
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
103
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
104
+
105
+ # Output video (annotated)
106
+ out_path = tempfile.mktemp(suffix="_result.mp4")
107
+ fourcc = cv2.VideoWriter_fourcc(*"mp4v")
108
+ writer = cv2.VideoWriter(out_path, fourcc, fps, (width, height))
109
+
110
+ tracker = SimpleTracker(iou_threshold=0.3, max_lost=int(fps * 1.5))
111
+ frame_idx = 0
112
+
113
+ # Rang palitasi
114
+ COLORS = [
115
+ (255, 80, 80), (80, 200, 120), (80, 160, 255),
116
+ (255, 200, 50), (200, 80, 255),(50, 220, 220),
117
+ ]
118
+
119
+ while True:
120
+ ret, frame = cap.read()
121
+ if not ret:
122
+ break
123
+
124
+ # ── YOLO detection
125
+ results = model(frame, classes=[PERSON_CLASS],
126
+ conf=conf_threshold, verbose=False)[0]
127
+
128
+ detections = []
129
+ for box in results.boxes:
130
+ x1, y1, x2, y2 = map(int, box.xyxy[0])
131
+ detections.append([x1, y1, x2, y2])
132
+
133
+ # ── Tracking
134
+ active_tracks = tracker.update(detections)
135
+
136
+ # ── Annotate frame
137
+ for tid, (x1, y1, x2, y2) in active_tracks.items():
138
+ color = COLORS[tid % len(COLORS)]
139
+ cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2)
140
+ label = f"#{tid}"
141
+ (tw, th), _ = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 2)
142
+ cv2.rectangle(frame, (x1, y1 - th - 8), (x1 + tw + 6, y1), color, -1)
143
+ cv2.putText(frame, label, (x1 + 3, y1 - 4),
144
+ cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
145
+
146
+ # ── Counter overlay
147
+ total_unique = len(tracker.unique_ids)
148
+ currently = len(active_tracks)
149
+ overlay_text = f"Jami: {total_unique} ta odam"
150
+ cv2.rectangle(frame, (8, 8), (300, 70), (20, 20, 20), -1)
151
+ cv2.putText(frame, overlay_text, (14, 36),
152
+ cv2.FONT_HERSHEY_SIMPLEX, 0.8, (50, 230, 120), 2)
153
+ cv2.putText(frame, f"Hozir: {currently} ta", (14, 62),
154
+ cv2.FONT_HERSHEY_SIMPLEX, 0.6, (180, 180, 180), 1)
155
+
156
+ writer.write(frame)
157
+ frame_idx += 1
158
+
159
+ if total_frames > 0:
160
+ progress(frame_idx / total_frames, desc=f"Frame {frame_idx}/{total_frames}")
161
+
162
+ cap.release()
163
+ writer.release()
164
+
165
+ # ── Natija matni
166
+ total_unique = len(tracker.unique_ids)
167
+ if total_unique == 0:
168
+ result_text = "🚢 Odam yo'q"
169
+ elif total_unique == 1:
170
+ result_text = "βœ… 1 ta odam"
171
+ else:
172
+ result_text = f"βœ… {total_unique} ta odam"
173
+
174
+ return out_path, result_text
175
+
176
+
177
+ # ── Gradio UI ──────────────────────────────────────────────────
178
+ css = """
179
+ body { font-family: 'Segoe UI', sans-serif; }
180
+ #title { text-align: center; }
181
+ #result-box {
182
+ font-size: 2rem;
183
+ font-weight: 700;
184
+ text-align: center;
185
+ padding: 1.2rem;
186
+ background: #1a1a2e;
187
+ color: #50e37c;
188
+ border-radius: 12px;
189
+ border: 2px solid #50e37c44;
190
+ margin-top: 10px;
191
+ }
192
+ .gr-button-primary { background: #50e37c !important; color: #000 !important; }
193
+ """
194
+
195
+ with gr.Blocks(css=css, title="People Counter") as demo:
196
+ gr.Markdown(
197
+ "# πŸ‘οΈ Videodagi Odamlar Sonini Hisoblash\n",
198
+ elem_id="title"
199
+ )
200
+
201
+ with gr.Row():
202
+ with gr.Column(scale=1):
203
+ video_input = gr.Video(label="πŸ“Ή Video yuklang", sources=["upload"])
204
+ conf_slider = gr.Slider(
205
+ minimum=0.2, maximum=0.9, value=0.4, step=0.05,
206
+ label="Ishonchlilik chegarasi (conf threshold)"
207
+ )
208
+ run_btn = gr.Button("β–Ά Hisoblashni boshlash", variant="primary")
209
+
210
+ with gr.Column(scale=1):
211
+ video_output = gr.Video(label="πŸ“Š Annotated natija")
212
+ result_text = gr.HTML(
213
+ value="<div id='result-box'>Natija bu yerda ko'rinadi</div>"
214
+ )
215
+
216
+ def run_and_format(video, conf):
217
+ out_video, text = count_people(video, conf)
218
+ html = f"<div id='result-box'>{text}</div>"
219
+ return out_video, html
220
+
221
+ run_btn.click(
222
+ fn=run_and_format,
223
+ inputs=[video_input, conf_slider],
224
+ outputs=[video_output, result_text]
225
+ )
226
+
227
+ gr.Markdown("""
228
+ ---
229
+ ### Qanday ishlaydi?
230
+ 1. **YOLOv8n** β€” har bir frame'da odamlarni bounding box bilan aniqlaydi
231
+ 2. **IoU Tracker** β€” bir odamni ketma-ket frame'larda bir xil ID bilan kuzatadi
232
+ 3. **Unique ID sanash** β€” video davomida paydo bo'lgan barcha yangi ID'lar sanaladi
233
+ 4. **Natija** β€” "odam yo'q" yoki "N ta odam"
234
+ """)
235
+
236
+ if __name__ == "__main__":
237
+ demo.launch()