Files
Traffic-Intersection-Monito…/qt_app_pyside1/ui/widgets/detection_overlay_widget.py
2025-08-26 13:24:53 -07:00

172 lines
5.9 KiB
Python

"""
Detection Overlay Widget - Displays object detection bounding boxes and labels
"""
from PySide6.QtWidgets import QWidget, QLabel, QVBoxLayout, QHBoxLayout, QFrame
from PySide6.QtCore import Qt, Signal, QRect, QPoint, QTimer
from PySide6.QtGui import QPainter, QPen, QBrush, QColor, QFont
import json
class DetectionOverlayWidget(QWidget):
"""
Widget for displaying object detection overlays on video frames
Features:
- Bounding box visualization
- Class labels and confidence scores
- Tracking IDs for multi-object tracking
- Color-coded detection classes
- Customizable overlay styles
"""
# Signals
detection_clicked = Signal(dict) # Emitted when a detection is clicked
def __init__(self, parent=None):
super().__init__(parent)
self.detections = []
self.class_colors = {
'person': QColor(255, 0, 0), # Red
'bicycle': QColor(0, 255, 0), # Green
'car': QColor(0, 0, 255), # Blue
'motorcycle': QColor(255, 255, 0), # Yellow
'bus': QColor(255, 0, 255), # Magenta
'truck': QColor(0, 255, 255), # Cyan
'traffic_light': QColor(255, 165, 0), # Orange
'stop_sign': QColor(128, 0, 128), # Purple
}
self.show_labels = True
self.show_confidence = True
self.show_tracking_id = True
self.overlay_opacity = 0.8
# Make widget transparent for overlay
self.setAttribute(Qt.WA_TransparentForMouseEvents, False)
self.setStyleSheet("background-color: transparent;")
def set_detections(self, detections):
"""
Set detection results to display
Args:
detections: List of detection dictionaries with format:
{
'bbox': [x1, y1, x2, y2],
'class': 'person',
'confidence': 0.95,
'track_id': 123 (optional)
}
"""
self.detections = detections
self.update() # Trigger repaint
def set_overlay_options(self, show_labels=True, show_confidence=True,
show_tracking_id=True, opacity=0.8):
"""Configure overlay display options"""
self.show_labels = show_labels
self.show_confidence = show_confidence
self.show_tracking_id = show_tracking_id
self.overlay_opacity = opacity
self.update()
def add_class_color(self, class_name, color):
"""Add or update color for a detection class"""
self.class_colors[class_name] = color
def paintEvent(self, event):
"""Paint detection overlays"""
if not self.detections:
return
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
# Set up font for labels
font = QFont("Arial", 10, QFont.Bold)
painter.setFont(font)
for detection in self.detections:
self._draw_detection(painter, detection)
def _draw_detection(self, painter, detection):
"""Draw a single detection"""
# Extract detection info
bbox = detection.get('bbox', [0, 0, 100, 100])
class_name = detection.get('class', 'unknown')
confidence = detection.get('confidence', 0.0)
track_id = detection.get('track_id', None)
x1, y1, x2, y2 = bbox
# Get color for this class
color = self.class_colors.get(class_name, QColor(128, 128, 128))
# Set up pen for bounding box
pen = QPen(color, 3)
painter.setPen(pen)
# Draw bounding box
rect = QRect(int(x1), int(y1), int(x2-x1), int(y2-y1))
painter.drawRect(rect)
# Prepare label text
label_parts = []
if self.show_labels:
label_parts.append(class_name)
if self.show_confidence:
label_parts.append(f"{confidence:.2f}")
if self.show_tracking_id and track_id is not None:
label_parts.append(f"ID:{track_id}")
if not label_parts:
return
label_text = " | ".join(label_parts)
# Calculate label background
metrics = painter.fontMetrics()
label_width = metrics.horizontalAdvance(label_text) + 10
label_height = metrics.height() + 6
# Draw label background
label_rect = QRect(int(x1), int(y1-label_height), label_width, label_height)
painter.fillRect(label_rect, color)
# Draw label text
painter.setPen(QPen(QColor(255, 255, 255), 1))
text_rect = QRect(int(x1+5), int(y1-label_height+3), label_width-10, label_height-6)
painter.drawText(text_rect, Qt.AlignLeft | Qt.AlignVCenter, label_text)
def mousePressEvent(self, event):
"""Handle mouse clicks on detections"""
if event.button() == Qt.LeftButton:
click_pos = event.pos()
# Check if click is within any detection bbox
for detection in self.detections:
bbox = detection.get('bbox', [0, 0, 100, 100])
x1, y1, x2, y2 = bbox
if x1 <= click_pos.x() <= x2 and y1 <= click_pos.y() <= y2:
self.detection_clicked.emit(detection)
break
def clear_detections(self):
"""Clear all detections"""
self.detections = []
self.update()
def get_detection_count(self):
"""Get number of current detections"""
return len(self.detections)
def get_class_counts(self):
"""Get count of detections by class"""
counts = {}
for detection in self.detections:
class_name = detection.get('class', 'unknown')
counts[class_name] = counts.get(class_name, 0) + 1
return counts