Ingénierie IA10 min de lecture
OCR rapide en production : RapidOCR, ONNX Runtime et files non bloquantes
Comprendre la chaîne RapidOCR fondée sur PP-OCR, mesurer sa vitesse et la déployer avec limites, files d’attente, reprise et observabilité.
Une photo de 24 mégapixels arrive, son orientation EXIF est incorrecte et le numéro recherché n’occupe que 2 % du cadre. L’OCR peut répondre en une fraction de seconde sur un test propre, puis saturer une API dès que vingt images similaires arrivent ensemble. Le choix du modèle n’explique qu’une partie du résultat : le redimensionnement, la concurrence, les limites d’entrée et la manière de rendre la réponse comptent tout autant.
RapidOCR associé à ONNX Runtime constitue une base intéressante pour une OCR locale, légère et utilisable sur CPU. Il faut toutefois le considérer comme une chaîne de vision, et non comme une fonction magique qui transforme toute image en texte exact.
Ce que le modèle exécute réellement
Une chaîne OCR complète enchaîne généralement trois réseaux :
- La détection localise les zones qui ressemblent à du texte et renvoie des quadrilatères.
- La classification d’orientation décide notamment si une ligne doit être retournée de 180°.
- La reconnaissance convertit chaque zone redressée en caractères et lui associe un score.
RapidOCR orchestre ces étapes et utilise ONNX Runtime comme moteur d’inférence CPU. Les modèles couramment distribués avec la génération PP-OCRv4 sont des modèles « mobile » : un détecteur PP-OCRv4 d’environ 4,5 Mo, un petit classifieur d’orientation et un reconnaisseur PP-OCRv4 d’environ 10 Mo. Le reconnaisseur v4 reçoit typiquement une ligne redimensionnée au format 3 × 48 × 320, tandis que le classifieur travaille sur 3 × 48 × 192.
Ces tailles décrivent les fichiers de poids, pas la mémoire totale du processus. Il faut ajouter ONNX Runtime, OpenCV ou Pillow, les tenseurs intermédiaires, l’image décodée et les espaces de travail de chaque session. Quatre processus chargent en principe quatre exemplaires de la chaîne. La RAM réelle doit donc être mesurée après échauffement, sur une image représentative.
Le vocabulaire de reconnaissance est également une décision de modèle. Un modèle chinois–anglais–chiffres n’a ni le même dictionnaire ni les mêmes erreurs qu’un modèle anglais ou latin spécialisé. Pour des dossards numériques, réduire les candidats possibles par une règle métier est souvent plus efficace que d’accepter silencieusement tous les caractères du dictionnaire.
La documentation de l’API RapidOCR détaille les dimensions d’entrée, les seuils de détection et les trois sous-étapes. Les valeurs par défaut sont un point de départ ; elles ne remplacent pas une calibration sur les images cibles.
Vitesse : des repères, pas une promesse
La publication PP-OCRv4 annonce une chaîne de 15,8 Mo, un Hmean de 62,24 % et 76 ms par image dans son évaluation de bout en bout. Cette mesure a été réalisée avec OpenVINO sur un Intel Gold 6148. Elle décrit ce protocole précis, pas le temps d’une requête web contenant décodage JPEG, correction EXIF, transfert réseau et sérialisation JSON. Le rapport officiel PP-OCRv4 indique d’ailleurs explicitement le matériel et le moteur utilisés.
Dans un autre benchmark du projet RapidOCR, le détecteur PP-OCRv4 ONNX de 4,5 Mo atteint 0,2256 seconde par image sur son jeu d’essai. Le reconnaisseur de 10 Mo affiche 0,6836 seconde par image de test, avec 83,23 % de correspondances exactes et 93,55 % de caractères corrects. Ces nombres ne s’additionnent pas directement : les corpus, les unités d’entrée et le nombre de lignes détectées ne sont pas identiques. Ils illustrent surtout l’écart possible entre deux protocoles. Les tableaux complets sont publiés dans la comparaison des modèles RapidOCR.
Une attente honnête se formule donc ainsi : sur CPU moderne, une image raisonnablement dimensionnée peut souvent être traitée de façon interactive, mais la latence varie fortement avec la résolution, le nombre de zones de texte, le processeur et la concurrence. Une page dense coûte plus cher qu’un recadrage contenant un seul numéro.
Mesurez au minimum :
- le temps de lecture et de décodage ;
- le prétraitement, séparé de l’inférence ;
- la détection, l’orientation et la reconnaissance ;
- le temps passé en file d’attente ;
- la latence totale p50, p95 et p99 ;
- le débit soutenu, les erreurs et la mémoire maximale ;
- la qualité sur le corpus métier, pas seulement sur du texte imprimé.
Un test utile comporte un échauffement, plusieurs tailles d’image, des fichiers sans texte, des scènes très chargées et des rafales de requêtes. Publier uniquement la moyenne masque les blocages qui apparaissent dans le p95.
Pourquoi réduire l’image, et jusqu’où
Décoder une image de 6000 × 4000 pixels crée déjà environ 72 Mo de données RGB non compressées. La passer telle quelle dans la détection augmente la mémoire et le temps de calcul, alors que les réseaux redimensionnent souvent encore leur entrée.
Limiter la plus grande dimension à 1 500 ou 2 000 pixels rend la charge plus prévisible. Il faut corriger l’orientation EXIF avant ce redimensionnement, conserver les proportions et convertir vers un espace couleur attendu. Mais cette limite n’est pas gratuite : un petit numéro éloigné peut devenir illisible.
Le meilleur compromis est souvent une stratégie en deux passes :
- une première analyse à résolution bornée pour localiser les zones probables ;
- une seconde reconnaissance sur les zones découpées depuis l’original, avec une marge autour du texte.
Si l’emplacement est déjà connu — par exemple une zone de dossard détectée en amont — envoyer uniquement le recadrage économise beaucoup plus que de changer de moteur d’inférence. Il faut alors conserver la transformation géométrique pour pouvoir rapporter les coordonnées dans l’image originale.
Déployer une chaîne reproductible
Un conteneur CPU suffit pour démarrer. L’image doit contenir les bibliothèques système nécessaires, les dépendances Python verrouillées et, idéalement, les fichiers ONNX déjà téléchargés. Précharger les poids pendant la construction évite qu’un premier appel dépende du réseau ou paie seul le coût d’initialisation.
Quelques règles rendent le déploiement reproductible :
- épingler la version de RapidOCR, d’ONNX Runtime et les empreintes des modèles ;
- conserver explicitement les chemins du détecteur, du classifieur, du reconnaisseur et du dictionnaire ;
- inscrire ces versions dans les métriques et les résultats ;
- construire l’image sans identifiants ni cache utilisateur ;
- lancer une inférence synthétique à la construction, puis un échauffement au démarrage ;
- exposer une sonde de disponibilité qui vérifie que les modèles sont chargés ;
- appliquer des limites CPU et mémoire, puis tester le comportement lorsqu’elles sont atteintes ;
- arrêter proprement le service en cessant d’accepter de nouveaux travaux avant de terminer ceux en cours.
Une simple sonde HTTP qui répond « OK » ne prouve pas que l’inférence fonctionne. Une sonde de démarrage peut charger les modèles ; la sonde de disponibilité peut vérifier l’état du worker sans lancer une OCR coûteuse à chaque passage. Un test synthétique périodique, moins fréquent, valide toute la chaîne.
Le modèle doit être créé une fois par processus et rester en mémoire. Le reconstruire pour chaque image ajoute une latence inutile. À l’inverse, augmenter sans limite le nombre de processus multiplie la RAM et peut ralentir l’ensemble par compétition entre threads.
Empêcher l’OCR de bloquer l’API
L’inférence est un calcul CPU synchrone. La placer directement dans une route async ne la rend pas asynchrone : tant que le calcul tourne, la boucle d’événements peut rester occupée. Trois architectures couvrent la plupart des besoins.
Réponse interactive à concurrence bornée
Pour une seule image, exécutez le calcul dans un pool de threads ou un worker synchrone, avec un sémaphore global. La limite doit être faible et déterminée par un test de charge. Lorsque tous les emplacements sont pris, attendez un temps court puis répondez 429 Too Many Requests ou 503 Service Unavailable avec Retry-After.
Le principe peut se résumer ainsi :
slots = Semaphore(MAX_IN_FLIGHT)
async def recognize(upload):
image = await decode_with_limits(upload)
if not await slots.acquire(timeout=0.2):
raise Busy(retry_after=1)
try:
return await run_in_worker_thread(ocr_engine, image)
finally:
slots.release()
La limite protège la latence et la mémoire. Elle ne doit pas dépasser mécaniquement le nombre de cœurs : ONNX Runtime utilise lui-même des threads internes. La documentation ONNX Runtime sur le threading explique les réglages intra-op et inter-op. Le produit processus × requêtes simultanées × threads ONNX doit rester cohérent avec les CPU réellement disponibles.
Travail asynchrone avec une file durable
Pour un lot, l’API valide le fichier, crée un identifiant idempotent et répond 202 Accepted. Un worker séparé consomme une file bornée, écrit le résultat, puis signale la fin. Le client interroge l’état ou reçoit un événement.
Cette architecture absorbe les pointes sans conserver des connexions HTTP pendant plusieurs minutes. Elle facilite aussi les priorités, l’annulation et la reprise après redémarrage. La file doit néanmoins avoir une capacité maximale : une file infinie transforme une surcharge visible en retard invisible.
Micro-lots
La reconnaissance de plusieurs lignes peut bénéficier d’un petit batch, alors que regrouper arbitrairement de grandes photos augmente l’attente du premier élément. Fixez à la fois une taille maximale et une courte fenêtre temporelle. Dès que l’une est atteinte, le lot part.
Reprise, délais et déduplication
Un appel peut échouer après que le calcul a réussi mais avant que le client reçoive la réponse. Sans identifiant stable, une relance effectue le même travail une seconde fois.
Calculez une clé à partir du contenu de l’image, de la version de la chaîne et des paramètres utiles. Le cache doit distinguer un résultat vide valide d’un échec technique. Une nouvelle version de modèle produit une nouvelle clé.
Les reprises doivent rester sélectives :
- ne pas relancer un format invalide ou une image trop grande ;
- relancer les indisponibilités temporaires avec attente exponentielle et jitter ;
- plafonner le nombre d’essais et la durée totale ;
- placer les échecs persistants dans une file d’examen ;
- utiliser un coupe-circuit si le worker ne répond plus ;
- propager une échéance afin qu’un travail déjà trop tardif ne consomme pas le CPU.
Un délai côté client ne suffit pas : il faut pouvoir annuler un travail en attente et empêcher qu’une requête abandonnée continue à remplir la file.
Points forts et limites
RapidOCR avec ONNX Runtime présente plusieurs avantages : poids compacts, exécution locale sur CPU, absence d’appel obligatoire à une API tierce, coordonnées de texte disponibles et chaîne configurable par étape. Le format ONNX facilite aussi le choix d’un moteur d’exécution adapté à la plateforme.
Ses limites sont celles d’une OCR généraliste légère. Les petits caractères, le flou, la perspective, les reflets, le tissu plié et les textes courbes réduisent fortement la qualité. La confiance du modèle n’est pas une probabilité métier calibrée. Une chaîne très confiante peut lire parfaitement le mauvais numéro, par exemple un marquage en arrière-plan.
Le classifieur standard corrige surtout les lignes à 0° ou 180° ; il ne redresse pas à lui seul toutes les rotations et perspectives. Les alphabets, polices et langues absents de l’entraînement créent d’autres erreurs. Enfin, un GPU n’est pas automatiquement plus rapide pour de petits modèles à formes dynamiques : transfert, initialisation et faible batch peuvent annuler son avantage.
Pour un dossard, une plaque ou un identifiant court, la validation métier est indispensable : format autorisé, longueur, plage de valeurs, liste de candidats et contexte de l’événement. Conservez le texte brut, le score, la zone et les alternatives. Le guide du classement par numéro de dossard montre comment utiliser ces observations sans les transformer en preuve d’identité.
Une checklist avant la mise en production
Avant d’ouvrir le trafic, vérifiez les points suivants :
- formats autorisés, taille en octets et nombre maximal de pixels contrôlés avant allocation complète ;
- orientation EXIF corrigée et métadonnées sensibles supprimées si elles ne sont pas nécessaires ;
- dépendances et poids épinglés, présents dans l’image et vérifiés par empreinte ;
- modèle chargé une fois, échauffement effectué et mémoire mesurée ;
- concurrence bornée, file plafonnée et surcharge renvoyée explicitement ;
- délais, annulation, idempotence, cache et politique de reprise testés ;
- métriques de file, p50/p95/p99, débit, mémoire, erreurs et résultats vides disponibles ;
- corpus de validation représentatif des angles, supports, appareils et éclairages réels ;
- seuils choisis sur ce corpus et cas incertains envoyés en revue ;
- test de redémarrage réalisé avec des travaux en attente et en cours.
Le workflow d’import de photos en direct complète cette approche pour éviter les états incomplets lors d’un traitement continu. Le guide de qualité des recadrages explique également pourquoi le contenu envoyé au modèle pèse souvent plus que quelques millisecondes gagnées sur l’inférence.
Une OCR rapide en production n’est donc pas seulement un petit modèle. C’est un modèle compact, une entrée maîtrisée, une concurrence bornée et une incertitude conservée jusqu’à la décision finale. C’est cette combinaison qui maintient l’API réactive quand les images cessent d’arriver une par une.