Correction : projet OCR

Principe

    • bit ( binary digit = chiffre binaire ) = plus petit élément d'information manipulable par un ordinateur; ne peut prendre que deux valeurs, 0 ou 1.
    • octet = ensemble de 8 bits
  1. Un pixel ( picture element ) est le "point' de base constitutif des images numériques.
    • avec n bits on peut écrire 2n valeurs différentes, donc avec 8 bits : 28 = 256 valeurs différentes ( NPCF le nombre de valeurs possibles = 2n, et la valeur la plus grande possible = 2n - 1).
    • l'image contient 100 x 100 = 10 000 pixels, le fichier fait donc 10 000 octets à raison d'un seul octet par pixel.
    • si il faut 3 octets pour chaque pixel, la taille du fichier est donc de 3 x 10 000 = 30 000 octets.
    • chaque couleur primaire pouvant prendre 256 valeurs différentes, le nombre de couleurs total est donc de 256 x 256 x 256 = 16,7 millions.
    • 010 = 000000002
    • 25510 = 111111112
    • 21910 = 128 + 64 + 16 + 8 + 2 + 1 = 110110112
  2. 100011102 = 14210
  3. 
    		11011011 ( 219 )					11011011 ( 219 )
    	XOR	10001110 ( 142 )				AND	10001110 ( 142 )
     	 =	01010101 ( 85 )			 		 =	10001010 ( 138 )						
    					

