""" Lemontropia Suite - Vision Test Dialog Test and debug Game Vision AI functionality. """ import time from pathlib import Path from typing import Optional from PyQt6.QtWidgets import ( QDialog, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QComboBox, QCheckBox, QGroupBox, QFormLayout, QMessageBox, QFileDialog, QTextEdit, QProgressBar, QListWidget, QListWidgetItem, QSplitter, QWidget, QTableWidget, QTableWidgetItem, QHeaderView ) from PyQt6.QtCore import Qt, QThread, pyqtSignal from PyQt6.QtGui import QPixmap, QImage, QFont import numpy as np import logging logger = logging.getLogger(__name__) class VisionTestWorker(QThread): """Worker thread for vision testing.""" test_complete = pyqtSignal(dict) progress = pyqtSignal(str) error_occurred = pyqtSignal(str) def __init__(self, image_path: Path, settings: dict): super().__init__() self.image_path = image_path self.settings = settings def run(self): try: from modules.game_vision_ai import GameVisionAI self.progress.emit("Initializing Game Vision AI...") vision = GameVisionAI( use_gpu=self.settings.get('use_gpu', True), ocr_lang=self.settings.get('ocr_lang', 'en') ) self.progress.emit("Processing image...") start_time = time.time() result = vision.process_screenshot( self.image_path, extract_text=self.settings.get('extract_text', True), extract_icons=self.settings.get('extract_icons', True) ) processing_time = (time.time() - start_time) * 1000 # Prepare results test_results = { 'success': True, 'processing_time_ms': processing_time, 'gpu_backend': result.gpu_backend, 'text_regions': [ { 'text': t.text, 'confidence': t.confidence, 'bbox': t.bbox, 'language': t.language } for t in result.text_regions ], 'icon_regions': [ { 'bbox': i.bbox, 'confidence': i.confidence, 'hash': i.icon_hash[:16] # Truncated hash } for i in result.icon_regions ], 'text_count': len(result.text_regions), 'icon_count': len(result.icon_regions) } self.test_complete.emit(test_results) except Exception as e: self.error_occurred.emit(str(e)) class VisionTestDialog(QDialog): """ Dialog for testing and debugging Game Vision AI. """ def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle("Test Game Vision") self.setMinimumSize(900, 700) self.current_image_path: Optional[Path] = None self.current_results: Optional[dict] = None self.setup_ui() def setup_ui(self): """Setup dialog UI.""" layout = QVBoxLayout(self) layout.setSpacing(10) # Title title_label = QLabel("🧪 Game Vision Test & Debug") title_font = QFont() title_font.setPointSize(14) title_font.setBold(True) title_label.setFont(title_font) layout.addWidget(title_label) # Main splitter splitter = QSplitter(Qt.Orientation.Horizontal) layout.addWidget(splitter) # Left panel - Controls left_panel = QWidget() left_layout = QVBoxLayout(left_panel) left_layout.setContentsMargins(5, 5, 5, 5) # Image selection image_group = QGroupBox("Test Image") image_layout = QVBoxLayout(image_group) self.image_path_label = QLabel("No image selected") self.image_path_label.setWordWrap(True) image_layout.addWidget(self.image_path_label) image_btn_layout = QHBoxLayout() self.browse_btn = QPushButton("Browse...") self.browse_btn.clicked.connect(self.browse_image) image_btn_layout.addWidget(self.browse_btn) self.capture_btn = QPushButton("Capture Screen") self.capture_btn.clicked.connect(self.capture_screen) image_btn_layout.addWidget(self.capture_btn) image_btn_layout.addStretch() image_layout.addLayout(image_btn_layout) left_layout.addWidget(image_group) # Test settings settings_group = QGroupBox("Test Settings") settings_layout = QFormLayout(settings_group) self.use_gpu_cb = QCheckBox("Use GPU acceleration") self.use_gpu_cb.setChecked(True) settings_layout.addRow(self.use_gpu_cb) self.extract_text_cb = QCheckBox("Extract text (OCR)") self.extract_text_cb.setChecked(True) settings_layout.addRow(self.extract_text_cb) self.extract_icons_cb = QCheckBox("Extract icons") self.extract_icons_cb.setChecked(True) settings_layout.addRow(self.extract_icons_cb) self.ocr_lang_combo = QComboBox() self.ocr_lang_combo.addItem("English", "en") self.ocr_lang_combo.addItem("Swedish", "sv") settings_layout.addRow("OCR Language:", self.ocr_lang_combo) left_layout.addWidget(settings_group) # Run test button self.test_btn = QPushButton("▶ Run Vision Test") self.test_btn.setStyleSheet(""" QPushButton { background-color: #4CAF50; color: white; font-weight: bold; padding: 10px; } QPushButton:hover { background-color: #45a049; } QPushButton:disabled { background-color: #cccccc; } """) self.test_btn.clicked.connect(self.run_test) self.test_btn.setEnabled(False) left_layout.addWidget(self.test_btn) # Progress self.progress_label = QLabel("") left_layout.addWidget(self.progress_label) self.progress_bar = QProgressBar() self.progress_bar.setRange(0, 0) # Indeterminate self.progress_bar.setVisible(False) left_layout.addWidget(self.progress_bar) # GPU Info gpu_group = QGroupBox("GPU Information") gpu_layout = QVBoxLayout(gpu_group) self.gpu_info_label = QLabel("Click 'Check GPU' to detect") self.gpu_info_label.setWordWrap(True) gpu_layout.addWidget(self.gpu_info_label) self.check_gpu_btn = QPushButton("Check GPU") self.check_gpu_btn.clicked.connect(self.check_gpu) gpu_layout.addWidget(self.check_gpu_btn) left_layout.addWidget(gpu_group) left_layout.addStretch() splitter.addWidget(left_panel) # Right panel - Results right_panel = QWidget() right_layout = QVBoxLayout(right_panel) right_layout.setContentsMargins(5, 5, 5, 5) # Image preview preview_group = QGroupBox("Image Preview") preview_layout = QVBoxLayout(preview_group) self.preview_label = QLabel("No image loaded") self.preview_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.preview_label.setMinimumHeight(200) self.preview_label.setStyleSheet("background-color: #f0f0f0; border: 1px solid #ccc;") preview_layout.addWidget(self.preview_label) right_layout.addWidget(preview_group) # Results tabs from PyQt6.QtWidgets import QTabWidget self.results_tabs = QTabWidget() right_layout.addWidget(self.results_tabs) # Summary tab self.summary_tab = QTextEdit() self.summary_tab.setReadOnly(True) self.results_tabs.addTab(self.summary_tab, "Summary") # Text regions tab self.text_table = QTableWidget() self.text_table.setColumnCount(4) self.text_table.setHorizontalHeaderLabels(["Text", "Confidence", "Position", "Language"]) self.text_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch) self.results_tabs.addTab(self.text_table, "Text Regions") # Icon regions tab self.icon_table = QTableWidget() self.icon_table.setColumnCount(3) self.icon_table.setHorizontalHeaderLabels(["Position", "Confidence", "Hash"]) self.icon_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch) self.results_tabs.addTab(self.icon_table, "Icon Regions") # Log tab self.log_text = QTextEdit() self.log_text.setReadOnly(True) self.results_tabs.addTab(self.log_text, "Log") splitter.addWidget(right_panel) splitter.setSizes([300, 600]) # Close button btn_layout = QHBoxLayout() btn_layout.addStretch() self.close_btn = QPushButton("Close") self.close_btn.clicked.connect(self.accept) btn_layout.addWidget(self.close_btn) layout.addLayout(btn_layout) def browse_image(self): """Browse for test image.""" file_path, _ = QFileDialog.getOpenFileName( self, "Select Test Image", str(Path.home()), "Images (*.png *.jpg *.jpeg *.bmp)" ) if file_path: self.load_image(Path(file_path)) def capture_screen(self): """Capture screen for testing.""" try: import mss import numpy as np import cv2 from PyQt6.QtGui import QImage, QPixmap self.progress_label.setText("Capturing screen...") with mss.mss() as sct: monitor = sct.monitors[1] # Primary monitor screenshot = sct.grab(monitor) # Convert to numpy array img = np.array(screenshot) img = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR) # Save temporarily temp_path = Path.home() / ".lemontropia" / "temp_capture.png" temp_path.parent.mkdir(parents=True, exist_ok=True) cv2.imwrite(str(temp_path), img) self.load_image(temp_path) self.progress_label.setText("Screen captured") except Exception as e: QMessageBox.critical(self, "Capture Failed", f"Failed to capture screen: {e}") self.progress_label.setText("") def load_image(self, image_path: Path): """Load and display image.""" self.current_image_path = image_path self.image_path_label.setText(str(image_path)) # Load and display preview pixmap = QPixmap(str(image_path)) if not pixmap.isNull(): # Scale to fit scaled = pixmap.scaled( self.preview_label.size(), Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation ) self.preview_label.setPixmap(scaled) self.test_btn.setEnabled(True) else: self.preview_label.setText("Failed to load image") self.test_btn.setEnabled(False) def run_test(self): """Run vision test.""" if not self.current_image_path: QMessageBox.warning(self, "No Image", "Please select an image first.") return # Collect settings settings = { 'use_gpu': self.use_gpu_cb.isChecked(), 'extract_text': self.extract_text_cb.isChecked(), 'extract_icons': self.extract_icons_cb.isChecked(), 'ocr_lang': self.ocr_lang_combo.currentData() } # Disable controls self.test_btn.setEnabled(False) self.browse_btn.setEnabled(False) self.capture_btn.setEnabled(False) self.progress_bar.setVisible(True) self.progress_label.setText("Running vision test...") # Clear previous results self.summary_tab.clear() self.text_table.setRowCount(0) self.icon_table.setRowCount(0) # Start worker self.worker = VisionTestWorker(self.current_image_path, settings) self.worker.test_complete.connect(self.on_test_complete) self.worker.progress.connect(self.on_test_progress) self.worker.error_occurred.connect(self.on_test_error) self.worker.start() def on_test_progress(self, message: str): """Handle test progress.""" self.progress_label.setText(message) self.log_text.append(f"[{time.strftime('%H:%M:%S')}] {message}") def on_test_complete(self, results: dict): """Handle test completion.""" self.current_results = results # Re-enable controls self.test_btn.setEnabled(True) self.browse_btn.setEnabled(True) self.capture_btn.setEnabled(True) self.progress_bar.setVisible(False) self.progress_label.setText("Test complete!") # Update summary summary = f"""

