Module L29 · Partie K · Ingénierie de l’IA
OCR : lire une image, du pixel au texte structuré.
Lire un document scanné, une plaque, un compteur, un ticket : l’OCR est un pipeline — acquisition, prétraitement (binarisation, redressement), détection des zones de texte, reconnaissance (des caractères isolés aux lignes entières par CTC ou par transformer), post-traitement (dictionnaire, modèle de langue) et compréhension du document (tableaux, formulaires, mise en page). Ce chapitre implémente les briques classiques en NumPy, explique la perte CTC et les modèles modernes (CRNN, TrOCR, LayoutLM, modèles vision-langage), et donne les métriques (CER, WER) et les articles.
Durée : 3 séances · Prérequis : L10, L14, L19, L25. Objectifs : prétraiter une image (Otsu, morphologie, redressement par Hough) ; segmenter en lignes et caractères ; comprendre CTC et l’implémenter ; évaluer par CER/WER ; choisir entre Tesseract, PaddleOCR, TrOCR, un VLM ; lire les articles.
Ce que vous saurez faire à la fin
- Binariser (Otsu), redresser, segmenter des lignes et des caractères par projections et composantes connexes.
- Expliquer la perte CTC (alignement sans segmentation) et calculer sa programmation dynamique.
- Mesurer CER et WER, et lire une matrice de confusion de caractères.
- Construire un pipeline OCR sur PC avec Tesseract/PaddleOCR et un modèle vision-langage, et les comparer honnêtement.
Fiche de cours · Définitions
Définitions
Fiche de cours · Formules
Formules à connaître
Fiche de cours · Théorèmes et démonstrations
Démonstrations à savoir refaire
01 / Prétraiter
Une image de texte synthétique, Otsu, morphologie, redressement par projection
01 / Prétraiter
Segmenter : lignes par projection, caractères par composantes connexes, puis reconnaître par gabarits
Ce pipeline « classique » (années 1990) marche sur du texte propre et une police connue ; il casse dès que la police, la taille ou le fond changent. C’est exactement pourquoi la reconnaissance est passée aux modèles appris sur des lignes entières — mais le prétraitement, lui, reste utile.
02 / Apprendre
La perte CTC, implémentée : alignement sans segmentation
CTC est ce qui a rendu possible l’OCR de lignes (CRNN, Tesseract 4) et la reconnaissance vocale de bout en bout (L30) : le réseau émet une distribution par colonne d’image ou par trame audio, et la perte somme sur tous les alignements compatibles avec la transcription — aucune annotation de position n’est nécessaire.
03 / Modèles
Vue informatique : les architectures d’OCR, du CRNN aux modèles vision-langage
| Modèle | Architecture | Forces | Limites | Coût |
|---|---|---|---|---|
| Tesseract 4/5 | LSTM + CTC sur lignes, segmentation classique | Libre, 100+ langues, rapide CPU, sortie hOCR/positions | Fragile sur photos, mises en page complexes, manuscrit | ~0,5 s/page CPU |
| CRNN (Shi 2015) | CNN → BiLSTM → CTC | Simple, entraînable sur ses données | Lignes seulement ; détection à part | ms/ligne GPU |
| PaddleOCR / EasyOCR | Détection (DBNet) + reconnaissance (SVTR/CRNN) | Photos, scènes, multilingue, open source | Compréhension de structure limitée | ~50 ms/image GPU |
| TrOCR (Li 2021) | Encodeur ViT + décodeur texte (BART/RoBERTa) pré-entraînés | Manuscrit, imprimé ; SOTA sur IAM, SROIE | Lignes ; lourd ; pas de détection | ~100 ms/ligne GPU |
| LayoutLMv3, Donut, Nougat | Multimodal texte+position+image ; Donut/Nougat sans OCR (image → JSON/Markdown) | Formulaires, tableaux, articles scientifiques | Domaine d’entraînement ; hallucinations de champs | s/page GPU |
| VLM (GPT-4V, Claude, Qwen-VL, PaliGemma) | Encodeur image + LLM | Lecture + raisonnement + structure en un prompt ; zero-shot | Coût, latence, hallucinations sur les chiffres, pas de positions précises, données qui sortent | 1–10 s/page, ¢ par page |
Choisir (L34) : documents propres et volumineux → Tesseract/Paddle (coût ~0) ; photos et scènes → Paddle/EasyOCR ; manuscrit → TrOCR affiné ; formulaires et tableaux avec structure → LayoutLM/Donut ou VLM ; besoin de raisonner sur le contenu → VLM, avec vérification des nombres par un OCR classique (deux systèmes qui s’accordent = confiance).
04 / Évaluer
CER, WER, matrice de confusion, et les pièges de normalisation
Sur des documents à valeur (factures, relevés), le CER moyen cache l’essentiel : une erreur sur un chiffre d’un montant est grave, une sur une lettre d’un mot ne l’est pas. Mesurer aussi l’exactitude par champ (montant exact ? date exacte ?) et le taux de documents entièrement corrects — c’est ce que le métier verra.
05 / Articles
Les articles à lire
| Article | Contribution | À retenir |
|---|---|---|
| Otsu, A Threshold Selection Method from Gray-Level Histograms, IEEE SMC 1979 | Seuil optimal par variance inter-classe | Théorème 1 ; global, donc sensible à l’éclairage → Sauvola (2000) |
| Graves et al., Connectionist Temporal Classification, ICML 2006 | Perte CTC pour séquences non alignées | Théorème 2 ; base de l’OCR et de l’ASR de bout en bout |
| Shi et al., An End-to-End Trainable Neural Network for Image-based Sequence Recognition (CRNN), 2015 — 1507.05717 | CNN + BiLSTM + CTC sur lignes | L’architecture de référence pendant 5 ans |
| Smith, An Overview of the Tesseract OCR Engine, ICDAR 2007 ; Tesseract 4 (LSTM) 2018 | Pipeline complet libre | Lire pour comprendre ce qu’un pipeline « classique » contient |
| Baek et al., What Is Wrong With Scene Text Recognition Model Comparisons?, ICCV 2019 — 1904.01906 | Comparaison honnête : mêmes données, mêmes protocoles | Les écarts publiés venaient souvent des données d’entraînement, pas des modèles (L24) |
| Liao et al., Real-time Scene Text Detection with Differentiable Binarization (DBNet), AAAI 2020 — 1911.08947 | Détection de texte par segmentation + binarisation apprise | Détecteur de PaddleOCR |
| Li et al., TrOCR, 2021 — 2109.10282 | Transformer image→texte pré-entraîné, sans CNN ni CTC | Le pré-entraînement (synthétique + réel) fait la différence |
| Xu et al., LayoutLM, KDD 2020 — 1912.13318 ; Huang et al., LayoutLMv3, 2022 — 2204.08387 | Texte + position 2D + image pour la compréhension de documents | Extraction clé-valeur, classification de documents |
| Kim et al., Donut: OCR-free Document Understanding, ECCV 2022 — 2111.15664 | Image → JSON directement | Pas d’erreurs d’OCR en cascade, mais hallucinations possibles |
| Blecher et al., Nougat, 2023 — 2308.13418 | PDF scientifique → Markdown avec formules | Utile pour alimenter un RAG (L26) |
| Liu et al., OCRBench, 2023 — 2305.07895 | Benchmark des VLM sur l’OCR | Les VLM lisent bien mais comptent et alignent mal (tableaux, chiffres) |
TP guidé
TP — Trois OCR sur vos documents, comparés (5 h)
- Corpus. 30 pages variées (scan propre, photo de smartphone, ticket, tableau, manuscrit) avec transcription de référence (tapée à la main, normalisation fixée). 10 avec des champs à extraire (date, montant, référence).
- Prétraitement. OpenCV : niveaux de gris, Sauvola (
cv2.ximgproc.niBlackThresholdou implémentation), redressement (Houghcv2.HoughLines), déformation de perspective pour les photos (getPerspectiveTransform). Mesurer le CER avec/sans. - Trois moteurs. Tesseract (
pytesseract,--psmadapté), PaddleOCR, et un VLM (API ou Qwen2-VL local). Même prompt de normalisation. Tableau : CER, WER, exactitude par champ, taux de pages parfaites, temps, coût. - Analyse d’erreurs. Matrice de confusion des caractères par moteur ; les 10 pires pages : pourquoi ? (résolution, police, fond, mise en page). Une figure « CER en fonction de la hauteur de texte en pixels ».
- Post-traitement. Correction par dictionnaire (SymSpell) et par grammaire de champs (regex de date/montant) ; accord entre deux moteurs comme score de confiance ; mesurer le gain.
- Livrable. Dépôt (pipeline, évaluation, jeu annoté), rapport, et une recommandation : quel moteur pour quel type de document, avec les chiffres.
Exercices
Exercices auto-corrigés
Exercice 1 — Otsu et sa propriété
Réimplémentez otsu_seuil(pixels) sur des valeurs entières 0–255 (histogramme, sommes cumulées, argmax de la variance inter-classe) et vérifiez le Théorème 1 : σ²intra + σ²inter = σ²totale pour tout seuil, et le seuil sépare deux modes.
Correction
def otsu_seuil(pixels):
h = np.bincount(pixels, minlength=256).astype(float); p = h / h.sum(); w0 = np.cumsum(p); m = np.cumsum(p * np.arange(256))
mt = m[-1]; inter = (mt * w0 - m) ** 2 / (w0 * (1 - w0) + 1e-12)
return int(np.argmax(inter))Exercice 2 — Décodage CTC par faisceau (préfixes)
Implémentez ctc_faisceau(logp, largeur, blanc=0) : recherche en faisceau sur les préfixes réduits, en maintenant pour chaque préfixe deux probabilités (se terminant par blanc / par non-blanc) — l’algorithme de Graves. Vérifiez qu’il trouve la séquence de probabilité totale maximale sur un petit cas où le glouton se trompe.
Correction
def ctc_faisceau(logp, largeur=4, blanc=0):
from collections import defaultdict
T, C = logp.shape; P = np.exp(logp)
faisceau = {(): (1.0, 0.0)} # préfixe → (p_blanc, p_nonblanc)
for t in range(T):
nouveau = defaultdict(lambda: [0.0, 0.0])
for pref, (pb, pnb) in faisceau.items():
nouveau[pref][0] += (pb + pnb) * P[t, blanc] # émettre un blanc
if pref: nouveau[pref][1] += pnb * P[t, pref[-1]] # répéter le dernier caractère (fusionné)
for c in range(C):
if c == blanc: continue
ext = pref + (c,)
if pref and c == pref[-1]: nouveau[ext][1] += pb * P[t, c] # même caractère : seulement après un blanc
else: nouveau[ext][1] += (pb + pnb) * P[t, c]
faisceau = dict(sorted(nouveau.items(), key=lambda kv: -(kv[1][0] + kv[1][1]))[:largeur])
faisceau = {k: (v[0], v[1]) for k, v in faisceau.items()}
return list(max(faisceau, key=lambda k: sum(faisceau[k])))Exercices
Exercices auto-corrigés (suite)
Exercice 3 — Exactitude par champ et taux de documents parfaits
Écrivez evaluer_champs(refs, hyps) : listes de dicts {champ: valeur} ; renvoyer (exactitude par champ : dict champ → fraction exacte après normalisation espaces/casse, taux de documents entièrement corrects, CER moyen sur les valeurs concaténées).
Correction
def evaluer_champs(refs, hyps):
norm = lambda s: re.sub(r"\s+", " ", str(s)).strip().lower()
champs = list(refs[0]); exact = {c: np.mean([norm(r[c]) == norm(h.get(c, "")) for r, h in zip(refs, hyps)]) for c in champs}
parfaits = np.mean([all(norm(r[c]) == norm(h.get(c, "")) for c in champs) for r, h in zip(refs, hyps)])
cers = [levenshtein(norm("|".join(h.get(c, "") for c in champs)), norm("|".join(r[c] for c in champs))) / len("|".join(r[c] for c in champs)) for r, h in zip(refs, hyps)]
return exact, float(parfaits), float(np.mean(cers))Exercice 4 — Correction par grammaire de champ
Écrivez corriger_montant(s) qui applique les confusions OCR classiques dans un contexte numérique (O→0, l/I→1, S→5, B→8, « . » ou « , » décimal → « , »), et corriger_date(s) (même idée + format JJ/MM/AAAA avec validation : mois 1–12, jour 1–31). Les chaînes déjà valides ne changent pas ; une chaîne irrécupérable renvoie None.
Correction
TABLE = str.maketrans({"O": "0", "o": "0", "l": "1", "I": "1", "S": "5", "B": "8"})
def corriger_montant(s):
t = s.strip().translate(TABLE).replace(".", ",")
return t if re.fullmatch(r"\d+(,\d{2})?", t) else None
def corriger_date(s):
t = s.strip().translate(TABLE); m = re.fullmatch(r"(\d{2})/(\d{2})/(\d{4})", t)
if not m: return None
j, mo, a = map(int, m.groups()); return t if 1 <= j <= 31 and 1 <= mo <= 12 else NoneFiche de cours · Exercices corrigés
Exercices corrigés (rédaction)
Vérification
Quel est l’apport principal de la perte CTC pour l’OCR de lignes ?
Deux questions supplémentaires
1. Pourquoi Otsu échoue-t-il sur un scan mal éclairé ? Le seuil est global ; un gradient d’éclairage mélange les deux modes → seuil local (Sauvola).
2. Pourquoi mesurer l’exactitude par champ en plus du CER ? Le CER est dominé par le texte facile ; les champs critiques (montants) sont rares et durs.
Référence
Les mots à retenir
| Mot | Définition |
|---|---|
| Binarisation (Otsu, Sauvola) | Seuil global par variance inter-classe / seuil local par moyenne et écart-type. |
| Morphologie | Érosion, dilatation, ouverture, fermeture sur images binaires. |
| Skew | Inclinaison du document ; Hough ou variance de projection. |
| CTC | Perte de séquence sans alignement, avec blanc et réduction. |
| CER / WER | Taux d’erreur par Levenshtein ; dépend de la normalisation. |
| CRNN / TrOCR | CNN+LSTM+CTC / ViT+décodeur : reconnaissance de lignes. |
| LayoutLM / Donut | Compréhension de documents : structure et champs. |
| VLM | Lecture et raisonnement zero-shot ; vérifier chiffres et structure. |
Suite
Après l’image, le son.
La reconnaissance vocale partage avec l’OCR la perte CTC et les encodeurs-décodeurs — mais le signal est une onde. Chapitre suivant : du spectrogramme à Whisper.