Optimiser la précision de l'OCR
Mis à jour le 29 juillet 2026
La qualité en entrée détermine la qualité en sortie
L’équipe Mistral le formulait sans détour lors de son webinaire Document AI : c’est du « garbage in, garbage out ». Aussi performant soit le modèle, il ne peut pas restituer une information que le scan n’a pas capturée. Quand un projet d’extraction déçoit, la cause est presque toujours en amont de l’API — une photocopie pâle, un fax reconverti, une photo prise de biais dans un entrepôt mal éclairé. Cette leçon parcourt les leviers qui agissent réellement sur la précision.
Résolution et pré-traitement
Le premier levier est la résolution du scan. Les seuils à viser dépendent de ce que contient le document :
- Documents imprimés : 200 DPI minimum, 300 DPI recommandé
- Écriture manuscrite : 300 DPI minimum
- Tableaux complexes : 300 DPI recommandé
- Documents historiques : 400 DPI ou plus
En dessous de ces valeurs, les traits fins disparaissent et aucun traitement logiciel ne les fera revenir. Au-dessus, vous gagnez surtout du poids de fichier. Lorsque vous maîtrisez la chaîne de numérisation, régler le scanner correctement vous épargnera bien plus d’ennuis que n’importe quelle astuce de code.
Quand le document vous arrive déjà numérisé, un pré-traitement d’image peut rattraper une partie du terrain. La fonction ci-dessous enchaîne quatre opérations classiques — passage en niveaux de gris, renforcement du contraste, accentuation de la netteté, puis binarisation — avant de renvoyer l’image encodée en base64, prête pour l’appel OCR :
from PIL import Image, ImageEnhance, ImageFilter
import io
import base64
def pre_traiter_image(chemin_image: str) -> str:
"""Pré-traite une image pour améliorer l'extraction OCR."""
img = Image.open(chemin_image)
# 1. Conversion en niveaux de gris
img = img.convert("L")
# 2. Augmentation du contraste
enhancer = ImageEnhance.Contrast(img)
img = enhancer.enhance(1.5)
# 3. Augmentation de la netteté
img = img.filter(ImageFilter.SHARPEN)
# 4. Binarisation (seuillage)
img = img.point(lambda x: 0 if x < 128 else 255, "1")
# Conversion en base64
buffer = io.BytesIO()
img.save(buffer, format="PNG")
return base64.b64encode(buffer.getvalue()).decode("utf-8")
Testez ce traitement sur un échantillon avant de l’appliquer à tout un lot. La binarisation, en particulier, est brutale : sur un document déjà net elle n’apporte rien, et sur un document au crayon elle peut effacer purement et simplement le texte.
L’orientation est l’autre correctif à ne pas négliger. Les scans arrivent régulièrement pivotés de 90 ou 180 degrés, et l’information est le plus souvent inscrite dans les métadonnées EXIF de l’image :
from PIL import Image
def corriger_orientation(chemin_image: str) -> Image.Image:
"""Corrige l'orientation d'une image scannée via les métadonnées EXIF."""
img = Image.open(chemin_image)
# Utilisation des données EXIF pour corriger l'orientation
try:
exif = img._getexif()
if exif:
orientation = exif.get(274) # Tag EXIF orientation
rotations = {3: 180, 6: 270, 8: 90}
if orientation in rotations:
img = img.rotate(rotations[orientation], expand=True)
except (AttributeError, KeyError):
pass
return img
Le try large se justifie ici : beaucoup d’images n’ont aucune donnée EXIF, et l’absence d’orientation ne doit pas interrompre le traitement d’un lot.
Adapter la configuration au type de document
Pour les documents imprimés courants — factures, contrats, rapports —, la configuration optimale se résume à demander explicitement l’extraction des tableaux plutôt que de s’en remettre au réglage par défaut :
# Configuration optimale pour documents imprimés
ocr_response = client.ocr.process(
model="mistral-ocr-latest",
document={
"type": "document_base64",
"document_base64": document_base64
},
table_format="markdown" # Extraction explicite des tableaux
)
L’écriture manuscrite demande davantage de soin en amont. Scannez à 300 DPI au minimum, augmentez le contraste entre l’encre et le papier, mais renoncez à la binarisation agressive du script précédent : elle efface les traits fins d’un stylo à bille et vous perdrez plus d’information que vous n’en gagnerez. Le modèle, lui, gère bien les écritures manuscrites y compris multilingues, ce qui n’était pas acquis avec les générations précédentes d’OCR.
Restent les scans franchement dégradés — archives, documents froissés, copies de copies. La stratégie change : on commence par réduire le bruit avec un filtre médian avant de pousser le contraste, puis on relève la luminosité si l’ensemble reste trop sombre.
from PIL import Image, ImageFilter, ImageEnhance
def ameliorer_scan(chemin: str) -> Image.Image:
"""Améliore un scan de mauvaise qualité."""
img = Image.open(chemin)
# Réduction du bruit
img = img.filter(ImageFilter.MedianFilter(size=3))
# Augmentation du contraste
enhancer = ImageEnhance.Contrast(img)
img = enhancer.enhance(2.0)
# Augmentation de la luminosité si trop sombre
enhancer = ImageEnhance.Brightness(img)
img = enhancer.enhance(1.2)
return img
L’ordre compte : accentuer le contraste avant d’avoir retiré le bruit revient à renforcer les taches autant que les lettres.
Traiter des volumes
Dès que le nombre de fichiers dépasse la poignée, il faut un traitement de lot doté d’une gestion d’erreurs par fichier. Pour les très gros volumes, Mistral propose le service Batch Inference ; le script suivant montre la logique de base sur un dossier de PDF, où chaque échec est consigné dans les résultats au lieu d’interrompre la boucle :
import os
import json
from pathlib import Path
from mistralai import Mistral
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
def traiter_dossier(dossier: str) -> dict:
"""Traite tous les PDF d'un dossier."""
resultats = {}
chemin = Path(dossier)
for fichier in chemin.glob("*.pdf"):
print(f"Traitement de {fichier.name}...")
with open(fichier, "rb") as f:
import base64
doc_b64 = base64.b64encode(f.read()).decode("utf-8")
try:
response = client.ocr.process(
model="mistral-ocr-latest",
document={
"type": "document_base64",
"document_base64": doc_b64
},
table_format="markdown"
)
resultats[fichier.name] = {
"pages": len(response.pages),
"contenu": "\n\n".join(
p.markdown for p in response.pages
)
}
except Exception as e:
resultats[fichier.name] = {"erreur": str(e)}
return resultats
Ce dictionnaire de résultats vous donne, en fin de traitement, la liste exacte des fichiers à rejouer. Sur un lot de plusieurs centaines de documents, c’est la différence entre relancer trois fichiers et tout recommencer.
Vérifier avant d’exploiter
Une extraction qui a réussi techniquement peut avoir échoué en pratique : page blanche renvoyée pour un scan illisible, flot de caractères parasites sur une image trop bruitée. Quelques heuristiques simples suffisent à lever une alerte automatique.
def verifier_qualite(markdown: str) -> dict:
"""Vérifie la qualité d'une extraction OCR."""
indicateurs = {
"longueur": len(markdown),
"mots": len(markdown.split()),
"lignes_vides_ratio": markdown.count("\n\n") / max(len(markdown), 1),
"caracteres_speciaux_ratio": sum(
1 for c in markdown if not c.isalnum() and c not in " \n\t.,;:!?-"
) / max(len(markdown), 1),
}
# Alertes
if indicateurs["mots"] < 10:
indicateurs["alerte"] = "Très peu de texte extrait"
elif indicateurs["caracteres_speciaux_ratio"] > 0.3:
indicateurs["alerte"] = "Ratio élevé de caractères spéciaux"
return indicateurs
Les deux seuils sont indicatifs et doivent être calibrés sur votre corpus : dix mots pour une page de contrat signalent un problème, mais pas pour un bordereau de livraison qui n’en contient qu’une quinzaine.
En résumé opérationnel
Retenez la chaîne dans son ordre logique. Scannez à 300 DPI au minimum pour tout document non trivial, et augmentez le contraste avant l’envoi si le document est pâle. Corrigez l’angle de rotation des scans. Préférez toujours le PDF natif au scan quand il est disponible : sa qualité est structurellement supérieure. Précisez explicitement table_format="markdown" ou "html" dès que des tableaux sont en jeu. Traitez par lots et parallélisez les appels sur les gros volumes. Et validez systématiquement la qualité de l’extraction avant de la verser dans votre système d’information — une donnée fausse coûte plus cher qu’une donnée manquante.
Points clés à retenir
- La qualité du document source est le facteur principal de précision
- Le pré-traitement d’image (contraste, netteté, binarisation) améliore les résultats
- Adaptez votre approche au type de document (imprimé, manuscrit, dégradé)
- Utilisez le batch processing pour les gros volumes
- Validez toujours la qualité de l’extraction avant exploitation