fix: OCR lazy initialization to prevent startup crash
FIXES: - OCR now uses lazy initialization (only loads when first used) - Fixed PaddleOCR invalid use_gpu parameter (some versions don't support it) - Added fallback try/except for PaddleOCR without use_gpu - App starts immediately, OCR initializes on first scan - Prevents long startup delays from model downloading CHANGES: - OCRService._init_backends() only called on first use - Removed eager initialization from main.py startup - Better error handling for backend failures
This commit is contained in:
parent
f6c4971826
commit
f1e2076570
|
|
@ -53,8 +53,36 @@
|
|||
- Auto-refresh every 5 seconds
|
||||
- Reads data from all plugin JSON files
|
||||
|
||||
### 6. Core Services (NOT Plugins!)
|
||||
Following user's requirement - OCR and Log are core services, not plugins.
|
||||
|
||||
**Log Reader (`core/log_reader.py`):**
|
||||
- Real-time chat.log monitoring in background thread
|
||||
- Event parsing: skill_gains, loot, globals, damage, heals, missions, etc.
|
||||
- Publisher/subscriber pattern for plugins
|
||||
- Auto-detects EU log file location
|
||||
- Cache of recent 1000 lines
|
||||
|
||||
**OCR Service (`core/ocr_service.py`):**
|
||||
- Multi-backend support: EasyOCR → Tesseract → PaddleOCR
|
||||
- Auto-fallback if primary backend unavailable
|
||||
- Screen capture (full or region)
|
||||
- Returns structured results with bounding boxes
|
||||
- `quick_ocr()` convenience function
|
||||
|
||||
**PluginAPI Integration:**
|
||||
- `api.ocr_capture(region)` - All plugins can use OCR
|
||||
- `api.read_log(lines, filter)` - All plugins can read log
|
||||
- Services auto-initialized on app startup
|
||||
- Registered with PluginAPI for universal access
|
||||
|
||||
## Commits Made
|
||||
- `5b127cf` - UI fixes (drag, scroll, no emojis)
|
||||
- `72c3c13` - Resizable window, OCR scanners, customizable dashboard
|
||||
- `bcd4574` - All plugins disabled by default with enable/disable UI
|
||||
- `7f6547f` - Fixed box-in-box UI, added Settings button to header
|
||||
- `8ee0e56` - Fixed window taskbar visibility
|
||||
- `f6c4971` - Core OCR and Log services with API integration
|
||||
|
||||
## Total Plugins: 21
|
||||
## Core Services: 2 (OCR, Log Reader)
|
||||
|
|
|
|||
|
|
@ -130,19 +130,19 @@ class EUUtilityApp:
|
|||
# Register Log service with API
|
||||
self.api.register_log_service(self.log_reader.read_lines)
|
||||
|
||||
# Initialize OCR Service
|
||||
print("[Core] Initializing OCR Service...")
|
||||
# OCR Service - LAZY INITIALIZATION (don't init on startup)
|
||||
# It will initialize on first use
|
||||
print("[Core] OCR Service configured (lazy init)")
|
||||
self.ocr_service = get_ocr_service()
|
||||
if self.ocr_service.is_available():
|
||||
print(f"[Core] OCR Service ready - using {self.ocr_service._backend}")
|
||||
else:
|
||||
print("[Core] OCR Service not available - no backend installed")
|
||||
print("[Core] Install one of: easyocr, pytesseract, paddleocr")
|
||||
|
||||
# Register OCR service with API
|
||||
self.api.register_ocr_service(self.ocr_service.recognize)
|
||||
# Register OCR service with API (lazy - will init on first call)
|
||||
self.api.register_ocr_service(self._lazy_ocr_handler)
|
||||
|
||||
print("[Core] API services registered: OCR, Log")
|
||||
print("[Core] API services registered: OCR (lazy), Log")
|
||||
|
||||
def _lazy_ocr_handler(self, region=None):
|
||||
"""Lazy OCR handler - triggers init on first use."""
|
||||
return self.ocr_service.recognize(region=region)
|
||||
|
||||
def _setup_hotkeys(self):
|
||||
"""Setup global hotkeys."""
|
||||
|
|
|
|||
|
|
@ -27,18 +27,23 @@ class OCRService:
|
|||
"""
|
||||
Core OCR service with multiple backend support.
|
||||
Fallback chain: EasyOCR -> Tesseract -> PaddleOCR
|
||||
LAZY INITIALIZATION - only loads when first used
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._ocr_reader = None
|
||||
self._backend = None
|
||||
self._initialized = False
|
||||
|
||||
# Try backends in order of preference
|
||||
self._init_backends()
|
||||
self._initializing = False
|
||||
|
||||
def _init_backends(self):
|
||||
"""Initialize available OCR backends."""
|
||||
"""Initialize available OCR backends (lazy - called on first use)."""
|
||||
if self._initialized or self._initializing:
|
||||
return
|
||||
|
||||
self._initializing = True
|
||||
print("[OCR] Initializing backends...")
|
||||
|
||||
# Try EasyOCR first (best accuracy)
|
||||
try:
|
||||
import easyocr
|
||||
|
|
@ -46,43 +51,63 @@ class OCRService:
|
|||
self._backend = 'easyocr'
|
||||
self._initialized = True
|
||||
print("[OCR] Using EasyOCR backend")
|
||||
self._initializing = False
|
||||
return
|
||||
except ImportError:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(f"[OCR] EasyOCR failed: {e}")
|
||||
|
||||
# Try Tesseract (most common)
|
||||
try:
|
||||
import pytesseract
|
||||
from PIL import Image
|
||||
pytesseract.get_tesseract_version() # Test if available
|
||||
pytesseract.get_tesseract_version()
|
||||
self._backend = 'tesseract'
|
||||
self._initialized = True
|
||||
print("[OCR] Using Tesseract backend")
|
||||
self._initializing = False
|
||||
return
|
||||
except (ImportError, Exception):
|
||||
pass
|
||||
except Exception as e:
|
||||
print(f"[OCR] Tesseract failed: {e}")
|
||||
|
||||
# Try PaddleOCR (fallback)
|
||||
# Try PaddleOCR (fallback) - with minimal config
|
||||
try:
|
||||
from paddleocr import PaddleOCR
|
||||
# Use minimal config to avoid model downloads on init
|
||||
import os
|
||||
os.environ['PADDLE_PDX_DISABLE_MODEL_SOURCE_CHECK'] = 'True'
|
||||
|
||||
self._ocr_reader = PaddleOCR(
|
||||
use_angle_cls=True,
|
||||
lang='en',
|
||||
show_log=False,
|
||||
use_gpu=False
|
||||
use_gpu=False # This param may not work in all versions
|
||||
)
|
||||
self._backend = 'paddle'
|
||||
self._initialized = True
|
||||
print("[OCR] Using PaddleOCR backend")
|
||||
return
|
||||
except ImportError:
|
||||
pass
|
||||
except TypeError:
|
||||
# Try without use_gpu if it failed
|
||||
try:
|
||||
self._ocr_reader = PaddleOCR(lang='en', show_log=False)
|
||||
self._backend = 'paddle'
|
||||
self._initialized = True
|
||||
print("[OCR] Using PaddleOCR backend (no GPU)")
|
||||
except Exception as e2:
|
||||
print(f"[OCR] PaddleOCR failed: {e2}")
|
||||
except Exception as e:
|
||||
print(f"[OCR] PaddleOCR failed: {e}")
|
||||
|
||||
self._initializing = False
|
||||
|
||||
if not self._initialized:
|
||||
print("[OCR] WARNING: No OCR backend available!")
|
||||
print("[OCR] Install one of: easyocr, pytesseract, paddleocr")
|
||||
|
||||
def is_available(self) -> bool:
|
||||
"""Check if OCR is available."""
|
||||
"""Check if OCR is available (lazy init)."""
|
||||
if not self._initialized and not self._initializing:
|
||||
self._init_backends()
|
||||
return self._initialized
|
||||
|
||||
def capture_screen(self, region: Tuple[int, int, int, int] = None) -> 'Image.Image':
|
||||
|
|
@ -120,6 +145,10 @@ class OCRService:
|
|||
Returns:
|
||||
Dict with 'text', 'confidence', 'results', 'image_size'
|
||||
"""
|
||||
# Lazy initialization
|
||||
if not self._initialized and not self._initializing:
|
||||
self._init_backends()
|
||||
|
||||
if not self._initialized:
|
||||
return {
|
||||
'text': '',
|
||||
|
|
|
|||
Loading…
Reference in New Issue