Cet article détaille la mise en place d'un environnement de reconnaissance optique de caractères (OCR) basé sur PaddleOCR, optimisé avec OpenVINO, sur une machine sous Windows 10. L'objectif est de permettre la détection et la reconnaissance de texte, particulièrement utile dans des contextes industriels.
I. Configuraton de l'environnement et téléchargement des modèles
Pour commencer, assurez-vous de disposer d'un environnement Python (version 3.10 recommandée, par exemple via Anaconda ou Miniconda). Il est conseillé de créer un environnement virtuel dédié.
conda create -n ocr_env python=3.10
conda activate ocr_env
1. Acquisition des fichiers source de PaddleOCR
Téléchargez la version 2.7 de PaddleOCR depuis le dépôt officiel de PaddlePaddle / PaddleOCR. Placez les fichiers dans le répertoire de votre choix sur votre système.
2. Installation de PaddlePaddle (version CPU)
Installez la version CPU de PaddlePaddle en utilisant la commande suivante, en spécifiant l'indice de paquetages de PaddlePaddle pour une installation stable :
pip install paddlepaddle==2.6.2 -i https://www.paddlepaddle.org.cn/packages/stable/cpu/
3. Téléchargement des modèles pré-entraînés
Les modèles pré-entraînés sont essentiels pour la détection et la reconnaissance. Rendez-vous sur la page officielle des modèles PaddleOCR et téléchargez les archives nécessaires. Créez un dossier nommé inference_model à la racine de votre répertoire PaddleOCR (version 2.7) et extrayez-y le contenu des archives. Vous devriez avoir une structure similaire à celle-ci pour les modèles de détection (det), de reconnaissance (rec) et de classification d'angle (cls) :
PaddleOCR-release-2.7/
├── inference_model/
│ ├── ch_PP-OCRv4_det_infer/ (modèle de détection)
│ ├── ch_PP-OCRv4_rec_infer/ (modèle de reconnaissance)
│ ├── ch_ppocr_mobile_v2.0_cls_infer/ (modèle de classification d'angle)
│ └── ppocr_keys_v1.txt (dictionnaire de caractères)
├── ...
4. Installation de PyTorch (optionnel, pour certaines dépendances ou débug)
Bien que l'accélération principale se fasse via OpenVINO, PyTorch peut être une dépendance pour certains outils ou une exigence pour le débogage. Installez la version CPU uniquement :
pip install torch==2.5.0 torchvision==0.20.0 torchaudio==2.5.0 --index-url https://download.pytorch.org/whl/cpu
5. Vérification de l'installation de PyTorch
Pour confirmer que PyTorch est correctement installé, exécutez les commandes suivantes dans votre interpréteur Python :
import torch
import torchvision
print(torch.__version__)
print(torchvision.__version__)
6. Installation de FastDeploy (pour le déploiement rapide et l'intégration OpenVINO)
FastDeploy est une bibliothèque de déploiement qui simplifie l'intégration de modèles d'apprentissage profond. Installez une version compatible avec OpenVINO :
pip install fastdeploy-python==1.0.7 -f https://www.paddlepaddle.org.cn/whl/fastdeploy.html
Assurez-vous également d'installer OpenVINO si ce n'est pas déjà fait, car ce sera le moteur principal pour les exemples de code suivants :
pip install openvino openvino-dev
II. Exécution et Utilisation
Les exemples suivants démontrent comment utiliser les modèles installés pour la reconnaissance OCR.
1. Exécution sur Windows 10
Pour une prédiction rapide sur une image statique, utilisez la commande suivante. Elle exploite les modèles de détection, reconnaissance et classification d'angle. Assurez-vous d'adapter le chemin vers votre image (12.jpg dans cet exemple).
set KMP_DUPLICATE_LIB_OK=TRUE && python tools/infer/predict_system.py --image_dir="12.jpg" --det_model_dir="./inference_model/ch_PP-OCRv4_det_infer" --rec_model_dir="./inference_model/ch_PP-OCRv4_rec_infer" --cls_model_dir="./inference_model/ch_ppocr_mobile_v2.0_cls_infer" --rec_char_dict_path="./ppocr_keys_v1.txt" --use_angle_cls=true --use_gpu=false
2. Exécution sur Linux
La commande pour Linux est similaire, en ajustant les chemins des répertoires et en utilisant python3 :
python3 tools/infer/predict_system.py --image_dir="./test_images/1.jpg" --det_model_dir="./inference_model/ch_PP-OCRv4_det_infer/" --rec_model_dir="./inference_model/ch_PP-OCRv4_rec_infer/"
3. Résolution des erreurs courantes (Exemple : Erreur de chagrement de PyTorch DLL)
Si vous rencontrez une erreur telle que OSError: [WinError 127] 找不到指定的程序。 Error loading "C:\...\torch\lib\shm.dll" or one of its dependencies. lors de l'importation de PyTorch, suivez ces étapes :
- Désinstallation de PyTorch :
pip uninstall torch torchvision torchaudio
- Nettoyage du cache pip :
pip cache purge
- Réinstallation avec Conda (recommandé pour la stabilité) :
conda install pytorch==2.5.0 torchvision==0.20.0 torchaudio==2.5.0 cpuonly -c pytorch
- Réinstallation de FastDeploy (si nécessaire) :
pip install fastdeploy-python==1.0.7 -f https://www.paddlepaddle.org.cn/whl/fastdeploy.html
- Nouvelle tentative d'exécution : Réexécutez votre script ou votre commande.
III. Exemples de Code pour Flux Vidéo
Les scripts Python suivants illustrent différentes applications de l'OCR en temps réel, notamment pour l'intégration avec des caméras ou des services web.
1. Détection OCR complète depuis une caméra (flux_video_ocr.py)
Ce script utilise OpenVINO pour la détection et la reconnaissance de tout type de texte (chiffres, lettres, caractères spéciaux) à partir d'un flux vidéo de caméra sur Windows 10. Il intègre la gestion des polices chinoises pour l'affichage des résultats.
import cv2
import numpy as np
import os
import time
from openvino.runtime import Core as OVCore
import logging
from PIL import Image, ImageDraw, ImageFont
import platform
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
class ProcesseurOCROpenVINO:
"""
Classe pour la détection et la reconnaissance OCR en utilisant OpenVINO.
"""
def __init__(self, resolution_cible=(640, 480)):
self.resolution_cible = resolution_cible
logger.info("Initialisation du moteur OpenVINO...")
self.coeur_ov = OVCore()
self.peripherique = self._determiner_peripherique()
# Chemins absolus pour les modèles
repertoire_base_modeles = r"C:\sxj\ppocr\inference_model" # Adaptez ce chemin
self.chemin_det_modele = os.path.join(repertoire_base_modeles, "ch_PP-OCRv4_det_infer", "ch_PP-OCRv4_det_infer.onnx")
self.chemin_rec_modele = os.path.join(repertoire_base_modeles, "ch_PP-OCRv4_rec_infer", "ch_PP-OCRv4_rec_infer.onnx")
self.chemin_cls_modele = os.path.join(repertoire_base_modeles, "ch_ppocr_mobile_v2.0_cls_infer", "ch_ppocr_mobile_v2.0_cls_infer.onnx")
self.chemin_dictionnaire = os.path.join(repertoire_base_modeles, "ppocr_keys_v1.txt")
self._verifier_existence_fichiers()
self._charger_modeles_ocr()
self.temps_detection = []
self.temps_reconnaissance = []
self.temps_classification = []
self.detections_recentes = []
self.dernier_affichage = 0
self.intervalle_affichage = 2 # secondes
self.police_chinois = self._initialiser_police_chinois()
def _determiner_peripherique(self):
"""Détermine le périphérique disponible (GPU ou CPU) pour OpenVINO."""
peripheriques_disponibles = self.coeur_ov.available_devices
logger.info(f"Périphériques OpenVINO disponibles: {peripheriques_disponibles}")
if "GPU" in peripheriques_disponibles:
logger.info("Utilisation du GPU pour OpenVINO.")
return "GPU"
else:
logger.info("Utilisation du CPU pour OpenVINO.")
return "CPU"
def _verifier_existence_fichiers(self):
"""Vérifie que tous les fichiers de modèle nécessaires existent."""
fichiers_a_verifier = [
(self.chemin_det_modele, "Modèle de détection"),
(self.chemin_rec_modele, "Modèle de reconnaissance"),
(self.chemin_cls_modele, "Modèle de classification"),
(self.chemin_dictionnaire, "Fichier dictionnaire")
]
tous_existent = True
for chemin, nom in fichiers_a_verifier:
if os.path.exists(chemin):
logger.info(f"✓ {nom} trouvé: {os.path.basename(chemin)}")
else:
logger.error(f"✗ {nom} manquant: {chemin}")
tous_existent = False
return tous_existent
def _charger_modeles_ocr(self):
"""Charge les modèles de détection, reconnaissance et classification."""
logger.info("Début du chargement des modèles...")
self.dictionnaire_rec = self._charger_dictionnaire(self.chemin_dictionnaire)
self.modele_det = self._charger_modele_individuel(self.chemin_det_modele, "Détection")
self.modele_rec = self._charger_modele_individuel(self.chemin_rec_modele, "Reconnaissance")
self.modele_cls = self._charger_modele_individuel(self.chemin_cls_modele, "Classification")
if not all([self.modele_det, self.modele_rec, self.dictionnaire_rec]):
logger.error("Échec du chargement d'un ou plusieurs modèles OCR critiques.")
else:
logger.info("Tous les modèles OCR critiques chargés avec succès.")
return all([self.modele_det, self.modele_rec, self.dictionnaire_rec])
def _charger_modele_individuel(self, chemin_onnx, etiquette):
"""Charge un modèle ONNX spécifique et le compile pour le périphérique."""
try:
logger.info(f"Chargement du modèle de {etiquette}...")
modele = self.coeur_ov.read_model(chemin_onnx)
modele_compile = self.coeur_ov.compile_model(modele, self.peripherique)
logger.info(f"Modèle de {etiquette} chargé avec succès.")
return modele_compile
except Exception as e:
logger.error(f"Échec du chargement du modèle de {etiquette}: {e}")
return None
def _charger_dictionnaire(self, chemin_dict):
"""Charge le dictionnaire de caractères pour la reconnaissance."""
try:
with open(chemin_dict, 'r', encoding="utf-8") as f:
caracteres = [ligne.strip() for ligne in f if ligne.strip()]
if caracteres and caracteres[0] != '': # Nécessaire pour le décodage CTC
caracteres = [''] + caracteres
logger.info(f"Dictionnaire chargé avec succès, {len(caracteres)} caractères.")
return caracteres
except Exception as e:
logger.error(f"Échec du chargement du dictionnaire: {e}")
return None
def _initialiser_police_chinois(self, taille=20):
"""Initialise une police pour l'affichage du texte chinois."""
try:
if platform.system() == "Windows":
chemins_polices = [
"C:/Windows/Fonts/simhei.ttf",
"C:/Windows/Fonts/simsun.ttc",
"C:/Windows/Fonts/msyh.ttc",
]
for chemin_police in chemins_polices:
if os.path.exists(chemin_police):
return ImageFont.truetype(chemin_police, taille)
return ImageFont.load_default()
except:
return ImageFont.load_default()
def _afficher_texte_chinois(self, image_cv2, texte, position, couleur_police=(0, 0, 0), couleur_fond=None):
"""Dessine du texte chinois sur une image OpenCV."""
try:
image_pil = Image.fromarray(cv2.cvtColor(image_cv2, cv2.COLOR_BGR2RGB))
dessin = ImageDraw.Draw(image_pil)
# Calculer la taille du texte pour le fond
bbox = dessin.textbbox((0, 0), texte, font=self.police_chinois)
largeur_texte = bbox[2] - bbox[0]
hauteur_texte = bbox[3] - bbox[1]
x, y = position
if couleur_fond:
dessin.rectangle([x, y - hauteur_texte, x + largeur_texte, y], fill=couleur_fond)
dessin.text((x, y - hauteur_texte), texte, font=self.police_chinois, fill=couleur_police)
return cv2.cvtColor(np.array(image_pil), cv2.COLOR_RGB2BGR)
except Exception as e:
logger.warning(f"Échec de l'affichage du texte chinois, utilisation de la police par défaut: {e}")
cv2.putText(image_cv2, "Text", position, cv2.FONT_HERSHEY_SIMPLEX, 0.5, couleur_police, 1)
return image_cv2
def _preparer_entree_modele(self, image_input, modele_compile, dimensions_defaut=(640, 640)):
"""Prépare une image pour l'inférence du modèle OpenVINO."""
try:
forme_entree = modele_compile.input(0).shape
h, w = forme_entree[2], forme_entree[3] if len(forme_entree) == 4 else dimensions_defaut[0], dimensions_defaut[1]
image_redimensionnee = cv2.resize(image_input, (w, h))
image_normalisee = image_redimensionnee.astype(np.float32) / 255.0
image_traitee = (image_normalisee - 0.5) / 0.5
blob_final = image_traitee.transpose(2, 0, 1)[None, ...] # (B, C, H, W)
return blob_final
except Exception as e:
logger.error(f"Échec de la préparation de l'entrée du modèle: {e}")
return None
def _post_traiter_reconnaissance(self, sortie_modele):
"""Post-traite la sortie du modèle de reconnaissance pour obtenir le texte et la confiance."""
if sortie_modele is None:
return "", 0.0
try:
donnees_sortie = sortie_modele[0] if isinstance(sortie_modele, (list, tuple)) else sortie_modele
if donnees_sortie.ndim != 3:
return "", 0.0
donnees_sortie = donnees_sortie[0] # Supprimer le batch dimension
indices_max = np.argmax(donnees_sortie, axis=1)
texte_reconnu = ""
dernier_idx = -1
for idx in indices_max:
if idx == 0: # Caractère vide (blank) pour CTC
dernier_idx = idx
continue
if idx == dernier_idx:
continue
if 0 <= idx < len(self.dictionnaire_rec):
texte_reconnu += self.dictionnaire_rec[idx]
dernier_idx = idx
valeurs_max = np.max(donnees_sortie, axis=1)
confiance = float(np.mean(valeurs_max))
return texte_reconnu.strip(), confiance
except Exception as e:
logger.error(f"Échec du post-traitement de la reconnaissance: {e}")
return "", 0.0
def _trier_boites_par_position(self, boites_detectees):
"""Trie les boîtes de détection par position (de haut en bas, puis de gauche à droite)."""
if not boites_detectees:
return []
centres_boites = []
for boite in boites_detectees:
x_min, y_min, x_max, y_max = boite
centre_y = (y_min + y_max) / 2
centres_boites.append((boite, centre_y))
triees_par_y = sorted(centres_boites, key=lambda x: x[1])
lignes_texte = []
ligne_actuelle = [triees_par_y[0]]
seuil_ligne = 20 # Seuil pour considérer les boîtes comme étant sur la même ligne
for i in range(1, len(triees_par_y)):
centre_y_prec = ligne_actuelle[-1][1]
centre_y_actuel = triees_par_y[i][1]
if abs(centre_y_actuel - centre_y_prec) < seuil_ligne:
ligne_actuelle.append(triees_par_y[i])
else:
# Trier les boîtes de la ligne actuelle par X (gauche à droite)
ligne_trie_x = sorted(ligne_actuelle, key=lambda x: x[0][0])
lignes_texte.append([item[0] for item in ligne_trie_x])
ligne_actuelle = [triees_par_y[i]]
if ligne_actuelle:
ligne_trie_x = sorted(ligne_actuelle, key=lambda x: x[0][0])
lignes_texte.append([item[0] for item in ligne_trie_x])
boites_finales_triees = []
for ligne in lignes_texte:
boites_finales_triees.extend(ligne)
return boites_finales_triees
def _preparer_image_detection(self, cadre_image):
"""Prépare l'image pour le modèle de détection."""
try:
forme_entree = self.modele_det.input(0).shape
h, w = forme_entree[2], forme_entree[3] if len(forme_entree) == 4 else (640, 640)
image_redimensionnee = cv2.resize(cadre_image, (w, h))
image_normalisee = image_redimensionnee.astype(np.float32) / 255.0
image_traitee = (image_normalisee - 0.5) / 0.5
blob = image_traitee.transpose(2, 0, 1)[None, ...]
return blob, (cadre_image.shape[1], cadre_image.shape[0])
except Exception as e:
logger.error(f"Échec de la préparation pour la détection: {e}")
return None, (cadre_image.shape[1], cadre_image.shape[0])
def _post_traiter_detection(self, sortie_det, dimensions_originales):
"""Post-traite la sortie du modèle de détection pour obtenir les boîtes englobantes."""
boites_resultat = []
if sortie_det is None or len(sortie_det.shape) != 4:
return boites_resultat
try:
probabilite = sortie_det[0, 0]
_, carte_binaire = cv2.threshold(probabilite, 0.3, 1, cv2.THRESH_BINARY)
carte_binaire = (carte_binaire * 255).astype(np.uint8)
contours, _ = cv2.findContours(carte_binaire, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
facteur_echelle_x = dimensions_originales[0] / probabilite.shape[1]
facteur_echelle_y = dimensions_originales[1] / probabilite.shape[0]
for contour in contours:
x_cnt, y_cnt, w_cnt, h_cnt = cv2.boundingRect(contour)
x1, y1 = int(x_cnt * facteur_echelle_x), int(y_cnt * facteur_echelle_y)
x2, y2 = int((x_cnt + w_cnt) * facteur_echelle_x), int((y_cnt + h_cnt) * facteur_echelle_y)
if (x2 - x1) > 15 and (y2 - y1) > 8: # Filtrer les petites boîtes
boites_resultat.append([x1, y1, x2, y2])
except Exception as e:
logger.error(f"Échec du post-traitement de la détection: {e}")
return boites_resultat
def traiter_cadre_video_ocr(self, cadre_entree):
"""Traite un seul cadre vidéo pour l'OCR."""
if cadre_entree is None:
return []
debut_traitement = time.time()
detections_courantes = []
try:
# 1. Détection de texte
t_det_debut = time.time()
blob_det, dims_orig = self._preparer_image_detection(cadre_entree)
if blob_det is None: return []
sortie_det = self.modele_det([blob_det])[self.modele_det.output(0)]
boites_det = self._post_traiter_detection(sortie_det, dims_orig)
self.temps_detection.append(time.time() - t_det_debut)
boites_triees = self._trier_boites_par_position(boites_det)
heure_actuelle = time.time()
doit_afficher = (heure_actuelle - self.dernier_affichage) >= self.intervalle_affichage
# Limiter le nombre de boîtes pour le traitement
for i, (x1, y1, x2, y2) in enumerate(boites_triees[:15]): # Max 15 boîtes
x1, y1 = max(0, x1), max(0, y1)
x2, y2 = min(cadre_entree.shape[1], x2), min(cadre_entree.shape[0], y2)
if x2 <= x1 or y2 <= y1: continue
region_recadree = cadre_entree[y1:y2, x1:x2]
if region_recadree.size == 0: continue
# 2. Classification d'angle (si le modèle est disponible)
if self.modele_cls:
t_cls_debut = time.time()
blob_cls = self._preparer_entree_modele(region_recadree, self.modele_cls, (48, 192))
if blob_cls is not None:
sortie_cls = self.modele_cls([blob_cls])[self.modele_cls.output(0)]
id_cls = np.argmax(sortie_cls[0])
if id_cls == 1: # Si le texte est à 180 degrés
region_recadree = cv2.rotate(region_recadree, cv2.ROTATE_180)
self.temps_classification.append(time.time() - t_cls_debut)
# 3. Reconnaissance de texte
t_rec_debut = time.time()
blob_rec = self._preparer_entree_modele(region_recadree, self.modele_rec, (48, 320))
if blob_rec is not None:
sortie_rec = self.modele_rec([blob_rec])[self.modele_rec.output(0)]
texte_rec, confiance = self._post_traiter_reconnaissance(sortie_rec)
if texte_rec and len(texte_rec.strip()) > 0 and confiance > 0.3:
detections_courantes.append({
'texte': texte_rec,
'confiance': confiance,
'boite': (x1, y1, x2, y2),
'index': i
})
if doit_afficher:
logger.info(f"Boîte {i+1}: '{texte_rec}' (Confiance: {confiance:.3f})")
self.temps_reconnaissance.append(time.time() - t_rec_debut)
self.detections_recentes = detections_courantes # Mise à jour des détections pour l'affichage
if doit_afficher and detections_courantes:
self.dernier_affichage = heure_actuelle
logger.info(f"Reconnu {len(detections_courantes)} textes en {time.time() - debut_traitement:.3f}s")
except Exception as e:
logger.error(f"Échec du traitement OCR sur le cadre: {e}")
return detections_courantes
def afficher_statistiques_performance(self):
"""Affiche les statistiques de temps moyen pour chaque étape."""
if self.temps_detection:
logger.info(f"Temps moyen de détection: {np.mean(self.temps_detection):.4f}s")
if self.temps_reconnaissance:
logger.info(f"Temps moyen de reconnaissance: {np.mean(self.temps_reconnaissance):.4f}s")
if self.temps_classification:
logger.info(f"Temps moyen de classification: {np.mean(self.temps_classification):.4f}s")
def initialiser_camera(idx_camera=0, resolution_demandee=(640, 480)):
"""Initialise la capture vidéo de la caméra."""
backends_testes = [cv2.CAP_DSHOW, cv2.CAP_MSMF, cv2.CAP_ANY]
for backend in backends_testes:
try:
cap = cv2.VideoCapture(idx_camera, backend)
if cap.isOpened():
cap.set(cv2.CAP_PROP_FRAME_WIDTH, resolution_demandee[0])
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, resolution_demandee[1])
cap.set(cv2.CAP_PROP_FPS, 15)
largeur_reelle = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
hauteur_reelle = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
logger.info(f"Caméra initialisée avec succès - Résolution: {largeur_reelle}x{hauteur_reelle}")
return cap
except:
continue
logger.error("Échec de l'initialisation de la caméra.")
return None
def demarrer_flux_video_ocr():
"""Fonction principale pour démarrer le flux vidéo et l'OCR."""
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE' # Pour éviter les conflits de bibliothèques Intel
cap = initialiser_camera(0, (640, 480))
if cap is None:
return
logger.info("Initialisation du processeur OCR...")
ocr_processeur = ProcesseurOCROpenVINO((640, 480))
if not all([ocr_processeur.modele_det, ocr_processeur.modele_rec, ocr_processeur.dictionnaire_rec]):
logger.error("Le processeur OCR n'a pas pu être initialisé correctement. Vérifiez les modèles.")
cap.release()
return
logger.info("Processeur OCR prêt. Appuyez sur 'Q' pour quitter.")
sauter_cadres = 3 # Traiter un cadre sur N pour optimiser
compteur_cadres = 0
try:
while True:
ret, cadre_video = cap.read()
if not ret:
logger.warning("Échec de la lecture d'un cadre vidéo.")
break
compteur_cadres += 1
if compteur_cadres % sauter_cadres == 0:
ocr_processeur.traiter_cadre_video_ocr(cadre_video)
cadre_visualisation = cadre_video.copy()
# Dessiner les boîtes de détection et le texte sur le cadre
for i, detection in enumerate(ocr_processeur.detections_recentes):
x1, y1, x2, y2 = detection['boite']
texte_aff = detection['texte']
cv2.rectangle(cadre_visualisation, (x1, y1), (x2, y2), (0, 255, 0), 2)
texte_pour_affichage = f"{i+1}:{texte_aff}"
cadre_visualisation = ocr_processeur._afficher_texte_chinois(
cadre_visualisation,
texte_pour_affichage,
(x1, y1 - 5),
couleur_police=(0, 0, 0),
couleur_fond=(0, 255, 0)
)
statut_affichage = f"Cadre: {compteur_cadres}, Detections: {len(ocr_processeur.detections_recentes)}"
cv2.putText(cadre_visualisation, statut_affichage, (10, cadre_visualisation.shape[0] - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
cv2.imshow("OpenVINO OCR en temps reel", cadre_visualisation)
touche = cv2.waitKey(1) & 0xFF
if touche == ord('q'):
break
except KeyboardInterrupt:
logger.info("Arrêt du programme par l'utilisateur.")
except Exception as e:
logger.error(f"Erreur inattendue du programme: {e}")
finally:
cap.release()
cv2.destroyAllWindows()
ocr_processeur.afficher_statistiques_performance()
logger.info("Programme terminé.")
if __name__ == "__main__":
demarrer_flux_video_ocr()
2. Reconnaissance de caractères spécifiques (chiffres, lettres, symboles) (ocr_filtre_specifique.py)
Ce script est une adaptation du précédent, mais sa fonction de post-traitement est modifiée pour ne retenir et afficher que les textes qui correspondent à des motifs spécifiques, comme les chiffres, les lettres de l'alphabet latin et certains symboles (par exemple, ./). La logique de filtrage est appliquée après la reconnaissance initiale, permettant de se concentrer sur des informations pertinentes comme les numéros de série, les codes produits ou d'autres identifiants alphanumériques. Le cœur de l'implémentation repose sur une méthode de filtrage par expression régulière similaire à celle présente dans l'exemple ocr5.py de l'article original.
python ocr_filtre_specifique.py
3. Intégration avec une API REST/WebSocket pour Java (serveur_ocr_api.py)
Ce script met en place un serveur Flask avec SocketIO pour interagir avec des applications externes, notamment des clients Java sur le port 5020. Il est conçu pour recevoir des images encodées en Base64, effectuer l'OCR et renvoyer les résultats structurés au format JSON. Cette approche permet une intégration fluide des capacités OCR dans des systèmes existants. Les détections incluent le texte reconnu, la confiance, les coordonnées des boîtes englobantes, et d'autres métadonnées.
python serveur_ocr_api.py
Il est compatible avec une architecture client-serveur, où le client envoie des requêtes d'image et reçoit des réponses JSON contenant les textes détectés. Ce script est une réécriture fonctionnelle de ocr.py de l'article d'origine, en s'appuyant sur les mêmes principes d'initialisation des modèles et de traitement OpenVINO, mais en exposant ces fonctionnalités via une interface réseau.
4. Détection et filtrage de numéros de lot/dates via API et caméra (serveur_ocr_lots_dates.py)
Ce script combine les fonctionnalités de la détection par caméra et de l'intégration API, en se concentrant spécifiquement sur l'extraction de numéros de lot et de dates. Il utilise une logique de filtrage avancée (similaire à ocr7.py dans l'article original) pour identifier des motifs de texte correspondant à des numéros de lot ou des dates dans le texte reconnu. Les résultats sont ensuite encapsulés dans des réponses JSON compatibles avec d'autres systèmes (par exemple, des clients Java ou des applications de suivi industriel). Le serveur offre à la fois des points de terminaison REST et WebSocket pour une flexibilité maximale.
python serveur_ocr_lots_dates.py
Ce service est idéal pour les applications industrielles nécessitant une traçabilité automatique des produits via la reconnaissance de leurs identifiants uniques.