Jaya242 commited on
Commit
a24843f
·
1 Parent(s): 495ac9b

add analytics dashboard: per-class chart + time-series chart

Browse files
Files changed (3) hide show
  1. app.py +3 -1
  2. requirements.txt +2 -0
  3. src/detector.py +69 -3
app.py CHANGED
@@ -3,7 +3,7 @@ from src.detector import process_video
3
 
4
  def run(video_input):
5
  if video_input is None:
6
- return None, None, "⚠️ Please upload a video first."
7
  return process_video(video_input)
8
 
9
 
@@ -14,6 +14,8 @@ demo = gr.Interface(
14
  gr.Video(label="Annotated Output (with tracking + counting lines)"),
15
  gr.File(label="Crossings CSV"),
16
  gr.Markdown(label="Summary"),
 
 
17
  ],
18
  title="🚦 Traffic Analytics Pipeline",
19
  description=(
 
3
 
4
  def run(video_input):
5
  if video_input is None:
6
+ return None, None, "⚠️ Please upload a video first.", None, None
7
  return process_video(video_input)
8
 
9
 
 
14
  gr.Video(label="Annotated Output (with tracking + counting lines)"),
15
  gr.File(label="Crossings CSV"),
16
  gr.Markdown(label="Summary"),
17
+ gr.Plot(label="📊 Crossings by Class"),
18
+ gr.Plot(label="⏱️ Crossings Over Time"),
19
  ],
20
  title="🚦 Traffic Analytics Pipeline",
