Code source de infrastructure.ocr.screen_capture

# screen_capture_ocr.py
"""
Module de capture d'écran en temps réel - Version minimale pour PyQt5
"""

import json
import time
import threading
from typing import Optional, Dict, Callable
from PIL import Image, ImageGrab

[docs] class LiveScreenOCR: """Gestionnaire de capture d'écran en temps réel"""
[docs] def __init__(self, ocr_module=None, pokemon_analyzer_callback=None): """ Initialise le module de capture Args: ocr_module: Instance du module OCR pokemon_analyzer_callback: Fonction callback pour les détections """ self.ocr = ocr_module self.callback = pokemon_analyzer_callback self.region = None self.is_running = False self.capture_thread = None self.min_consecutive = 1 self.confidence_threshold = 0.6 self.max_pokemon = 1 # Nombre max de pokémon à détecter (1, 2 ou 3) self.last_detection = None self.consecutive_count = 0 self.save_debug_images = False self.use_mss = False # Compteur de détections self.detection_count = {} self.last_confirmed_detection = None # Dernière détection confirmée et envoyée au callback
[docs] def select_region_interactive(self) -> bool: """ Obsolète: La sélection se fait maintenant via l'UI (RegionSelector) Cette méthode est gardée pour compatibilité mais ne fait rien. """ print("[WARN] select_region_interactive est obsolète. Utilisez CapturePresenter.") return False
[docs] def save_region_config(self, filename: str = "screen_region.json") -> bool: """Sauvegarde la configuration de région""" if not self.region: return False try: with open(filename, 'w', encoding='utf-8') as f: json.dump(self.region, f, indent=2) return True except Exception as e: print(f"[ERREUR] Erreur sauvegarde région: {e}") return False
[docs] def load_region_config(self, filename: str = "screen_region.json") -> bool: """Charge la configuration de région""" try: import os if not os.path.exists(filename): return False with open(filename, 'r', encoding='utf-8') as f: self.region = json.load(f) return True except Exception as e: print(f"[ERREUR] Erreur chargement région: {e}") return False
[docs] def capture_region(self) -> Optional[Image.Image]: """Capture la région sélectionnée""" if not self.region: return None try: # Utilise PIL ImageGrab bbox = ( self.region['left'], self.region['top'], self.region['left'] + self.region['width'], self.region['top'] + self.region['height'] ) image = ImageGrab.grab(bbox) return image except Exception as e: print(f"[ERREUR] Erreur capture: {e}") return None
[docs] def analyze_capture(self, image: Image.Image, multi_pokemon: bool = True, max_pokemon: int = 2) -> Dict: """ Analyse une capture avec OCR Args: image: Image à analyser multi_pokemon: Si True, essaie de détecter plusieurs Pokémon (défaut: True) max_pokemon: Nombre maximum de Pokémon à détecter (1-3, défaut: 2) """ if not self.ocr: return { 'success': False, 'error': 'Module OCR non disponible - Tesseract n\'est pas installé' } try: # Sauvegarde temporaire pour OCR import tempfile with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp: image.save(tmp.name) tmp_path = tmp.name # Analyse OCR - essaie d'abord multiple, puis simple si échec if multi_pokemon and max_pokemon > 1: result = self.ocr.recognize_multiple_pokemon( tmp_path, max_pokemon=max_pokemon, confidence_threshold=self.confidence_threshold ) # Si plusieurs Pokémon détectés, retourne ce format if result['success'] and result['pokemon_count'] > 1: # Supprime le fichier temporaire import os os.unlink(tmp_path) return result # Si un seul Pokémon détecté, convertit au format simple if result['success'] and result['pokemon_count'] == 1: pkmn = result['pokemons'][0] import os os.unlink(tmp_path) return { 'success': True, 'pokemon_name': pkmn['pokemon_name'], 'confidence': pkmn['confidence'], 'alternatives': pkmn.get('alternatives', []) } # Mode simple ou si multi a échoué result = self.ocr.recognize_pokemon( tmp_path, confidence_threshold=self.confidence_threshold ) # Supprime le fichier temporaire import os os.unlink(tmp_path) return result except Exception as e: return { 'success': False, 'error': str(e) }
[docs] def start_live_capture(self, interval: float = 2.0, callback=None, sensitivity: int = 1, confidence: float = 0.6) -> bool: """Démarre la capture en temps réel""" if not self.region: return False # Note: L'OCR peut être None - dans ce cas la capture fonctionnera # mais l'analyse OCR sera désactivée if self.is_running: return False # Configure les paramètres if callback: self.callback = callback self.set_sensitivity(sensitivity) self.set_confidence_threshold(confidence) self.is_running = True self.capture_thread = threading.Thread( target=self._capture_loop, args=(interval,), daemon=True ) self.capture_thread.start() return True
[docs] def stop_live_capture(self): """Arrête la capture en temps réel""" self.is_running = False if self.capture_thread: self.capture_thread.join(timeout=2.0)
def _capture_loop(self, interval: float): """Boucle de capture""" print(f"🔄 Démarrage de la boucle de capture (intervalle: {interval}s)") capture_count = 0 while self.is_running: try: capture_count += 1 print(f"\n📸 Capture #{capture_count}...") # Capture image = self.capture_region() if image: # Analyse print(f"[OCR] Analyse OCR en cours (mode: {self.max_pokemon} pokémon max)...") result = self.analyze_capture(image, multi_pokemon=True, max_pokemon=self.max_pokemon) print(f"[OCR] Résultat OCR: success={result.get('success')}") if result['success']: # Gère le cas multi-pokémon if 'pokemon_count' in result and result['pokemon_count'] > 1: count = result['pokemon_count'] mode_label = "DUO" if count == 2 else "TRIO" if count == 3 else f"{count} Pokémon" detection_key = "_".join(sorted([p['pokemon_name'] for p in result['pokemons']])) # Vérifie les détections consécutives if detection_key == self.last_detection: self.consecutive_count += 1 else: self.consecutive_count = 1 self.last_detection = detection_key print(f"[DETECT] Détections consécutives: {self.consecutive_count}/{self.min_consecutive}") # Appelle le callback si suffisamment de détections ET si c'est une nouvelle détection if self.consecutive_count >= self.min_consecutive: # Vérifie si c'est la même détection que la dernière confirmée if detection_key != self.last_confirmed_detection: if self.callback: result['image'] = image # Format spécial pour multi-pokémon self.callback(None, None, result) self.last_confirmed_detection = detection_key # Cas simple (1 seul pokémon) else: pokemon_name = result['pokemon_name'] confidence = result['confidence'] # Vérifie les détections consécutives if pokemon_name == self.last_detection: self.consecutive_count += 1 else: self.consecutive_count = 1 self.last_detection = pokemon_name print(f"[DETECT] Détections consécutives: {self.consecutive_count}/{self.min_consecutive}") # Appelle le callback si suffisamment de détections ET si c'est une nouvelle détection if self.consecutive_count >= self.min_consecutive: # Vérifie si c'est la même détection que la dernière confirmée if pokemon_name != self.last_confirmed_detection: if self.callback: # Ajoute l'image au résultat pour l'affichage result['image'] = image self.callback(pokemon_name, confidence, result) self.last_confirmed_detection = pokemon_name else: error = result.get('error', 'Aucune détection') else: print(f"[ERREUR] Échec de la capture d'image") # Attente time.sleep(interval) except Exception as e: print(f"[ERREUR] Erreur boucle capture: {e}") import traceback traceback.print_exc() time.sleep(interval)
[docs] def set_sensitivity(self, min_consecutive: int): """Configure la sensibilité (nombre de détections consécutives)""" self.min_consecutive = max(1, min_consecutive)
[docs] def set_confidence_threshold(self, threshold: float): """Configure le seuil de confiance OCR""" self.confidence_threshold = max(0.1, min(1.0, threshold))
[docs] def set_max_pokemon(self, max_pokemon: int): """Configure le nombre maximum de Pokémon à détecter (1-3)""" self.max_pokemon = max(1, min(3, max_pokemon))
[docs] def enable_mss_capture(self, enable: bool): """Active/désactive la capture MSS (non utilisé ici)""" self.use_mss = enable
[docs] def enable_debug_mode(self, enable: bool): """Active/désactive le mode debug""" self.save_debug_images = enable