La reconnaissance optique de caractères ( OCR = Optical Character Regognition ) est le procédé qui consiste, à partir de l'image d'une information écrite sur un support analogique ( livre, journal,...), d'en extraire le texte et de le transformer en un fichier directement lisible par un système informatique.
C'est un domaine de recherche très actif afin de trouver les algorithmes les plus fiables qui permettent de faire sans erreur cette conversion, et de gros progrès ont été faits ces dernières années.
Nous verrons dans ce projet une méthode très simple de reconnaissance de caractères dans des images; ce n'est pas cette méthode qui est utilisée en réalité, mais elle est simple et donne des résultats satisfaisants; de plus, cela va vous permettre de rafraîchir vos connaissances en programmation Python, sur les variables, les boucles, les tableaux, etc...!
Dans tout ce projet, les mots en gras désignent des notions ou des concepts importants à bien connaître et maîtriser.
Bien lire les en-têtes de fonction et notamment les annotations de type pour identifier le type des paramètres et celui des valeurs renvoyées.
Ce travail s'inspire d'un tutoriel du site Zeste de Savoir.
L'idée est de "comparer" bit à bit l'image d'un caractère manuscrit à identifier avec des images "témoins" de caractères connus, en réalisant des opérations booléennes ( = logiques ) entre les bits de chacune de ces images.
Plus précisément, on réalise deux opérations successives :
A l'issue de ce traitement, plus l'image du chiffre témoin a été "effacée" ( donc plus elle est noire ), et plus le caractère témoin est proche du caractère à identifier, ce qui permet alors de déterminer ce dernier.
Si tout cela n'est pas très clair, c'est peut-être l'occasion de revenir sur la représentation des nombres en mémoire d'un ordinateur.
010
, 25510
et 21910
.100011102
.A | B | A ET B | A OU EXCLUSIF B |
---|---|---|---|
0 | 0 | 0 | 0 |
1 | 0 | 0 | 1 |
0 | 1 | 0 | 1 |
1 | 1 | 1 | 0 |
Donnez en base 10 le résultat des opérations booléennes : 21910 XOR 14210
et 21910 AND 14210
Dans cette archive, vous trouverez les ressources suivantes :
A.png
à Z.png
) correspondant aux caractères témoins de l'alphabet ( en majuscules )1.png
à 7.png
) correspondant aux caractères manuscrits à identifierimages.py
qui vous permettra :
Ce module devra être préalablement importé au début de votre script; pour son utilisation, à vous de lire les docstring des fonctions incluses dans ce module...
Pour commencer, quelques manipulations de fichiers que l'on pourra avec profit réaliser en ligne de commande :
Projet OCR
a été créé.ocr.py
Pour prendre ensuite en main les ressources :
images.py
Une phase de "pré-traitement" des caractères à identifier est à réaliser : ceux-ci sont en effet codés en niveaux de gris, alors que les caractères témoins sont en deux couleurs ( noir et blanc ).
Vous allez donc écrire un code qui permette de transformer une images en niveaux de gris en une image en noir et blanc.
Le principe est assez simple :
Tout le problème est de déterminer la valeur du seuil à utiliser; en pratique, on utilisera la moyenne des valeurs de tous les pixels de l'image.
La détermination de cette moyenne fera l'objet de l'écriture de votre première fonction.
Toutes les fonctions que vous allez écrire devront bien sûr être complétées avec leur docstring.
Écrire une fonction seuil()
qui :
def seuil(data: list)->int:
.....
.....
Un petit rappel pour le parcours des éléments d'un tableau de tableaux : on utilisera ici un parcours par indice afin de pouvoir modifier la valeur de chaque élément; le parcours se fera à l'aide de deux boucles imbriquées, l'une pour les "lignes", l'autre pour les "colonnes" de chaque ligne :
pour i variant de 0 à l'indice de la dernière ligne:
pour j variant de 0 à l'indice de la dernière colonne :
.....
.....
Faire, bien entendu, les tests unitaires nécessaires à la vérification du bon fonctionnement de la fonction, éventuellement en utilisant le module doctest
; par exemple,
la fonction doit renvoyer 179 pour l'image 1.png
, et 195 pour l'image 5.png
.
Écrire une fonction noir_blanc()
qui :
def noir_blanc(data: list)->list:
.....
.....
Cette fonction fera appel à la fonction seuil()
pour la détermination de la valeur du seuil à utiliser.
Vous écrirez les conditions à évaluer pour transformer un pixel donné en un pixel soit noir ( 0 ) soit blanc ( 255 ).
Pour tester cette fonction, vous pouvez afficher le résultat qu'elle renvoie, et vérifier que le tableau de tableaux ne contient plus que des 0 et des 255.
Maintenant que toutes les images sont au même format, vous allez pouvoir vous intéresser au traitement d'un caractère en vue de son identification.
Et voila le cœur du problème...l'idée est de parcourir en même temps les pixels des deux images, ceux du caractère à identifier et ceux d'un caractère témoin, et d'effectuer les opérations booléennes entre les valeurs de ces pixels comme décrit ci-dessus.
Dans un premier temps, vous allez écrire une fonction traitement()
qui :
def traitement(data1: list, data2: list)->list:
.....
.....
Les opérations logiques se feront directement sur les octets des tableaux de tableaux. On pourra enchaîner directement les deux opérations booléennes XOR et AND
Du fait que les tableaux sont des objets mutables, il sera impératif de travailler sur une copie faite au préalable des tableaux afin de ne pas modifier ces derniers.
En Python, l'opérateur booléen AND correspond au symbole &
; l'opérateur XOR au symbole ^
.
Pour tester la fonction, vous pouvez enregistrer l'image obtenue à partir du résultat renvoyée par la fonction, et vérifier qu'elle a bien le même aspect que celui décrit au premier paragraphe.
Et pour terminer, la fonction qui va permettre de déterminer à quel caractère un fichier image donné correspond.
Écrire une fonction ocr()
qui :
def ocr(nom: str)->str:
.....
.....
Il y a beaucoup à faire dans cette fonction...en voila un algorithme succinct pour vous guider un peu :
A.png
à Z.png
:
Si on appelle b1 le nombre de pixels blancs dans une image témoin d'origine, et b2 le nombre de pixels blancs restant à l'issue du traitement de cette image, alors le pourcentage de
pixels blancs ayant disparu est donné par la relation : p = 100*(1 - b2/b1)
Voila une ébauche de fonction qui pourra vous guider dans ce travail :
def ocr(lettre: list)->str:
'''Reconnaissance d'une lettre manuscrite par comparaison avec les lettres témoins de l'alphabet.
ENTREEE :
lettre : tableau des pixels de la lettre manuscrite
SORTIE :
lettre_identifiee : le caractère ( str ) identifié.
'''
dico = {} # dictionnaire des scores de chaque comparaison
lettre = noir_blanc(lettre) # pré-traitement du caractère à identifier
# traitement
alphabet = [chr(i) for i in range(65,91)] # création par compréhension d'un tableau contenant les lettres de l'alphabet
for temoin in alphabet:
lettre_temoin = get_data_from_image(temoin + '.png') # récupération du tableau des pixels du caractère témoin correspondant à 'temoin'
resultat = traitement(lettre, lettre_temoin) # opérations logiques entre les pixels des deux caractères
# comptage des pixels blancs dans l'image témoin, puis dans le résultat du traitement
pass
# calcul du pourcentage de pixels blancs ayant disparu après traitement
pass
# placement de ce pourcentage dans le dictionnaire comme valeur associée au caractère témoin courant
pass
# recherche du maximum dans les valeurs du dictionnaire
pass
# renvoi du caractère identifié
return lettre_identifiee
Bon, il ne reste pas grand-chose à faire; il serait bien d'optimiser la reconnaissance des caractères dans les 7 images; de façon à ce que le script nous donne directement la chaîne de caractères constituée de ces caractères
Dans le programme principal de votre script, écrire les instructions qui permettront de traiter successivement et automatiquement les 7 images, et de construire une chaîne de caractères qui contiendra les caractères identifiés dans ces images.
L'exécution de votre script affichera donc le mot VAUGELAS
😉.
La méthode présentée ici est loin d'être optimale, et nécessite que les caractères à identifier soient déjà bien formés pour être correctement reconnus.
Globalement, deux grand principes sont utilisés dans le cadre de la reconnaissance optique de caractères :