| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- """
- RGB Side View Panel
- Panel for displaying side-view RGB camera with shape analysis and outline detection.
- """
- import os
- from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QSizePolicy
- from PyQt5.QtCore import Qt
- from PyQt5.QtGui import QFont, QPixmap, QImage, QPainter, QColor, QPen, QBrush
- from ui.widgets.panel_header import PanelHeader
- class QualityRGBSidePanel(QWidget):
- """
- Panel for RGB side view camera display with shape analysis.
- Shows sample fruit profile with shape outline detection and analysis.
- """
- def __init__(self, parent=None):
- super().__init__(parent)
- self.shape_analysis_active = True
- self.init_ui()
- def init_ui(self):
- """Initialize the panel UI."""
- # Set size policy to expand equally
- self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
- layout = QVBoxLayout(self)
- layout.setContentsMargins(0, 0, 0, 0)
- layout.setSpacing(0)
- # Main panel container with card styling
- self.setStyleSheet("""
- QWidget {
- background-color: white;
- border: 1px solid #ddd;
- }
- """)
- # Header using the PanelHeader widget
- header = PanelHeader(
- title="RGB Side View",
- color="#3498db" # Blue for RGB
- )
- layout.addWidget(header)
- # Content area
- content = QWidget()
- content.setStyleSheet("""
- background-color: #2c3e50;
- border: 1px solid #34495e;
- border-top: none;
- """)
- content_layout = QVBoxLayout(content)
- content_layout.setContentsMargins(10, 10, 10, 10)
- content_layout.setAlignment(Qt.AlignCenter)
- content_layout.setSpacing(5)
- # Create sample fruit side view with shape outline
- self.image_display = SampleFruitSideWidget()
- self.image_display.setMinimumSize(200, 150)
- content_layout.addWidget(self.image_display)
- # Info labels
- info_widget = QWidget()
- info_layout = QVBoxLayout(info_widget)
- info_layout.setContentsMargins(0, 5, 0, 0)
- info_layout.setSpacing(2)
- # Shape detection info
- shape_label = QLabel("Shape Detection Active")
- shape_label.setFont(QFont("Arial", 10))
- shape_label.setStyleSheet("color: #27ae60; font-weight: bold;")
- shape_label.setAlignment(Qt.AlignCenter)
- info_layout.addWidget(shape_label)
- # Analysis info
- analysis_label = QLabel("Symmetry: 91.2% | Regular Shape")
- analysis_label.setFont(QFont("Arial", 9))
- analysis_label.setStyleSheet("color: #bdc3c7;")
- analysis_label.setAlignment(Qt.AlignCenter)
- info_layout.addWidget(analysis_label)
- content_layout.addWidget(info_widget)
- layout.addWidget(content, 1)
- def update_shape_analysis(self, symmetry, shape_type):
- """Update shape analysis results."""
- self.image_display.update_analysis(symmetry, shape_type)
- self.update()
- def set_image(self, image_path=None):
- """Set the image to display."""
- if image_path and os.path.exists(image_path):
- try:
- pixmap = QPixmap(image_path)
- if not pixmap.isNull():
- # Scale to fit display area
- scaled_pixmap = pixmap.scaled(
- 240, 170, Qt.KeepAspectRatio, Qt.SmoothTransformation
- )
- # Update display
- self.image_display.update_with_image(scaled_pixmap)
- # Update status
- filename = os.path.basename(image_path)
- self.image_display.setToolTip(f"Loaded: {filename}")
- except Exception as e:
- print(f"Error loading image: {e}")
- else:
- # Show sample data
- self.image_display.update()
- class SampleFruitSideWidget(QWidget):
- """Widget showing sample fruit side view with shape outline."""
- def __init__(self, parent=None):
- super().__init__(parent)
- self.symmetry = 91.2
- self.shape_type = "Regular"
- self.setAttribute(Qt.WA_StyledBackground, True)
- def update_analysis(self, symmetry, shape_type):
- """Update shape analysis data."""
- self.symmetry = symmetry
- self.shape_type = shape_type
- self.update()
- def update_with_image(self, pixmap):
- """Update display with external image."""
- self.external_pixmap = pixmap
- self.has_external_image = True
- self.update()
- def paintEvent(self, event):
- """Custom paint event to draw fruit side view, shape outline, or external image."""
- painter = QPainter(self)
- painter.setRenderHint(QPainter.Antialiasing)
- # Get widget dimensions
- width = self.width()
- height = self.height()
- # If we have an external image, display it
- if hasattr(self, 'external_pixmap') and self.external_pixmap:
- # Draw the external image scaled to fit
- scaled_pixmap = self.external_pixmap.scaled(
- width, height, Qt.KeepAspectRatio, Qt.SmoothTransformation
- )
- # Center the image
- x = (width - scaled_pixmap.width()) // 2
- y = (height - scaled_pixmap.height()) // 2
- painter.drawPixmap(x, y, scaled_pixmap)
- else:
- # Draw default fruit visualization
- self._draw_fruit_visualization(painter, width, height)
- def _draw_fruit_visualization(self, painter, width, height):
- """Draw the default fruit side view visualization."""
- center_x = width // 2
- center_y = height // 2
- # Draw background
- painter.fillRect(0, 0, width, height, QColor("#2c3e50"))
- # Draw main fruit (elliptical shape for side view)
- fruit_width = min(width, height) // 2
- fruit_height = fruit_width * 3 // 4 # Slightly flattened
- fruit_rect = (
- center_x - fruit_width // 2,
- center_y - fruit_height // 2,
- fruit_width,
- fruit_height
- )
- # Fruit body
- fruit_color = QColor("#8B4513") # Brown/orange fruit color
- painter.setBrush(fruit_color)
- painter.setPen(QPen(QColor("#654321"), 2)) # Darker border
- painter.drawEllipse(*fruit_rect)
- # Draw shape outline (dashed border for detected shape)
- painter.setBrush(Qt.NoBrush)
- painter.setPen(QPen(QColor("#27ae60"), 2, Qt.DashLine))
- painter.drawEllipse(*fruit_rect)
- # Draw symmetry indicators (small lines showing symmetry axis)
- symmetry_y = center_y
- symmetry_start_x = center_x - fruit_width // 4
- symmetry_end_x = center_x + fruit_width // 4
- painter.setPen(QPen(QColor("#3498db"), 1, Qt.SolidLine))
- painter.drawLine(symmetry_start_x, symmetry_y - 5, symmetry_start_x, symmetry_y + 5)
- painter.drawLine(symmetry_end_x, symmetry_y - 5, symmetry_end_x, symmetry_y + 5)
- # Draw aspect ratio visualization
- self._draw_aspect_ratio_info(painter, center_x, center_y, fruit_width, fruit_height)
- # Draw locule counting on side view
- self._draw_locule_side_view(painter, center_x, center_y, fruit_width, fruit_height)
- def _draw_aspect_ratio_info(self, painter, center_x, center_y, fruit_width, fruit_height):
- """Draw aspect ratio and shape information."""
- # Calculate aspect ratio
- aspect_ratio = fruit_width / fruit_height if fruit_height > 0 else 1.0
- # Draw info text
- info_x = center_x - 60
- info_y = center_y + fruit_height // 2 + 20
- painter.setPen(QPen(QColor("#ecf0f1"), 1))
- # Shape type
- painter.drawText(info_x, info_y, f"Shape: {self.shape_type}")
- # Symmetry
- painter.drawText(info_x, info_y + 15, f"Symmetry: {self.symmetry:.1f}%")
- # Aspect ratio
- painter.drawText(info_x, info_y + 30, f"Aspect Ratio: {aspect_ratio:.2f}")
- def _draw_locule_side_view(self, painter, center_x, center_y, fruit_width, fruit_height):
- """Draw locule counting visualization on side view."""
- # Draw internal locule structure (cross-section view)
- locule_radius = min(fruit_width, fruit_height) // 8
- # Draw 4 locules in a circular pattern within the fruit
- for i in range(4):
- angle = (i * 360 / 4) * (3.14159 / 180) # Convert to radians
- locule_x = center_x + int((fruit_width * 0.3) * (1 if i % 2 == 0 else -1) * (1 if i < 2 else 0.7))
- locule_y = center_y + int((fruit_height * 0.3) * (1 if i % 2 == 1 else -1) * (1 if i < 2 else 0.7))
- # Draw locule as colored circle
- locule_colors = ["#3498db", "#e74c3c", "#2ecc71", "#f39c12"]
- painter.setBrush(QBrush(QColor(locule_colors[i])))
- painter.setPen(QPen(QColor("#34495e"), 1))
- painter.drawEllipse(locule_x - locule_radius, locule_y - locule_radius,
- locule_radius * 2, locule_radius * 2)
- def update(self):
- """Override update to ensure repaint."""
- super().update()
- self.repaint()
|