llm_aided_ocr : corriger la sortie de Tesseract avec un LLM, local ou via API
Enhances Tesseract OCR output using LLMs (local or API) for error correction, smart chunking, and markdown formatting of scanned PDFs
En bref
- De quoi s’agit-il ?
- Le projet de Dicklesworthstone enveloppe Tesseract dans un pipeline Python qui reconvertit les PDF en images, découpe le texte, puis confie la correction et la mise en forme Markdown à un LLM. Voici ce que la documentation décrit, et où elle s'arrête.
- À qui s’adresse-t-il ?
- À adopter si vous avez déjà Tesseract en place et qu'il vous manque une couche de correction sur des scans propres, avec un modèle local si la confidentialité du document compte. À éviter si vos PDF contiennent des tableaux, des formules ou une mise en page que le Markdown ne peut pas représenter.
- Puis-je l’utiliser commercialement ?
- À vérifier. La licence de ce dépôt n’entre pas dans les catégories que nous classons automatiquement : lisez son fichier LICENSE avant tout usage commercial.
- Est-il encore maintenu ?
- Oui. Les derniers commits datent d’il y a 44 jours.
- En quel langage est-il écrit ?
- Principalement Python, d’après les statistiques de langage de GitHub.
Ces réponses reposent sur les données GitHub du projet (dernière synchronisation le 15 septembre 2026) et sur notre analyse. Elles ne constituent pas un avis juridique.
ANALYSE OPEN SOURCE APPROFONDIE
Le problème : Tesseract lit, mais ne comprend pas
Tesseract extrait des caractères. Il ne sait pas qu'un « rn » a été lu à la place d'un « m », ni qu'une ligne de pied de page s'est glissée au milieu d'un paragraphe, ni qu'un titre doit être un titre. Le projet part de ce constat et ajoute une couche de post-traitement confiée à un grand modèle de langage, qui dispose du contexte de la phrase pour arbitrer. La cible est donc étroite : des documents scannés déjà lisibles par Tesseract, dont on veut corriger les erreurs résiduelles et obtenir une sortie Markdown exploitable. Les lettres dactylographiées, les rapports numérisés, les archives administratives correspondent à ce profil. Un scan de mauvaise qualité, très bruité ou manuscrit, reste hors de portée : le pipeline ne fait rien pour améliorer la reconnaissance elle-même au-delà du prétraitement d'image décrit plus bas.
Le trajet d'une page, de pdf2image à la sortie Markdown
La chaîne est explicite dans le README. convert_pdf_to_images() s'appuie sur pdf2image pour transformer les pages en images, avec deux paramètres documentés, max_pages et skip_first_n_pages, qui permettent de traiter un sous-ensemble du document. ocr_image() passe ensuite l'image à pytesseract, après preprocess_image() : conversion en niveaux de gris, seuillage binaire par la méthode d'Otsu, puis dilatation pour renforcer le contraste des glyphes. Le texte complet est ensuite découpé par process_document(), qui coupe aux frontières de phrases et conserve un chevauchement entre segments pour ne pas perdre le contexte à la jointure. Vient process_chunk(), le coeur du système, en deux temps : correction des erreurs OCR, puis mise en forme Markdown si l'option est activée. La déduplication des paragraphes répétés est intégrée à cette seconde étape, ce qui est un choix discutable : si vous désactivez le Markdown, vous perdez aussi le nettoyage des doublons. La qualité finale peut être évaluée par assess_output_quality(), qui compare le texte OCR d'origine à la sortie traitée et demande au modèle une note accompagnée d'une explication.
Deux moteurs possibles, deux régimes de coût
Le projet accepte un LLM local via llama_cpp, avec un modèle GGUF et la possibilité de fournir une grammaire personnalisée pour contraindre la sortie, ou un fournisseur distant parmi OpenAI et Anthropic. Le choix se fait dans le fichier .env, avec USE_LOCAL_LLM et API_PROVIDER. La différence de comportement ne tient pas seulement au coût. En mode API, le traitement des segments est asynchrone via asyncio, ce qui permet de lancer plusieurs requêtes en parallèle tout en réordonnant les résultats pour garder la cohérence du document. En mode local, l'inférence est séquentielle et dépend du GPU. Le README mentionne l'accélération GPU pour l'inférence locale sans donner de chiffres, et je n'en avancerai aucun. La gestion des tokens est traitée par estimate_tokens(), qui utilise le tokenizer du modèle quand il est disponible et retombe sur approximate_tokens() sinon, avec deux constantes nommées TOKEN_BUFFER et TOKEN_CUSHION pour ajuster max_tokens en fonction de la longueur du prompt. C'est le genre de détail qui trahit un usage réel : sans cette marge, les réponses longues se font tronquer en silence.
Installation : pyenv, Tesseract, puis un .env
La procédure du README vise Python 3.12 et passe par pyenv. Après le clone du dépôt, on fixe la version locale avec pyenv local 3.12, on crée l'environnement avec python -m venv venv et on l'active, puis pip install -r requirements.txt. Tesseract s'installe séparément, par exemple sudo apt-get install tesseract-ocr sur Ubuntu ou brew install tesseract sur macOS. La configuration tient dans un fichier .env dont le README donne ce squelette : USE_LOCAL_LLM=False, API_PROVIDER=OPENAI, OPENAI_API_KEY, ANTHROPIC_API_KEY. L'usage se résume à déposer un PDF dans le répertoire du projet et à renseigner une variable input_ dont le nom exact est tronqué dans la documentation fournie. C'est un point à vérifier dans le dépôt avant de commencer. Les sorties sont nommées de façon prévisible : {base_name}__raw_ocr_output.txt pour l'OCR brut, et {base_name}_llm_corrected.md ou .txt pour le résultat corrigé. Aucun paquet n'est publié, aucune version n'est étiquetée : on utilise le dépôt tel quel.
Ce que le pipeline ne sait pas faire
Le Markdown est un format plat. Un tableau scanné, une formule, un graphique annoté ne survivent pas à une conversion en titres, listes et emphases. Le README décrit le traitement des titres, des listes et des emphases, rien de plus. Si vos documents sont des états financiers ou des articles scientifiques, l'outil produira un texte propre mais amputé de sa structure porteuse. Deuxième limite, la déduplication des paragraphes répétés repose sur une détection de similarité exacte ou quasi exacte, et elle est enfouie dans l'étape Markdown. Sur un document où un paragraphe est légitimement répété, par exemple un encadré repris en conclusion, vous risquez de perdre du contenu sans avertissement. Troisième point, la correction par LLM peut inventer. Le prompt demande de conserver la structure et le contenu d'origine, mais rien dans la documentation ne décrit un mécanisme de vérification qui comparerait la sortie corrigée au texte source pour détecter une reformulation abusive. assess_output_quality() produit un score, pas une garantie. Sur un document contractuel ou réglementaire, cette absence de contrôle est un vrai problème.
Face à ocrmypdf, un désaccord sur le rôle du LLM
L'alternative la plus directe est ocrmypdf, qui ajoute une couche de texte à un PDF existant en s'appuyant sur Tesseract et sur des prétraitements d'image, sans modèle de langage. La différence d'approche est nette : ocrmypdf reste dans le monde du PDF, produit un fichier consultable et indexable, et ne modifie jamais le contenu textuel au-delà de ce que la reconnaissance a produit. llm_aided_ocr sort du PDF, produit du texte ou du Markdown, et confie à un modèle le soin de réécrire ce que Tesseract a mal lu. Le premier est déterministe et vérifiable, le second corrige davantage mais introduit une variabilité difficile à auditer. Un troisième cas de figure existe : les services OCR commerciaux qui intègrent leur propre modèle de reconnaissance, où vous ne contrôlez ni le modèle ni les données envoyées. Le projet de Dicklesworthstone a l'avantage de rendre ce choix explicite via USE_LOCAL_LLM, ce qui permet de garder le document sur votre machine.
Maintenance, licence et ce que le dépôt ne dit pas
Le dépôt est actif, la dernière poussée datant d'août 2026, et il n'est pas archivé. Aucune release n'est publiée, ce qui signifie qu'il n'existe pas de version stable à épingler : vous suivez la branche main. Pour un script lancé à la demande, c'est acceptable. Pour un traitement intégré à une chaîne de production, cela veut dire relire les changements avant chaque mise à jour. La licence est en revanche un point bloquant potentiel : GitHub renvoie NOASSERTION, ce qui veut dire que le fichier de licence n'a pas été reconnu comme un texte standard. Sans identification claire, on ne peut pas savoir si l'usage commercial est permis, ni quelles obligations de redistribution s'appliquent. Si vous envisagez de l'intégrer dans un produit, c'est la première chose à éclaircir auprès de l'auteur. Le coût de maintenance dépend aussi du fournisseur : les API OpenAI et Anthropic évoluent, et les fonctions generate_completion_from_claude() et generate_completion_from_openai() devront suivre. Le mode local, lui, dépend de llama_cpp et de la compatibilité du fichier GGUF choisi.
Conclusion éditoriale
À adopter si vous avez déjà Tesseract en place et qu'il vous manque une couche de correction sur des scans propres, avec un modèle local si la confidentialité du document compte. À éviter si vos PDF contiennent des tableaux, des formules ou une mise en page que le Markdown ne peut pas représenter. Avant de vous engager, vérifiez deux choses concrètes : la licence exacte du dépôt, que GitHub ne classe pas, et le comportement de process_chunk() sur un document de plusieurs centaines de pages, puisque la documentation ne décrit ni le coût en tokens ni le temps de traitement à cette échelle.
Notes de la communauté