Aller au contenu principal

OCR et Transcription de Texte dans les Images

Mis à jour le 29 juillet 2026

Lire du texte dans les images

L’OCR (Optical Character Recognition) est l’un des cas d’usage les plus courants de la Vision API. Grâce aux modèles multimodaux de Mistral, vous extrayez du texte depuis des photos, des captures d’écran, des documents scannés, des factures ou des reçus sans installer la moindre bibliothèque OCR dédiée — ni Tesseract, ni ses modèles de langue, ni sa chaîne de pré-traitement.

L’avantage n’est pas seulement l’économie d’une dépendance. Un OCR classique restitue des caractères ; un modèle multimodal comprend ce qu’il lit et peut, dans le même appel, distinguer un numéro de facture d’un numéro de commande. Cette leçon parcourt les usages courants et les techniques qui améliorent concrètement la précision.

OCR simple : lire le texte visible

La manière la plus directe tient en un prompt, à condition qu’il soit formulé sans ambiguïté :

from mistralai import Mistral
import os

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

response = client.chat.complete(
    model="mistral-small-latest",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "Transcrivez exactement tout le texte visible dans cette image. Ne commentez pas, ne résumez pas -- retournez uniquement le texte tel qu'il apparaît."},
                {"type": "image_url", "image_url": "https://exemple.com/document-scanne.jpg"}
            ]
        }
    ]
)

print(response.choices[0].message.content)

Le mot clé est « exactement ». Sans cette instruction, le modèle a tendance à paraphraser ou à résumer le contenu, parce que c’est ce qu’un assistant serviable ferait spontanément : vous demandez ici l’inverse d’un service, une restitution littérale.

Extraire le texte d’une facture

Les factures contiennent des informations structurées — émetteur, destinataire, montants, dates — que le modèle sait identifier par leur rôle et pas seulement par leur position. C’est précisément l’avantage sur un OCR à zones fixes, qui casse dès que le fournisseur change de mise en page :

prompt_facture = """Analysez cette facture et extrayez les informations suivantes :
- Émetteur (nom, adresse, SIRET si visible)
- Destinataire (nom, adresse)
- Numéro de facture
- Date d'émission
- Date d'échéance
- Lignes de facturation (description, quantité, prix unitaire, total)
- Sous-total HT
- TVA (taux et montant)
- Total TTC

Retournez le résultat en JSON structuré."""

response = client.chat.complete(
    model="mistral-large-latest",
    messages=[
        {
            "role": "system",
            "content": "Vous êtes un expert-comptable. Extrayez les données de facturation avec précision. Retournez uniquement du JSON valide."
        },
        {
            "role": "user",
            "content": [
                {"type": "text", "text": prompt_facture},
                {"type": "image_url", "image_url": data_uri_facture}
            ]
        }
    ]
)

Transcrire des captures d’écran

Un autre besoin fréquent est la récupération du contenu d’une interface ou d’un terminal transmis en capture, typiquement dans un ticket de support. Ici, la mise en forme fait partie de l’information : une trace de terminal désindentée devient inutilisable.

import base64

def encoder_image(chemin):
    with open(chemin, "rb") as f:
        b64 = base64.standard_b64encode(f.read()).decode("utf-8")
    return f"data:image/png;base64,{b64}"

# Transcrire une capture d'écran de terminal
response = client.chat.complete(
    model="mistral-small-latest",
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": "Transcrivez la sortie du terminal visible dans cette capture d'écran. Conservez le formatage et l'indentation."
                },
                {"type": "image_url", "image_url": encoder_image("captures/terminal.png")}
            ]
        }
    ]
)

Lire des reçus et tickets de caisse

Les reçus thermiques sont le cas le plus ingrat : impression pâle, papier froissé, encre qui s’efface. La technique décisive consiste à autoriser explicitement le modèle à déclarer forfait sur un élément, faute de quoi il comblera les trous par des valeurs plausibles.

prompt_recu = """Lisez ce ticket de caisse et extrayez :
1. Nom du commerce
2. Date et heure
3. Liste des articles (nom, prix)
4. Sous-total
5. Mode de paiement
6. Montant total

Si un élément est illisible, indiquez [illisible].
Retournez le résultat en JSON."""

response = client.chat.complete(
    model="mistral-large-latest",  # Large pour la meilleure lecture
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": prompt_recu},
                {"type": "image_url", "image_url": encoder_image("recu-supermarche.jpg")}
            ]
        }
    ]
)

