Skip to content

Commit b2c67bb

Browse files
streamgrid=1.0.6 Add analytics storage in CSV format for streams (#23)
1 parent 7b18dda commit b2c67bb

4 files changed

Lines changed: 51 additions & 2 deletions

File tree

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ StreamGrid(sources=sources, model=model)
3232

3333
# Inference on GPU
3434
StreamGrid(sources=sources, device="cuda")
35+
36+
# Store stream results in CSV file
37+
StreamGrid(sources=sources, analytics=True)
3538
```
3639

3740
### CLI (Command Line Interface)
@@ -47,6 +50,9 @@ streamgrid model=yolo11n.pt device=cpu save=True
4750

4851
# Pass source
4952
streamgrid model=yolo11n.pt sources=["video1.mp4", "video2.mp4"]
53+
54+
# Store Stream results in CSV file
55+
streamgrid model=yolo11n.pt analytics=True
5056
```
5157

5258
## Performance (Beta, final benchmarks will be released soon)

streamgrid/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""StreamGrid - Ultra-fast multi-stream video display."""
22

3-
__version__ = "1.0.5"
3+
__version__ = "1.0.6"
44
__all__ = ["StreamGrid"]
55

66
import argparse

streamgrid/analytics.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import csv
2+
import time
3+
from datetime import datetime
4+
from pathlib import Path
5+
6+
7+
class StreamAnalytics:
8+
"""Simple analytics logger for StreamGrid."""
9+
10+
def __init__(self, output_file="streamgrid_analytics.csv"):
11+
self.output_file = Path(output_file)
12+
self.start_time = time.time()
13+
14+
# Create CSV with headers
15+
with open(self.output_file, 'w', newline='') as f:
16+
writer = csv.writer(f)
17+
writer.writerow(['timestamp', 'stream_id', 'detections', 'fps'])
18+
19+
print(f"📊 Analytics: {self.output_file}")
20+
21+
def log(self, stream_id, detections=0, fps=0.0):
22+
"""Log frame data."""
23+
with open(self.output_file, 'a', newline='') as f:
24+
writer = csv.writer(f)
25+
writer.writerow([
26+
datetime.now().strftime('%H:%M:%S'),
27+
stream_id,
28+
detections,
29+
round(fps, 1)
30+
])
31+
32+
def summary(self):
33+
"""Print summary."""
34+
uptime = time.time() - self.start_time
35+
print(f"📊 Runtime: {uptime:.1f}s | Data saved: {self.output_file}")

streamgrid/grid.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import requests
1010
from tqdm import tqdm
1111
from streamgrid.utils import LOGGER, get_optimal_grid_size
12+
from streamgrid.analytics import StreamAnalytics
1213
from ultralytics.utils.plotting import Annotator, colors
1314

1415

@@ -39,7 +40,7 @@ class StreamGrid:
3940
video_writer: OpenCV video writer object.
4041
"""
4142

42-
def __init__(self, sources=None, model=None, save=True, device="cpu"):
43+
def __init__(self, sources=None, model=None, save=True, device="cpu", analytics=False):
4344
"""Initialize StreamGrid with video sources and configuration.
4445
4546
Args:
@@ -48,6 +49,7 @@ def __init__(self, sources=None, model=None, save=True, device="cpu"):
4849
model (optional): YOLO model instance for object detection.
4950
save (bool, optional): Save output video. Output will be saved as "streamgrid_output_{N}_streams.mp4".
5051
device (str, optional): Wheather to run inference on GPU or CPU device.
52+
analytics (bool, optional): Wheather to store streams results in CSV file.
5153
"""
5254
# GitHub repository URLs for default videos
5355
self.GITHUB_ASSETS_BASE = "https://github.com/RizwanMunawar/streamgrid/releases/download/v1.0.0/"
@@ -111,6 +113,7 @@ def __init__(self, sources=None, model=None, save=True, device="cpu"):
111113
self.auto_shutdown = True # Control auto-shutdown behavior
112114
self.shutdown_delay = 3.0 # Seconds to wait after streams end before shutdown
113115

116+
self.analytics = StreamAnalytics() if analytics else None # Enable analytics storage.
114117
self.run()
115118

116119
def get_default_videos(self):
@@ -324,6 +327,9 @@ def update_source(self, source_id, frame, yolo_results=None):
324327
detections = len(yolo_results.boxes)
325328
resized = self.draw_boxes(resized, yolo_results, frame.shape[:2])
326329

330+
if self.analytics:
331+
self.analytics.log(source_id, detections, self.prediction_fps)
332+
327333
# Store processed frame and statistics
328334
self.frames[source_id] = resized
329335
self.stats[source_id] = {'detections': detections, 'time': time.time()}
@@ -531,6 +537,8 @@ def stop(self):
531537
for i, thread in enumerate(self.stream_threads): # Wait for threads to finish (with timeout)
532538
thread.join(timeout=0.5)
533539

540+
if self.analytics:
541+
self.analytics.summary()
534542

535543
if self.save and self.video_writer:
536544
self.video_writer.release()

0 commit comments

Comments
 (0)