Ressources

  1. Un impératif pour importer un module externe depuis un script : soit que le module fasse directement partie de l'installation Python, soit qu'il soit ( comme ici ) dans un fichier situé dans le même répertoire que le script.

    Plusieurs façons d'importer le module ( instruction à toujours placer au début du script ) :

    
    from images import *				
    					

    → importe toutes les fonctions du module ( l'astérisque * signifie "toutes les fonctions" )

    
    from images import get_data_from_image				
    					

    → n'importe que la/les fonction(s) dont on aura effectivement besoin ( permet d'économiser de la mémoire )

    
    import images			
    					

    → importe toutes les fonctions du module, mais il sera alors nécessaire de préfixer leur nom avec celui du module lors de l'appel de ces fonctions ( voir ci-dessous ).

    Ceci est nécessaire quand on utilise plusieurs modules qui contiennent des fonctions ayant le même nom; ce préfixe permet de lever l'ambiguïté sur le module à utiliser.

  2. Pour lire la docstring des fonctions d'un module, il faut importer ce dernier, puis utiliser la fonction help() dans la console :
    
    help('images')	
    				
    La docstring de la fonction get_data_from_image() attend un nom de fichier ( sous forme de chaîne de caractères ) comme paramètre; la fonction renvoie un tableau de tableaux contenant les valeurs des pixels ( sous forme d'octets ) :
    
    data = get_data_from_image() # ou : data = images.get_data_from_image()			
    					
  3. 
    print(data[0])  # la première ligne est le premier élément du tableau de tableaux, soit celui d'indice 0
    print(data[5])  # 6ème ligne = indice 5	
    print(data[7][2])  # le premier indice est celui de la ligne, le deuxième celui de la colonne
    print(len(data))  # nombre de lignes = nombre d'éléments dans le tableau de tableaux 
    print(len(data[0]))  # nombre de colonnes = nombre d'éléments dans un des éléments du tableau de tableaux, par exemple le premier
    					

Pré-traitement

Détermination du seuil


def seuil(data: list)->int:
	'''
		Fonction de calcul de la moyenne des valeurs dans un tableau de tableaux
		ENTREE : data = le tableau de tableaux
		SORTIE : la moyenne entière
	'''
	
    m = 0 # contiendra la somme des valeurs de tous les pixels
    h = len(data) # nombre de lignes du tableau de tableaux
    l = len(data[0]) # nombre de colonnes
    for i in range(h): # pour chaque ligne du tableau
        for j in range(l): # pour chaque colonne de la ligne
            m += data[i][j] # on ajoute à la somme la valeur du pixel à la ligne i/colonne j

    return m//(h*l) # renvoi de la moyenne = somme des valeurs // nombre total de pixels
			

Passage au noir et blanc


def noir_blanc(data: list)->list:
	'''
		Fonction de passage des éléménts d'un tableau de tableaux en 2 valeurs  : 0 ou 255
		Utilise la fonction seuil() pour déterminer le seuil par rapport auquel on donnera à l'élément l'une ou l'autre de ces deux valeurs
		ENTREE : data = le tableau de tableaux
		SORTIE :  le tableau de tableaux modifié
	'''
    m = seuil(data) # appel de la fonction seuil() pour connaître la valeur moyenne, et renvoi dans la variable 'm'
    h = len(data)
    l = len(data[0])
    for i in range(h):
        for j in range(l):
            if data[i][j] >= m: # si la valeur du pixel est supérieure ( ou égale ) au seuil
               data[i][j] = 255 # alors le pixel devient blanc ( 255 )
            else:
                data[i][j] = 0 # sinon le pixel devient noir ( 0 )
    return data # renvoi du tableau des pixels modifiés
			

Analyse

Une fonction de traitement


def traitement(l1: list, l2: list)->list:
	'''
		Fonction de traitement par opérations logiques entre deux tableaux de tableaux de même dimension.
		Deux opérations logiques, un XOR et un AND sont effectuées successivement entre les valeurs de chacun des éléments des deux tableaux situés à la même position ( même ligne, même colonne )
		
		ENTREE : l1 = le tableau du caractère à identifier, l2 = le tableau d'un caractère témoin
		SORTIE :  le tableau contenant le résultat des opérations logiques
	'''
	
    lettre_script = [row[:] for row in l1] # copie du tableau des pixels du caractère à identifier ( l1 ) : on modifie ensuite cette copie, pas le caractère lui-même
    # le caractère témoin ( l2 ) ne sera pas modifié, inutile d'en faire une copie...
    for i in range(len(l1)):
        for j in range(len(l1[0])):
             lettre_script[i][j] = lettre_script[i][j] ^ l2[i][j] # XOR entre caractère à identifier et caractère témoin
             lettre_script[i][j] = lettre_script[i][j] & l2[i][j] # AND entre résultat précédent et caractère témoin
    return lettre_script # renvoi du tableau résultant des opérations logiques
			

Une fonction d'identification


def ocr(lettre_script: list)->str:

    dico = {}  # dictionnaire des scores de chaque comparaison
    lettre_script = noir_blanc(lettre_script) # pré-traitement du caractère à identifier

    # traitement
    alphabet = [chr(i) for i in range(65,91)] # création par compréhension du tableau des lettres de l'alphabet
    for lettre in alphabet:

        lettre_temoin = get_data_from_image(lettre + '.png') # pixels du caractère témoin correspondant à 'lettre2'

        resultat = traitement(lettre_script, lettre_temoin) # opérations logiques entre les pixels des deux caractères

        # comptage des pixels blancs du témoin avant/après traitement
        b1 = 0 # nombre de pixels blancs avant traitement
        b2 = 0 # nombre de pixels blancs après

        for i in range(len(resultat)):
            for j in range(len(resultat[0])):
                if lettre_temoin[i][j] == 255:  # dans le témoin
                    b1 += 1
                if resultat[i][j] == 255: # dans le résultat du traitement
                    b2 += 1
        score = 100*(1-b2/b1) # calcul du score : pourcentage de pixels blancs ayant disparu
        dico[lettre] = score # clé = la lettre / valeur = son score

    # recherche du maximum dans les valeurs du dictionnaire
    maxi = 0 # valeur du maximum
    lettre_identifiee = '' # lettre identifiée
    for cle in dico: # ou : for cle in dico.keys()
        if dico[cle] > maxi: # si la valeur est supérieur au maximum déjà trouvé
            maxi = dico[cle] # nouveau maximum = la valeur courante
            lettre_identifiee = cle # lettre identifiée = la clé courante

    return lettre_identifiee