Clean push: Removed heavy files & added only latest snapshot

This commit is contained in:
2025-07-26 05:16:12 +05:30
commit acf84e8767
250 changed files with 58564 additions and 0 deletions

View File

@@ -0,0 +1,341 @@
from PySide6.QtCore import QObject, Signal, Slot
import numpy as np
from collections import defaultdict, deque
import time
from datetime import datetime, timedelta
from typing import Dict, List, Any
class AnalyticsController(QObject):
"""
Controller for traffic analytics and statistics.
Manages:
- Vehicle counts by class
- Violation statistics
- Temporal analytics (traffic over time)
- Speed statistics
"""
analytics_updated = Signal(dict) # Emitted when analytics are updated
def __init__(self):
"""Initialize the analytics controller"""
super().__init__()
# Detection statistics
self.detection_counts = defaultdict(int)
self.detection_history = []
# Violation statistics
self.violation_counts = defaultdict(int)
self.violation_history = []
# Time series data (for charts)
self.time_series = {
'timestamps': [],
'vehicle_counts': [],
'pedestrian_counts': [],
'violation_counts': []
}
# Performance metrics
self.fps_history = deque(maxlen=100)
self.processing_times = deque(maxlen=100)
# Aggregated metrics
self.aggregated_metrics = {
'total_vehicles': 0,
'total_pedestrians': 0,
'total_violations': 0,
'avg_processing_time': 0,
'avg_fps': 0,
'peak_vehicle_count': 0,
'peak_violation_hour': None
}
# Initialize current time window
self.current_window = datetime.now().replace(
minute=0, second=0, microsecond=0
)
self.window_stats = defaultdict(int)
# Add traffic light analytics
self.traffic_light_counts = defaultdict(int) # Counts by color
self.traffic_light_color_series = [] # List of (timestamp, color)
self.traffic_light_color_numeric = [] # For charting: 0=unknown, 1=red, 2=yellow, 3=green
self.traffic_light_color_map = {'unknown': 0, 'red': 1, 'yellow': 2, 'green': 3}
self._last_update = time.time()
@Slot(object, list, float)
def process_frame_data(self, frame, detections, metrics):
"""
Process frame data for analytics.
Args:
frame: Video frame
detections: List of detections
metrics: Dictionary containing metrics like 'detection_fps' or directly the fps value
"""
try:
# Empty violations list since violation detection is disabled
violations = []
# Debug info
det_count = len(detections) if detections else 0
print(f"Analytics processing: {det_count} detections")
except Exception as e:
print(f"Error in process_frame_data initialization: {e}")
violations = []
# Update FPS history - safely handle different metrics formats
try:
if isinstance(metrics, dict):
fps = metrics.get('detection_fps', None)
if isinstance(fps, (int, float)):
self.fps_history.append(fps)
elif isinstance(metrics, (int, float)):
# Handle case where metrics is directly the fps value
self.fps_history.append(metrics)
else:
# Fallback if metrics is neither dict nor numeric
print(f"Warning: Unexpected metrics type: {type(metrics)}")
except Exception as e:
print(f"Error processing metrics: {e}")
# Add a default value to keep analytics running
self.fps_history.append(0.0)
# Process detections
vehicle_count = 0
pedestrian_count = 0
# --- Traffic light analytics ---
traffic_light_count = 0
traffic_light_colors = []
for det in detections:
class_name = det.get('class_name', 'unknown').lower()
self.detection_counts[class_name] += 1
# Track vehicles vs pedestrians
if class_name in ['car', 'truck', 'bus', 'motorcycle']:
vehicle_count += 1
elif class_name == 'person':
pedestrian_count += 1
if class_name in ['traffic light', 'trafficlight', 'tl', 'signal']:
traffic_light_count += 1
color = det.get('traffic_light_color', {}).get('color', 'unknown')
self.traffic_light_counts[color] += 1
traffic_light_colors.append(color)
# Track most common color for this frame
if traffic_light_colors:
from collections import Counter
most_common_color = Counter(traffic_light_colors).most_common(1)[0][0]
else:
most_common_color = 'unknown'
now_dt = datetime.now()
self.traffic_light_color_series.append((now_dt.strftime('%H:%M:%S'), most_common_color))
self.traffic_light_color_numeric.append(self.traffic_light_color_map.get(most_common_color, 0))
# Keep last 60 points
if len(self.traffic_light_color_series) > 60:
self.traffic_light_color_series = self.traffic_light_color_series[-60:]
self.traffic_light_color_numeric = self.traffic_light_color_numeric[-60:]
# Update time series data (once per second)
now = time.time()
if now - self._last_update >= 1.0:
self._update_time_series(vehicle_count, pedestrian_count, len(violations), most_common_color)
self._last_update = now
# Update aggregated metrics
self._update_aggregated_metrics()
# Emit updated analytics
self.analytics_updated.emit(self.get_analytics())
def _update_time_series(self, vehicle_count, pedestrian_count, violation_count, traffic_light_color=None):
"""Update time series data for charts"""
now = datetime.now()
# Check if we've moved to a new hour
if now.hour != self.current_window.hour or now.day != self.current_window.day:
# Save current window stats
self._save_window_stats()
# Reset for new window
self.current_window = now.replace(minute=0, second=0, microsecond=0)
self.window_stats = defaultdict(int)
# Add current counts to window
self.window_stats['vehicles'] += vehicle_count
self.window_stats['pedestrians'] += pedestrian_count
self.window_stats['violations'] += violation_count
# Add to time series
self.time_series['timestamps'].append(now.strftime('%H:%M:%S'))
self.time_series['vehicle_counts'].append(vehicle_count)
self.time_series['pedestrian_counts'].append(pedestrian_count)
self.time_series['violation_counts'].append(violation_count)
# Add traffic light color to time series
if traffic_light_color is not None:
if 'traffic_light_colors' not in self.time_series:
self.time_series['traffic_light_colors'] = []
self.time_series['traffic_light_colors'].append(traffic_light_color)
if len(self.time_series['traffic_light_colors']) > 60:
self.time_series['traffic_light_colors'] = self.time_series['traffic_light_colors'][-60:]
# Keep last 60 data points (1 minute at 1 Hz)
if len(self.time_series['timestamps']) > 60:
for key in self.time_series:
self.time_series[key] = self.time_series[key][-60:]
def _save_window_stats(self):
"""Save stats for the current time window"""
if sum(self.window_stats.values()) > 0:
window_info = {
'time': self.current_window,
'vehicles': self.window_stats['vehicles'],
'pedestrians': self.window_stats['pedestrians'],
'violations': self.window_stats['violations']
}
# Update peak stats
if window_info['vehicles'] > self.aggregated_metrics['peak_vehicle_count']:
self.aggregated_metrics['peak_vehicle_count'] = window_info['vehicles']
if window_info['violations'] > 0:
if self.aggregated_metrics['peak_violation_hour'] is None or \
window_info['violations'] > self.aggregated_metrics['peak_violation_hour']['violations']:
self.aggregated_metrics['peak_violation_hour'] = {
'time': self.current_window.strftime('%H:%M'),
'violations': window_info['violations']
}
def _update_aggregated_metrics(self):
"""Update aggregated analytics metrics"""
# Count totals
self.aggregated_metrics['total_vehicles'] = sum([
self.detection_counts[c] for c in
['car', 'truck', 'bus', 'motorcycle']
])
self.aggregated_metrics['total_pedestrians'] = self.detection_counts['person']
self.aggregated_metrics['total_violations'] = sum(self.violation_counts.values())
# Average FPS
if self.fps_history:
# Only sum numbers, skip dicts
numeric_fps = [f for f in self.fps_history if isinstance(f, (int, float))]
if numeric_fps:
self.aggregated_metrics['avg_fps'] = sum(numeric_fps) / len(numeric_fps)
else:
self.aggregated_metrics['avg_fps'] = 0.0
# Average processing time
if self.processing_times:
self.aggregated_metrics['avg_processing_time'] = sum(self.processing_times) / len(self.processing_times)
def get_analytics(self) -> Dict:
"""
Get current analytics data.
Returns:
Dictionary of analytics data
"""
return {
'detection_counts': dict(self.detection_counts),
'violation_counts': dict(self.violation_counts),
'time_series': self.time_series,
'metrics': self.aggregated_metrics,
'recent_violations': self.violation_history[-10:] if self.violation_history else [],
'traffic_light_counts': dict(self.traffic_light_counts),
'traffic_light_color_series': self.traffic_light_color_series,
'traffic_light_color_numeric': self.traffic_light_color_numeric
}
def get_violation_history(self) -> List:
"""
Get violation history.
Returns:
List of violation events
"""
return self.violation_history.copy()
def clear_statistics(self):
"""Reset all statistics"""
self.detection_counts = defaultdict(int)
self.violation_counts = defaultdict(int)
self.detection_history = []
self.violation_history = []
self.time_series = {
'timestamps': [],
'vehicle_counts': [],
'pedestrian_counts': [],
'violation_counts': []
}
self.fps_history.clear()
self.processing_times.clear()
self.window_stats = defaultdict(int)
self.aggregated_metrics = {
'total_vehicles': 0,
'total_pedestrians': 0,
'total_violations': 0,
'avg_processing_time': 0,
'avg_fps': 0,
'peak_vehicle_count': 0,
'peak_violation_hour': None
}
def register_violation(self, violation):
"""
Register a new violation in the analytics.
Args:
violation: Dictionary with violation information
"""
try:
# Add to violation counts - check both 'violation' and 'violation_type' keys
violation_type = violation.get('violation_type') or violation.get('violation', 'unknown')
self.violation_counts[violation_type] += 1
# Add to violation history
self.violation_history.append(violation)
# Update time series
now = datetime.now()
self.time_series['timestamps'].append(now)
# If we've been running for a while, we might need to drop old timestamps
if len(self.time_series['timestamps']) > 100: # Keep last 100 points
self.time_series['timestamps'] = self.time_series['timestamps'][-100:]
self.time_series['vehicle_counts'] = self.time_series['vehicle_counts'][-100:]
self.time_series['pedestrian_counts'] = self.time_series['pedestrian_counts'][-100:]
self.time_series['violation_counts'] = self.time_series['violation_counts'][-100:]
# Append current totals to time series
self.time_series['violation_counts'].append(sum(self.violation_counts.values()))
# Make sure all time series have the same length
while len(self.time_series['vehicle_counts']) < len(self.time_series['timestamps']):
self.time_series['vehicle_counts'].append(sum(self.detection_counts.get(c, 0)
for c in ['car', 'truck', 'bus', 'motorcycle']))
while len(self.time_series['pedestrian_counts']) < len(self.time_series['timestamps']):
self.time_series['pedestrian_counts'].append(self.detection_counts.get('person', 0))
# Update aggregated metrics
self.aggregated_metrics['total_violations'] = sum(self.violation_counts.values())
# Emit updated analytics
self._emit_analytics_update()
print(f"📊 Registered violation in analytics: {violation_type}")
except Exception as e:
print(f"❌ Error registering violation in analytics: {e}")
import traceback
traceback.print_exc()
def _emit_analytics_update(self):
"""Emit analytics update signal with current data"""
try:
self.analytics_updated.emit(self.get_analytics())
except Exception as e:
print(f"❌ Error emitting analytics update: {e}")
import traceback
traceback.print_exc()