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:
Dom
2026-02-18 22:19:44 +01:00
parent 786e640de9
commit 773ee78949
3 changed files with 130 additions and 51 deletions

View File

@@ -406,7 +406,7 @@ Retourne maintenant le JSON du tableau:"""
def _extraire_avec_ocr(self, image_base64: str) -> Optional[List]:
"""
Extrait le tableau avec OCR.
Extrait le tableau avec OCR (docTR).
Args:
image_base64: Image en base64
@@ -415,28 +415,20 @@ Retourne maintenant le JSON du tableau:"""
Liste de listes
"""
try:
# Décoder l'image
from PIL import Image
from services.ocr_service import ocr_extract_words
image_data = base64.b64decode(image_base64)
pil_image = Image.open(io.BytesIO(image_data))
# Essayer EasyOCR
try:
import easyocr
import numpy as np
words = ocr_extract_words(pil_image)
reader = easyocr.Reader(['fr', 'en'], gpu=True)
img_array = np.array(pil_image)
# Grouper par lignes (par coordonnée Y)
return self._grouper_ocr_en_lignes(words)
results = reader.readtext(img_array)
# Grouper par lignes (par coordonnée Y)
return self._grouper_ocr_en_lignes(results)
except ImportError:
print("⚠️ EasyOCR non disponible")
return None
except ImportError:
print("⚠️ docTR non disponible")
return None
except Exception as e:
print(f"⚠️ Erreur OCR: {e}")
@@ -447,7 +439,7 @@ Retourne maintenant le JSON du tableau:"""
Groupe les résultats OCR en lignes de tableau.
Args:
results: Résultats EasyOCR [(bbox, text, conf), ...]
results: Liste de dicts docTR [{"text", "bbox": (x1,y1,x2,y2), "confidence"}, ...]
Returns:
Liste de lignes
@@ -457,13 +449,13 @@ Retourne maintenant le JSON du tableau:"""
# Extraire les positions et textes
items = []
for bbox, text, conf in results:
if conf < 0.3: # Ignorer basse confiance
for word in results:
if word["confidence"] < 0.3: # Ignorer basse confiance
continue
# Calculer le centre Y
y_center = (bbox[0][1] + bbox[2][1]) / 2
x_center = (bbox[0][0] + bbox[2][0]) / 2
items.append({'text': text, 'x': x_center, 'y': y_center})
x1, y1, x2, y2 = word["bbox"]
y_center = (y1 + y2) / 2
x_center = (x1 + x2) / 2
items.append({'text': word["text"], 'x': x_center, 'y': y_center})
if not items:
return []