Un [illisible] dans le résultat vaut infiniment mieux qu’un montant inventé : le premier déclenche une vérification humaine, le second passe inaperçu jusqu’au rapprochement comptable. Sur ces documents dégradés, utilisez Mistral Large 3 — la différence de précision avec Small est significative sur les textes flous ou partiellement lisibles.

Traitement en lot de documents

Le passage à l’échelle reprend le schéma déjà rencontré : une fonction unitaire paramétrée par le modèle, puis une boucle sur le dossier.

import json
from pathlib import Path

def ocr_document(chemin_image, modele="mistral-small-latest"):
    """Effectue l'OCR d'un document et retourne le texte."""
    response = client.chat.complete(
        model=modele,
        messages=[
            {
                "role": "user",
                "content": [
                    {
                        "type": "text",
                        "text": "Transcrivez intégralement le texte de ce document. Conservez la mise en forme (titres, paragraphes, listes)."
                    },
                    {"type": "image_url", "image_url": encoder_image(chemin_image)}
                ]
            }
        ]
    )
    return response.choices[0].message.content

# Traiter un dossier
dossier = Path("documents_scannes/")
resultats = {}

for fichier in sorted(dossier.glob("*.jpg")):
    print(f"OCR de {fichier.name}...")
    resultats[fichier.name] = ocr_document(fichier)

# Sauvegarder
with open("transcriptions.json", "w", encoding="utf-8") as f:
    json.dump(resultats, f, ensure_ascii=False, indent=2)

print(f"{len(resultats)} documents transcrits.")

Le paramètre modele en signature n’est pas cosmétique : il vous permet de relancer les seuls documents douteux avec Large sans réécrire la fonction.

Améliorer la précision de l’OCR

Deux leviers agissent en amont du modèle. Le premier est le pré-traitement de l’image : un scan gris et peu contrasté redevient lisible après passage en niveaux de gris, augmentation du contraste et accentuation de la netteté.

from PIL import Image, ImageEnhance, ImageFilter

def pretraiter_pour_ocr(chemin):
    """Améliore une image pour l'OCR : contraste, netteté, niveaux de gris."""
    img = Image.open(chemin)

    # Convertir en niveaux de gris
    img = img.convert("L")

    # Augmenter le contraste
    enhancer = ImageEnhance.Contrast(img)
    img = enhancer.enhance(2.0)

    # Augmenter la netteté
    img = img.filter(ImageFilter.SHARPEN)

    return img

Le facteur 2.0 convient à un scan délavé ; poussé trop haut, il crée des artefacts qui dégradent la lecture. Testez sur trois documents représentatifs avant de figer la valeur.

Le second levier est la spécialisation du prompt par type de document. Un formulaire appelle « Lisez chaque champ et sa valeur correspondante », une lettre « Transcrivez le corps de la lettre en conservant les paragraphes », un tableau « Extrayez les données du tableau en format markdown ». Trois formulations, trois structures de sortie : classez vos documents avant de les transcrire, et la qualité change d’un cran.

Vision API vs Document AI

Pour l’OCR avancé et le parsing documentaire, Mistral propose également Document AI (OCR 4), un service spécialisé. Le tableau suivant situe la frontière entre les deux.

CritèreVision APIDocument AI (OCR 4)
OCR simpleSuffisantSurqualifié
Documents complexesLimitéOptimisé
Tableaux imbriquésApproximatifPrécis
PDF multi-pagesNon supportéSupporté
Mise en page complexeBasiqueAvancée

La ligne décisive est celle des PDF multi-pages : la Vision API traite des images, pas des documents paginés. Dès que votre source est un PDF de plusieurs dizaines de pages, la question ne se pose plus. Nous aborderons Document AI dans la leçon 12 (Prochaines étapes).

Points clés à retenir

  • La Vision API fait un OCR correct pour les cas simples (factures, captures, reçus)
  • Utilisez le mot « exactement » dans votre prompt pour éviter la paraphrase
  • Mistral Large 3 est nettement meilleur que Small pour les documents de mauvaise qualité
  • Le pré-traitement d’image (contraste, niveaux de gris) améliore les résultats
  • Pour l’OCR avancé et les PDF multi-pages, considérez Document AI (OCR 4) de Mistral