Clean push: Removed heavy files & added only latest snapshot
This commit is contained in:
279
qt_app_pyside1/utils/helpers.py
Normal file
279
qt_app_pyside1/utils/helpers.py
Normal file
@@ -0,0 +1,279 @@
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import cv2
|
||||
import numpy as np
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Tuple, Optional, Any
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
def bbox_iou(box1, box2):
|
||||
"""
|
||||
Calculate IoU (Intersection over Union) between two bounding boxes
|
||||
|
||||
Args:
|
||||
box1: First bounding box in format [x1, y1, x2, y2]
|
||||
box2: Second bounding box in format [x1, y1, x2, y2]
|
||||
|
||||
Returns:
|
||||
IoU score between 0 and 1
|
||||
"""
|
||||
# Ensure boxes are in [x1, y1, x2, y2] format and have valid dimensions
|
||||
if len(box1) < 4 or len(box2) < 4:
|
||||
return 0.0
|
||||
|
||||
# Convert to float and ensure x2 > x1 and y2 > y1
|
||||
x1_1, y1_1, x2_1, y2_1 = map(float, box1[:4])
|
||||
x1_2, y1_2, x2_2, y2_2 = map(float, box2[:4])
|
||||
|
||||
if x2_1 <= x1_1 or y2_1 <= y1_1 or x2_2 <= x1_2 or y2_2 <= y1_2:
|
||||
return 0.0
|
||||
|
||||
# Calculate area of each box
|
||||
area1 = (x2_1 - x1_1) * (y2_1 - y1_1)
|
||||
area2 = (x2_2 - x1_2) * (y2_2 - y1_2)
|
||||
|
||||
if area1 <= 0 or area2 <= 0:
|
||||
return 0.0
|
||||
|
||||
# Calculate intersection area
|
||||
x1_i = max(x1_1, x1_2)
|
||||
y1_i = max(y1_1, y1_2)
|
||||
x2_i = min(x2_1, x2_2)
|
||||
y2_i = min(y2_1, y2_2)
|
||||
|
||||
if x2_i <= x1_i or y2_i <= y1_i:
|
||||
return 0.0 # No intersection
|
||||
|
||||
intersection_area = (x2_i - x1_i) * (y2_i - y1_i)
|
||||
|
||||
# Calculate IoU
|
||||
union_area = area1 + area2 - intersection_area
|
||||
|
||||
if union_area <= 0:
|
||||
return 0.0
|
||||
|
||||
iou = intersection_area / union_area
|
||||
return iou
|
||||
|
||||
def load_configuration(config_file: str) -> Dict:
|
||||
"""
|
||||
Load configuration from JSON file.
|
||||
|
||||
Args:
|
||||
config_file: Path to configuration file
|
||||
|
||||
Returns:
|
||||
Configuration dictionary
|
||||
"""
|
||||
default_config = {
|
||||
"detection": {
|
||||
"confidence_threshold": 0.5,
|
||||
"enable_ocr": True,
|
||||
"enable_tracking": True,
|
||||
"model_path": None
|
||||
},
|
||||
"violations": {
|
||||
"red_light_grace_period": 2.0,
|
||||
"stop_sign_duration": 2.0,
|
||||
"speed_tolerance": 5
|
||||
},
|
||||
"display": {
|
||||
"max_display_width": 800,
|
||||
"show_confidence": True,
|
||||
"show_labels": True,
|
||||
"show_license_plates": True
|
||||
},
|
||||
"performance": {
|
||||
"max_history_frames": 1000,
|
||||
"cleanup_interval": 3600
|
||||
}
|
||||
}
|
||||
|
||||
if not os.path.exists(config_file):
|
||||
return default_config
|
||||
|
||||
try:
|
||||
with open(config_file, 'r') as f:
|
||||
config = json.load(f)
|
||||
|
||||
# Merge with defaults
|
||||
for section in default_config:
|
||||
if section in config:
|
||||
default_config[section].update(config[section])
|
||||
|
||||
return default_config
|
||||
except Exception as e:
|
||||
print(f"Error loading config: {e}")
|
||||
return default_config
|
||||
|
||||
def save_configuration(config: Dict, config_file: str) -> bool:
|
||||
"""
|
||||
Save configuration to JSON file.
|
||||
|
||||
Args:
|
||||
config: Configuration dictionary
|
||||
config_file: Path to save configuration file
|
||||
|
||||
Returns:
|
||||
True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
with open(config_file, 'w') as f:
|
||||
json.dump(config, f, indent=2)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error saving config: {e}")
|
||||
return False
|
||||
|
||||
def format_timestamp(timestamp: float) -> str:
|
||||
"""
|
||||
Format timestamp as readable string.
|
||||
|
||||
Args:
|
||||
timestamp: Unix timestamp
|
||||
|
||||
Returns:
|
||||
Formatted timestamp string
|
||||
"""
|
||||
dt = datetime.fromtimestamp(timestamp)
|
||||
return dt.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
def format_duration(seconds: float) -> str:
|
||||
"""
|
||||
Format duration in seconds as readable string.
|
||||
|
||||
Args:
|
||||
seconds: Duration in seconds
|
||||
|
||||
Returns:
|
||||
Formatted duration string
|
||||
"""
|
||||
if seconds < 60:
|
||||
return f"{seconds:.1f}s"
|
||||
elif seconds < 3600:
|
||||
minutes = seconds / 60
|
||||
return f"{minutes:.1f}m"
|
||||
else:
|
||||
hours = seconds / 3600
|
||||
return f"{hours:.1f}h"
|
||||
|
||||
def create_export_csv(detections: List[Dict], filename: str) -> bool:
|
||||
"""
|
||||
Export detections to CSV file.
|
||||
|
||||
Args:
|
||||
detections: List of detection dictionaries
|
||||
filename: Output CSV filename
|
||||
|
||||
Returns:
|
||||
True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
import pandas as pd
|
||||
|
||||
# Create DataFrame from detections
|
||||
rows = []
|
||||
for det in detections:
|
||||
row = {
|
||||
'timestamp': det.get('timestamp', 0),
|
||||
'class': det.get('class_name', 'unknown'),
|
||||
'confidence': det.get('confidence', 0),
|
||||
'x1': det.get('bbox', [0, 0, 0, 0])[0],
|
||||
'y1': det.get('bbox', [0, 0, 0, 0])[1],
|
||||
'x2': det.get('bbox', [0, 0, 0, 0])[2],
|
||||
'y2': det.get('bbox', [0, 0, 0, 0])[3]
|
||||
}
|
||||
rows.append(row)
|
||||
|
||||
df = pd.DataFrame(rows)
|
||||
|
||||
# Save to CSV
|
||||
df.to_csv(filename, index=False)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error exporting to CSV: {e}")
|
||||
return False
|
||||
|
||||
def create_export_json(data: Dict, filename: str) -> bool:
|
||||
"""
|
||||
Export data to JSON file.
|
||||
|
||||
Args:
|
||||
data: Data to export
|
||||
filename: Output JSON filename
|
||||
|
||||
Returns:
|
||||
True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
with open(filename, 'w') as f:
|
||||
json.dump(data, f, indent=2)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error exporting to JSON: {e}")
|
||||
return False
|
||||
|
||||
def create_unique_filename(prefix: str, ext: str) -> str:
|
||||
"""
|
||||
Create unique filename with timestamp.
|
||||
|
||||
Args:
|
||||
prefix: Filename prefix
|
||||
ext: File extension
|
||||
|
||||
Returns:
|
||||
Unique filename
|
||||
"""
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
return f"{prefix}_{timestamp}.{ext}"
|
||||
|
||||
def save_snapshot(frame: np.ndarray, filename: str = None) -> str:
|
||||
"""
|
||||
Save video frame as image file.
|
||||
|
||||
Args:
|
||||
frame: Video frame
|
||||
filename: Output filename (optional)
|
||||
|
||||
Returns:
|
||||
Path to saved image
|
||||
"""
|
||||
if filename is None:
|
||||
filename = create_unique_filename("snapshot", "jpg")
|
||||
|
||||
try:
|
||||
cv2.imwrite(filename, frame)
|
||||
return filename
|
||||
except Exception as e:
|
||||
print(f"Error saving snapshot: {e}")
|
||||
return None
|
||||
|
||||
def get_video_properties(source):
|
||||
"""
|
||||
Get video file properties.
|
||||
|
||||
Args:
|
||||
source: Video source (file path or device number)
|
||||
|
||||
Returns:
|
||||
Dictionary of video properties
|
||||
"""
|
||||
try:
|
||||
cap = cv2.VideoCapture(source)
|
||||
if not cap.isOpened():
|
||||
return {}
|
||||
|
||||
props = {
|
||||
'width': int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),
|
||||
'height': int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)),
|
||||
'fps': cap.get(cv2.CAP_PROP_FPS),
|
||||
'frame_count': int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
||||
}
|
||||
|
||||
cap.release()
|
||||
return props
|
||||
except Exception as e:
|
||||
print(f"Error getting video properties: {e}")
|
||||
return {}
|
||||
Reference in New Issue
Block a user