Ingénierie
Tesseract.js en production
Faire reconnaître un mot à Tesseract.js prend dix minutes. Le faire tourner en production pendant des années demande de comprendre ce qu'il télécharge, quand et depuis où — car c'est là qu'il casse.
EasyInvoiceOCR · Publié le · 11 min de lecture
Tesseract.js est une enveloppe JavaScript autour de Tesseract, le moteur d'OCR open source, compilé en WebAssembly pour tourner dans un navigateur. C'est un projet communautaire indépendant : nous l'utilisons, nous contribuons à son suivi de tickets, et nous n'avons aucune affiliation avec lui au-delà de cela.
Ce qui le rend intéressant en production n'est pas l'API de reconnaissance, qui est réduite. C'est l'histoire des ressources qu'il y a en dessous.
Ce qui est réellement téléchargé
Une reconnaissance récupère trois choses distinctes, et les confondre est à l'origine de la plupart des problèmes de déploiement.
- Le script worker — le JavaScript qui exécute la reconnaissance hors du fil principal.
- Le cœur WASM — le moteur compilé. Plusieurs variantes existent (simple, SIMD, SIMD relâché, versions LSTM de chacune) et le navigateur reçoit la plus rapide qu'il sache exécuter.
- Les données de langue — un fichier .traineddata par langue, de loin le composant le plus lourd.
Les modèles de langue sont la partie coûteuse
Chaque langue est un fichier distinct de plusieurs mégaoctets. Nos cinq modèles de base totalisent 32,75 Mo : anglais 10,42 Mo, espagnol 7,98 Mo, allemand 6,77 Mo, français 5,99 Mo et arabe 1,60 Mo. Avec les cœurs WASM, le total hébergé atteint 76,00 Mo.
Ce chiffre inquiète tant qu'on n'a pas précisé ce qu'il n'est pas : rien de tout cela ne se trouve dans le bundle initial. Un modèle est récupéré une fois, à la demande, quand quelqu'un lance réellement une reconnaissance dans cette langue, puis mis en cache. Un visiteur qui fusionne seulement deux PDF n'en télécharge rien.
Les modes combinés ne sont pas des modèles supplémentaires
Tesseract accepte un argument de langue tel que eng+ara, qui charge deux modèles dans une même passe pour lire une page bilingue sans choisir un camp. L'arithmétique mérite d'être précise : sept options dans un sélecteur peuvent correspondre à cinq fichiers. Nous proposons cinq langues de base et deux modes combinés, non sept modèles indépendants.
Par défaut, c'est un CDN — et cela a des conséquences
Par défaut, Tesseract.js récupère ses données de langue depuis un CDN public à l'exécution. C'est pratique et cela fonctionne — jusqu'à ce que cela ne fonctionne plus. Une requête bloquée laisse une conversion suspendue en plein vol, et comme la requête défaillante ne touche jamais vos propres serveurs, la panne est invisible à votre supervision tout en étant parfaitement visible pour vos utilisateurs.
Il existe un second problème, plus discret. Le chemin par défaut n'est pas figé sur une version publiée et le téléchargement n'est pas vérifié par empreinte, si bien que les octets fournis au moteur peuvent changer sans que personne s'en aperçoive. C'est une discussion ouverte dans le projet plutôt qu'une critique : la même tension existe dans toute récupération de ressource à l'exécution.
Héberger les ressources soi-même
Nous servons désormais le worker, les cœurs et les modèles depuis notre propre domaine. Un script de préparation copie le worker et les cœurs depuis node_modules — ils correspondent donc toujours à la version installée plutôt que de diverger de ce qu'un CDN sert à un instant donné — et télécharge les données de langue une fois, à la construction, jamais à l'exécution.
Cela retire entièrement la dépendance tierce du chemin de reconnaissance. Cela supprime aussi une fuite plus subtile : récupérer le moteur depuis un CDN tiers signifie que ce tiers voit une requête, avec un référent, chaque fois qu'une conversion démarre. Aucun contenu de document, mais un motif de requête qui trahit l'intention.
Le défaut qu'introduit l'auto-hébergement
C'est la partie qui mérite d'être transmise, parce que nous l'avons apprise à nos dépens. Quand vous hébergez les modèles, la liste des langues que votre interface propose et celle que votre script télécharge deviennent deux listes distinctes, dans deux fichiers différents, sans rien qui garantisse leur correspondance.
Les nôtres ont divergé. Deux langues étaient sélectionnables dans le sélecteur alors que leurs fichiers n'avaient jamais été ajoutés à l'étape de téléchargement. Comme le chemin pointait vers notre propre domaine, il n'y avait aucun CDN vers lequel retomber : la requête renvoyait 404 et la reconnaissance échouait pour ces deux langues seulement. Rien dans la suite de tests ne l'a détecté, car aucun test ne reliait les deux listes.
Le vrai correctif n'a pas été d'ajouter les deux fichiers. C'est un test d'invariant qui échoue dans les deux sens — une langue proposée mais non hébergée, et un modèle hébergé mais plus proposé — et qui lit le script de construction comme du texte source, sans réseau ni ressources téléchargées.
Conseils pratiques
Quatre choses que nous dirions à nos versions passées.
- Figez une version explicite des données de langue plutôt que de dépendre d'un chemin qui résout vers la version courante.
- Si vous auto-hébergez, ajoutez un test vérifiant que chaque langue proposée correspond à un fichier existant.
- Regardez l'onglet réseau au démarrage du worker. Un modèle manquant ressemble à « cette langue ne marche pas », pas à un fichier absent.
- N'utilisez pas de liste blanche de caractères avec le moteur LSTM, et ne demandez pas le mode moteur historique sur des données qui n'en contiennent pas. Les deux dégradent le résultat tout en ressemblant à du réglage fin.
Ce que cela ne change pas
L'auto-hébergement change l'origine des octets, pas ce que fait la reconnaissance : nous utilisons le jeu de modèles standard, identique octet pour octet à ce que Tesseract.js récupère par défaut, délibérément, car un changement d'hébergement qui modifierait aussi silencieusement le résultat serait pénible à déboguer.
Cela ne rend pas non plus l'application exempte de réseau. Les octets du document sont traités localement dans le navigateur et ne sont jamais envoyés ; un bref enregistrement — nom du fichier, type, taille, nombre de pages et une clé identifiant la tentative — reste transmis pour que le quota puisse être appliqué côté serveur.
Sources primaires
- Tesseract.js — le projet lui-même, avec les options de worker qui contrôlent les chemins de ressources.
- tessdata — données de langue Tesseract — les fichiers .traineddata publiés, dont le jeu standard 4.0.0 utilisé ici.
- Documentation Tesseract : segmentation de page et modes moteur — référence pour les arguments PSM et OEM évoqués plus haut.
- WebAssembly — MDN — contexte sur la cible de compilation et la prise en charge SIMD dans les navigateurs.
Cinq conversions sont gratuites. Les octets du document sont traités dans votre navigateur.
Articles liés
- OCR navigateur
Qu'est-ce que l'OCR dans le navigateur ?
L'OCR dans le navigateur lit le texte des images et des PDF dans l'onglet plutôt que sur un serveur. Fonctionnement, comparaison avec l'OCR cloud, et le bon choix selon le cas.
- Produit
Extraire des factures en arabe, en français et en écritures mixtes
Mise en page de droite à gauche, chiffres arabes orientaux, factures bilingues : autant de pièges pour un analyseur conçu pour une seule écriture. Ce qui se casse, et comment on le détecte.