21
  description=(
requirements.txt CHANGED
@@ -1,3 +1,5 @@
1
  gradio>=4.0
2
  ultralytics>=8.0
3
  opencv-python-headless>=4.8
 
 
 
1
  gradio>=4.0
2
  ultralytics>=8.0
3
  opencv-python-headless>=4.8
4
+
5
+ matplotlib>=3.7
src/detector.py CHANGED
@@ -4,6 +4,10 @@ import csv
4
  from ultralytics import YOLO
5
  import tempfile
6
  import re
 
 
 
 
7
 
8
  model = YOLO('yolov8n.pt')
9
 
@@ -43,6 +47,61 @@ def detect(frame):
43
  return detections, annotated
44
 
45
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  def process_video(input_video_path):
47
  output_dir = tempfile.mkdtemp(prefix="traffic_")
48
  output_video_path = os.path.join(output_dir, "traffic_tracked.mp4")
@@ -161,7 +220,11 @@ def process_video(input_video_path):
161
  _Built with YOLOv8 + ByteTrack. See repo for methodology._
162
  """
163
 
164
- return output_video_path, output_csv_path, summary
 
 
 
 
165
 
166
 
167
  if __name__ == "__main__":
@@ -170,7 +233,7 @@ if __name__ == "__main__":
170
  project_dir = os.path.dirname(script_dir)
171
  VIDEO_PATH = os.path.join(project_dir, "data", "traffic.mp4")
172
 
173
- annotated, csv_out, summary = process_video(VIDEO_PATH)
174
 
175
  print(f"\n✅ Done!")
176
  print(f"📹 Annotated video: {annotated}")
@@ -183,4 +246,7 @@ if __name__ == "__main__":
183
  if m:
184
  auto = int(m.group(1))
185
  acc = round(100 * (1 - abs(auto - MANUAL_UNIQUE) / MANUAL_UNIQUE), 1)
186
- print(f"\n🎯 Unique vehicle accuracy vs manual ({MANUAL_UNIQUE}): {acc}%")
 
 
 
 
4
  from ultralytics import YOLO
5
  import tempfile
6
  import re
7
+ import matplotlib
8
+ matplotlib.use("Agg") # non-interactive backend (required for server / Gradio)
9
+ import matplotlib.pyplot as plt
10
+ from collections import Counter, defaultdict
11
 
12
  model = YOLO('yolov8n.pt')
13
 
 
47
  return detections, annotated
48
 
49
 
50
+ def make_per_class_chart(crossings):
51
+ """Bar chart: count of crossings per vehicle class."""
52
+ class_counts = Counter(c["class"] for c in crossings)
53
+ if not class_counts:
54
+ # Empty fallback
55
+ fig, ax = plt.subplots(figsize=(6, 4))
56
+ ax.text(0.5, 0.5, "No crossings detected", ha="center", va="center", fontsize=14)
57
+ ax.axis("off")
58
+ return fig
59
+
60
+ classes = list(class_counts.keys())
61
+ counts = list(class_counts.values())
62
+
63
+ fig, ax = plt.subplots(figsize=(6, 4))
64
+ ax.bar(classes, counts, color=["#3498db", "#e67e22", "#2ecc71", "#9b59b6"][:len(classes)])
65
+ ax.set_title("Crossings by Vehicle Class", fontsize=13, fontweight="bold")
66
+ ax.set_xlabel("Vehicle Class")
67
+ ax.set_ylabel("Number of Crossings")
68
+ for i, v in enumerate(counts):
69
+ ax.text(i, v + 0.1, str(v), ha="center", fontweight="bold")
70
+ plt.tight_layout()
71
+ return fig
72
+
73
+ def make_time_series_chart(crossings, fps, total_frames):
74
+ """Time-series: crossings per 5-second window."""
75
+ if not crossings:
76
+ fig, ax = plt.subplots(figsize=(8, 4))
77
+ ax.text(0.5, 0.5, "No crossings detected", ha="center", va="center", fontsize=14)
78
+ ax.axis("off")
79
+ return fig
80
+
81
+ bucket_size_sec = 5
82
+ total_duration_sec = total_frames / fps if fps > 0 else 0
83
+
84
+ buckets = defaultdict(int)
85
+ for c in crossings:
86
+ bucket = int(c["timestamp_sec"] // bucket_size_sec)
87
+ buckets[bucket] += 1
88
+
89
+ max_bucket = int(total_duration_sec // bucket_size_sec) + 1
90
+ x = list(range(max_bucket))
91
+ y = [buckets.get(b, 0) for b in x]
92
+ x_labels = [f"{b * bucket_size_sec}s" for b in x]
93
+
94
+ fig, ax = plt.subplots(figsize=(8, 4))
95
+ ax.plot(x_labels, y, marker="o", linewidth=2, color="#e67e22")
96
+ ax.fill_between(range(len(x_labels)), y, alpha=0.2, color="#e67e22")
97
+ ax.set_title("Crossings Over Time (5-second windows)", fontsize=13, fontweight="bold")
98
+ ax.set_xlabel("Time into clip")
99
+ ax.set_ylabel("Crossings in window")
100
+ ax.grid(True, alpha=0.3)
101
+ plt.xticks(rotation=45, ha="right")
102
+ plt.tight_layout()
103
+ return fig
104
+
105
  def process_video(input_video_path):
106
  output_dir = tempfile.mkdtemp(prefix="traffic_")
107
  output_video_path = os.path.join(output_dir, "traffic_tracked.mp4")
 
220
  _Built with YOLOv8 + ByteTrack. See repo for methodology._
221
  """
222
 
223
+ per_class_chart = make_per_class_chart(crossings)
224
+ time_series_chart = make_time_series_chart(crossings, fps, frame_count)
225
+
226
+ return output_video_path, output_csv_path, summary, per_class_chart, time_series_chart
227
+
228
 
229
 
230
  if __name__ == "__main__":
 
233
  project_dir = os.path.dirname(script_dir)
234
  VIDEO_PATH = os.path.join(project_dir, "data", "traffic.mp4")
235
 
236
+ annotated, csv_out, summary, _, _ = process_video(VIDEO_PATH)
237
 
238
  print(f"\n✅ Done!")
239
  print(f"📹 Annotated video: {annotated}")
 
246
  if m:
247
  auto = int(m.group(1))
248
  acc = round(100 * (1 - abs(auto - MANUAL_UNIQUE) / MANUAL_UNIQUE), 1)
249
+ print(f"\n🎯 Unique vehicle accuracy vs manual ({MANUAL_UNIQUE}): {acc}%")
250
+
251
+
252
+