Vision Test Results

Processing Time: {results['processing_time_ms']:.1f}ms

GPU Backend: {results['gpu_backend']}

Text Regions Detected: {results['text_count']}

Icon Regions Detected: {results['icon_count']}

""" self.summary_tab.setHtml(summary) # Update text table self.text_table.setRowCount(len(results['text_regions'])) for i, text in enumerate(results['text_regions']): self.text_table.setItem(i, 0, QTableWidgetItem(text['text'])) self.text_table.setItem(i, 1, QTableWidgetItem(f"{text['confidence']:.2%}")) bbox_str = f"({text['bbox'][0]}, {text['bbox'][1]})" self.text_table.setItem(i, 2, QTableWidgetItem(bbox_str)) self.text_table.setItem(i, 3, QTableWidgetItem(text['language'])) # Update icon table self.icon_table.setRowCount(len(results['icon_regions'])) for i, icon in enumerate(results['icon_regions']): bbox_str = f"({icon['bbox'][0]}, {icon['bbox'][1]}, {icon['bbox'][2]}x{icon['bbox'][3]})" self.icon_table.setItem(i, 0, QTableWidgetItem(bbox_str)) self.icon_table.setItem(i, 1, QTableWidgetItem(f"{icon['confidence']:.2%}")) self.icon_table.setItem(i, 2, QTableWidgetItem(icon['hash'])) logger.info(f"Vision test complete: {results['text_count']} texts, {results['icon_count']} icons") def on_test_error(self, error: str): """Handle test error.""" self.test_btn.setEnabled(True) self.browse_btn.setEnabled(True) self.capture_btn.setEnabled(True) self.progress_bar.setVisible(False) self.progress_label.setText(f"Error: {error}") QMessageBox.critical(self, "Test Failed", f"Vision test failed:\n{error}") self.log_text.append(f"[ERROR] {error}") logger.error(f"Vision test failed: {error}") def check_gpu(self): """Check GPU availability.""" try: from modules.game_vision_ai import GPUDetector info = GPUDetector.get_gpu_info() text = f""" GPU Information
Backend: {info['backend']}
CUDA Available: {info['cuda_available']}
MPS Available: {info['mps_available']}
""" if info.get('devices'): for dev in info['devices']: mem_gb = dev.get('memory_total', 0) / (1024**3) text += f"Device {dev['id']}: {dev['name']} ({mem_gb:.1f} GB)
" self.gpu_info_label.setText(text) except Exception as e: self.gpu_info_label.setText(f"Error detecting GPU: {e}") logger.error(f"GPU detection failed: {e}") def resizeEvent(self, event): """Handle resize to update preview.""" super().resizeEvent(event) if self.current_image_path and self.preview_label.pixmap(): pixmap = QPixmap(str(self.current_image_path)) scaled = pixmap.scaled( self.preview_label.size(), Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation ) self.preview_label.setPixmap(scaled) # Export __all__ = ['VisionTestDialog']