feat: Fix OCR errors and add backend installation/management
BUG FIXES: 1. EasyOCR 'Invalid input type' error: - PIL Images must be converted to numpy arrays - Added np.array() conversion before passing to EasyOCR 2. PaddleOCR 'Unknown argument: show_log' error: - Some versions don't support show_log parameter - Added try/except to handle both versions - Falls back to creating PaddleOCR without show_log NEW FEATURES: 1. OCR Backend Manager (core/ocr_backend_manager.py): - Detects installed OCR backends - Checks Windows Registry for Tesseract installation - Searches common installation paths - Auto-configures pytesseract with found binary 2. Calibration Tab Improvements: - Added 'Install EasyOCR' button (pip install easyocr) - Added 'Install Tesseract Package' button (pip install pytesseract) - Added 'Auto-Detect Tesseract' button (checks registry/paths) - Added 'Install PaddleOCR' button (pip install paddleocr) - Shows which backends are available and where they're installed 3. Better Error Messages: - Clear instructions when backends are missing - Direct links to download pages - Shows detected installation paths The app now: - Automatically finds Tesseract from registry - Allows installing backends from within the UI - Properly handles all OCR input formats
This commit is contained in:
parent
d7b7b491b5
commit
999bdfca35
|
|
@ -0,0 +1,209 @@
|
||||||
|
"""
|
||||||
|
EU-Utility - OCR Backend Manager
|
||||||
|
|
||||||
|
Manages OCR backend installation and configuration.
|
||||||
|
Checks registry for installed software and provides auto-configuration.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Optional, Dict, List, Tuple
|
||||||
|
|
||||||
|
# Windows registry checking
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
import winreg
|
||||||
|
WINDOWS = True
|
||||||
|
else:
|
||||||
|
WINDOWS = False
|
||||||
|
|
||||||
|
|
||||||
|
class OCRBackendManager:
|
||||||
|
"""Manages OCR backend detection and installation."""
|
||||||
|
|
||||||
|
TESSERACT_REGISTRY_PATHS = [
|
||||||
|
(winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Tesseract-OCR"),
|
||||||
|
(winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\WOW6432Node\Tesseract-OCR"),
|
||||||
|
(winreg.HKEY_CURRENT_USER, r"SOFTWARE\Tesseract-OCR"),
|
||||||
|
]
|
||||||
|
|
||||||
|
TESSERACT_DEFAULT_PATHS = [
|
||||||
|
Path(r"C:\Program Files\Tesseract-OCR"),
|
||||||
|
Path(r"C:\Program Files (x86)\Tesseract-OCR"),
|
||||||
|
Path.home() / "Tesseract-OCR",
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.backends = {
|
||||||
|
'easyocr': {'installed': False, 'available': False, 'path': None},
|
||||||
|
'tesseract': {'installed': False, 'available': False, 'path': None},
|
||||||
|
'paddleocr': {'installed': False, 'available': False, 'path': None},
|
||||||
|
}
|
||||||
|
self._scan_backends()
|
||||||
|
|
||||||
|
def _scan_backends(self):
|
||||||
|
"""Scan for installed OCR backends."""
|
||||||
|
self._check_easyocr()
|
||||||
|
self._check_tesseract()
|
||||||
|
self._check_paddleocr()
|
||||||
|
|
||||||
|
def _check_easyocr(self):
|
||||||
|
"""Check if EasyOCR Python package is installed."""
|
||||||
|
try:
|
||||||
|
import easyocr
|
||||||
|
self.backends['easyocr']['installed'] = True
|
||||||
|
self.backends['easyocr']['available'] = True
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _check_tesseract(self):
|
||||||
|
"""Check if Tesseract is installed (Python package and binary)."""
|
||||||
|
# Check Python package
|
||||||
|
try:
|
||||||
|
import pytesseract
|
||||||
|
self.backends['tesseract']['installed'] = True
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Check for binary in various locations
|
||||||
|
tesseract_exe = self._find_tesseract_binary()
|
||||||
|
if tesseract_exe:
|
||||||
|
self.backends['tesseract']['path'] = str(tesseract_exe)
|
||||||
|
self.backends['tesseract']['available'] = True
|
||||||
|
# Configure pytesseract if Python package is installed
|
||||||
|
try:
|
||||||
|
import pytesseract
|
||||||
|
pytesseract.pytesseract.tesseract_cmd = str(tesseract_exe)
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _find_tesseract_binary(self) -> Optional[Path]:
|
||||||
|
"""Find Tesseract binary in registry and common paths."""
|
||||||
|
# Check if already in PATH
|
||||||
|
try:
|
||||||
|
result = subprocess.run(['tesseract', '--version'],
|
||||||
|
capture_output=True, text=True, timeout=5)
|
||||||
|
if result.returncode == 0:
|
||||||
|
# Tesseract is in PATH
|
||||||
|
return Path('tesseract')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if WINDOWS:
|
||||||
|
# Check Windows Registry
|
||||||
|
for hkey, reg_path in self.TESSERACT_REGISTRY_PATHS:
|
||||||
|
try:
|
||||||
|
with winreg.OpenKey(hkey, reg_path) as key:
|
||||||
|
install_path, _ = winreg.QueryValueEx(key, 'InstallDir')
|
||||||
|
if install_path:
|
||||||
|
exe_path = Path(install_path) / 'tesseract.exe'
|
||||||
|
if exe_path.exists():
|
||||||
|
return exe_path
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Check default installation paths
|
||||||
|
for path in self.TESSERACT_DEFAULT_PATHS:
|
||||||
|
exe_path = path / 'tesseract.exe'
|
||||||
|
if exe_path.exists():
|
||||||
|
return exe_path
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _check_paddleocr(self):
|
||||||
|
"""Check if PaddleOCR Python package is installed."""
|
||||||
|
try:
|
||||||
|
from paddleocr import PaddleOCR
|
||||||
|
self.backends['paddleocr']['installed'] = True
|
||||||
|
self.backends['paddleocr']['available'] = True
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_backend_status(self, backend: str) -> Dict:
|
||||||
|
"""Get status of a specific backend."""
|
||||||
|
return self.backends.get(backend, {}).copy()
|
||||||
|
|
||||||
|
def get_all_status(self) -> Dict:
|
||||||
|
"""Get status of all backends."""
|
||||||
|
return self.backends.copy()
|
||||||
|
|
||||||
|
def get_best_available(self) -> Optional[str]:
|
||||||
|
"""Get the best available backend."""
|
||||||
|
priority = ['easyocr', 'tesseract', 'paddleocr']
|
||||||
|
for backend in priority:
|
||||||
|
if self.backends[backend]['available']:
|
||||||
|
return backend
|
||||||
|
return None
|
||||||
|
|
||||||
|
def install_backend(self, backend: str) -> Tuple[bool, str]:
|
||||||
|
"""Install a backend via pip."""
|
||||||
|
packages = {
|
||||||
|
'easyocr': 'easyocr',
|
||||||
|
'tesseract': 'pytesseract',
|
||||||
|
'paddleocr': 'paddleocr',
|
||||||
|
}
|
||||||
|
|
||||||
|
if backend not in packages:
|
||||||
|
return False, f"Unknown backend: {backend}"
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
[sys.executable, '-m', 'pip', 'install', packages[backend]],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=300
|
||||||
|
)
|
||||||
|
if result.returncode == 0:
|
||||||
|
self._scan_backends() # Re-scan after install
|
||||||
|
return True, f"Successfully installed {backend}"
|
||||||
|
else:
|
||||||
|
return False, f"Failed to install {backend}: {result.stderr}"
|
||||||
|
except Exception as e:
|
||||||
|
return False, f"Error installing {backend}: {e}"
|
||||||
|
|
||||||
|
def get_tesseract_install_info(self) -> str:
|
||||||
|
"""Get instructions for installing Tesseract."""
|
||||||
|
return """Tesseract is not installed or not in PATH.
|
||||||
|
|
||||||
|
To install Tesseract:
|
||||||
|
|
||||||
|
1. Download installer from:
|
||||||
|
https://github.com/UB-Mannheim/tesseract/wiki
|
||||||
|
|
||||||
|
2. Run the installer and note the installation path
|
||||||
|
(usually C:\Program Files\Tesseract-OCR)
|
||||||
|
|
||||||
|
3. Add to PATH or use the auto-detect feature
|
||||||
|
|
||||||
|
4. Restart EU-Utility
|
||||||
|
|
||||||
|
Alternative: Use EasyOCR instead (pip install easyocr)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def auto_configure_tesseract(self) -> bool:
|
||||||
|
"""Auto-configure Tesseract from registry/paths."""
|
||||||
|
if not WINDOWS:
|
||||||
|
return False
|
||||||
|
|
||||||
|
exe_path = self._find_tesseract_binary()
|
||||||
|
if exe_path and exe_path != Path('tesseract'):
|
||||||
|
try:
|
||||||
|
import pytesseract
|
||||||
|
pytesseract.pytesseract.tesseract_cmd = str(exe_path)
|
||||||
|
self.backends['tesseract']['available'] = True
|
||||||
|
self.backends['tesseract']['path'] = str(exe_path)
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# Singleton instance
|
||||||
|
_ocr_manager = None
|
||||||
|
|
||||||
|
def get_ocr_backend_manager() -> OCRBackendManager:
|
||||||
|
"""Get the OCR backend manager singleton."""
|
||||||
|
global _ocr_manager
|
||||||
|
if _ocr_manager is None:
|
||||||
|
_ocr_manager = OCRBackendManager()
|
||||||
|
return _ocr_manager
|
||||||
|
|
@ -59,11 +59,14 @@ class OCRTestThread(QThread):
|
||||||
if self.backend in ('auto', 'easyocr'):
|
if self.backend in ('auto', 'easyocr'):
|
||||||
try:
|
try:
|
||||||
import easyocr
|
import easyocr
|
||||||
|
import numpy as np
|
||||||
self.progress_update.emit(50, "Loading EasyOCR...")
|
self.progress_update.emit(50, "Loading EasyOCR...")
|
||||||
reader = easyocr.Reader(['en'], gpu=False, verbose=False)
|
reader = easyocr.Reader(['en'], gpu=False, verbose=False)
|
||||||
self.progress_update.emit(70, "Processing with EasyOCR...")
|
self.progress_update.emit(70, "Processing with EasyOCR...")
|
||||||
|
# Convert PIL Image to numpy array
|
||||||
|
screenshot_np = np.array(screenshot)
|
||||||
ocr_result = reader.readtext(
|
ocr_result = reader.readtext(
|
||||||
screenshot,
|
screenshot_np,
|
||||||
detail=0,
|
detail=0,
|
||||||
paragraph=True
|
paragraph=True
|
||||||
)
|
)
|
||||||
|
|
@ -86,11 +89,19 @@ class OCRTestThread(QThread):
|
||||||
if not text and self.backend in ('auto', 'paddle'):
|
if not text and self.backend in ('auto', 'paddle'):
|
||||||
try:
|
try:
|
||||||
from paddleocr import PaddleOCR
|
from paddleocr import PaddleOCR
|
||||||
|
import numpy as np
|
||||||
self.progress_update.emit(70, "Processing with PaddleOCR...")
|
self.progress_update.emit(70, "Processing with PaddleOCR...")
|
||||||
ocr = PaddleOCR(use_angle_cls=True, lang='en', show_log=False)
|
# Try with show_log, fall back without for compatibility
|
||||||
result = ocr.ocr(screenshot, cls=True)
|
try:
|
||||||
texts = [line[1][0] for line in result[0]]
|
ocr = PaddleOCR(use_angle_cls=True, lang='en', show_log=False)
|
||||||
text = '\n'.join(texts)
|
except TypeError:
|
||||||
|
ocr = PaddleOCR(use_angle_cls=True, lang='en')
|
||||||
|
# Convert PIL to numpy
|
||||||
|
screenshot_np = np.array(screenshot)
|
||||||
|
result = ocr.ocr(screenshot_np, cls=True)
|
||||||
|
if result and result[0]:
|
||||||
|
texts = [line[1][0] for line in result[0]]
|
||||||
|
text = '\n'.join(texts)
|
||||||
backend_used = "paddleocr"
|
backend_used = "paddleocr"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if self.backend == 'paddle':
|
if self.backend == 'paddle':
|
||||||
|
|
@ -504,10 +515,62 @@ class GameReaderTestPlugin(BasePlugin):
|
||||||
|
|
||||||
self.backend_status = QTextEdit()
|
self.backend_status = QTextEdit()
|
||||||
self.backend_status.setReadOnly(True)
|
self.backend_status.setReadOnly(True)
|
||||||
self.backend_status.setMaximumHeight(150)
|
self.backend_status.setMaximumHeight(200)
|
||||||
self._check_backends()
|
self._check_backends()
|
||||||
backend_layout.addWidget(self.backend_status)
|
backend_layout.addWidget(self.backend_status)
|
||||||
|
|
||||||
|
# Install buttons
|
||||||
|
install_layout = QHBoxLayout()
|
||||||
|
|
||||||
|
install_easyocr_btn = QPushButton("📦 Install EasyOCR")
|
||||||
|
install_easyocr_btn.setStyleSheet("""
|
||||||
|
QPushButton {
|
||||||
|
background-color: #4ecdc4;
|
||||||
|
color: #141f23;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
install_easyocr_btn.clicked.connect(self._install_easyocr)
|
||||||
|
install_layout.addWidget(install_easyocr_btn)
|
||||||
|
|
||||||
|
install_tesseract_btn = QPushButton("📦 Install Tesseract Package")
|
||||||
|
install_tesseract_btn.setStyleSheet("""
|
||||||
|
QPushButton {
|
||||||
|
background-color: #ff8c42;
|
||||||
|
color: #141f23;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
install_tesseract_btn.clicked.connect(self._install_pytesseract)
|
||||||
|
install_layout.addWidget(install_tesseract_btn)
|
||||||
|
|
||||||
|
detect_tesseract_btn = QPushButton("🔍 Auto-Detect Tesseract")
|
||||||
|
detect_tesseract_btn.setStyleSheet("""
|
||||||
|
QPushButton {
|
||||||
|
background-color: #a0aec0;
|
||||||
|
color: #141f23;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
detect_tesseract_btn.clicked.connect(self._auto_detect_tesseract)
|
||||||
|
install_layout.addWidget(detect_tesseract_btn)
|
||||||
|
|
||||||
|
install_paddle_btn = QPushButton("📦 Install PaddleOCR")
|
||||||
|
install_paddle_btn.setStyleSheet("""
|
||||||
|
QPushButton {
|
||||||
|
background-color: #4a5568;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
install_paddle_btn.clicked.connect(self._install_paddleocr)
|
||||||
|
install_layout.addWidget(install_paddle_btn)
|
||||||
|
|
||||||
|
backend_layout.addLayout(install_layout)
|
||||||
layout.addWidget(backend_group)
|
layout.addWidget(backend_group)
|
||||||
|
|
||||||
# Tips
|
# Tips
|
||||||
|
|
@ -587,9 +650,12 @@ class GameReaderTestPlugin(BasePlugin):
|
||||||
if backend in ('auto', 'easyocr'):
|
if backend in ('auto', 'easyocr'):
|
||||||
try:
|
try:
|
||||||
import easyocr
|
import easyocr
|
||||||
|
import numpy as np
|
||||||
reader = easyocr.Reader(['en'], gpu=False, verbose=False)
|
reader = easyocr.Reader(['en'], gpu=False, verbose=False)
|
||||||
|
# Convert PIL Image to numpy array for EasyOCR
|
||||||
|
image_np = np.array(image)
|
||||||
ocr_result = reader.readtext(
|
ocr_result = reader.readtext(
|
||||||
image,
|
image_np,
|
||||||
detail=0,
|
detail=0,
|
||||||
paragraph=True
|
paragraph=True
|
||||||
)
|
)
|
||||||
|
|
@ -618,6 +684,27 @@ class GameReaderTestPlugin(BasePlugin):
|
||||||
)
|
)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
if not text and backend in ('auto', 'paddle'):
|
||||||
|
try:
|
||||||
|
from paddleocr import PaddleOCR
|
||||||
|
# Try without show_log argument for compatibility
|
||||||
|
try:
|
||||||
|
ocr = PaddleOCR(use_angle_cls=True, lang='en', show_log=False)
|
||||||
|
except TypeError:
|
||||||
|
# Older version without show_log
|
||||||
|
ocr = PaddleOCR(use_angle_cls=True, lang='en')
|
||||||
|
# Convert PIL to numpy for PaddleOCR
|
||||||
|
import numpy as np
|
||||||
|
image_np = np.array(image)
|
||||||
|
result = ocr.ocr(image_np, cls=True)
|
||||||
|
if result and result[0]:
|
||||||
|
texts = [line[1][0] for line in result[0]]
|
||||||
|
text = '\n'.join(texts)
|
||||||
|
backend_used = "paddleocr"
|
||||||
|
except Exception as e:
|
||||||
|
if backend == 'paddle':
|
||||||
|
raise e
|
||||||
|
|
||||||
processing_time = time.time() - start_time
|
processing_time = time.time() - start_time
|
||||||
|
|
||||||
# Update UI (thread-safe via Qt signals would be better, but this works for simple case)
|
# Update UI (thread-safe via Qt signals would be better, but this works for simple case)
|
||||||
|
|
@ -786,28 +873,83 @@ class GameReaderTestPlugin(BasePlugin):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.dpi_label.setText(f"Error: {e}")
|
self.dpi_label.setText(f"Error: {e}")
|
||||||
|
|
||||||
|
def _install_easyocr(self):
|
||||||
|
"""Install EasyOCR backend."""
|
||||||
|
from core.ocr_backend_manager import get_ocr_backend_manager
|
||||||
|
manager = get_ocr_backend_manager()
|
||||||
|
success, message = manager.install_backend('easyocr')
|
||||||
|
if success:
|
||||||
|
self.notify_success("Installation Complete", message)
|
||||||
|
else:
|
||||||
|
self.notify_error("Installation Failed", message)
|
||||||
|
self._check_backends()
|
||||||
|
|
||||||
|
def _install_pytesseract(self):
|
||||||
|
"""Install pytesseract Python package."""
|
||||||
|
from core.ocr_backend_manager import get_ocr_backend_manager
|
||||||
|
manager = get_ocr_backend_manager()
|
||||||
|
success, message = manager.install_backend('tesseract')
|
||||||
|
if success:
|
||||||
|
self.notify_success("Installation Complete", message + "\n\nNote: You also need to install the Tesseract binary from:\nhttps://github.com/UB-Mannheim/tesseract/wiki")
|
||||||
|
else:
|
||||||
|
self.notify_error("Installation Failed", message)
|
||||||
|
self._check_backends()
|
||||||
|
|
||||||
|
def _install_paddleocr(self):
|
||||||
|
"""Install PaddleOCR backend."""
|
||||||
|
from core.ocr_backend_manager import get_ocr_backend_manager
|
||||||
|
manager = get_ocr_backend_manager()
|
||||||
|
success, message = manager.install_backend('paddleocr')
|
||||||
|
if success:
|
||||||
|
self.notify_success("Installation Complete", message)
|
||||||
|
else:
|
||||||
|
self.notify_error("Installation Failed", message)
|
||||||
|
self._check_backends()
|
||||||
|
|
||||||
|
def _auto_detect_tesseract(self):
|
||||||
|
"""Auto-detect Tesseract from registry/paths."""
|
||||||
|
from core.ocr_backend_manager import get_ocr_backend_manager
|
||||||
|
manager = get_ocr_backend_manager()
|
||||||
|
if manager.auto_configure_tesseract():
|
||||||
|
self.notify_success("Tesseract Found", f"Auto-configured Tesseract at:\n{manager.backends['tesseract']['path']}")
|
||||||
|
else:
|
||||||
|
self.notify_warning("Not Found", "Could not find Tesseract installation.\n\nPlease install from:\nhttps://github.com/UB-Mannheim/tesseract/wiki")
|
||||||
|
self._check_backends()
|
||||||
|
|
||||||
def _check_backends(self):
|
def _check_backends(self):
|
||||||
"""Check OCR backend availability."""
|
"""Check OCR backend availability with install buttons."""
|
||||||
|
from core.ocr_backend_manager import get_ocr_backend_manager
|
||||||
|
|
||||||
|
manager = get_ocr_backend_manager()
|
||||||
statuses = []
|
statuses = []
|
||||||
|
|
||||||
try:
|
# EasyOCR
|
||||||
import easyocr
|
easyocr_status = manager.get_backend_status('easyocr')
|
||||||
|
if easyocr_status['available']:
|
||||||
statuses.append("✅ EasyOCR - Available (recommended)")
|
statuses.append("✅ EasyOCR - Available (recommended)")
|
||||||
except ImportError:
|
else:
|
||||||
statuses.append("❌ EasyOCR - Not installed\n Install: pip install easyocr")
|
statuses.append("❌ EasyOCR - Not installed\n Click 'Install EasyOCR' button below")
|
||||||
|
|
||||||
try:
|
# Tesseract
|
||||||
import pytesseract
|
tesseract_status = manager.get_backend_status('tesseract')
|
||||||
# Also check if tesseract binary is available
|
if tesseract_status['available']:
|
||||||
try:
|
path_info = f" at {tesseract_status['path']}" if tesseract_status['path'] else ""
|
||||||
pytesseract.get_tesseract_version()
|
statuses.append(f"✅ Tesseract - Available{path_info}")
|
||||||
statuses.append("✅ Tesseract - Available")
|
elif tesseract_status['installed']:
|
||||||
except Exception:
|
statuses.append("⚠️ Tesseract - Python package installed but binary not found\n Click 'Auto-Detect Tesseract' or install binary from:\n https://github.com/UB-Mannheim/tesseract/wiki")
|
||||||
statuses.append("⚠️ Tesseract - Python wrapper installed but tesseract binary not found\n Install binary from: https://github.com/UB-Mannheim/tesseract/wiki")
|
else:
|
||||||
except ImportError:
|
statuses.append("❌ Tesseract - Not installed\n Click 'Install Tesseract Package' then install binary")
|
||||||
statuses.append("❌ Tesseract - Not installed\n Install: pip install pytesseract\n Then install binary from: https://github.com/UB-Mannheim/tesseract/wiki")
|
|
||||||
|
|
||||||
try:
|
# PaddleOCR
|
||||||
|
paddle_status = manager.get_backend_status('paddleocr')
|
||||||
|
if paddle_status['available']:
|
||||||
|
statuses.append("✅ PaddleOCR - Available")
|
||||||
|
else:
|
||||||
|
statuses.append("❌ PaddleOCR - Not installed\n Click 'Install PaddleOCR' button below")
|
||||||
|
|
||||||
|
statuses.append("\n💡 Recommendation: EasyOCR is easiest (auto-downloads models)")
|
||||||
|
|
||||||
|
self.backend_status.setPlainText('\n'.join(statuses))
|
||||||
from paddleocr import PaddleOCR
|
from paddleocr import PaddleOCR
|
||||||
statuses.append("✅ PaddleOCR - Available")
|
statuses.append("✅ PaddleOCR - Available")
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue