feat(vwb): Remplacer EasyOCR par docTR (Mindee) pour l'OCR
docTR est plus performant et mieux maintenu. Crée un service OCR partagé (singleton paresseux) utilisé par verify_text_content et extraire_tableau, avec les mêmes signatures et fallbacks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,7 @@ de l'écran, en utilisant l'OCR pour extraire et comparer le texte.
|
||||
|
||||
Modes OCR disponibles:
|
||||
- ollama: Utilise un modèle de vision local (GPU, meilleure qualité)
|
||||
- easyocr: OCR traditionnel (CPU/GPU, plus rapide)
|
||||
- doctr: OCR traditionnel (CPU/GPU, plus rapide)
|
||||
"""
|
||||
|
||||
from typing import Dict, Any, List, Optional
|
||||
@@ -34,7 +34,7 @@ class VWBVerifyTextContentAction(BaseVWBAction):
|
||||
|
||||
Supporte deux modes OCR:
|
||||
- ollama: Modèle de vision local (meilleure qualité, utilise GPU)
|
||||
- easyocr: OCR traditionnel (plus rapide, fallback)
|
||||
- doctr: OCR traditionnel (plus rapide, fallback)
|
||||
"""
|
||||
|
||||
# Configuration Ollama par défaut (centralisée via variable d'environnement)
|
||||
@@ -70,7 +70,7 @@ class VWBVerifyTextContentAction(BaseVWBAction):
|
||||
self.case_sensitive = parameters.get('case_sensitive', False)
|
||||
|
||||
# Configuration OCR
|
||||
self.ocr_mode = parameters.get('ocr_mode', 'ollama') # ollama (GPU) ou easyocr
|
||||
self.ocr_mode = parameters.get('ocr_mode', 'ollama') # ollama (GPU) ou doctr
|
||||
self.ollama_model = parameters.get('ollama_model', self.OLLAMA_MODEL)
|
||||
self.ollama_url = parameters.get('ollama_url', self.OLLAMA_URL)
|
||||
|
||||
@@ -100,8 +100,8 @@ class VWBVerifyTextContentAction(BaseVWBAction):
|
||||
errors.append(f"Mode de matching invalide: {self.match_mode}")
|
||||
|
||||
# Vérifier le mode OCR
|
||||
if self.ocr_mode not in ['ollama', 'easyocr']:
|
||||
errors.append(f"Mode OCR invalide: {self.ocr_mode} (utilisez 'ollama' ou 'easyocr')")
|
||||
if self.ocr_mode not in ['ollama', 'doctr', 'easyocr']:
|
||||
errors.append(f"Mode OCR invalide: {self.ocr_mode} (utilisez 'ollama' ou 'doctr')")
|
||||
|
||||
return errors
|
||||
|
||||
@@ -214,7 +214,7 @@ class VWBVerifyTextContentAction(BaseVWBAction):
|
||||
if self.ocr_mode == 'ollama':
|
||||
return self._extract_with_ollama(screenshot)
|
||||
else:
|
||||
return self._extract_with_easyocr(screenshot)
|
||||
return self._extract_with_doctr(screenshot)
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Erreur extraction texte: {e}")
|
||||
@@ -264,20 +264,20 @@ class VWBVerifyTextContentAction(BaseVWBAction):
|
||||
return extracted_text
|
||||
else:
|
||||
print(f" ⚠️ Erreur Ollama: {response.status_code}")
|
||||
# Fallback sur easyocr
|
||||
return self._extract_with_easyocr(image)
|
||||
# Fallback sur docTR
|
||||
return self._extract_with_doctr(image)
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
print(f" ⚠️ Ollama non disponible, fallback sur easyocr")
|
||||
return self._extract_with_easyocr(image)
|
||||
print(f" ⚠️ Ollama non disponible, fallback sur docTR")
|
||||
return self._extract_with_doctr(image)
|
||||
|
||||
except Exception as e:
|
||||
print(f" ⚠️ Erreur Ollama: {e}, fallback sur easyocr")
|
||||
return self._extract_with_easyocr(image)
|
||||
print(f" ⚠️ Erreur Ollama: {e}, fallback sur docTR")
|
||||
return self._extract_with_doctr(image)
|
||||
|
||||
def _extract_with_easyocr(self, image) -> Optional[str]:
|
||||
def _extract_with_doctr(self, image) -> Optional[str]:
|
||||
"""
|
||||
Extrait le texte en utilisant EasyOCR.
|
||||
Extrait le texte en utilisant docTR.
|
||||
|
||||
Args:
|
||||
image: Image PIL à analyser
|
||||
@@ -286,29 +286,20 @@ class VWBVerifyTextContentAction(BaseVWBAction):
|
||||
Texte extrait ou None si erreur
|
||||
"""
|
||||
try:
|
||||
import easyocr
|
||||
import numpy as np
|
||||
from services.ocr_service import ocr_extract_text
|
||||
|
||||
print("📝 Extraction OCR via EasyOCR...")
|
||||
print("📝 Extraction OCR via docTR...")
|
||||
|
||||
# Convertir en array numpy
|
||||
img_array = np.array(image)
|
||||
|
||||
# EasyOCR (GPU si disponible)
|
||||
reader = easyocr.Reader(['fr', 'en'], gpu=True)
|
||||
results = reader.readtext(img_array)
|
||||
|
||||
# Combiner les résultats
|
||||
extracted_text = ' '.join([result[1] for result in results])
|
||||
extracted_text = ocr_extract_text(image)
|
||||
print(f" ✅ Texte extrait ({len(extracted_text)} caractères)")
|
||||
return extracted_text.strip()
|
||||
|
||||
except ImportError:
|
||||
print(" ⚠️ easyocr non disponible")
|
||||
print(" ⚠️ docTR non disponible")
|
||||
return ""
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ Erreur EasyOCR: {e}")
|
||||
print(f" ❌ Erreur docTR: {e}")
|
||||
return None
|
||||
|
||||
def _compare_text(self, extracted: str, expected: str) -> bool:
|
||||
|
||||
Reference in New Issue
Block a user