Logiciel de reconnaissance de caractère dédié à la lecture de

Transcription

Logiciel de reconnaissance de caractère dédié à la lecture de
Logiciel de reconnaissance de caractère dédié à la lecture de
tickets de caisses
Aubin Christophe, Desmoulin Cédric, Dubois Jonathan, Roigt Julian
Client : Y. Le Borgne
Chargé de TD : E. Fleury
06 avril 2007
Résumé
Selon certaines sources, il est difficile de faire des relevés de prix dans les grandes surfaces. On
souhaite pouvoir faciliter ces relevés à l’aide d’Internet : le consommateur numérise son ticket puis
l’envoie à un site web qui se charge d’analyser les tickets et de stocker les informations récoltées.
Ce projet se concentrera essentiellement sur la création d’un logiciel de reconnaissance de caractère
pour faciliter cette tâche. Il devra, à partir d’une image scannée par un consommateur, pouvoir extraire
les données intéressantes comme le nom du magasin, la date, la liste des articles ainsi que leurs prix.
Ces informations devront pouvoir être par la suite, stockées dans une base de données pour pouvoir
être comparées. La création d’un site web permettant de regrouper les tickets et d’utiliser l’OCR créé
pourra être envisagée.
Table des matières
I
Analyse
5
1 Description du domaine d’application
1.1 Présentation générale . . . . . . . . . . . .
1.1.1 Un bref historique . . . . . . . . .
1.1.2 Les moyens d’acquisitions . . . . .
1.1.3 Les applications . . . . . . . . . .
1.2 Description d’un OCR . . . . . . . . . . .
1.2.1 Les différentes approches . . . . .
1.2.2 Fonctionnement général . . . . . .
1.2.3 Décomposition d’un OCR classique
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
6
. 6
. 6
. 7
. 7
. 7
. 8
. 9
. 10
2 Analyse de l’existant
2.1 Quelques logiciels de reconnaissance optique
2.1.1 Solutions commerciales . . . . . . .
2.1.2 Solutions à sources ouvertes . . . . .
2.1.3 Solutions gratuiciel . . . . . . . . . .
2.2 Des solutions modifiables : les frameworks .
2.2.1 Comparaison des frameworks . . . .
2.3 Des sources extérieures utiles . . . . . . . .
de caractères
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
12
12
12
12
12
12
13
15
3 Introduction au projet
3.1 But du Projet . . . . . . . . . . . . . .
3.2 Les différentes approches envisageables
3.2.1 Prétraitement . . . . . . . . . .
3.2.2 L’utilisation de Unpaper . . . .
3.2.3 Analyse, La segmentation . . .
3.2.4 Reconnaissance . . . . . . . . .
3.2.5 Post-traitement . . . . . . . . .
3.3 Choix . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
16
16
16
16
19
19
20
24
24
II
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Cahier des charges
26
4 Besoins fonctionnels
4.1 Traitement de l’image . . . . . . . . . . . . . . . . .
4.2 Numérisation . . . . . . . . . . . . . . . . . . . . . .
4.3 Elimination du bruit . . . . . . . . . . . . . . . . . .
4.4 Détection de l’angle d’inclinaison et Redressement de
4.5 Segmentation du ticket . . . . . . . . . . . . . . . . .
4.6 Création d’une bibliothèque de caractères . . . . . .
1
. . . . .
. . . . .
. . . . .
l’image
. . . . .
. . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
27
27
27
28
28
28
28
4.7
Apprentissage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
5 Besoins non fonctionnels
5.1 Le débugage . . . . . . . . . . . . . . . . . . . . . . . .
5.2 Framework / Modularité . . . . . . . . . . . . . . . . .
5.3 Tests de précisions / Comparaison avec d’autres OCR
5.4 Ergonomie . . . . . . . . . . . . . . . . . . . . . . . . .
5.5 Langage . . . . . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
30
30
30
30
31
31
6 Le planning
32
6.1 Les parties indispensables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
6.2 Les parties facultatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
III
Présentation de l’application
34
7 Présentation de l’application
7.1 Les librairies nécessaires . . . . . . . . . . .
7.2 Le Makefile . . . . . . . . . . . . . . . . . .
7.3 Première exécution du logiciel . . . . . . . .
7.4 Gestion du dialogue programme/utilisateur
7.5 Modification des modules . . . . . . . . . .
7.6 Est-il possible d’affiner les réglages ? . . . .
7.7 Exemple d’exécution . . . . . . . . . . . . .
IV
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Architecture
40
8 Organisation séquentielle du traitement
9 Description des modules
9.1 Programme principal . . . . . . . . .
9.2 libGOCR . . . . . . . . . . . . . . .
9.2.1 Attributs . . . . . . . . . . .
9.2.2 Structures . . . . . . . . . . .
9.2.3 Fonctions du fichier d’entête .
9.3 Chargement de l’image . . . . . . . .
9.4 Prétraitement . . . . . . . . . . . . .
9.5 Analyse . . . . . . . . . . . . . . . .
9.6 Reconnaissance . . . . . . . . . . . .
9.7 Post-traitement . . . . . . . . . . . .
V
35
35
35
36
36
36
37
37
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
41
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Présentation technique des algorithmes
10 Chargement et prétraitement de l’image
10.1 Chargement de l’image . . . . . . . . . . .
10.1.1 description . . . . . . . . . . . . .
10.1.2 Complexité . . . . . . . . . . . . .
10.2 Prétraitement de l’image . . . . . . . . . .
10.2.1 description . . . . . . . . . . . . .
10.2.2 Comment adapter Unpaper à notre
10.2.3 Le “wrapper” . . . . . . . . . . . .
2
. . . .
. . . .
. . . .
. . . .
. . . .
projet
. . . .
42
43
43
43
43
44
45
45
46
46
46
47
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
48
48
48
49
49
49
49
49
10.2.4 Les filtres et paramètres utilisés . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
10.2.5 Complexité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
11 Segmentation de l’image
11.1 Choix du type d’algorithme . . . . . . . . .
11.2 Implémentation de la segmentation . . . . .
11.3 Les outils utilisés . . . . . . . . . . . . . . .
11.3.1 Les histogrammes et les intervalles .
11.3.2 Les Blocs . . . . . . . . . . . . . . .
11.4 Résumé du déroulement de la segmentation
11.5 Complexité . . . . . . . . . . . . . . . . . .
11.5.1 Histogramme . . . . . . . . . . . . .
11.5.2 Segmentation . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
52
52
52
53
53
53
54
54
54
54
12 Reconnaissance de caractères
12.1 Chargement de la liste des modèles . . . . . . .
12.1.1 Le représentation physique des modèles
12.1.2 La structure modèle . . . . . . . . . . .
12.1.3 La création de la liste de modèles . . . .
12.2 Calcul de la distance avec chaque caractère . .
12.3 Complexité . . . . . . . . . . . . . . . . . . . .
12.3.1 Création de la liste de modèles . . . . .
12.3.2 Calcul de la distance . . . . . . . . . . .
12.3.3 En conclusion . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
56
56
56
56
57
58
58
58
59
59
VI
.
.
.
.
.
.
.
.
.
Problèmes rencontrés
13 Débugage
13.1 Architectures utilisées . . . . . . .
13.1.1 Il était une fois linux 64 ...
13.1.2 Les conséquences . . . . . .
13.2 Nos bugs logiciels . . . . . . . . . .
13.2.1 Les bugs de Unpaper . . . .
13.2.2 Bugs dûs à la segmentation
13.3 Problèmes restant à corriger . . . .
60
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
61
61
61
61
62
62
62
62
14 Problèmes rencontrés dans les modules
14.1 Chargement des modules . . . . . . . . .
14.2 Chargement de l’image . . . . . . . . . .
14.3 Ecriture de l’image . . . . . . . . . . . .
14.4 Reconnaissance de caractères . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
64
64
64
64
64
VII
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Tests
65
15 Tests de l’application
15.1 Tests unitaires . . . . . . . . . . . . . . . . . . . . . .
15.1.1 Chargement et écriture de l’image . . . . . . .
15.1.2 Les prétraitements . . . . . . . . . . . . . . . .
15.1.3 La segmentation . . . . . . . . . . . . . . . . .
15.1.4 Segmentation du ticket en ensemble de lignes .
15.1.5 Segmentation d’un ensemble de ligne en lignes
3
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
66
66
66
66
69
70
71
15.1.6 Segmentation d’une ligne en mots .
15.1.7 Segmentation des mots en caractères
15.1.8 La reconnaissance de caractère . . .
15.2 Tests d’intégrations . . . . . . . . . . . . . .
15.3 Tests de l’application . . . . . . . . . . . . .
16 Tests comparatifs
16.1 Les challengers . . . . . .
16.2 Les résultats . . . . . . . .
16.2.1 GOCR [2] . . . . .
16.2.2 SimpleOCR [4] . .
16.2.3 FineReader Pro [1]
16.3 Ce que nous retiendrons .
VIII
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
71
72
72
73
74
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
77
77
77
77
78
78
80
Extensions possibles
81
17 Extensions des modules
17.1 Chargement d’image . . . . . . . . . . . . . . . .
17.1.1 Formats d’image . . . . . . . . . . . . . .
17.2 Segmentation . . . . . . . . . . . . . . . . . . . .
17.2.1 Segmentation en mot . . . . . . . . . . . .
17.2.2 Segmentation en caractère . . . . . . . . .
17.3 Reconnaissance de caractères . . . . . . . . . . .
17.3.1 Seuil de reconnaissance . . . . . . . . . .
17.3.2 Apprentissage de nouveaux caractères . .
17.3.3 Nouveaux algorithmes de reconnaissances
17.4 Apprentissage . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
82
82
82
82
82
82
82
82
83
83
83
18 Extensions de l’application
18.1 Optimisation . . . . . . . . . .
18.1.1 Temps . . . . . . . . . .
18.1.2 Mémoire . . . . . . . . .
18.2 La notion de Multiplateformes
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
84
84
84
84
84
IX
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Bilan
85
19 La fin de l’aventure
19.1 Bilan sur les objectifs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
19.2 Gestion du projet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
19.3 De nouveaux outils de développement... . . . . . . . . . . . . . . . . . . . . . . . . . . .
4
86
86
86
86
Première partie
Analyse
5
Chapitre 1
Description du domaine d’application
1.1
Présentation générale
Supposons que vous voulez numériser un livre pour le mettre à disposition sur Internet. Deux
solutions s’offre à vous. La première réécrire tous le livre en le tapant au clavier, une tache plutôt
longue sachant que le document fais plus de 500 pages, la deuxième, utiliser un logiciel reconnaissance
de caractère qui analyse chaque page et transforme les caractères écrits en caractères numériques. Ces
logiciels, plus communément appelé OCR1 , ont donc pour but de transformer un texte écrit en une
représentation interprétable par une machine.
Aujourd’hui avec le développement des nouvelles technologies de l’information on assiste à un
changement radical de support, l’écriture passe d’un support physique à un support numérique. Cette
dématérialisation est en train de changer totalement le mode de fonctionnement des entreprises. Par
exemple, on considère qu’encore 80 % de l’information traitée par les entreprises est sous forme papier,
et on estime le coût de son traitement à 10 % du chiffre d’affaire. De plus la cadence moyenne de frappe
d’un texte au clavier est de l’ordre de cinq caractères par seconde tandis que les systèmes de lecture
automatique travaillent à une vitesse d’une centaine de caractères par seconde avec un faible taux
d’erreur. Et comme cette transition implique la numérisation d’une énorme quantité de documents, on
comprend donc bien vite l’intérêt de traiter de plus en plus vite d’où l’utilisation d’OCR.
1.1.1
Un bref historique
Le concept d’OCR en lui-même est relativement ancien, son origine remonte aux années 1900 au
cours desquelles on inventa le scanner à balayage pour la télévision et les machines à lire. Tuyrin
développa alors la première application d’aide aux handicapés visuels, mais il a fallu encore attendre
1940 pour voir se réaliser la première version informatique de cette application. Depuis, une gamme
importante de logiciels sont apparus avec de très bonnes performances.
Les premières applications étaient essentiellement orientées vers la reconnaissance de chiffres imprimés et de quelques lettres anglaises (latines). Ensuite, les applications ont été étendues progressivement
vers la reconnaissance de caractères isolés, les caractères manuscrits isolés, les textes mono-fontes, les
textes multi-fontes et enfin le manuscrit.
Plus tard, l’introduction des réseaux de neurones permit une nouvelle avancée dans le monde de
la reconnaissance de caractère. Cette nouvelle technologie permit d’améliorer encore les résultats des
OCR.
De nos jours nous disposons de systèmes fiables avec un haut taux de reconnaissance même sur des
textes composites(c’est-à-dire composées de textes et d’images) et il est courant de trouver des systèmes
« intelligents » qui peuvent reconnaître la plupart des polices avec un haut niveau de précision.
1 Ce
terme vient de l’anglais Optical Character Recognition ou reconnaissance optique de caractères en français
6
Pour finir nous pouvons dire à ce jour que la reconnaissance optique de caractère est un enjeu pour
l’industrie et que même avec un haut taux de reconnaissance elle reste un secteur de recherche très
actif surtout dans le domaine de l’analyse de l’écriture manuscrite. [6]
1.1.2
Les moyens d’acquisitions
Le traitement d’un texte ne peut se faire sans une acquisition préalable de l’image du texte. Cette
première étape est très importante car une image de mauvaise qualité risque de créer des erreurs. Le
choix du capteur est donc particulièrement important suivant l’utilisation et le taux d’erreurs désirés.
Il existe un grand nombre de formes différentes de capteur dont voici les plus courants :
– Scanner (plat ou manuel)
– Appareil photo numérique
– Caméra
– Tablette graphique
La résolution
Une image numérique est constituée de pixels, la résolution définit le nombre de pixels par unité
de longueur (centimètre ou pouce). La résolution d’une image numérique s’exprime en PPI (Pixels Per
Inch) ou PPP (Pixels Par Pouce). La résolution d’acquisition d’un capteur optique se détermine en
DPI (Dots Per Inch) ou PPP (Points Par Pouce). Elle est le paramètre principal permettant de définir
la qualité d’un document numérisé. Dans des limites de coût (en espace) raisonnables, il sera toujours
préférable de numériser avec la meilleure qualité possible. Il faut cependant, rappeler que la définition
maximale n’apporte pas forcément cette meilleure qualité, en effet elle fait apparaître des défauts sur
des documents de qualité médiocre. [15]
Des essais récents montrent que la diminution d’une résolution de 300 ppp à 200ppp augmente le
taux d’erreurs de 75% pour un document complexe. Mais par contre une augmentation de 300ppp à
400ppp a un effet négligeable.[8]
Par ailleurs, la définition absolue n’est pas le seul paramètre. La qualité réelle de la numérisation
peut varier fortement pour une même définition annoncée.
Il faut aussi préciser que le principe de résolution d’un capteur ne s’applique que dans le cas de
capteurs optique.
1.1.3
Les applications
Comme on a vu précédemment cette technologie permet des gains de productivités important, elle
fait donc l’objet de gros enjeux dans de nombreux secteurs de l’industrie. Il existe de très nombreuses
applications pour ces logiciels donc nous n’en citerons qu’une petite partie :
– La numérisation de livres permet la sauvegarde de livres anciens ou la diffusion de livre au format
électronique.
– Le tri postal : utilisation d’un OCR pour déterminer l’adresse.
– La reconnaissance de plaque minéralogique lors des contrôles routier.
– L’authentification des chèques de banques : analyse de la signature pour permettre de valider
l’identification du signataire
– La reconnaissance d’écriture pour un PDA : interprétation des informations inscrites à l’aide du
stylet.
1.2
Description d’un OCR
Maintenant que nous avons vu quelles sont les utilisations possibles d’un OCR nous allons nous
intéresser plus précisément à son mode de fonctionnement.
7
1.2.1
Les différentes approches
Cette première partie permet de cibler plus précisément les différentes approches possible pour
un OCR car il est évident que les traitements ne seront pas les mêmes s’il faut traiter une écriture
manuscrite et une écriture imprimée.
Gestion des couleurs de l’image
Par défaut un texte est bicolore, mais dans le cas de documents complexes contenant des images
on peut avoir besoin d’une image en niveaux de gris ou en couleur pour restituer de la façon la plus
fidèle possible le document source.
Les images en noir et blanc sont les plus simple à traiter et les moins coûteuses en espace, c’est
donc la solution la plus généralement utilisé pour numériser du texte.
Les traitements sur une image en couleur ou en niveaux de gris sont plus complexes car ils impliquent
la gestion d’un seuillage pour l’analyse du texte. Le seuillage est une opération qui consiste à considérer
comme noir les pixels dont la valeur est en dessous d’un certain seuil et à considérer comme blanc tous
les autres.
Selon le type de documents à traiter il faudra prendre en compte la gestion de la couleur dans les
algorithmes de traitement et dans ceux de gestion de la mémoire. La gestion de la couleur peut être
effectuée simplement par un seuillage lors du chargement en mémoire de l’image.
Imprimé ou manuscrit ?
L’approche entre l’étude de l’écriture manuscrite et de l’imprimé est très différente. Dans le cas
de l’imprimé, les caractères sont réguliers et correctement alignés alors que dans l’autre cas il faut
pourvoir reconnaître des formes variables en fonction de la personne qui a écrit le caractère. Dans
notre étude et pour ne pas dépasser le cadre du sujet nous nous contenterons de traiter le cas des
caractères imprimés (en effet il est plutôt rare de trouver des tickets de caisses rédigé à la main). [6]
Reconnaissance mono-fonte, multi-fonte ou omnifonte
Tout d’abord, une fonte de caractères, en typographie, est un ensemble de glyphes2 , c’est-à-dire de
représentations visuelles de caractères, d’une même famille, de même style, corps et graisse. Il ne faut
pas confondre la fonte et la police qui regroupe tous les corps et graisses d’une même famille. Un OCR
est dit mono fonte s’il n’est capable que de reconnaître qu’une seule fonte, il est dit multifonte s’il est
capable de reconnaître plusieurs fontes et enfin il est omnifonte s’il peut reconnaître n’importe quel
type de fonte sans apprentissage préalable. [6]
Reconnaissance en-ligne ou hors-ligne
Cette différence possède une importance particulière, il permet de choisir vers quels types de traitement s’orienter. Les traitements utilisés pour l’acquisition en ligne doivent être beaucoup plus rapide
que ceux utilisés dans le hors-ligne car il faut pouvoir comprendre en temps réel les informations transmises par l’utilisateur, ce qui implique des algorithmes moins lourd et donc moins efficaces. A l’inverse
un traitement hors-ligne permet une plus grande liberté quand au temps d’analyse. [6]
L’apprentissage
L’apprentissage est la partie qui permet à l’OCR de retenir des détails qui lui permettront par la
suite d’améliorer la reconnaissance, par exemple un nouveau caractère. Ce module est assez particulier
car, de nos jours, il est courant de voir des OCR se passant de cette partie grâce à l’utilisation de
système intelligent qui peuvent reconnaître la plupart des polices avec un haut niveau de précision. De
2 c’est-à-dire
de représentations visuelles de caractères
8
plus selon le type d’apprentissage choisi il peut ne pas avoir à intervenir dans le traitement, comme
dans le cas d’une base de données de caractère par exemple ou l’apprentissage se fait de manière
statique, ou au contraire interroger l’utilisateur pour combler un manque de données.
1.2.2
Fonctionnement général
L’architecture
On peut assimiler l’architecture générale d’un OCR à un ensemble de modules qui reliés entre eux
à l’aide d’un pipeline 3 , et pouvant être eux-mêmes à nouveau décomposé en sous modules. Une fois
qu’un module a fini son traitement, il transmet le résultat au module suivant au travers du pipeline,
ainsi l’implémentation de chaque module n’a aucun lien avec celle des autres, ils sont donc totalement
indépendants.
On peut distinguer deux grandes classes pour les modules :
1. Les modules de reconnaissance du document
2. Les modules de reconnaissance de caractères
Les modules de reconnaissance du document
Dans n’importe quel document les caractères ne sont pas agencés au hasard, leurs positions correspondent à un ordre précis pour former un ensemble structuré en ligne, en paragraphe, en colonne etc.,
que nous pouvons exploiter dans l’OCR. La connaissance de la structure du document peut largement
contribuer à son analyse. On distingue deux types de structures :
– La structure physique. Elle correspond au travail du typographe, c’est-à-dire l’organisation des
caractères, des lignes, des colonnes etc.
– La structure logique. Elle correspond au travail de l’éditeur, c’est la manière de structurer le
texte pour le rendre plus lisible. Par exemple un roman s’organise en chapitre, section, titre,
alinéa ect.
Chaque partie du document peut contenir des informations spécifiques précieuses ou au contraire
inutile. Dans un premier temps ces modules doivent reconnaître et isoler ces informations, par exemple
déterminer les zones contenant des images et indiquer qu’on ne doit pas les analyser. L’analyse de la
mise en page permet aussi d’atteindre plus rapidement des informations précises comme par exemple
dans un même livre on sait approximativement où on pourra trouver le numéro de page il suffit de
détecter le mot le plus bas de la page pour savoir qu’il s’agit du numéro de page.
La seconde étape, appelée segmentation, est la plus importante car, c’est elle qui va permettre
de diviser l’image en caractère. En analysant chaque ligne de texte on en extrait l’image de chaque
caractère. Toutes ces images sont d’abord stockées puis transmises, avec leurs contextes, aux modules
de reconnaissance.
Si la première partie peut être optionnelle la segmentation est indispensable car sans elle il n’y a
pas de reconnaissance de caractère. Pour pouvoir analyser le document il faut donc connaître au moins
sa structure physique.
Les modules de reconnaissance des caractères
Tous les documents peuvent être décomposés en entités plus petites comme des paragraphes ou des
lignes, mais ici on ne s’intéresse qu’à la plus petite d’entre elles : le caractère. Ces modules ont pour
objectif le traitement de chaque image de caractère en vu de son identification.
Trois techniques sont utilisées :
– La comparaison par modèles : c’est une technique qui consiste à comparer l’image du caractère
avec des modèles pré-enregistrés.
3 Un
pipeline prend ici le sens de tube ou de tunnel permettant d’acheminer les informations d’un point à un autre.
9
– Extraction de propriétés : c’est un ensemble de techniques qui consistent à déterminer les propriétés géométriques des caractères à reconnaître, telles que les fins de segments, les angles prononcés,
les croisements, etc.
– Méthodes structurelles : c’est un ensemble de méthodes dont le principe consiste à exprimer une
lettre par une combinaison d’attributs généraux, comme par exemple, le rapport entre la hauteur
et la largeur du caractère, etc.
Ces modules peuvent aussi s’appuyer sur les informations obtenues à partir du document pour
améliorer leurs capacités de reconnaissance. En effet, un caractère n’est jamais seul il est toujours
inclut dans un contexte. Une zone de texte pourra contenir tous les caractères existant alors qu’une
zone de chiffre ne contiendra que les alpha-numérique. Cette action diminue le nombre de comparaison
à effectuer4 .
Ils peuvent inclure des phases d’apprentissage si nécessaire.
1.2.3
Décomposition d’un OCR classique
Classiquement on pourrait décomposer un OCR en 5 modules :
1. la numérisation
2. le pré-traitement de l’image
3. l’analyse
4. la reconnaissance
5. le post-traitement
Numérisation
Ce module est la toute première étape qui consiste à récupérer une image. On peut aussi bien
utiliser un capteur optique que charger un fichier préalablement numérisé. A sa sortie on dispose d’une
image en mémoire.
Pré-traitement de l’image
Ce module n’est pas obligatoire mais il est très utile car, il contient tous les pré-traitements effectués
sur l’image pour la préparer aux autres modules. Il permet de diminuer les erreurs de reconnaissances
en améliorant la qualité de l’image, par exemple en réduisant le bruit ou en corrigeant son inclinaison.
A sa sortie on dispose d’une image claire et suffisamment propre pour être correctement analysée.
Analyse
L’analyse fait partie de la reconnaissance de document.
C’est l’étape qui permet reconnaître les différentes parties d’une image. Elle détecte les parties
importantes de l’images comme les différentes zones de textes, les images et la mise en page. Elle
s’occupe ensuite de segmenter les zones de textes en plus petites parties pour finalement isoler chaque
caractère. Ce module peut également contenir un sous module permettant d’analyser les spécificités
du texte comme la police ou la taille. Il fournit en sortie une liste de caractères à analyser et peut aussi
fournir la structure du document.
Reconnaissance
La reconnaissance fait partie de la reconnaissance de caractère.
C’est la partie qui va analyser l’image d’un caractère pour pouvoir le reconnaître, il est le premier
module à traiter les caractères.
4 Cette
option est surtout utile dans le cas de la première technique.
10
Ce module peut inclure une phase d’apprentissage si l’algorithme utilisé nécessite une base de
données de caractères pour la reconnaissance. Cependant, il existe aujourd’hui certains algorithmes se
passant totalement d’apprentissage. A la fin du traitement tous les caractères ont étés analysés et ceux
ayant étés reconnus disposent d’une étiquette signalant la valeur reconnue.
Post-traitement
Le post-traitement gère la mise en formes des caractères reconnus précédemment, il a pour tâche de
créer le document de sortie. Il doit convertir les données récupérées au format demandé et reproduire la
mise en page si nécessaire. Il peut inclure une phase de vérification des mots à l’aide d’un dictionnaire
qui permet de corriger les erreurs commises lors de l’identification d’un caractère ou de compléter les
mots incomplets. C’est la dernière étape du traitement global et en sortie on dispose du texte reconnu
sous le format désiré.
11
Chapitre 2
Analyse de l’existant
2.1
Quelques logiciels de reconnaissance optique de caractères
Voici une liste (non exhaustive) des principaux OCR sur le march.
2.1.1
–
–
–
–
–
–
–
Solutions commerciales
FineReader de Abbyy, leader mondial des logiciels d’OCR.
Readiris de I.R.I.S., logiciel précis et rapide, OCR en Arabe, Persan, Hébreu et langues asiatiques !
OmniPage de Nuance (ex Scansoft).
PDFCompressor de CVISION, très précis.
Intelliant OCR d’Intelliant, basé sur Tiger OCR.
BIT-Alpha de Bureau Ingénieur Tomasi.
FormScan d’Itesoft, s’adresse aux entreprises qui traitent un volume important de document, il
bénéficie d’un paramétrage spécifique aux documents qu’il doit traiter.
2.1.2
Solutions à sources ouvertes
–
–
–
–
–
–
Clara OCR.
FreePress (Windows).
GOCR (Unix,Windows), un framework .
Conjecture (Unix), un autre framework se basant sur GOCR.
Ocre (Unix)
Tesseract (Unix) est un ancien OCR fabriqué par Hewlett Packard puis abandonné. Il est entré
en 2005 dans le domaine public.
– Ocrad ().
2.1.3
Solutions gratuiciel
– SimpleOCR (Windows)
2.2
Des solutions modifiables : les frameworks
Un framework est un ensemble de bibliothèques permettant le développement rapide d’applications. Il fournit suffisamment de briques logicielles pour pouvoir produire une application aboutie. Ces
composants sont organisés pour être utilisé en interaction les uns avec les autres.
12
L’intérêt de ce type de logiciel est qu’ils fournissent une base de programmation aisément modifiable. Ils permettent de passer directement au codage des algorithmes grâce aux fonctionnalités qu’ils
fournissent.
Dans le cadre de notre projet nous allons nous concentrer sur ce type de logiciels car, nous n’auront
pas forcement le temps d’implémenter notre propre architecture.
Nous avons trouvé trois framework pouvant être réutilisé :
– GOCR
– Conjecture
– GStreamer : c’est un framework multimédia, mais il peut être réutilisé dans le cadre de la reconnaissance de caractère.
GOCR
Comme on l’a vu précédemment, GOCR est un logiciel de reconnaissance de caractère open source,
mais il fournit aussi une bibliothèque permettant le développement de son propre OCR. Cette librairie
a pour nom libgocr et est codé entièrement en C. Il a été développé en tirant profit de l’expérience
acquise lors de la création de GOCR.
Il faut aussi noter que le développement de libgocr est arrêté depuis le 24 décembre 2006 il est
précisé sur le site que ce projet a été remplacé par Conjecture.
Conjecture
Contrairement à GOCR, Conjecture est seulement un framework qui n’implémente aucun algorithme, mais par défaut, il contient le code de GOCR et donc il permet de disposer des mêmes traitements que celui-ci. Cette librairie est entièrement codé en C++ et a été conçue dans le but d’être
le plus modulaire possible. Elle peut aussi faire appel à n’importe quel OCR tant que celui-ci peut se
lancer en ligne de commande.
GStreamer
GStreamer n’a, à la base, rien à voir avec le monde des OCR, il a été développé pour le multimédia.
Pourtant, il pourrait être adapté pour la reconnaissance de caractère.
Le principe de ce framework est simple : il suffit de créer une chaîne d’éléments reliés par des
pipelines. Ce type de fonctionnement se rapproche normalement du fonctionnement d’un OCR d’où
une facilité d’adaptation en y implémentant les modules d’un OCR.
2.2.1
Comparaison des frameworks
Nous allons maintenant comparer ces librairies ensemble, pour voir laquelle serait la mieux adaptée
pour notre projet. Les critères de comparaisons seront les suivants :
– Les formats d’images supports : quels sont les formats d’image que le framework peut charger.
– La gestion de l’image : comment est l’image est stocké et découpe.
– La facilité d’implémentation et d’intégration des nouveaux modules : est ce que l’implémentation
de nouveau module est simple et est ce qu’ils sont facilement interchangeables ?
– Les algorithmes d’analyse déjà présents : est ce que le framework fournit déjà des algorithmes ?
– La facilité de réalisation des comparaisons : est que les comparaisons sont possibles facilement ?
– La présence d’un module de communication et/ou d’affichage : est ce qu’il existe déjà une option
permettant l’affichage des données ?
– Observations : tout ce que nous pensons pertinent.
13
Librairie
Formats supports
Gestion de l’image
Facilité d’implémentation et
d’intégration
des
nouveaux
modules
Chargement dynamique des modules à partir de son nom de fichier. LibGOCR permet de définir sept types de modules différents.
– imageLoader
– imageFilter
– blockFinder
– charFinder
– charRecognizer
– contextCorrection
– outputFormatter
Algorithmes
déjà présents
d’analyse
Facilité de réalisation
des comparaisons
LibGOCR
implémente
déjà tous les traitements
d’un OCR de base
Il ne dispose pas de
modules
spécifiques
pour la comparaison
de performances.
Présence d’un module
de communication et/ou
d’affichage
Il dispose d’un système de
dialogue entre l’utilisateur
et les modules avec affichage graphique.
LibGOCR Lis les formats
de
fichiers
les
plus
courants
Il permet de découper l’image en blocs
puis en caractères.
LibGOCR
permet
de convertir l’image
chargée dans les types
suivants :
– noir et blanc
– niveau de gris
– couleur 24 bits
Conjecture Charge les
formats les
plus
courants
qui
seront par la
suite convertir au format
pnm
La représentation interne de l’image est basée sur le format pnm.
La structure de l’image
peut être découpée en :
– page
– région
– ligne
– mot
– glyphe
De plus conjecture gère
les couleurs suivantes :
– noir et blanc
– niveau de gris
– couleur 24 bits
Le chargement des modules se
fait au démarrage l’aide d’un fichier de configuration qui permet de définir la configuration
des modules. Conjecture ne dispose que de trois types de modules différents :
– SegmentComponent
– IdentifyComponent
– FormatComponent
Pour pouvoir en implémenter de
nouveaux, il faut les décomposer
en sous modules.
Conjecture dispose des
modules de libGOCR il
possède donc les mêmes
algorithmes de traitement.
Il dispose de possibilités de comparaisons
entre différentes implémentations. Il peut gérer une base de données d’images d’entrées
et du texte qui a
été produit par un
OCR donné. Il permet
d’exécuter un ou plusieurs OCR sur une ou
plusieurs images pour
comparer le résultat.
Il ne dispose pas d’affichage intégré.
GStreamer Il ne gère que
les images au
format jpeg.
Il gère les données sous
forme de flux car il est
prévu pour travailler
avec de l’audio ou de la
vidéo.
Les modules sont appelés éléments et sont liés à l’aide d’un
pipeline. Pour créer un élément
dynamiquement on passe par une
fabrique (factory). Pour lier les
éléments on créer un pipeline auquel on ajoute les éléments. Le
nombre de module n’est pas limité.
GStreamer ne dispose
d’aucun algorithme de
reconnaissance de caractères. Il dispose au
maximum de fonctions de
traitements de l’image.
Il ne dispose d’aucune
fonction de comparaison et de précision.
GStreamer dispose d’une
méthode de communication qui passe par le pipeline. Chaque pipeline dispose d’un objet nommé
bus qui permet de transmettre un message d’un
module. On peut y faire
passer des messages personnalisés, mais ce système ne permet pas l’affichage
Observations
Son développement a été
abandonné
au profit de
Conjecture.
Très orienté
sur les traitements vidéos
et audios on
dispose
de
peu de bases
pour
les
traitements
spécifiques
aux OCR.
2.3
Des sources extérieures utiles
Unpaper [7]
Le logiciel Unpaper, sous licence GPL, offre des services intéressants. C’est un outil de posttraitement de pages numérisées. Il permet par exemple le redressement de texte dans une image,
mais également l’application de divers filtres (élimination de bruit, du flou, etc...). Ce logiciel présente
tout de même quelques inconvénients :
– Les paramètres appliqués sont nombreux pour une bonne utilisation du logiciel ;
– Pour obtenir un bon résultat après le traitement de l’image, il faudrait adapter chaque paramètre
à chaque utilisation. Le lancement avec une ligne de commande unique pour différentes photos
est donc trop générique. La qualité de l’image de sortie s’en fera ressentir.
– La compréhension du code et des différents algorithmes présent reste difficilement abordable. Il
n’y a qu’un seul fichier en langage C comprenant plus de 5000 lignes de codes !
Libnetpbm [3]
Libnetpbm est une librairie programmée en C et conçue pour la lecture, l’écriture et la manipulation
d’images Netpbm. Elle n’est absolument pas destinée à être une bibliothèque d’outils graphiques. Les
fonctions de libnetpbm sont divisées en plusieurs catégories :
– Les fonctions PBM qui ne travaillent que sur les images PBM.
– Les fonctions PGM qui ne travaillent que sur les images PGM.
– Les fonctions PPM qui ne travaillent que sur les images PPM.
– Les fonctions PNM qui travaillent sur les images PBM, PGM, PPM.
– Les fonctions PAM qui travaillent sur les images de Netpbm.
– Les fonctions PM qui sont des utilitaires et qui n’ont pas de format précis.
Libnetpbm peut donc nous être très utile pour changer de formats entre l’image de départ à traiter et
l’image dont nous allons nous servir lors de l’exécution.
15
Chapitre 3
Introduction au projet
3.1
But du Projet
Notre but dans ce projet est d’implémenter un logiciel de reconnaissance de caractère qui aurait
pour principale fonction de récupérer certaines informations sur un ticket de caisse. Mais avant cela
il nous est nécessaire de réfléchir à une bonne architecture pour notre logiciel. En effet notre client
désirerait avoir une version personnelle de son OCR, une version avec ses propres approches, mais on
pourra modifier les s’il s’avère qu’il en existe de plus efficaces. Le client désire en priorité sa version
personnelle et, si possible, notre propre approche et donc une architecture modulaire serait la plus
approprié. Les approches que désire notre client sont :
– Localisation des rectangles englobant les caractères. (La segmentation dans l’étape d’analyse)
– Reconnaissance de caractère. (Étape de reconnaissance)
– Lecture du ticket de caisse. (Étape de post-traitement)
Nous verrons par la suite plus en détails les approches du client. Pour le développement de cet OCR
il est nécessaire de nous pencher sur d’autre étapes comme la binarisation, le nettoyage de l’image,
le redressement de l’image (étape de Prétraitement), ainsi qu’une étape d’apprentissage (étape de
Reconnaissance).
3.2
3.2.1
Les différentes approches envisageables
Prétraitement
Le prétraitement consiste à nettoyer l’image numérisée, qui lors de l’acquisition, peut comporter
des défauts. Ces défauts sont corrigés par des filtres, mais avant cela il faut procéder à la binarisation
de l’image.
Numérisation
La numérisation est une étape importante du prétraitement puisque c’est l’étape qui sélectionne
les données à traiter, elle sélectionne quel pixel de l’image va être gardé. Elle transforme une image en
niveau de gris en une image noire et blanche par une opération de seuillage.
Soit la fonction B() de binarisation, p(x,y) représentant le niveau de gris du pixel de coordonnée
(x,y), 1 le codage d’un pixel noir et 0 celui d’un pixel blanc, on a :
B(p(x,y)) ∈ 0,1
Il existe différents types de seuillage global ou local.
16
Le seuillage global est réalisé de manière uniforme sur la totalité des pixels du document, à partir
de seuils déterminés à l’avance. La difficulté se situe sur le choix des seuils. La méthode classique
est de calculer les seuils à partir de l’histogramme de la fréquence d’apparition des niveaux de gris
dans l’image. Les pics de l’histogramme correspondant aux régions existantes du document quand aux
“vallées” elles correspondent au fond de l’image. Cette méthode donne peu de résultats satisfaisants.
La plupart du temps, on effectue des opérations de traitements d’image avant de déterminer les seuils,
comme des convolutions, des filtres, cela permet de séparer le fond et les objets.
Le seuillage local se base sur la détermination d’un seuil local à un pixel. Un seuil est déterminé
pour chaque pixel par l’étude du niveau de gris du pixel en fonction de ceux des voisins. Le seuillage
d’un point se fait à partir de ces voisins et non à partir de l’ensemble des points du document.
Elimination du bruit
Après plusieurs recherches sur le web, nous avons trouvé plusieurs techniques (filtrage non linéaire)
pour résoudre le problème du bruit. Les principaux filtres sont [11] :
– Un filtre moyenneur de taille 3*3. Dans le cas d’un bruit de faible intensité, la plupart des pics
de bruit sont éliminés. Si l’intensité est augmentée, le résultat est plus mitigé.
– Un filtre moyenneur de taille 7*7. Dans les différents cas d’intensité, le bruit est bien éliminé,
mais les contours et les détails sont très nettement détériorés !
– Un filtre smooth de taille 3*3. Il reste peu performant mais ne détériore pas (ou peu) l’image.
– Un filtre smooth de taille 7*7. Il conserve mieux les détails et contours que le filtre moyenneur
7*7 mais le bruit est moyennement bien traité.
– Un filtre Nagao. Le résultat concernant le bruit est bon. Cependant, il a un gros défaut : il
déforme très sensiblement l’image !
– Un filtre FAS1. Il élimine bien le bruit mais ne conserve pas correctement les contours.
– Un filtre FAS2. Il élimine très bien le bruit... mais également les contours !
– Un filtre FAS3. Idem que le précédant mais encore plus accentué !
– Un filtre médian de taille 3*3. Pour une faible intensité de bruit, une utilisation de ce filtre
semble être la meilleure des possibilités offertes. Le bruit est (quasiment) éliminé et les détails
et contours sont parfaitement conservés. Quand l’intensité augmente, il faut alors une seconde
utilisation de ce filtre pour éliminer le bruit. Ce filtre semble être le meilleur pour éliminer le
bruit qui nous concerne.
– Un filtre médian de taille 7*7. Avec une faible intensité ou une plus forte, le bruit est bien éliminé.
Cependant, l’image peu devenir un peu floue (atténuation des contours).
Le filtre médian semble être approprié pour éliminer le bruit de l’image. En voici brièvement son
fonctionnement.
Le filtre se compose d’une fenêtre de taille 3*3 que l’on va déplacer sur chaque pixel de l’image.
Pour un pixel P de coordonnées (i,j), cette fenêtre ressemble à :
Pi−1,j−1
Pi−1,j
Pi−1,j+1
Pi,j−1
Pi,j
Pi,j+1
Pi+1,j−1
Pi+1,j
Pi+1,j+1
Traitement à faire avec cette fenêtre [12] :
– S’il y a une majorité de pixels noirs dans la fenêtre, le pixel central est alors mis en noir.
– Dans le cas inverse, s’il y a une majorité de pixels blancs, le pixel central est alors mis en blanc.
L’image étant en noir et blanc, l’application du filtre médian est simplifiée. Dans le cas où l’image
serait en niveaux de gris, le filtre calculerait pour chaque pixel la médiane des niveaux de gris des pixels
de sa fenêtre, ce qui donnerait le niveau de gris du pixel dans l’image filtrée. Concernant le codage de
ce filtre, il est relativement simple à mettre en place. Comme nous mettons notre image sous la forme
d’une matrice, il nous suffira de faire une boucle "for" pour parcourir chaque pixel de l’image. Ensuite,
pour chaque pixel, il faut regarder la valeur des pixels voisins, de faire la somme de ceux-ci, et si la
somme dépasse une certaine valeur, on code le pixel en noir.
17
Cependant, tous ces filtres sont dépassés. Certes ils sont faciles à mettre en place, mais les résultats
sont médiocres. Des filtres récents sont bien plus complexes et nous prendraient plus de temps. Or ce
dernier nous faisant défaut, nous ne nous attarderons pas là dessus. Nous n’implémenterons donc pas
ces solutions. L’utilisation de Unpaper va remédier à ces problèmes.
Inclinaison et orientation
Détection de l’inclinaison La méthode de Trincklin [14] utilise la méthode des moindres carrés,
qui consiste à déterminer une droite à partir d’un nuage de point. Trincklin détermine le nuage de
point en calculant la distance gauche du premier pixel de chaque ligne. La méthode des moindres
carrés est utilisée sur l’ensemble de ces distances, cela produit différentes lignes dont leur inclinaison
sont déterminées par leurs coefficients directeurs. L’angle α qui revient le plus souvent correspondra à
l’inclinaison du document. Une rotation d’angle -α est effectuée.
La méthode de Baird [5] se compose en trois étapes :
– Coordonnées des composantes connexes : La méthode repose sur la segmentation du document en
composante connexe, chaque composante connexe est représentée par un rectangle englobant. Un
point se situant au centre de la ligne basse de la composante représentera le rectangle. L’ensemble
des points de chaque composante forme un nuage de points sur lequel nous allons travailler.
– Estimation de l’inclinaison : Chaque point du nuage sont projetés sur une suite de droites de
différentes inclinaisons, chaque droite sont composées d’un nombre de segment de taille fixe. Puis
la quantité suivante est calculée :
Pm
A(α) = i=1 Ni2 (α)
avec Ni (α) le nombre de pixel projeté sur le segment i. On peut représenter le nombre de points
projetés pas un histogramme. La fonction A( α ) possède un maximum, qui est affiné petit à
petit par chaque calcul de la fonction pour différente inclinaison de la droite de projection. Ce
maximum représente l’angle dont lequel est incliné le document. Pour obtenir l’angle il faut
utiliser la méthode des moindres carrés sur le nuage de point de l’histogramme. On obtient ainsi
deux droites dont l’angle qu’elles forment est l’angle d’inclinaison.
– Redressement : Le redressement se fait par translation des rectangles englobant de chaque composante.
L’approche du client correspond à la détection des espaces blancs suivant une inclinaison. Le principe est de dresser l’ensemble des histogrammes du nombre de pixel de chaque ligne du document
suivant une inclinaison, chaque histogramme représentera une inclinaison. Sur chaque histogramme on
détectera le nombre de lignes blanches, et l’histogramme dont le nombre de ligne blanche est maximal
correspondra au meilleur angle d’inclinaison.
Rotation Une rotation classique par une matrice de rotation implique de nombreuses pertes d’informations dues à l’approximation des arrondis (calculs sur les sinus et cosinus). Pour remédier à
ce problème, plusieurs solutions s’offrent à nous. Nous pouvons considérer ces pertes d’informations
comme l’apparition d’un bruit sur l’image et le traiter comme précédemment à l’aide d’un filtre médian.
La rotation simple de centre C(a,b) d’angle α se fait par :
′
x−a
x −a
;
=
M
∗
y−b y′ − b cosα −sinα
avec M =
sinα cosα
Pour obtenir un bon résultat et un meilleur temps de calcul nous allons prendre comme centre de
rotation le point de coordonnée (0,0) à la place du centre de l’image, qui si on le prendrait, entraînerait
des erreurs de précisions dues à l’approximation possible de ce centre dans le cas où les dimensions de
l’image sont de taille paire. La formule de rotation se réduit à :
′ x
x
cosα −sinα
;
∗
=
y
sinα cosα
y′
18
avec (x’,y’) les coordonnées du point après rotation et (x,y) celles du point d’origine.
Il existe des rotations discrètes, après quelques recherches sur le web, ces rotations nous semblent
bien difficiles à mettre en oeuvre. Certes elles peuvent être efficaces, mais la difficulté de ces rotations
nous prendraient plus de temps à les surmonter qu’à mettre en place une rotation simple suivie d’un
filtrage ; et cela pour avoir un résultat semblable !
Ici aussi l’utilisation de Unpaper va résoudre nos problèmes. En effet le logiciel gère plutôt bien la
détection d’inclinaison et la rotation qui s’en suit.
3.2.2
L’utilisation de Unpaper
Pourquoi Unpaper ? Lors des premières recherches, nous nous étions concentrés sur différents
algorithmes possibles pour “nettoyer” une image. Il nous fallait différents algorithmes pour réduire (voir
enlever) le bruit présent sur l’image à traiter, effectuer une binarisation, détecter l’angle d’inclinaison
d’une image, effectuer la rotation adéquate, etc... Tout ceci était réalisable, mais le temps nous est
précieux. La mise en place de tous ces algorithmes nous aurait pris beaucoup de temps, mais surtout,
le résultat n’aurait pas été excellent. Les algorithmes décrits précédemment étaient relativement vieux
et « obsolètes ». Si l’on voulait obtenir une image correct à la fin de notre prétraitement, il fallait
revoir nos différentes méthodes : s’inspirer d’algorithmes récents et bien plus complexes !
Nous avons donc repris nos recherches. Au cours de celles-ci, nous avons découvert un logiciel :
Unpaper. Après quelques essais, ce logiciel semblait être une bonne solution à tous nos problèmes de
prétraitement. Il effectuait l’ensemble de nos souhaits de traitement d’image (rotation, élimination du
bruit, etc...) et le résultat était acceptable. C’est pourquoi nous avons décidé d’intégrer ce logiciel à
notre projet.
Cependant, il y a quelques contraintes au niveau des entrées/sorties du logiciel. En effet, le logiciel
Unpaper n’accepte qu’un certain type de format en paramètre d’entrée : tous les formats de la famille
des PNM. C’est-à-dire les extensions de la forme PBM, PGM ou encore PPM. Nous devons donc
absolument en tenir compte si nous voulons utiliser Unpaper. De plus, nous pouvons passer jusqu’à
deux images en paramètre. Il suffira de mettre les bons arguments. Unpaper regroupera les deux
images en une seule dans son fichier de sortie si nous le souhaitons, ou simplement deux fichiers
distincts. Dans notre projet, nous n’utiliserons pas cette fonctionnalité. Elle peut devenir une des
améliorations intéressantes à apporter, notamment lors de gros ticket de caisses qui ne tiendraient pas
sur une seule page A4. Concernant le fichier de sortie, nous pouvons choisir entre les formats PBM ou
PGM cités ci-dessus. Pour nous faciliter la tache par la suite, nous avons choisit le format PBM. Il
existe également une fonction « overwrite ». Elle pourrait être elle aussi faire partie des améliorations à
apporter. Elle nous permettrait d’économiser une image en mémoire. Cependant, nous préférons dans
un premier temps avoir deux images distinctes. En effet, cela nous permet d’avoir un meilleur contrôle
des possibles erreurs lors de l’exécution et de pouvoir effectuer une comparaison avec l’image en entrée
d’Unpaper.
Nous verrons dans la suite du rapport comment adapter Unpaper à notre projet.
3.2.3
Analyse, La segmentation
Le principe de la segmentation est de diviser le document en plusieurs zones, ces zones peuvent
contenir différents type de données, des graphiques, des dessins, du texte, des tableaux. De nombreuses
méthodes permettent de segmenter le document, on expliquera les grandes lignes de deux méthodes
qui nous semblent implémentable et dont l’une d’entre elle ressemble à l’approche du client.
LE LISSAGE La méthode RLSA utilise la méthode de lissage, cette méthode consiste à noircir
tous les espaces blancs entre les pixels de taille inférieur à un seuil S. Le lissage est appliqué deux
fois au document, un lissage vertical est effectué et un lissage horizontal, formant deux images. Un
"ET" logique est appliqué à ces deux images produisant une image des composantes connexes. La
19
segmentation se fait à partir de l’image des composantes connexes, pour se faire il est nécessaire de
calculer quatre quantités pour chaque composante, hauteur, excentricité, densité et longueur des traits
noir de la composante dans l’image d’origine. A partir de ces valeurs on peut segmenter le document
d’origine en image ( graphique), texte, ligne horizontale, verticale et former une arborescence des
microstructures, Wong et al. [10] utilise cette méthode. On peut rappliquer cette méthode sur les
lignes pour détecter les mots et les caractères en diminuant le seuil de l’étape du lissage.
LA PROJECTION Cette méthode s’applique sur les documents dont les lignes sont parallèles et
régulièrement espacées. Elle consiste à projeter l’image du texte sur un axe. On obtient un histogramme
où l’on peut facilement détecter la position des lignes. La méthode de XY-CUT [9] complète la méthode
des projections, en appliquant successivement des projections sur les axe x et y des différents blocs, on
obtient une arborescence des blocs. En rappliquant cette méthode sur les zones contenant du texte on
pourra segmenter le texte en ligne, mot, caractère.
L’approche du client de la segmentation, qui correspond a la méthode de la projection, consiste
à repérer les espaces qui séparent les lignes, les mots ou les caractères depuis un histogramme, cet
histogramme représente la projection des pixels suivant un axe.
3.2.4
Reconnaissance
La reconnaissance regroupe deux tâches relativement similaires, l’apprentissage et la décision (reconnaissance des caractères). En effet par des approches différentes et depuis la même source d’information, dans notre cas la zone où se situe le caractère, les tâches essayent de faire correspondre un
caractère de base aux caractère situé dans la zone.
Apprentissage
Il s’agit lors de cette étape d’apprendre au système des informations qui lui seront nécessaire lors
de la décision. Il existe différentes méthodes d’apprentissage dont nous allons expliquer quelques une
d’entre elles.
L’apprentissage est dit manuel lorsque les informations nécessaires à l’étape suivante, la reconnaissance (décision), sont récupérées par l’intermédiaire de l’utilisateur, il effectue une correspondance
entre le caractère détecté et le caractère qu’il représente.
L’apprentissage automatique crée de manière automatique les informations nécessaires à la reconnaissance des caractères. Pour se faire il faut une assistance minimale, c’est à dire que le système doit
posséder un grand nombre d’échantillons pour pouvoir détecter leurs caractéristiques.
Lors de la décision il se peut que la base de connaissance ne soit pas suffisante pour que la reconnaissance se fasse de manière automatique, il faut à ce moment effectuer une nouvelle phase d’apprentissage
manuel ou automatique pour effectuer la correspondance, c’est l’apprentissage continu.
Reconnaissance des caractères
Calcul de la transformée de la distance [13]
Dans le cadre d’une image en deux dimensions représentées par un tableaux de pixels on ne peut
pas parler de distance Euclidienne1 mais plutôt de distance de chanfrein qui est une approximation de
la distance Euclidienne.
Pour estimer la différence entre une image de référence et une image acquise on peu chercher à
calculer pour chaque pixel de l’image à tester la distance minimum au premier pixel de l’image de
référence.
Cette méthode s’appelle la transformée de la distance. Elle consiste plus précisément à obtenir une
carte de distance de chanfrein où chaque distance sur la carte représente la distance minimum au pixel
noir le plus proche de l’image et à comparer les cartes de chaques images.
1 La
distance Euclidienne est la distance entre deux points dans un espace.
20
La première étape consiste à calculer les tables des distances de chacune des images.
Une approche primaire consisterait à calculer pour chaque point de l’image à comparer la distance
au premier pixel rencontré. Mais cette méthode impliquerait une très grande complexité.
Il existe une autre méthode plus efficace appelée algorithme de Rosenfeld qui permet de calculer
la transformée de la distance. Cet algorithme s’appuie sur l’ordre de balayage de la grille et sur la
décomposition du masque de chanfrein en un masque antérieur et postérieur.
L’ordre de balayage est le sens dans lequel la grille de l’image sera parcourue il doit respecter la
contrainte de régularité : si p<q alors p+x<q+x. Par exemple nous pouvons prendre l’ordre lexicographique qui consiste à parcourir le tableau de la première à la dernière ligne, puis dans chaque ligne de
la première à la dernière colonne et son opposé l’ordre lexicographique inverse.
(a) lexicographique
(b) lexicographique inverse
Fig. 3.1 – Ordre de balayage
Le masque de chanfrein antérieur et postérieur est la décomposition du masque suivant l’ordre de
balayage. Le masque antérieur correspond à l’ensemble des pixels qui sont antérieurs ou égaux dans
l’ordre de balayage respectivement le masque postérieur correspond à l’ensemble des pixels qui sont
antérieurs ou égaux dans l’ordre de balayage.
Fig. 3.2 – Exemple de masque de chanfrein avec ses masques antérieur et postérieur
L’algorithme en lui même se décompose en trois parties une initialisation, un balayage avant puis
un balayage arrière.
L’initialisation consiste à mettre à 0 la distance des points appartenant à la fois à l’image à tester
et à l’image de comparaison.
Pendant le balayage avant la grille est parcourue dans un premier sens et pour chaque pixel on
calcule la distance minimum
21
Procedure CalculeTFDR
I est l’image de référence.
M est un tableau d’entier à deux dimensions de la taille de l’image I.
M- représente le masque de chanfrein antérieur
et M-(p) la valeur du masque au point p.
M+ représente le masque de chanfrein postérieur
et M+(p) la valeur du masque au point p.
V- représente le voisinage antérieur du point p.
V+ représente le voisinage postérieur du point p.
0) Initialisation
Pour chaque pixel p de I on pose
M(p) := 0
M(p) := infini
si p appartient à I,
sinon.
1) Balayage dans l’ordre direct :
Pour chaque pixel p allant du premier au dernier, faire
Pour chacun des points q appartenant au voisinage antérieur de p
M(p) := min { I(q) + M- (q)}.
2) Balayage dans l’ordre inverse :
Pour chaque pixel p allant du dernier au premier, faire
Pour chacun des points q appartenant au voisinage postérieur de p
M(p) := min { I(q) + M+(q)}.
On a TFDR(p) = M(p).
La preuve de cet algorithme peut se démontrer ainsi :
Soit p un pixel de S, et soit q le pixel de R le plus proche de p. Comme la distance de chanfrein
satisfait les conditions de Montanari, tout chemin de poids minimum de q à p emprunte au plus
deux directions, et on peut arbitrairement choisir l’ordre dans lequel sont combinés les déplacements
selon chacune des deux directions. Si le chemin emprunte une seule direction, ou deux directions
orientées toutes deux vers l’avant ou toutes deux vers l’arrière (pour l’ordre de balayage), on combine
ces déplacements dans n’importe quel ordre. Au cas où le chemin utilise deux directions dont l’une
va vers l’avant et l’autre vers l’arrière (pour l’ordre de balayage), on choisira un chemin empruntant
d’abord la direction vers l’avant, et ensuite celle vers l’arrière (voir le dessin ci-dessous). Comme S est
rectangulaire, ce chemin sera entièrement inclus dans S. Lors de l’initialisation, le pixel q obtient la
valeur correcte pour la transformée de la distance. Au cours du premier balayage dans l’ordre direct,
les pixels sur le tronçon de chemin vers l’avant obtiennent l’un après l’autre la valeur correcte. Au cours
du second balayage dans l’ordre inverse, les pixels sur le tronçon de chemin vers l’arrière obtiennent
l’un après l’autre la valeur correcte.
22
La seconde étape de la comparaison consiste à calculer les barycentres des deux images.
Finalement le calcul de la distance en elle même se décompose en 6 actions :
1. On initialise la valeur de la distance à 0.
2. Pour chaque pixel de l’image acquise on obtient sa position par rapport au barycentre(on a donc
un vecteur).
3. On obtient la position du pixel dans l’image de référence en ajoutant le vecteur obtenu précédemment au barycentre de l’image de référence.
4. Si le point obtenu est inclut dans l’image de référence, alors on obtient la valeur de la carte des
distances de l’image de référence pour ce point.
5. On ajoute la valeur précédente à la distance et on passe au pixel suivant.
6. Une fois tous les pixels traités on recommence les opérations 2 à 4 en inversant l’image de référence
et l’image acquise.
Au final le nombre obtenu correspond à la distance entre les deux images.
Extraction de paramètres [6]
Une autre méthode de reconnaissance consiste à extraire pour chaque caractère des paramètres de
types géométrique et topologique. Ces paramètres extraits peuvent ensuite être codés en une suite de
chiffres représentant la forme globale du caractère. Voici ces paramètres :
Paramètres de transition. Le cadre de l’image du caractère est subdivisé en trois bandes
verticales et en trois bandes horizontales. L’étape suivante consiste à calculer le nombre de transition
blanc-noir-blanc pour toutes les bandes. Ces transitions nous donnent des informations importantes
sur la forme de l’objet. Par exemple le caractère "c" a deux transitions dans les bandes supérieures et
inférieures horizontales mais il n’en possède qu’une dans la bande centrale, dans les bandes verticales
seule celle de gauche ne possède qu’une transition les deux autres en contiennent deux. Pour coder ces
23
informations, on utilise six chiffres, trois pour les nombres de divisions verticales et trois autres pour
les divisions horizontales ainsi notre exemple du c se codera "122 212".
Paramètre de densité spatiale. Pour commencer on subdivise l’image du caractère en une
grille de taille 5X5. La seconde partie consiste à calculer la densité des points noirs dans chacune des
25 cases. Si la densité des points noirs est supérieure à celle des points blancs, on affecte la valeur
"1" à la case et "0" sinon. Pour le codage, chaque ligne et chaque colonne sont représentées par un
chiffre dépendant de l’agencement des cases majoritairement noires ou blanches qui les composent. On
distingue cinq configurations de points différentes :
– noirs
– noirs-blancs
– blancs-noirs
– noirs-blancs-noirs
– blancs-noirs-blancs
Ces configurations sont représentées par des chiffres compris entre 0 et 4 pour les lignes et entre 5 et
9 pour les colonnes. Ces paramètres permettent de conserver l’allure globale de la forme et sont très
peu sensibles au bruit.
Paramètres de cavité. Les cavités ne se retrouvent que sur certains caractères (environ un tiers
dans l’alphabet). La présence d’une cavité ainsi que sa position permet de distinguer ces caractères
entre eux. Pour déterminer la présence d’une cavité on réutilise la grille du paramètre de densité
spatiale et on cherche une case à "0" entourée de case à "1". Une fois la cavité détectée, on détermine
sa position dans l’image du caractère : centrée, centrée à gauche, en haut à droite etc... .
Paramètre d’allongement. L’allongement est un indice assez intéressant sur la forme du caractère. Par exemple : un "i" est plus long que large alors que les lettres "m" et "a" sont mieux
proportionnées. Pour déterminer l’allongement, il suffit de calculer le rapport hauteur/largeur. Le
codage de ce rapport est assez simple, il correspond à l’arrondi du rapport.
3.2.5
Post-traitement
Le post-traitement consistera à traiter les chaînes de caractères extraites pendant l’analyse. Cette
étape devra aussi permettre de détecter certaines erreurs comme des lignes non valides (lignes annulées,
etc...) ou les caractères erronés comme la présence du chiffre 0 à la place d’un O et de les traiter dans la
mesure du possible. Pour optimiser ce traitement, on pourrait envisager d’utiliser des outils d’analyses
syntaxique et lexicale comme par exemple Lex et Yacc.
On pourra difficilement envisager l’implémentation d’une fonction de correction à l’aide d’un dictionnaire car les mots que nous allons identifier sont des noms de produits ; or on ne dispose pas de
dictionnaire pour permettre la comparaison. Une solution possible consisterait à utiliser la base de
données des produits qui sera construite à l’aide de cet OCR comme dictionnaire.
Une fois les informations récupérées fiables, on pourra passer à la phase de stockage des informations
dans une table, puis envisager si le temps le permet, l’enregistrement dans une base de données.
Ces étapes de traitements sur des chaînes de caractères sont classiques et ne posent pas de problème
majeur.
3.3
Choix
Pour une majeure partie des étapes de notre OCR, on doit implémenter les approches du client. Si
nous disposons d’assez de temps, nous essayerons de mettre en place une méthode de reconnaissance
de caractère par extraction de paramètres que nous avons vu précédemment.
Pour l’architecture globale du logiciel nous avions quatre choix possibles :
24
– Réutiliser libGOCR.
– Réutiliser Conjecture.
– Réutiliser GStreamer.
– Créer notre propre architecture.
Dans le cadre de ce projet, nous n’aurions pas le temps de repartir à zéro et d’implémenter les
algorithmes demandés nous avons donc décidé de reprendre un framework existant.
Sur les trois librairies que nous avons trouvé, GStreamer semble la moins adaptée à nos besoin car
elle ne fournit aucune base sur laquelle nous pourrions nous appuyer pour comparer l’efficacité de nos
algorithmes. De plus, il faudrait écrire des modules de chargement de fichier qui sont normalement
déjà implémentés sur les deux autres frameworks.
Entre les deux dernières possibilités, nous avons optés pour libGOCR qui nous a semblé plus
complet et plus facilement configurable que Conjecture. De plus Conjecture étant un projet récent il
ne semble pas encore être aussi fiable que son prédécesseur.
25
Deuxième partie
Cahier des charges
26
Chapitre 4
Besoins fonctionnels
Dans cette partie, nous présenterons les fonctions que doivent réaliser nos modules.
4.1
Traitement de l’image
La conversion de l’image numérisée du format tiff au format pbm sera une étape de notre développement. Le format tiff est un format codé en hexadécimal avec une structure complexe. Un fichier tiff
se résume à trois blocs de données. Le premier, est l’en-tête et contient des informations sur l’ensemble
du fichier. Il est lu en premier quand on travaille sur ce format ; sa taille est de huit octets. Le second
est le répertoire de paramètres (parfois plusieurs répertoires). Ces paramètres se suivent et donnent
des informations sur le type de données comme la structure d’un pixel de couleur ou non, sa position...
A la fin de chaque répertoire, il existe un pointeur qui donne accès à un autre répertoire ; sa valeur
est nulle s’il n’y en a pas d’autre. Quand au format PBM, il nous paraît plus facile d’utilisation pour
diverses raisons :
– Tout d’abord, les entêtes sont plus lisibles et plus faciles à traiter. En effet, un fichier au format
PBM se décompose comme suit :
1. Un identifiant de format. Ici “P5” pour le format PBM ;
2. La hauteur et la largeur de l’image en caractères ASCII (en décimal) ;
3. Le nombre maximal du niveau de gris (ici 255) ;
4. Enfin, les valeurs de chaque pixel de l’image en niveau de gris ;
– Ensuite, le fichier est plus maniable pour nos traitements matriciels. L’image sera en noir et blanc
(valeur des pixels 0 ou 255).
Pour effectuer la conversion, nous utiliserons la fonction “tifftopnm” de “libnetpbm”.
4.2
Numérisation
La numérisation est l’étape de traitement d’une image qui change la valeur de pixel afin de passer
au niveaux de gris au noir et blanc. Si notre image est en niveaux de gris, nous aurions pu implémenter
nous même un algorithme pour convertir l’image en noir et blanc (valeur du pixel supérieur à une
valeur donnée, on le passe à 255, sinon on le met à 0). Cependant, Unpaper réalise cette étape pour
nous, avec un résultat très satisfaisant. Nous avons donc jugé qu’implémenter cette étape nous même
était inutile.
27
4.3
Elimination du bruit
Une numérisation ne peut pas être parfaite à cause de plusieurs paramètres comme la qualité du
document (froissé ou non), de l’état de propreté du scanner (dépôt de poussière) et des traces de
doigts potentielles de l’utilisateur sur le scanner. Il est donc nécessaire pour que la reconnaissance
de caractère soit optimale, que le maximum de défauts soient corrigés. Dans le cas contraire, les
conséquences pourraient être importantes ! Ainsi, deux caractères identiques en théorie pourraient être
reconnus différemment (voir pas reconnu du tout). La chaîne de caractère obtenue n’aurait alors aucun
sens. Une seconde conséquence importante concerne la bibliothèque. Si nous décidons de l’implémenter
dynamiquement, il y aurait alors une surcharge de la bibliothèque totalement inutile ! Tout cela nuirait
donc à l’utilisateur, par exemple plusieurs entrées dans un tableau pour un même produit, considération
de deux produits alors qu’ils sont identiques, etc.
Le bruit produit lors de la numérisation peut-être de deux types : un ajout de pixels noirs ou blancs
(si l’image est en noir et blanc) ou un ajout de pixels en niveau de gris aléatoirement dans l’image.
Dans la première partie du rapport, nous avons passé en revu les différentes méthodes possibles
pouvant nous servir à l’élimination du bruit.
4.4
Détection de l’angle d’inclinaison et Redressement de l’image
Pour redresser l’image à l’aide d’une rotation, nous avons besoin de détecter l’angle d’inclinaison.
Nous avons au moins deux solutions possibles. La première consiste à mettre en place les algorithmes
vus précédemment dans le rapport. La seconde, l’utilisation de Unpaper ! L’angle calculé par Unpaper
n’est pas très précis. Il le calcul à 0.5 degré près. Après plusieurs tests, cette approximation d’angle ne
nous gène pas vraiment. La segmentation reste opérationnelle.
4.5
Segmentation du ticket
La segmentation du ticket doit permettre de découper le ticket de caisse en bloc. Mais tout le
ticket n’a pas besoin d’être segmenté ; seules certaines parties du ticket nous sont nécessaire, comme
la liste des articles, et leurs prix, le nom du magasin ainsi que la date d’achat. La segmentation doit
être capable de segmenter seulement ces parties. Pour se faire, nous avons besoin de connaître la
configuration du ticket qui sera définie lors de l’apprentissage.
4.6
Création d’une bibliothèque de caractères
La bibliothèque peut se faire de plusieurs façons différentes. Nous avons pensé à deux d’entres elles.
La première, la plus simple (et plus rapide à mettre en oeuvre) mais peut-être pas la plus efficace :
nous créons dès le début la bibliothèque nous même. C’est à dire, nous créons une image pour chaque
lettre que nous stockons dans un répertoire précis. En partant du principe que chaque magasin possède
sa propre police de caractère, cette méthode risque de vite poser un problème si l’utilisateur désire
traiter une masse importante de tickets de provenances différentes. Le nombre de bibliothèque à créer
au préalable est alors trop important. Le second problème de cette méthode est que la bibliothèque
n’est pas évolutive ! L’utilisateur ne va pas s’amuser à créer sa propre bibliothèque si jamais un magasin
change de police de caractère !
La seconde solution est de faire une bibliothèque “interactive”. La bibliothèque ne sera pas construite
au départ, mais va s’agrandir au fur et à mesure de l’utilisation du logiciel. Ainsi, l’utilisateur va traiter
son premier ticket de caisse. A chaque nouvelle lettre reconnue, le logiciel affichera le caractère à traiter
à l’écran (s’il n’est pas reconnu ; donc non présent dans la bibliothèque) et l’utilisateur indiquera à quelle
lettre (ou chiffre, ou symbole) le caractère correspond . Le caractère se rajoutera donc à la bibliothèque
et la prochaine fois qu’il sera rencontré dans le document, il sera automatiquement reconnu.
28
Cette méthode pause, elle aussi, quelques problèmes, notamment celui de l’augmentation d’erreur
lors de la reconnaissance. En effet, lorsque que le caractère sera lu pour la première fois, il se peut qu’il
y ait des imperfections dans l’image, mais il sera stocké néanmoins dans la bibliothèque. Lors de la
suite du traitement, si le même caractère est de nouveau rencontré, mais qu’il ne possède pas les même
erreurs, les différences entre l’image de la bibliothèque et le caractère peuvent être trop importante
pour qu’il soit reconnu. Il sera alors de nouveau ajouter à la bibliothèque (par l’utilisateur). Cette
dernière peut donc être surchargée inutilement.
4.7
Apprentissage
L’apprentissage est une phase clef de la reconnaissance mais peut être aussi utile pour la segmentation. Il y aura deux types d’apprentissage :
– Un apprentissage continu : c’est l’apprentissage vu précédemment, l’ajout de caractère dans notre
base de connaissance si il n’a pas été reconnu pendant la reconnaissance.
– Un apprentissage manuel : cet apprentissage est nécessaire pour connaître la structure du ticket
et donc connaître quelles sont les zones que nous allons segmenter. Lors de la première utilisation d’un type de ticket (provenance d’un magasin X), l’utilisateur devrait indiquer si les blocs
reconnus sont pertinents ou pas. La configuration du ticket serait enregistrée pour les prochaines
utilisations du logiciel sur ce type de ticket.
29
Chapitre 5
Besoins non fonctionnels
5.1
Le débugage
L’un des premiers besoins non fonctionnels que nous nous fixons, est de pouvoir ajouter des options
de débugage à la console. Ces options consistent à donner le choix à l’utilisateur, et de lui permettre
d’améliorer les étapes de la reconnaissance. Nous avons choisi de commander ces options de débugage
à partir de la ligne de commande. L’utilisateur devra préciser au moment de l’appel au logiciel en
passant un argument précisant quel sera le niveau de dialogue entre le logiciel et l’utilisateur. Nous
définirons trois niveaux de dialogue qui ceux basent sur ceux de libgocr :
– niveau 0 aucun dialogue
– niveau 1 seulement les erreurs
– niveau 2 les erreurs et les warnings
– niveau 3 niveaux servant pour le débugage
Ce genre de débugage devra être généralisé pour chaque module. Cela nous permettrait de voir les
étapes de chaque processus et d’avoir toutes les informations nécessaires en cas de problème.
Cette fonctionnalité devra permettre de faciliter l’implémentation de nouveaux algorithmes de
reconnaissance de caractère en nous permettant de visualiser l’avancement du traitement. C’est à dire
que nous aurions un mode détaillé et un mode résultat uniquement.
5.2
Framework / Modularité
Notre client nous a demandé une certaine implémentation pour pouvoir tester différentes méthodes.
Les modules de notre OCR doivent pouvoir être interchangeable. On doit pouvoir changer facilement
les modules pour les adapter à notre implémentation. Cette fonctionnalité permettra d’optimiser les
compétences d’analyse en combinant les modules les plus performants.
5.3
Tests de précisions / Comparaison avec d’autres OCR
En se basant sur l’architecture de GOCR par exemple, on pourra tester les fonctions d’origines
et les comparer en termes de performances, de temps et de résultats avec nos propres modules, voir
d’autres méthodes si nous en avons le temps d’en implémenter d’autre et de faire un comparatif. Nous
pourrons alors isoler les modules les plus intéressants et optimiser nos résultats. Ces tests permettront
surtout de vérifier la qualité et la performance de nos travaux.
Nous avons décidé de comparer notre logiciel avec deux types d’ocr un en version libre puis un
commercial en version d’évaluation car nous ne pouvons pas nous permettre d’en acheter un ! Ces tests
devront permettre de montrer la précision de notre application par rapport aux autres. Nous essaierons
30
plusieurs configurations pour voir comment évoluent les résultats en fonction des paramètres de l’image
passée.
5.4
Ergonomie
L’ergonomie d’un logiciel peut être un besoin non fonctionnel que nous avons évoqué au cours de nos
entretiens avec notre client. Ce dernier ne nous demande pas spécialement de développer une interface
graphique. Une utilisation type avec une console est suffisante, mais un suivi du travail qui s’exécute,
avec affichage des étapes successives, pourrait être une alternative intéressante à programmer (voir
section sur le débugage).
5.5
Langage
Nous avons fais le choix d’utiliser la librairie GOCR, nous nous limiterons à l’utilisation du langages
C et plus précisément le langage C normalisé au standard C99 (ISO/CEI 9899 :1999).
31
Chapitre 6
Le planning
6.1
Les parties indispensables
Le déroulement de ces parties peut se diviser en 3 morceaux :
– La première est la compréhension de la structure de GOCR que nous allons par la suite réutiliser.
Nous nous réservons une semaine chacun pour bien comprendre son fonctionnement. Cette étape
peut nous prendre moins d’une semaine puisque auparavant, nous aurons réaliser l’architecture
de notre projet.
– La seconde consiste à réaliser des modules, indépendant les uns des autres. Nous pouvons donc
effectuer un travail plus ou moins autonome.
– La dernière étape se fera en équipe complète. Elle permettra de regrouper nos modules et de
réaliser le rapport final.
6.2
Les parties facultatives
Dans le planning ci-dessous, nous n’avons pas tenu compte des parties facultatives que nous pouvons
développer, comme le site Internet ou encore une nouvelle méthode de reconnaissance de caractère. Il
nous semble difficile de les réaliser dans les délais. C’est pourquoi elles n’en font pas parti. Cependant, si
le déroulement de notre projet se passe sans encombre, et que nous prenons de l’avance sur le planning
qui est soumis, alors nous envisagerons de traiter ces partis (réalisation par binôme par exemple).
32
Planning d’organisation du projet
Travail à effectuer
Sem 7
Architecture
du projet
Aubin
Desmoulin
Dubois
Roigt
Sem 8
Sem 9
Compréhension du framework
+ chapitre du rapport
Aubin
Desmoulin
Dubois
Roigt
Création du jeu de test
+ chapitre protocol de tests
Desmoulin
Dubois
Sem 10
Sem 11
Sem 12
Dubois
Roigt
Module de segmentation
Roigt
Module reconnaissance de caractères
+ sous-module d’apprentissage
Dubois
Roigt
Dubois
Roigt
Module pré-traitement :
inclinaison
Desmoulin
Desmoulin
Module pré-traitement :
binarisation + bruit
Aubin
Aubin
Module de
post-traitement
Aubin
Desmoulin
Sem 13
Sem 14
Aubin
Desmoulin
Intégration des
différents modules
Dubois
Roigt
Rapport final
du projet
Aubin
Desmoulin
Dubois
Roigt
Aubin
Desmoulin
Dubois
Roigt
Troisième partie
Présentation de l’application
34
Chapitre 7
Présentation de l’application
7.1
Les librairies nécessaires
Pour fonctionner, nous avons besoin de certaines librairies. Ces librairies sont :
– libgocr
– libnetpbm
– libmath
– libnetpbm-dev
Nous avons aussi intégré, directement Unpaper à notre système linux : l’avantage est que l’on a
plus besoin de se soucier de cette partie.
Nous avons installé pour rendre notre application plus sympathique, Eye Of Gnome (EOG) : ce
logiciel permet d’afficher nos images. L’avantage de l’intégration de ce programme est que l’on a un
affichage autre que celui de la console. On voit ce qui se passe à chaque étape des traitements.
Pour des raisons de commodité, nous avons utilisé l’outil dselect pour les installer directement sur
le système. Il est donc indispensable de passer par là pour compiler chez soit notre travail.
7.2
Le Makefile
Au cours de nos séances, nous avons intégré un Makefile généré avec les Autotools et Libtools. Pour
compiler, on conseille donc d’exécuter les commandes suivantes :
– autoscan : cette commande génère un fichier configure.ac en analysant les sources du dossier où
il se trouve.
– aclocal : le rôle d’aclocal est de générer un fichier aclocal.m4. Il est nécessaire car utilisé par le
script configure pour réaliser le Makefile.
– autoconf : on génère ici le script configure dépendant du fichier configure.ac. Ce script est à
exécuter pour créer le Makefile final.
– automake : enfin on récupère notre fichier Makefile.am dans lequel nous avons mis les options
de compilations, les librairies à appeler, les fichiers sources à compiler, ainsi que notre propre
librairie dynamique à compiler.
35
7.3
Première exécution du logiciel
Il est nécessaire d’avoir une imagé numérisée du restaurant universitaire pour obtenir un résultat
correct. Ce format d’image doit être exclusivement au format tif ou au format pbm.
Les options disponibles concernent le verbose (-v) et l’utilisation de Unpaper (-no). La valeur 0 du
verbose désactive l’affichage des détails dans la console et l’utilisation de EOG. La valeur 3, c’est
l’option du débugage : le programme sera très lent, car il affichera tous les détails (carte des distances,
...).
Exemple d’utilisation : ./main <image> [-v] [valeur du verbose] [-no]
Nous nous sommes restreint, c’est à dire que lorsque l’on numérise une image, elle doit nécessairement être inclinée avec un angle inférieur à 40 degrés ou droite. Nous détaillerons ce petit problème
plus loin.
7.4
Gestion du dialogue programme/utilisateur
Notre logiciel peut avoir plusieurs niveaux de dialogue. Ces niveaux se calquent sur ceux de libgocr :
– niveau 0 : n’affiche aucune information.
– niveau 1 : affiche les erreurs.
– niveau 2 : affiche les erreurs et les warnings.
– niveau 3 : affiche absolument tout, surtout utilisé pour le débugage.
Ces options sont disponibles en passant en argument l’expression "-v n" où n est le niveau de dialogue
qui doit être compris entre 0 et 3. Par défaut le niveau de verbose est défini au niveau 3.
7.5
Modification des modules
Nos modules sont totalement compatibles avec libGOCR, il est donc possible à un utilisateur de
les réutiliser avec les siens. Il n’a qu’à suivre les instructions de l’api pour intégrer ses modules aux
nôtres.
36
7.6
Est-il possible d’affiner les réglages ?
Notre logiciel est modulaire, on peut donc l’améliorer par de nouveau module, cette partie sera
développée plus tard. Unpaper est un logiciel très souple disposant de nombreux réglages. Ces réglages
concernent notamment la façon de faire une rotation. On peut décider si oui ou non on doit faire une
rotation. On peut aussi décider dans quel sens cette rotation s’effectuera.
Pour effectuer des réglages, nous avons pensé à l’utilisation d’un appel système sur un script qui
appelle Unpaper avec ses options.
Nous ne l’avons pas mis en place encore, ce qui implique que les réglages de Unpaper se font au
niveau de la ligne 139 du fichier my_imageFilter.c
7.7
Exemple d’exécution
(a) Image initiale
(b) Après Unpaper
Fig. 7.1 – Pré-traitements
Fig. 7.2 – Exemple de segmentation en blocs
37
Fig. 7.3 – Exemple de segmentation en caractères
(a) Affichage 1
(b) Affichage 2
Fig. 7.4 – Reconnaissance des caractères
38
(c) Carte des distances
Fig. 7.5 – Comparaison avec les modèles de caractères
Fig. 7.6 – Résultat à la sortie
39
Quatrième partie
Architecture
40
Chapitre 8
Organisation séquentielle du
traitement
Initialisation et configuration : Permet d’initialiser la bibliothèque libGOCR et de mettre modifier les paramètres si nécessaire.
Chargement du plug-in : Chargement du plug-in en mémoire, prépare le chargement des fonctions.
Chargements d es fonctions : Associe chaque fonction à utiliser, contenue dans le plug-in, à un
des modules de libGOCR.
Chargement de l’image : Chargement de l’image à traiter.
Traitement :
LibGOCR fait appel séquentiellement aux modules dans un ordre prédéfinis :
1. ImageFilter
2. BlockFinder
3. CharFinder
4. CharRecognizer
5. ContextCorrection
6. OutputFormatter
Dans chaque module, les fonctions sont exécutées dans l’ordre de leur insertion. Un module peut ne
pas contenir de fonction dans ce cas la on passe directement au module suivant.
Finalisation :
Libération de la mémoire.
41
Chapitre 9
Description des modules
42
9.1
Programme principal
Il correspond à la partie principale de notre application qui va utiliser la librairie libgocr, nous
permettant ainsi de créer un OCR adapté à la reconnaissance des tickets de caisses.
9.2
libGOCR
LibGOCR est la bibliothèque contenant les fonctions principales qui serviront de base à notre
application.
9.2.1
Attributs
– Verbose permet d’indiquer le niveau de sortie, c’est à dire d’avoir une idée de l’importance du
message :
1. niveau 0 : rien de spécial.
2. niveau 1 : message d’erreur.
3. niveau 2 : Warnings et erreurs
4. niveau 3 : utilisé pour le débogage.
– block_overlap : c’est une variable booléenne qui autorise le chevauchement de deux blocks.
– no_block : c’est aussi une variable booléenne qui, si elle est initialisée à vrai et qu’il n’y a pas
de bloc trouvé dans l’image, en créé un qui recouvre complètement l’image.
– char_overlap : c’est une autre variable booléenne qui autorise le chevauchement de caractère.
– char_rectangles : une variable booléenne qui, si elle est positionnée à vrai, sélectionne tout les
caractères dans des rectangles.
– find_all : ce booléen permet de chercher tous les caractères en les sauvegardant en mémoire puis
de les traiter. si ce booléen est positionné à vrai alors, le programme recherchera d’abord tous
les caractères, puis les sauvegardera, avant de les traiter.
– file_error
– print
– print_image
– lock
9.2.2
Structures
Pendant la lecture des sources et de la documentation de la bibliothèque libGOCR, nous avons
identifié ces structures :
1. gocrimage
Attributs publics :
– char* filename : le nom du fichier.
– int x
– int y : Les dimensions de l’image.
– gocrImageType type : type de l’image.
– gocrPixel** data : les données.
– List blocklist : la liste des blocs.
2. gocrblock
Attributs publics :
– gocrblockType t : le type du bloc.
– int x0
– int y0
– int x1
43
– int y1 : les coordonnées délimitant le bloc.
– gocrImage image : l’image du bloc.
– List boxlist : une liste de bloc.
3. gocrbox
Attributs publics :
– int x0
– int y0
– int x1
– int y1 : le cadre du caractère.
– wchar_t* attributes : les attributs du caractère.
– gocrChar* ch : le caractère identifié.
– List possible : la liste des caractères possibles.
– wchar_t modifier
– wchar_t c
– wchar_t ac
– int num : si cet entier est le même, alors c’est le même caractère.
– int x
– int y
– int dots : pixel de référence.
4. gocrchar
Attributs publics :
– wchar_t c : le caractère.
– float prob : la probabilité.
5. gocrpixel
Attributs publics :
– unsigned char value : La valeur du pixel
– unsigned char mark1
– unsigned char mark2
– unsigned char mark3 : marqueurs définis par l’utilisateur.
– unsigned char isblock : Définit si le pixel fait parti d’un block ou non
– unsigned char ischar : Définit si le pixel fait parti d’un caractère
– unsigned char padding : pourra être utilisé plus tard.
9.2.3
Fonctions du fichier d’entête
Pour faire appel à la puissance de libGOCR, nous devrons passer par les fonctions contenues dans
son fichier d’entête :
– extern int gocr_init ( int argc, char **argv ) ; Cette fonction permet d’initialiser gocr. Elle doit
toujours précéder les autres fonctions de la librairie.
– extern void gocr_finalize ( void ) ; elle permet de terminer l’utilisation de gocr.
– extern int gocr_setAttribute( gocr_AttributeType t, void *value ) ; Cette fonction affecte un
entier à chaque attribut de la librairie GOCR.
– extern void * gocr_getAttribute( gocr_AttributeType t ) ; Une fois affecté, le numéro de l’attribut peut être récupérer grâce à cette fonction.
– int (*gocr_imageLoad)( const char *filename, void *data ) ; cette fonctions permet d’ouvrir plusieurs formats d’image (PGM, TIFF, PPM ...). Elle retourne -1 en cas d’erreur, 0 sinon.
44
– extern void gocr_imageClose( void ) ; cette fonction permet de fermer l’image ouverte avec la
fonction ci dessus.
– extern gocr_ModuleId gocr_moduleLoad( char *filename ) ; C’est une fonction qui simplifie l’ouverture d’un module car elle charge le module placé en paramètre sans pour autant spécifier le
chemin. Un entier id est retourné en cas de succès. Cela permet par la suite de le retrouver pour
initialiser ses attributs.
– extern void gocr_moduleClose( gocr_ModuleId id ) ; On ferme un module qui a été ouvert en
le retrouvant grâce à son ”id”.
– extern int gocr_moduleSetAttribute( gocr_ModuleId id, char *a, char *b ) ; quand un module
le permet, cette fonction permettra de passer en paramètre de nouveau attribut au module dont
le id a été spécifié.
– extern const struct gocr_moduleattributeinfo * gocr_moduleGetAttributeList( gocr_ModuleId
id ) ; Chaque module ouvert, est stocké dans une liste. Cette fonction permet ainsi de retrouver
un module ouvert et de donner ses attributs.
– extern const struct gocr_modulefunctioninfo * gocr_moduleGetFunctionList( gocr_ModuleId
id ) ; On fait la même chose, sauf que l’on retourne la liste des fonctions présente dans le module
qui a été spécifier avec l’id.
– extern gocr_ModuleFunctionId gocr_functionInsertBefore( char *functionname, gocr_ModuleId
mid, void *data, gocr_ModuleFunctionId func ) ; On insère les fonctions du module qui nous intéressent. Cette fonction permet aussi de changer l’ordre d’exécution des fonctions.
– extern gocr_ModuleFunctionId gocr_functionAppend( char *functionname, gocr_ModuleId mid,
void *data) ; On peut ajouter à nos modules des fonctions.
– extern void * gocr_functionDeleteById( gocr_ModuleFunctionId id ) ; Cette fonction retire de
la liste, la fonction dont l’id est placé en paramètre.
– extern int gocr_guiSetFunction( gocr_GUIFunction type, void *func ) ; Cette fonction a pour
but d’initialiser l’interface graphique (bouton + fonction associée).
9.3
Chargement de l’image
Cette partie n’était pas prévue initialement mais il s’est avéré par la suite que nous ne pouvions
pas utiliser celle fournie par libgocr. Elle permet de charger une image de type pbm, pgm, ou tif en
mémoire.
Fonction chargée par libgocr
– int my_imageLoader(const char* filename, void* dat) ;
9.4
Prétraitement
Ce module est le plug-in qui contient toutes les opérations de prétraitement effectuées sur l’image
avant son analyse. Il sert à améliorer la qualité de l’image pour augmenter le taux de reconnaissance
en modifiant l’image chargée.
Fonction chargée par libgocr
45
– int my_imageFilter(gocr_Image* im,void* a) ;
9.5
Analyse
L’analyse est le plug-in qui contient toutes les fonctions relatives à la segmentation de l’image que
ce soit en blocs ou en caractères. Elle fournie en sortie une image segmentée en mots et en caractères.
Fonction chargée par libgocr
– int my_blockFinder(gocr_Image *image, void *data) ;
– int my_charFinder(gocr_Block *b, void *v) ;
9.6
Reconnaissance
Cette partie contient toutes les fonctions relatives à la reconnaissance de caractères. Elle récupère
une image segmentée puis recherche une correspondance entre l’image d’un caractère et sa valeur.
Fonction chargée par libgocr
– int my_charRecognizer(gocr_Box *b, void *v) ;
9.7
Post-traitement
La partie de post-traitement ne contient qu’une fonction qui permet de formater les informations
obtenues précédemment.
Fonction chargée par libgocr
– int my_outputFormatter(gocr_Block **b,void *v) ;
46
Cinquième partie
Présentation technique des
algorithmes
47
Chapitre 10
Chargement et prétraitement de
l’image
10.1
10.1.1
Chargement de l’image
description
Ce module n’était pas prévu initialement, nous pensions utiliser celui fournit par libgocr mais nous
avons du rapidement nous orienter vers un module de notre conception pour deux raisons majeures :
1. La correction des erreurs du code originel.
2. La création d’un code plus adapté à notre projet.
Nous avions eu des erreurs lors du chargement de grosses images au début de l’utilisation de la
bibliothèque libgocr. Cette nouvelle version nous a non seulement permis de les corriger, mais en plus
elle nous a permis de spécialiser la fonction de chargement. Notre projet se limitant à analyser des
tickets de caisses nous n’avions pas besoin d’images en couleurs ou en niveau de gris, nous avons donc
décidé de convertir toutes les images en noir et blanc dès le chargement pour n’avoir à ne nous servir
que d’un champ dans la structure gocr_Image.
Du fait de notre méthode de reconnaissance de caractères, qui consiste à comparer la différence
entre deux images, nous avions besoin d’une fonction de chargement qui pourrait aussi charger un
fichier image dans une autre instance de gocrImage que l’instance globale nommée currentimage. Or
cette fonctionnalité n’était par permise par la libgocr.
Techniquement la structure de notre module reste semblable à celle d’origine, elle réutilise la même
bibliothèque de chargement d’image ainsi qu’une grande partie des fonctions que l’ancienne utilisait
déjà. Le nom de la bibliothèque de chargement d’image est libnetpbm, elle a l’avantage d’être nécessaire
à l’utilisation de libgocr et donc déjà installée cela évite d’avoir à en intégrer une autre, de plus elle
suffit pour les opérations que nous avons à faire. Elle est simple d’utilisation mais ne permet que de
gérer que les formats pgm et pbm. Pour lire les autres formats il faut d’abord les convertir avec des
outils fournis par libnetpbm comme tifftopbm ect.
Notre module de chargement de l’image se décompose en deux fonctions :
– int charger_image(const char* filename, void* data)
– int load_image(const char* filename, void* data, gocr_Image *image)
La première fonction est celle qui sera appelée celle fournie par la bibliothèque lors de l’appel
à gocr_imageLoad elle consiste dans un premier temps à vérifier que l’image courante est vierge et allouée ainsi qu’à convertir le fichier au format pbm si celui-ci ne l’est pas. La conversion d’un format à un
autre se fait par un appel à la fonction system qui permet d’exécuter une chaîne de caractère comme
s’il s’agissait d’une commande shell. La ligne de commande à exécuter est construite dynamiquement
en fonction du format d’image à convertir. L’image convertie porte le nom de "‘conversion.pbm"’.
48
Dans un deuxième temps elle fait appel à la fonction load_image en lui passant en paramètre le nom
du fichier à charger et l’image courante currentimage.
La fonction load_image est la fonction qui se charge d’ouvrir le fichier qu’on lui passe en paramètre
et le charge dans la structure image. Le chargement de l’image en elle même est effectué par la librairie
libnetpbm. L’entête du fichier, qui contient la taille et le type de l’image, est stocké dans la structure
pam fournie avec la bibliothèque à l’aide de la fonction pnm_readpaminit. A partir de cette structure on
initialise la taille et le type de l’image ainsi qu’un tableau de pixel(nommées tuple) qui nous permettra
de récupérer toutes les données du fichier. Chaque ligne de l’image est stockée dans le tableau de pixel à
l’aide de la fonction pnm_readpamrow et ensuite parcourue pour remplir le tableau de la gocr_Image
passée en paramètre.
Si le type de l’image est pgm alors l’image est en niveaux de gris, or nous ne travaillons qu’avec
du noir et blanc, donc une fonction de seuillage est utilisée pour transformer le pixel niveau de gris en
pixel noir et blanc. Cette fonction est nommée binarisation ne se consiste que renvoyer un pixel noir
et blanc en fonction de sa valeur en niveau de gris.
Nous pouvons donc résumer ainsi les actions effectuées par le chargement :
1. Récupère les données de l’entête dans une structure pam.
2. Initialise la gocr_Image passée en paramètre à l’aide de la structure pam.
3. Récupère chaque ligne de l’image dans le fichier à l’aide d’un tableau de tuple.
4. Affecte la valeur de chaque pixel de la ligne récupérée à la case correspondante e la gocr_Image
10.1.2
Complexité
Cette partie ne consiste qu’à ouvrir un fichier et à parcourir un tableau à deux dimensions pour y
enregistrer la valeur de pixels sa complexité est donc d’ordre O(n2 ).
10.2
10.2.1
Prétraitement de l’image
description
Pour effectuer tous les prétraitements nécessaires sur notre image, l’utilisation de Unpaper nous
a été d’une grande aide. Il regroupe plusieurs fonctionnalités intéressantes. Nous allons donc détailler
comment nous utilisons Unpaper dans notre projet.
10.2.2
Comment adapter Unpaper à notre projet
Tout d’abord, il nous faut l’adapter à notre projet. Puisque les formats d’entrée et de sortie sont
assez restreints, nous devons faire quelques adaptions pour utiliser Unpaper correctement dans notre
projet. Il est possible qu’entre le moment du chargement de l’image et l’appel à Unpaper, il y ai eu, au
cour d’un module, des modifications sur l’image. Nous avons donc choisit de construire un « wrapper ».
Quel en est l’objectif ? Dans un premier temps, il va créer un nouveau fichier image (au format PBM)
au cas où il y aurait eu des modifications au préalable. Ensuite, il va faire appel à Unpaper avec
ce nouveau fichier image. Comme nous l’avons vu précédemment, une nouvelle image est créée. Le
« wrapper » va donc effectuer un nouveau chargement d’image au sein du programme. Une fois toutes
les modifications effectuées, l’image de sortie d’Unpaper peut être déchargée.
10.2.3
Le “wrapper”
Le wrapper nous permet de passer des images entre Unpaper et libgocr. Pour passer à Unpaper
une gocr_Image nous sommes obligés de passer par un fichier image au format pbm ou pgm, nous
devons donc créer un fichier de ce type sur le disque. Pour créer ce fichier nous avons dus réutiliser
49
la bibliothèque libnetpbm. Nous avions déjà utilisé cette bibliothèque lors du chargement de l’image,
l’écriture d’une image est assez similaire et peut se décomposer ainsi :
1. Initialiser un instance de la structure pam avec : le nom du fichier de sortie, le type du fichier de
sortie, le type des données du fichier de sortie, les dimensions de l’image.
2. Créer l’entête du fichier avec la fonction pnm_writepaminit() de libnetpbm en lui passant la
structure pam précédemment initialisée en paramètre.
3. Récupérer chaque ligne de l’image source et l’inscrire dans le fichier à l’aide de la fonction
pnm_writepamrow() de libnetpbm en lui passant la structure pam et un tableau de “tuple”
contenant la ligne courante.
Pour récupérer le résultat du traitement d’Unpaper nous utilisons juste notre fonction de chargement de l’image que nous avons décris un peu plus tôt.
10.2.4
Les filtres et paramètres utilisés
Après avoir passé pas mal de temps à tester les fonctions de Unpaper, nous avons trouvé une
combinaison de paramètres qui nous semble relativement généraux et qui donne une image de sortie
très correcte pour travailler dessus. Nous allons faire une description de chacun d’entre eux :
– Le mode verbose « -v ». Il nous a été très précieux lors des différents tests effectués et dans le cas
des traitements d’erreurs. Ce mode sera par la suite mis en option utilisateur ; il sera désactivé
par défaut pour ne pas encombrer le terminal de l’utilisateur inutilement.
– L’option « -l single-rotated » permet de spécifier à Unpaper qu’il n’y a qu’une page de texte dans
l’image que nous lui donnons et que la rotation se fait dans le sens contraire des aiguilles d’une
montre. Il est nécessaire de lui spécifier cet argument car Unpaper ne reconnaît pas intuitivement
le nombre de page dans une image. Ici, cette option reste fixe et contraint l’utilisateur à ne donner
qu’une image avec une seule page de texte. Comme évoqué plus haut, ceci reste une amélioration
possible à apporter pour une meilleure fonctionnalité de notre logiciel.
– L’option « -s dimensions » va nous permettre de définir la taille de l’image de sortie. En effet,
l’image de sortie (après rotation) peut être plus grande que l’image de départ. Dans le cas
extrême, le texte est incliné à 45˚et fait toute la diagonale de l’image. Pour obtenir les nouvelles
dimensions de l’image, nous avons donc simplement appliqué le théorème de Pythagore. Ce calcul
simple nous permet d’être sur que l’image sera assez grande, mais dans un grand nombre de cas,
l’image contiendra des grandes zones blanches (inutilement).
– L’option « -bn h ». Cette option concerne le sens de direction lors du repérage de zone de blocs
noirs (« blackfilter-scan-direction »). Nous lui demandons de repérer ces zones en parcourant
horizontalement l’image.
– L’option «- bt valeur» fixe le rapport des pixels noirs au-dessus desquels un secteur noir a été
détecté.
– L’option « -li valeur » fixe un rapport pour lequel les pixels isolés seront effacés. N’importe quel
secteur bruité qui contient tout au plus ce rapport de pixels foncés sera éliminé.
– L’option « -dr angle » indique à Unpaper l’angle maximum pour lequel il doit chercher l’angle
d’inclinaison du texte sur l’image. Par défaut, nous avons mis ici 90 degrés.
– Et pour finir, l’option « -t pbm » nous permet de spécifier le format de l’image de sortie. Nous
avons choisit le format pbm comme indiqué plus haut.
Unpaper exécute toutes ces fonctions dans l’ordre suivant :
– Il lit le fichier d’entrée ;
– Il effectue une première rotation grossière ;
– Il applique les premiers filtres (élimination du bruit) ;
– Puis il applique les filtres « blackfilter » et « grayfilter » ;
– Il effectue la rotation finale ;
– Il prépare la sortie en détectant par exemple les bordures, l’alignement, etc...
50
– Enfin, il écrit le fichier de sortie.
Nous pouvons remarquer qu’il n’applique pas de réel filtre de lissage sur l’image une fois la rotation
effectuée. Pour une meilleure qualité de l’image, il est possible de coder ce filtre. Ceci viendrait dans
les améliorations à apporter.
10.2.5
Complexité
La complexité due à l’utilisation de Unpaper est difficile à évaluer. En effet, le code du logiciel est
regroupé en un seul fichier de plus de 5000 lignes. Sa lecture et compréhension est donc peut évidente.
Cependant, nous pouvons faire une approximation de la complexité. Nous avons remarqué que pour
chaque traitement que le logiciel doit effectuer, il effectue une double boucle «for» où les limites sont
les dimensions de l’image. Il parcourt donc chaque pixel de l’image à chaque fois. La complexité des
boucles est donc de l’ordre de O(n2 ) (où n est la plus grande dimension de l’image). De plus, pour
chaque pixel parcourut, il y a un traitement à effectuer. Ce traitement dépend des filtres utilisés. Il peut
aller d’une simple addition pour calculer le nombre de voisins à des calculs beaucoup plus complexes.
Nous ne pouvons dire exactement la complexité de ces calculs.
Nous pouvons faire une approximation. Avec les paramètres que nous utilisons, Unpaper parcourt
au minimum 4 fois l’image. Si l’on note par «m» la complexité maximum des calculs sur un pixel, nous
obtenons une complexité de O(4n2 ∗ m) ; ce qui correspond à O(n2 ∗ m).
La constatation que nous pouvons faire, c’est que l’exécution de Unpaper prend pas mal de temps.
Dans l’optique d’optimisation temporel de notre logiciel, il nous faudra sûrement utiliser autre chose
que Unpaper (par exemple, implémenter nous même les algorithmes qui nous serons nécessaires).
51
Chapitre 11
Segmentation de l’image
L’utilisation de la librairie libgocr nous impose l’utilisation de deux modules de segmentation,
blockFinder et charFinder. Comme leurs noms l’indiques , blockFinder a pour but de diviser le document en blocs, plus exactement en blocs de mot, et charFinder, de diviser les blocs de mot en blocs
de caractères. Nous allons voir comment nous allons procéder pour effectuer cette segmentation. Ces
deux modules utilisent la même méthode de segmentation.
11.1
Choix du type d’algorithme
Comme nous l’avons présenté précédemment dans l’étude de l’existant, il y a différents algorithmes
que l’on peut envisager pour segmenter le ticket de caisse, en particulier la méthode RLSA ou celle
de PROJECTION. Nous utiliserons la méthode de PROJECTION pour effectuer cette segmentation.
En effet nous travaillons sur des tickets de caisse dont toutes les lignes sont parallèles et régulièrement
espacées, et pour ce type de configuration la méthode de PROJECTION est la plus appropriée puisqu’elle est de complexité moins grande que celle de la méthode RLSA ,qui s’applique plutôt sur des
documents qui possèdes des structures particulières (toutes les lignes du document ne sont pas alignées
).
Nous avons aussi vu précédemment que la méthode de PROJECTION pouvait se compléter par la
méthode XY-CUT, ici nous ne l’utiliserons pas puisqu’elle nous n’est pas utile et nous gênerait dans
certaines conditions. En effet XY-CUT permet de découper les zones détectées en sous-zones, hors si
nous l’utilisons nous nous retrouverons avec une zone contenant une sous-zone de liste d’article est
une sous-zone de liste de prix, mais il nous ai nécessaire de faire une correspondance entre un article
et son prix. En effet, il arrive que sur des tickets de caisse, l’article, et ces informations, s’étendent sur
plusieurs lignes.
La méthode de PROJECTION consiste a projeter les pixels d’une zone sur un axe , on obtient ainsi
un histogramme et par l’étude de celui-ci on peut déterminer les positions des blocs de pixels noirs.
11.2
Implémentation de la segmentation
Il y a quatre types de segmentation :
– segmentation du document en ensemble de lignes.
– segmentation des ensembles de lignes en lignes.
– segmentation des lignes en mots.
– segmentation des mots en caractères.
Comme la segmentation repose sur le même principe il n’est pas nécessaire d’implémenter quatre fois
la même fonction , mais chaque type de segmentation ferons appel a la même fonction de segmentation
mais avec des arguments différents.
52
On crée d’abord l’histogramme du bloc à segmenter et de cet histogramme on repère les positions
des blocs que l’on stocke dans des intervalles. Puis on parcourt ces intervalles et à chaque fois que l’on
en rencontre un, on ajoute un bloc à la liste des blocs, du bloc que l’on segmente.
11.3
11.3.1
Les outils utilisés
Les histogrammes et les intervalles
On a vu précédemment que l’histogramme est la base de la segmentation, pour la méthode de
PROJECTION. De l’histogramme on repère la position de chaque bloc de pixel noir (ensemble de
ligne, ligne, mot, caractère), ces positions sont définies par un intervalle. Pour déterminer les bornes
d’un intervalle on parcourt le tableau de l’histogramme ; dés que l’on rencontre un pic on crée un
intervalle qui a pour valeurs la position du début du pic et la position de la fin du pic (Chaque
pic de l’histogramme correspond soit à une ligne soit à un caractère suivant le bloc dont on a crée
l’histogramme).
Prototype de la fonction de création d’un histogramme :
Histogramme* createHisto(int sens,int x0,int y0,int x1,int y1,int val_min) ;
Un histogramme se crée a partir de l’image du ticket de caisse que l’on veut segmenter , il nous
ai donc nécessaire d’indiquer les coordonnées de la zone dont on doit créer l’histogramme. Il y a deux
types d’histogrammes, les histogrammes qui découlent d’une projection sur l’axe vertical et ceux qui
découle d’une projection sur l’axe horizontal d’ou le choix de l’argument ’sens’ dans le prototype, qui
permet d’indiquer dans quel sens on effectue la projection.
Il reste un problème a résoudre , lorsque l’on crée les intervalles de l’histogramme on ne peut repérer
que les lignes ou les caractères, or il nous faut aussi repérer les ensembles de lignes et les mots qui sont
des ensembles de caractère.
Les tickets de caisse ont une particularité, celle que tous les caractères sont rangés dans une grille,
ils sont tous alignés en ligne et en colonne. Alors l’espace qui sépare un mot d’un autre est de taille
minimum d’une largeur de caractère et l’espace qui sépare deux ensembles de lignes est de taille
minimum d’une hauteur de ligne. Ce sont ces deux valeurs qui sont utilisées pour distinguer les blocs
(et qui correspondent a la val_min dans le prototype de l’histogramme). Pour calculer la hauteur
moyenne d’une ligne du ticket , nous allons obtenir l’histogramme de la projection des pixels noirs sur
l’axe vertical, on obtiendra les intervalles (qui représentent les lignes), il nous restera plus qu’à faire
leurs moyennes. On fait de même pour calculer la largeur moyenne d’un caractère , mais l’on effectuera
ce calcul sur chaque ligne puisque, avant sont traitement, puisque le logiciel Unpaper n’effectue pas
un redressement parfais et donc on ne trouvera pas les colonnes , du ticket , correctement. Le fait de
calculer cette valeur sur seulement une ligne entraînera des bugs. Grâce a ces valeurs l’histogramme va
retourner seulement les intervalles qui représentent les ensembles de lignes , ou les mots, et non tous
les intervalles qui représentent des lignes ou des caractères, lors de la segmentation du document en
ensembles de lignes et de la segmentation d’une ligne en mots.
11.3.2
Les Blocs
Libgocr nous impose un certain format d’entrée et de sortie pour chacun des deux modules.
– Le module BlockFinder complète une liste de gocr_Block (défini en globale par la Libgocr) contenant les blocs de mots à partir d’une image préalablement chargée par le module imageLoader.
– Le module CharFinder travaille à partir d’un gocrBlock et ne retourne rien , ce module se
contente de compléter les informations du bloc passé comme argument, il ajoute des gocrBox au
gocr_Block.
A la sortie de blockFinder il faut que nous ayons complété une liste de gocr_Block, cette liste est
initialisée par libgocr, elle doit contenir les blocs qui contiennent les mots. Or durant la segmentation
nous travaillons d’abord sur des ensembles de lignes puis sur des lignes , et chaque ligne contiennent une
liste de blocs de mots. Or les gocr_Block (structure de libgocr qui représente un bloc) ne contiennent
53
pas de liste de gocr_Block. Et la fonction qui crée un nouveau type de gocr_Block (que nous utiliserons
plus tard) ne nous ai pas intéressante puisqu’elle crée un bloc contient forcement un gocr_Block et
cette structure est de taille trop grande pour ce que nous comptons en faire (trop de variable que nous
utiliserons pas et qui prendrons de la place mémoire). Nous utiliserons donc une nouvelle structure
de bloc, qui contiendra uniquement les deux points qui représente notre bloc et la liste des blocs qui
le compose. Quant aux blocs que nous utiliserons pour stocker les mots, ne seront pas n’on plus les
gocr_Block de la libgocr, puisque cette fois-ci ils ne contiennent pas assez de variables qui nous sont
nécessaires. En effet on doit pouvoir savoir à quel ensemble de ligne le mot appartient et aussi à quel
ligne pour le formatage du ticket. Pour créer ce nouveau type de bloc on utilise la fonction de la libgocr
qui permet d’initialiser un nouveau type de bloc gocr_Block avec des variables supplémentaires , et
se seront nos type de bloc qui seront stocké dans la liste des blocs de mots en tant que gocr_Block.
Durant le module de formatage on travaillera sur la liste des blocs de mots, et pour accéder aux données
supplémentaires il nous faut effectuer un cast en notre type de bloc.
11.4
Résumé du déroulement de la segmentation
Dans un premier temps on projette les pixels noirs sur l’axe vertical, de cette projection on obtiendra
un histogramme. De celui-ci nous allons pouvoir trouver la position des ensembles de lignes, pour cela
nous allons étudier les espaces situés entre les lignes et les ensembles de ligne. Si, lors du parcourt de
l’histogramme on rencontre une ligne, on regarde l’espace blanc qui le suit. Si cet espace blanc a une
hauteur plus grande que celle d’une ligne, alors cet espace correspond a un intervalle qui sépare deux
ensembles de lignes. Mais si cet espace est de hauteur plus faible que celle d’une ligne alors il s’agit
d’un intervalle entre deux lignes. Ainsi nous allons trouver les positions des ensembles de lignes.
Pour repérer les lignes nous allons procéder de la même manière sauf qu’à chaque fois que nous
rencontrerons un espace blanc, il s’agira d’un intervalle entre deux lignes, puisque l’on effectuera la
projection seulement sur le blocs et non sur l’image toute entière comme avant. On réitère cette
opération sur tous les ensembles de lignes pour récupérer toutes les lignes. Quant à la segmentation
des lignes en mots et des mots en caractères on procède respectivement de la même manière que la
segmentation du ticket en blocs et de la segmentation des ensembles de lignes en lignes mais avec une
projection des pixels noirs sur l’axe horizontal.(Pour plus de détails sur le fonctionnement voir le test
unitaire sur la segmentation Part 7).
11.5
11.5.1
Complexité
Histogramme
Pour créer un histogramme on parcourt d’abord l’ensemble des pixels de la partie d’image à traiter
en effectuant la projection, ensuite on parcourt le tableau contenant le résultat de la projection. Donc
la complexité est de h2 +h avec h le maximum entre la hauteur et la largeur de la zone. La création
d’un histogramme est d’ordre O(h2 ) ;
11.5.2
Segmentation
On crée tout d’abord un histogramme du document , puis nous parcourons la liste des intervalles
trouvé , c’est à dire le nombre d’ensemble de ligne trouvé, soit m ce nombre. Ensuite nous allons
segmenter ces m ensembles de lignes en ligne ; on créera donc m histogramme, chaque histogramme
comportera un nombre d’intervalle qui correspondront aux lignes trouvées, soit n le nombre de ligne
maximum d’un ensemble de ligne. Puis chaque lignes sera segmenter en mot , pour chaque ligne on
crée un histogramme qui retourne un nombre d’intervalle qui correspond au nombre de mot de la ligne
, soit l le nombre de mots maximum que contient une ligne. Et de même pour segmenter un mot en
caractère, avec c le nombre de caractère maximum que contient un mot. Quand on segmente un bloc
54
en plusieurs bloc on rajoute un bloc à la liste de bloc du bloc que l’on segmente cet ajout est de
complexité linéaire (ajout d’un élément dans une liste) donc négligeable.
En résumé :
– complexité pour segmenter un mot : O(h2 ) = segM ot
– complexité pour segmenter une ligne : O(h2 + l ∗ segM ot) = segLine
– complexité pour segmenter un ensemble de ligne : O(h2 + n ∗ segLine) = segEns
– complexité pour segmenter le document : O(h2 + m ∗ segEns)
complexité totale : O(h2 + m ∗ (h2 + n ∗ (h2 + l ∗ h2 )))
55
Chapitre 12
Reconnaissance de caractères
Pour reconnaître les caractères que nous avons isolés, nous avons décidé de les comparer avec une
liste de modèles. Pour réaliser cette comparaison nous utilisons l’algorithme de calcul de la distance
entre les deux images que nous avons décrit précédemment. Nous allons donc devoir dans un premier
temps créer une banque d’images qui servira de référence pour comparer chaque caractère isolé par le
logiciel.
12.1
Chargement de la liste des modèles
12.1.1
Le représentation physique des modèles
Pour pouvoir reconnaître un caractère nous nous basons sur la comparaison de ce caractère avec une
liste de modèles pré-enregistrés. Les modèles sont stockés dans différents sous répertoires du répertoire
"modeles", où ils sont enregistrés sous forme d’image au format pgm. Toutes les images devant être
utilisées sont recensées dans le fichier "modeles.txt" du répertoire "modeles". Chaque ligne de ce fichier
est composée de deux informations :
1. Le chemin d’accès de l’image à partir du dossier modeles.
2. La représentation hexadécimale, selon le format Unicode, du caractère qu’elle représente.
Par exemple l’entrée correspondante à la lettre majuscule "A" est : "majuscule/A1.pgm 0x0041".
Ainsi "majuscule/A1.pgm" est le chemin d’accès de l’image à partir du répertoire "modele" et la
valeur "0x0041" représente le caractère "A" en format Unicode. La dernière ligne du fichier doit se
terminer par la chaîne de caractères "end".
12.1.2
La structure modèle
Dans notre application chacune des images modèles ou des images des caractères à analyser est
représentée par une instance de la structure Modele. Cette structure permet de stocker toutes les
informations nécessaires au calcul de la distance entre deux images, elle s’organise ainsi :
– char ∗ chemin le chemin d’accès de l’image.
– wchar_t val ; la valeur hexadécimale du caractère.
– intx l’abscisse maximum de la carte des distances.
– inty l’ordonnée maximum.
– int ∗ ∗map la carte des distances de l’image.
– P oint ∗ bary le barycentre de l’image.
On peut initialiser une instance de modèle à partir de deux sources :
– Un nom d’un fichier image et la valeur du caractère qu’il représente.
– Une gocr_Box
56
La création d’une instance de Modèle à partir d’un nom de fichier et d’une valeur est effectuée à
l’aide de la fonction createm odele(char ∗ chemin, wchart valeur);. A partir du nom de fichier et de la
valeur, elle charge l’image et calcule avec la carte des distances ainsi que le barycentre. Pour finir elle
retourne le pointeur du Modèle initialisé. Les champs chemin et val ne sont utilisés que dans le cas
d’un modèle créé à partir d’un nom de fichier et d’une valeur.
La création d’une instance de Modèle à partir d’une gocr_Box se fait à l’aide de la fonction
createm odeleBox(gocrB ox ∗ b), elle effectue exactement les mêmes types d’opérations que la fonction
createm odele mais en les adaptant à une gocr_Box. Cette fonction n’initialise pas le champ chemin
car elle ne nécessite pas le chargement d’un fichier image. Le champ val n’est pas non plus initialisé
car à ce moment nous n’avons pas encore déterminé le caractère que représente cette gocr_Box.
Calcul de la carte des distances
La fonction de calcul de la carte des distances se base sur l’algorithme de la transformée des
distances. La première étape consiste à initialiser la carte des distances, cette opération consiste à
parcourir l’image à l’aide d’une double boucle et pour chaque point noir de l’image on affecte la
valeur 0 à la case correspondante de la carte, pour les autres points on affecte la distance infini que
nous représentons ici par -1. Une fois la carte initialisée on fait appel à la fonction calcmap(int ∗
∗map, int x, int y) qui prend en paramètre la carte initialisée ainsi que ses dimensions. Pendant les
parcours dans les deux sens les voisinages antérieurs et postérieurs de chaque point sont déterminés
respectivement à l’aides des fonctions V m et V p (selon que l’on est dans le parcours en sens normal
ou inverse) qui prennent en paramètre la carte des distances, ses dimensions, le point courant et un
pointeur sur une liste qui contiendra les voisins du points. Ces fonctions n’ajoutent dans la liste que
les voisins utiles lors du calcul, c’est à dire ceux qui n’ont pas une distance infinie et donc qui pourront
avoir un impact sur la distance du point courant.
Calcul du barycentre
Le calcul du barycentre de chaque modèle est effectué par la fonction barycentre(M odele ∗modele)
qui retourne un pointeur sur un point représentant le barycentre.
Problèmes dus à l’algorithme de segmentation utilisé
Notre méthode de reconnaissance de caractère se base sur la comparaison de deux images et comme
nous l’avons expliqué nous utilisons une banque d’images comme modèles. Ces modèles ont une largeur
et une hauteur standard quelque soit les dimensions réelles du caractère. Lors de la segmentation on
isole une gocr_Box de largeur égale à la largeur du caractère. Cette différence de taille nous cause des
problèmes pendant la création de la liste de modèles, et plus précisément lors du calcul de la distance,
car lors de la comparaisons de petits caractères avec un modèles il y aura peu de points communs aux
deux images du fait de la taille de la carte du petit caractère. Or si seule une partie des points est
prise en compte la distance calculée sera toujours inférieure à la distance réelle. Pour éviter d’avoir
une fausse distance nous devons agrandir la taille de la carte pour qu’elle atteigne la taille moyenne
des modèles que nous utilisons.
Cette opération s’imbrique dans la création et l’initialisation de la carte des distance, elle consiste à
calculer la différence entre la largeur moyenne des modèles et celle du caractère et ajouter deux bandes
blanches de chaque coté du caractère pour combler cette différence et obtenir une carte des distances
de taille équivalente à celle des modèles.
12.1.3
La création de la liste de modèles
La liste des modèles est représentée par la structure List fournie par libgocr. La liste des modèles
nommée "list_modeles" est définie dans le fichier my_charRecognizer.c et initialisée à NULL. Lors du
premier appel à la fonction my_charRecognizer par libgocr, on initialise la liste de modèles. Cette
57
(a)
(b)
Fig. 12.1 – Exemple de différence de largeur entre les modèles enregistrés (a) et les caractères obtenus
par segmentation (b) pour i
étape consiste à parcourir le fichier modeles.txt et pour chacune des lignes à créer un nouveau modèle
que nous ajoutons à la liste.
12.2
Calcul de la distance avec chaque caractère
Une fois la liste de modèles chargé en mémoire on peut commencer la reconnaissance de caractères
en elle même. La bibliothèque libgocr nous passe en paramètre une gocr_Box contenant les coordonnées
du caractère à analyser, il faut donc dans un premier temps initialiser les données nécessaires au calcul
de la distance pour ce caractère. Cette partie est effectuée par la fonction createm odeleBox(gocrB ox∗b)
que nous avons vue précédemment. La dernière étape consiste à appeler la fonction calculdist(M odele∗
image, M odele∗m) qui parcoure chaque carte des distances des modeles passés en paramètre et retourne
la distance globale entre les deux images.
12.3
Complexité
Nous allons maintenant voir les compléxités des différentes parties de la reconnaissance de caractères.
Soit h la hauteur de l’image, l la largeur de l’image ,N le nombre de pixels de l’image donc on a
N=l*h, M le nombre de modèles, C le nombre de caractères à analyser.
12.3.1
Création de la liste de modèles
Pour chacun des modèles contenus dans le fichier on doit créer une structure modèle correspondante.
Création et ajout d’un modèle à partir d’un nom de fichier et d’une valeur
Chargement de l’image : comme on a pu le voir dans la partie du chargement de l’image la
compléxité de cette opération est de O(N ).
Allocation de la carte : cette opération consiste à allouer le double tableau d’entier représentant
la carte des distances. Sa complexité est de l’ordre de O(h).
Initialisation de la carte des distances : pendant cette opération on parcourt l’image pour
initialiser les valeurs de la carte, donc sa complexité est en O(N ).
58
Calcul de la carte des distances : une implémentation naïve de l’algorithme chercherait pour
chaque pixel de l’image le plus proche pixel noir en parcourant à nouveau toute l’image, cette solution
impliquerait une complexité en O(N ∗ N ). L’algorithme que nous avons décidé d’implémenter ne
parcours que deux fois l’image et, pour chaque pixel lors de ces parcours, ne s’intéresse qu’à un nombre
fini de voisins dépendant du masque utilisé. Cette méthode nous permet d’obtenir une complexité en
O(N ∗ m) où m est une constante dépendant de la taille du masque utilisée.
Calcul du barycentre : ce calcul ne nécessite qu’un parcours simple de l’image sa complexité est
donc d’ordre de O(N ).
12.3.2
Calcul de la distance
Pour chacun des caractères à analyser on doit créer sa structure modèle et calculer la distance
minimum pour chacun des modèles de la liste.
Création du modèle à partir d’une gocr_Box
Cette partie ressemble beaucoup à la création du modèle à partir d’un nom de fichier, la différence
majeure réside dans le fait que nous n’avons pas à charger une image, mais nous pouvons avoir à
agrandir la carte. Ces deux opérations ont des complexités du même ordre, donc les deux créations de
modèles ont elles aussi le même ordre de complexité.
Comparaison des deux modèles
Cette partie consiste à comparer chaque modèle de la liste avec le caractère à identifier. Pour
chacune de ces comparaisons nous devons calculer la distance entre le modèle de la liste et le caractère à
identifier, cette opération consiste à parcourir chacune des deux images pour comparer leurs différences.
Nous obtenons donc une complexité d’ordre O(N 2 ) pour la comparaison avec un modèles. La complexité
totale du calcul de la distance est donc d’ordreO(M ∗ N 2 ).
12.3.3
En conclusion
Voici les complexités des deux parties décrites plus haut.
– Complexité de la création de liste de modèles : d’ordre O(N ∗ m).
– Complexité du calcul de la distance : d’ordre O(M ∗ N 2 ).
Pour finir Nous avons estimé que la complexité de ce module était d’ordre O(N ∗ m).
59
Sixième partie
Problèmes rencontrés
60
Chapitre 13
Débugage
13.1
13.1.1
Architectures utilisées
Il était une fois linux 64 ...
Pour notre projet de programmation, nous avons utilisé les distributions linux et ordinateurs suivants :
– Debian (32bits) sur ordinateur portable à base de Celeron.
– Debian (32bits) sur ordinateur à base de Core 2 Duo.
– Kubuntu (32bits) sur ordinateur à base de P4 HT.
– Kubuntu (64bits) sur ordinateur portable à base de Turion X2.
Notre projet avançait bien, jusqu’à l’intégration du wrapper pour le chargement des images. Les
problèmes rencontrés sont dûs à la version de la distribution linux en version 64bits. A notre niveau de
connaissance en programmation C, nous n’imaginions même pas l’existence des problèmes de compatibilités en nos sources compilées avec gcc entre des machines standarts et cette machine à problème !
13.1.2
Les conséquences
Après avoir tenté durant plusieurs jours, de corriger nos propres erreurs, et en suivant les traces de
notre chargé de PDP avec Tesseract, nous avons dû abandonner à cause du temps qui commençait à
nous manquer.
Les problèmes rencontrés avec une architecture 64 sont souvent causés par une mauvaise utilisation
des casts ou d’utilisation des pointeurs. Ces erreurs ont provoqué des erreurs de segmentation à répétitions. Notre architecture logiciel est modulaire ; il nous est donc possible de ne pas exécuter certains
modules à conditions qu’il reste des traces exploitables d’un usage précédent (fichiers temporaires).
Nous avons désactivé notre module my_image_Filter qui réalise les pré traitements sur notre image.
De nouvelles erreurs de segmentation apparaissent au niveau de la segmentation. On a alors remanié
notre code sur la machine 64 : nous avons respecté la documentation et la définition des fonctions de la
librairie de gocr. Les résultats sont que la structure List s’initialise mal. Par exemple, avec l’utilisation
de gdb on a pu constater que le nombre d’élément ajouté à notre liste était cent fois supérieurs à la
réalité (603 au lieu de 6). Ce nombre correspond au bloc de texte détecter dans le ticket de caisse. On
a aussi utilisé Valgrind qui s’est bloqué avec le message suivant : VALGRIND INTERNAL ERROR.
Les manipulations apportées ne sont sûrement pas innocentes car au début de notre expérience,
l’utilitaire Valgrind, grâce à sa méthode qui consiste à retarder les erreurs, nous permettait de voir à
la console une exécution potable de notre programme.
La solution finale à ce problème a été de rajouter l’option de compilation à gcc -m32. Cette option
nous permets de compiler notre code en 32 bits et donc le rend compatible avec notre système en 64
61
bits. C’est une solution qui contourne le problème mais qui implique la mise en place d’un chroot32
(voir Optimisation).
Cependant, le développement de libgocr étant arrêté, il n’est pas exclu que cette librairie ne soit
pas pourvue d’erreur sur une compilation en version 64.
13.2
13.2.1
Nos bugs logiciels
Les bugs de Unpaper
Au cours de nos tests, on s’est efforcé de trouver la liste des options universelles qui permettraient
de travailler avec les pires images possibles. Par ce qualificatif, nous parlons d’une numérisation avec
un angle non conventionnel comme les classiques PI et PI/2, et une quantité de bruits non négligeable.
Cependant, nous avons constaté que ce programme qui réalise donc une rotation matricielle de l’image,
ne rend pas un résultat parfait : au niveau du logiciel nous avons un décalage qui peut atteindre environs
1 degré !
Ensuite il est nécessaire d’avoir une mise en page du ticket de caisse présentant une inclinaison sur
la droite. Cette restriction est dûe à notre liste d’option passer à Unpaper. En effet, Unpaper est bien
conçu mais ne reconnaît pas le sens d’inclinaison d’un texte. L’intervention de l’utilisateur doit être
nécessaire pour corriger cela, il doit donner une image incliné avec un angle de moins de 40 degrés (à
la main avec GIMP ou en modifiant le paramètre de l’option de rotation).
Des tests sur des images simples, tel qu’une image droite, posent des problèmes à Unpaper. Ce
dernier provoque une erreur et interrompt notre logiciel. Pour cela, nous avons mis en place une option
permettant de désactiver Unpaper. Ceci pose évidemment un problème majeur : même si la rotation
n’a pas besoin d’être effectuée par Unpaper, l’image contient du bruit. Or les filtres pour l’éliminer
sont appliqués par Unpaper. Il y aura donc un souci si l’utilisateur décide de travailler sur une image
droite et non parfaitement numérisée. Il faudrait donc soit implémenter des filtres nous même au cas
où Unpaper doit être désactivé, ou modifier cela au niveau d’Unpaper.
Les résultats sur une image non travaillée seront donc moins bon, voir médiocre si la qualité de
l’image est mauvaise.
13.2.2
Bugs dûs à la segmentation
Libgocr fourni une fonction qui permet d’obtenir une image en sortie, cette image représente le
ticket de caisse après le prétraitement avec la représentation, par des rectangles, du résultat de la
segmentation. Des rectangles bleus qui entourent chaque mot et des rectangles rouges qui entourent
chaque caractère. Mais il nous affiche aussi des rectangles pleins rouge en haut a gauche du ticket mais
nous ne savons pas pourquoi.
Lors de la segmentation, pour pouvoir distinguer les mots des caractères nous calculons la largeur
moyenne d’un caractère. La valeur obtenue est approximative puisque nous la calculons uniquement
sur la ligne donc elle ne reflète pas vraiment la largeur moyenne d’un caractère la police. Cette valeur
peut être trop petite dans le cas où l’on a plus de caractères peu large que de caractère de largeur
normale. Cela à pour conséquence de mal découper les mots, puisque nous l’utilisons pour comparer
les espaces situés entre les caractères pour, savoir si le caractère est le dernier d’un mot.
Le logiciel rencontrera un problème lorsque plusieurs caractères seront collés, la segmentation ne
les sépareras pas, et donc la reconnaissance ne donnera pas un résultat correct.
13.3
Problèmes restant à corriger
L’utilisation de valgrind nous crée un énorme fichier log, nous permettant de détecter les fuites
mémoires et autres bugs. Nous décrirons ces problèmes de mémoires dans le chapitre ”Optimisation”.
62
(a)
(b)
Fig. 13.1 – Bugs segmentation
63
Chapitre 14
Problèmes rencontrés dans les modules
14.1
Chargement des modules
Cette fonctionnalité de libgocr devait nous permettre de charger nos modules à partir de différentes
bibliothèques dynamiques. Mais lorsque nous chargeons plus d’une bibliothèque seule celle que nous
venons d’ouvrir reste ouverte toutes les autres sont fermés ce qui provoques des erreurs lors de l’appel
des fonctions. Pour résoudre ce problème nous avons dus réunir tous nos modules dans une seule
bibliothèque dynamique. Ce problème interne à libgocr réduit ses possibilités de modularités car nous
ne pouvons charger qu’une seule bibliothèque, nous ne pouvons pas mélanger les fonctions de différentes
bibliothèques comme la documentation l’indiquait.
14.2
Chargement de l’image
Lors de nos premiers essais avec libgocr nous nous sommes rendus compte que la fonction de
chargement des images créait des erreurs avec des images de tailles trop importantes. Nous avons donc
dus coder nous même une nouvelle fonction qui n’était pas prévue initialement.
14.3
Ecriture de l’image
Lors de la création du wrapper pour le module imageFilter nous contions utiliser la fonction permettant d’écrire une image sur le disque fournie par libGOCR, mais nous nous sommes rendus compte
qu’elle n’écrivait que l’entête du fichier. Nous avons donc dû écrire notre propre code.
14.4
Reconnaissance de caractères
Lors de cette partie du traitement nous initialisons une liste de modèles au premier appel de la
fonction my_charRecognizer. La désallocation de cette liste pose plus de problème. Nous ne pouvons
pas la désallouer à la fin de la fonction my_charRecognizer, car on ne peut pas savoir quel est le dernier
caractère à analyser or si nous la désallouons à chaque appel, nous devrons à nouveau la recharger d’où
une complexité beaucoup plus grande. La solution la plus naturelle aurait été d’intégrer la désallocation
dans la fonction closeM odule() de notre bibliothèque. Cette fonction est appelée automatiquement par
libgocr lors de la fermeture de notre module, selon la documentation elle doit nous permettre de libérer
les ressources de notre bibliothèque. Mais lorsque nous avons implémentons cette fonction elle créé une
erreur lors de l’exécution, nous ne pouvons donc pas l’utiliser. Nous avons donc décider d’intégrer la
libérations de la liste dans le module suivant.
64
Septième partie
Tests
65
Chapitre 15
Tests de l’application
Pour effectuer chacun des tests nous avons créé un jeu de différents tickets en faisant plusieurs
paramètres :
– le contraste de l’image.
– image en niveau de gris ou noir et blanc.
– image droite ou avec différentes inclinaisons.
– résolution de l’image.
Ces paramètres nous permettent de tester notre application sur des cas différents.
15.1
15.1.1
Tests unitaires
Chargement et écriture de l’image
Les tests pour ce module ne se contentent que de charger une image et de la ré-enregister sous un
nom différent puis comparer le résultat. Ces tests ont été réalisés successivement avec plusieurs images
différentes en taille et en format, à chaque fois les tailles et les images correspondent aux fichiers
d’origine. Voici un exemple : en entrée une image au format tif et sa sortie au format pbm après avoir
été chargée.
15.1.2
Les prétraitements
Notre module réalisant le prétraitement de l’image reçu grâce au chargement ci dessus, appelle le
programme Unpaper. Le moyen de le tester est donc de faire varier les options passées à ce dernier.
Voici un exemple concret :
Chronologie du traitement : on a besoin d’avoir une image en noir et blanc ; on spécifie le fichier
de sortie, et Unpaper effectue la conversion.
Ensuite il faut éliminer le bruit, cela correspond aux options suivantes : -bt 10 et -li 0.02.
Pour finir, il fut effectuer la rotation. Nous avons limité l’angle de détection de celle-ci à 40 degrés
(à gauche et à droite). C’est à dire que lorsque l’on a testé des tickets ayant une inclinaison supérieure,
la rotation ne s’effectuait pas ou elle est réalisé mais pas dans le bon sens.
De plus, si l’on place une image droite à Unpaper, il y aura un problème. Il n’effectuera pas la
rotation. Pour remédier à ce problème, nous avons opté pour une option permettant de désactiver
Unpaper.
66
(a) Image de base
(b) Sortie
Fig. 15.1 – Exemple d’image chargée puis écrite dans un nouveau fichier
(a) Image de départ
(b) Image en noir et blanc
Fig. 15.2 – Exemple de conversion d’une image en niveaux de gris en image en noir et blanc
67
(a) Image avec bruit
(b) Image sans bruit
Fig. 15.3 – Exemple d’élimination du bruit
(a) Image inclinée
(b) Image après rotation
Fig. 15.4 – Exemple de rotation d’image
68
15.1.3
La segmentation
Il y a quatre étapes lords de la segmentation :
– segmentation du ticket en ensemble de lignes
– segmentation des ensembles de lignes en lignes
– segmentation des lignes en mots
– segmentation des mots en caractères
Nous allons effectuer la segmentation sur le ticket suivant, mais nous n’effectuerons pas la segmentation
sur l’ensemble du ticket car cela prendrait trop de temps.
Fig. 15.5 – Exemple
69
15.1.4
Segmentation du ticket en ensemble de lignes
Avant d’effectuer cette segmentation, il faut déterminer la hauteur minimal de l’espace situé entre
deux ensembles de ligne. Cet espace est de taille minimal d’hauteur d’une ligne. Donc pour déterminer
cette valeur il nous faut calculer la hauteur moyenne de l’ensemble des lignes. Pour ce faire on projette
l’ensemble des pixels du ticket sur l’axe vertical on obtient un histogramme, sur celui-ci on récupère
les hauteurs des lignes , qui correspondent à la largeur des pics de l’histogramme, il nous reste plus
qu’à faire la moyenne et nous obtenons la hauteur moyenne d’une ligne. Maintenant sur ce même
histogramme nous pouvons déterminer les positions des ensembles de lignes en recherchant les espaces
entre les pics de l’histogramme dont la largeur est supérieure a la hauteur moyenne d’une ligne. Chaque
bloc sont repérés par deux points.
Fig. 15.6 – Segmentation en zones
70
15.1.5
Segmentation d’un ensemble de ligne en lignes
Pour segmenter un ensemble de ligne en ligne , il suffit de tracer l’histogramme de la projection
des pixels de l’ensemble sur l’axe vertical et de déterminer la position des lignes en repérant les pics
de l’histogramme, qui correspondent chacun a une ligne. Chaque bloc sont repérés par deux points.
Fig. 15.7 – Segmentation en lignes
15.1.6
Segmentation d’une ligne en mots
Comme pour la segmentation du ticket en ensemble de lignes, il nous faut d’abord calculer la largeur
moyenne d’un caractère, on procède de la même manière que précédemment mais en opérant sur l’axe
horizontal :
– projection des pixels sur l’axe horizontal
– détermination de la largeur moyenne d’un caractère
– repérage des mots ,par celui des espaces entre les pics dont la largeur est supérieure a celle de la
largeur moyenne d’un caractère.
Fig. 15.8 – Segmentation en mots
71
15.1.7
Segmentation des mots en caractères
De même que précédemment on procède de la même manière que la segmentation d’un ensemble
de ligne en lignes mais en travaillant sur l’axe horizontal.
– projection des pixels sur l’axe horizontal
– repérage de la positions des mots en fonction de la position des pics de l’histogramme (chaque
pic représente un caractère)
Fig. 15.9 – Segmentation en caractères
15.1.8
La reconnaissance de caractère
Tests avec des images identiques
Pour vérifier la validité de notre algorithme de reconnaissance de caractère nous devons, pour deux
image identiques, trouver par comparaison une distance de 0. Nous avons donc décider de vérifier la
comparaison de chaque image de modèle qui nous sert de base de donnée avec son sosie dans la liste
de modèles chargée nous donne une distance de 0.
Cette manipulation a été effectué avec succès chaque image était correctement identifiée avec une
distance de 0.
Techniquement nous avons déclaré et initialisé une nouvelle liste de modèle, et pour chaque élément
de cette nouvelle liste nous l’avons comparé avec chaque élément de la liste de modèle standard.
Tests avec des images décalées
Trop facile nous diriez vous de comparer deux image identique ! Poussons le vice un peu plus loin
en mettant à l’épreuve notre algorithme. Nous avons déplacé le caractère dessiné dans l’image des
modèles et nous recommencé la manoeuvre.
Nous avons déclaré et initialisé une nouvelle liste de modèle avec des caractères décalés pour certaines images, nous avons comparé cette nouvelle liste avec chaque élément de la liste de modèle. Les
résultats sont les mêmes les images même les décalées sont correctement reconnues avec une distance
de 0.
72
(a) Modèle de base
(b) Modèle décalé
Fig. 15.10 – Différence entre un modèle normal et décalé
Tests en conditions réelle sur le jeu de tickets
Lors de ces tests nous avons essayés l’algorithme de reconnaissance de caractère sur le jeu de tickets.
Tous les calculs ont étés correctement effectués et il n’y eu aucune erreur pendant ces tests.
Par contre nous nous sommes rendus compte que cet algorithme n’est efficace que sur les tickets
ayant leurs caractères enregistrés dans la banque de données. L’analyse de la précision sera développée
ultérieurement.
15.2
Tests d’intégrations
Ces tests ont pour but de vérifier que tous les modules sont compatibles entre eux. Nous avons testé
les configurations possibles de module, en rajoutant ou en enlevant tour à tour un module. Cependant,
libgocr nous impose des dépendances entre certains modules :
– le module imageLoader qui est nécessaire quelque soit la configuration.
– le module charFinder nécessite le module blockFinder.
– le module charReconizer nécessite le module charFinder.
– le module outputFormater nécessite le module charReconizer.
Voici la liste exhaustive des configurations testées :
– Le module blocFinder seul avec puis sans le module imageFilter.
– Les modules blockfinder et charFinder avec puis sans le module imageFilter.
– Les modules blockfinder, charFinder et charRecognizer avec puis sans le module imageFilter.
– Les modules blockfinder, charFinder, charRecognizer et outputFormater toujours avec puis sans
le module imageFilter.
Tous ces tests se sont avérés positifs, cependant nous ne nous intéressions qu’à la compatibilité entre
les différents modules et non aux résultats obtenus. Il est évident que nous aurions du mal à analyser
73
une image inclinée ou de trop mauvaise qualité. Les tests portant sur les résultats seront traités
ultérieurement.
15.3
Tests de l’application
Pour tester notre application, nous avons choisi d’effectuer des tests en boîte noire. Pour cela, nous
lui passons un ticket en entrée et nous vérifions qu’il affiche bien le résultat.
Voici un exemple pris dans le jeu de tickets utilisé pour les tests :
(a) Affichage 1
(b) Affichage 2
(c) Carte des distances
Fig. 15.11 – Reconnaissance des caractères
74
Tableau récapitulatif des tests
Type d’image
Contraste 75
Noir-Blanc
Droite
600 dpi
Contraste 75
Noir-Blanc
Inclinée
600 dpi
Contraste 100
Noir-Blanc
Droite
300 dpi
Contraste 100
Noir-Blanc
Droite
600 dpi
Contraste 100
Noir-Blanc
Inclinée
300 dpi
Contraste 100
Noir-Blanc
Inclinée
600 dpi
Contraste 150
Noir-Blanc
Droite
600 dpi
Contraste 150
Noir-Blanc
Inclinée
600 dpi
Contraste 100
Niveaux Gris
Droite
600 dpi
Contraste 100
Niveaux Gris
Inclinée
600 dpi
Chargement Image
Rotation
Filtres
Segment blocs
Segment char
Résultats
Taux de réussite
Sortie
OK
Désactivée
Sans Unpaper
Filtre de
base OK
OK
OK
Ok mais
confond les
0 et o
Erreurs : 6+115
tirets
63.1%
OK
OK
OK
OK
OK
OK mais
confond les
0 et o
Erreurs : 6+29
tirets
87.9%
OK
OK
Désactivée
OK
OK
Rien
0%
OK
Désactivée
OK
OK
OK mais
confond les
0 et o
Erreurs : 6+29
tirets
89.3%
OK
OK
OK
OK
Rien
0%
OK
OK
OK
OK
OK
OK mais
confond les
0 et o
Erreurs : 6+14
tirets
93.9%
OK
OK
Désactivée
Sans Unpaper
Filtre de
base OK
OK
OK
OK mais
confond les
0 et o
Erreurs : 18+2
tirets
93.9%
OK
OK
OK
OK
OK
OK mais
confond les
0et o
Erreurs : 8+14
tirets
92%
OK
OK
Désactivée
OK
OK
OK mais
confond les
0et o
Erreurs : 11+34
tirets
86.6%
OK
OK
OK
OK
OK
OK mais
confond les
0 et o
Erreurs : 6+29
tirets
87%
OK
OK mais
enlève une
partie des
tirets
Sans Unpaper
Filtre de
base OK
Sans Unpaper
Filtre de
base OK
Enlève les tirets
et créé des
espacements
étranges
Moyen
Enlève un
bloc
du ticket
Sans Unpaper
Filtre de
base OK
les articles
OK
mais pertes
d’informations
Rien
Caractères trop
petit pour
être identifiés
OK
Rien
Caractères trop
petits pour
être identifiés
Attention, les résultats concernant les Taux de réussite varient pas mal d’un test à l’autre. Cette
variation est surtout due aux erreurs concernant les tirets présents sur le ticket. Ces derniers sont mal
reconnus si le contraste n’est pas assez élevé. Le taux de reconnaissance de lettres ou chiffres varie peu !
Nous avons effectués d’autres tests sur des tickets provenant d’un autre endroit (soucoupe). Dans
un premier temps, aucun test ne marchait. La raison était l’inclinaison de l’image : trop importante
pour Unpaper (>45˚). Après avoir effectué une rotation nous même, nous avons remarqué un taux de
réussite de reconnaissance bien moins important que précédemment.
Nous pouvons expliquer cela par deux choses :
– Tout d’abord, la police de caractère n’est pas la même que nos premiers tickets de caisses. La
comparaison à faire pour chaque caractère est donc faussée.
– La seconde est que l’intervalle entre deux lettres est parfois inexistant. Nos algorithmes considèrent donc qu’il n’y à qu’une seule lettre au lieu de deux. Et par conséquent, nous ne pouvons
les reconnaître. Nous verrons ceci en image dans la partie concernant les bugs.
76
Chapitre 16
Tests comparatifs
16.1
Les challengers
Nous avons choisi de tester le frère ennemi de notre programme : GOCR, qui utilise donc la
même librairie que nous. La deuxième série de comparaison se fait avec SimpleOCR. Le dernier duel
s’effectuera avec l’un des leaders des OCR commerciaux : ABBYY FineReader 8.0 PRO.
Nous avons donc trois OCR à tester ; étant donné le nombre, nous limiterons le nombre d’image à
tester aux images nous permettant d’obtenir les meilleurs résultats avec notre programme.
Ces images ont les critères suivants :
– Une résolution de 600 dpi.
– Des images droites ou avec une inclinaison inférieure à 40 degrés.
– Un contraste de 100.
Les critères des tests ne sont pas la vitesse d’exécution, mais le résultat final de chaque exécution.
On calculera alors le pourcentage de réussite de la reconnaissance.
16.2
Les résultats
Le ticket original contient 252 caractères (+ 60 tirets). Enfin, notre ticket de référence contient
une moyenne de 246 caractères reconnus et à leur place relative par rapport à l’image originale. Notre
programme à donc un pourcentage moyen de réussite de 97% dans le cas optimal. Par la suite, nous
mettons en évidence, grâce aux captures d’écran, les performances des programmes.
16.2.1
GOCR [2]
Nous avons compilé les dernières sources disponibles de GOCR (la version 0.44) qui est paru début
mars 2007. Nous lui avons converti des images tiff au format pgm, car le module de chargement de
GOCR n’a pas été conçu pour ce format. Nous avons remarqué qu’il était capable de faire une rotation
sur des ticket inclinés, mais les résultats obtenus sont très médiocres. GOCR n’a pas de module de
correction de contexte. Enfin, GOCR conserve partiellement la mise en page du ticket. Le nombre de
caractères reconnus et à la bonne place est de 39, soit un pourcentage de réussite de 7,7%.
77
Fig. 16.1 – Capture sous Linux de GOCR
16.2.2
SimpleOCR [4]
Ce logiciel est un freeware. Au cours de nos essais, nous avons remarqué qu’il ne prenait pas
en charge les images inclinées. Les résultats sont inexploitables. Avec une image droite et propre, la
reconnaissance est très mauvaise (voir capture ci dessous).
De plus, SimpleOCR est vraiment trop simple, car même s’il dispose d’un mode de correction
de contexte, le choix proposé ne contient pas le mot d’origine. Cela implique donc une intervention
importante de l’utilisateur. Dernier point, la mise en page du ticket n’est pas conservé.
Le nombre de caractères reconnus et à la bonne place est de 39, soit un pourcentage de réussite de
15,4%.
16.2.3
FineReader Pro [1]
C’est donc un logiciel commercial. Au premier essai, nous étions stupéfaits. Même si la vitesse
de traitement n’est pas prise en compte, on doit quand même reconnaître qu’il est très efficace. De
nombreuses options sont disponibles.
FineReader est ”le” logiciel de reconnaissance de caractère ; par cela nous voulons écrire tout simplement que le résultat est irréprochable, on atteint les 100% de réussite avec un ticket droit. Par
contre, un ticket incliné donnera un résultat opposé, soit 0%. Il n’existe donc pas de vrai module de
rotation automatique (hormis la rotation classique de 90 degrés par l’intervention de l’utilisateur).
Au final, nous avons alors un pourcentage moyen de réussite de 50%. Il est possible que le logiciel
réagisse mieux avec des images moins inclinées, mais par souci d’équité et de pertinence de résultat
vis-à-vis des autres logiciels, nous n’essayerons pas avec une image moins inclinée.
78
Fig. 16.2 – Capture sous Windows de SimpleOCR
Fig. 16.3 – Capture sous Windows de FineReader PRO
79
16.3
Ce que nous retiendrons
En conclusion de ces tests, nous pouvons dire que la solution commerciale fait bien mieux que nous
pour des tickets droits. SimpleOCR n’est pas du tout adapté à notre domaine d’utilisation. GOCR a
du potentiel, mais ne parvient pas à faire mieux. La grande faiblesse de tous les OCR que nous avons
testé est la reconnaissance sur document incliné, c’est le seul endroit où nous sommes les plus précis.
80
Huitième partie
Extensions possibles
81
Chapitre 17
Extensions des modules
17.1
17.1.1
Chargement d’image
Formats d’image
La bibliothèque que nous utilisons pour le chargement d’image est libnetpbm, elle est fournie avec
libgocr et est relativement ancienne. Elle ne permet que de charger les images de type pbm et pgm,
pour les autres types nous devons d’abord les convertir à l’aide d’appels systems. Cette solution est
assez contraignante. L’intégration d’une nouvelle bibliothèque pour le chargement d’image pourrait
permettre de charger plus de types d’images sans avoir recours à cet appel system.
17.2
17.2.1
Segmentation
Segmentation en mot
Améliorer la segmentation en mot en affinant la détection des espaces séparant chaque mot ; cet
affinement peut être effectué en calculant la largeur moyenne d’un caractère sur l’ensemble du ticket
ou par un apprentissage qui permettrait de connaître la largeur maximale d’un caractère de la police
utilisé par le ticket.
17.2.2
Segmentation en caractère
Détecter si plusieurs caractères se situe dans un seul un box de caractère, grâce a la largeur moyenne
d’un caractère et la largeur du box. Si on trouve une gocr_Box contenant une image plus large qu’un
caractère, on pourrait tracer l’histogramme de la projection des pixels noir sur l’axe horizontal , et de
celui-ci séparer les caractères collés.
17.3
17.3.1
Reconnaissance de caractères
Seuil de reconnaissance
Une modification intéressante pourrait être le calcul d’un seuil à partir duquel nous déclarons ne pas
reconnaître un caractère. Pour l’instant quelque soit la distance, nous affectons toujours le caractère
ayant la distance minimale avec le caractère isolé. Mais nous pourrions essayer de calculer un seuil qui,
si la distance finale lui est supérieure, permettrait d’affirmer que ce caractère ne correspond à aucun
modèle de la liste.
82
17.3.2
Apprentissage de nouveaux caractères
Dans le cas où un caractère ne correspondrait à aucun des modèles, nous pourrions ajouter ce
nouveau caractère à la liste. Cet ajout consisterait dans un premier temps à demander à l’utilisateur
d’identifier le caractère. Puis avec les informations recueillies nous enregistrerions ce nouveau caractère
comme modèle. Cette opération nécessiterait d’ajouter son image dans le dossier modeles et de mettre
à jour le fichier modeles.txt.
17.3.3
Nouveaux algorithmes de reconnaissances
Nous avons décrits un autre type d’algorithme de reconnaissance de caractères se basant sur l’extraction de paramètres du caractère, il pourrait être intéressant de l’implémenter pour comparer les
performances entre la méthode que nous utilisons et ce nouvel algorithme.
17.4
Apprentissage
Nous n’avons pas eu le temps d’implémenter la phase d’apprentissage que nous avions défini dans le
cahier des charges. L’ajout de l’apprentissage permettrait d’augmenter les performances de reconnaissance en ajoutant des caractères dans les modèles comme vu précédemment, mais aussi, de connaître
la structure du ticket pour la segmentation.
83
Chapitre 18
Extensions de l’application
18.1
18.1.1
Optimisation
Temps
D’après nos tests de numérisation d’un ticket de caisse : nous avons paramétré notre logiciel d’acquisition pour une résolution de 600 dpi (pixel par pouce), soit une résolution très grande, qui nous a
permi un travail précis. On suppose aussi que le ticket n’est représenté que d’une seule image.
La quantité de pixel à traiter est très grande et suivant les modules appliqués, les calculs peuvent
prendre du temps avant de passer la main (utilisation de Unpaper par exemple, segmentation ...).
18.1.2
Mémoire
Le temps nous a manqué pour pouvoir corriger les fuites mémoires qui ne sont pas négligeable.
L’utilisation de valgrind, et la vérification des sources au niveau de la gestion mémoire comme les
allocation dynamiques et leur libérations devront être réalisés pour continuer le développement dans
de bonne condition. Ces erreurs corrigées, nous permettraient de pouvoir adapter notre travail sur un
linux en version 64 bits.
18.2
La notion de Multiplateformes
Notre client n’est pas un spécialiste de l’image et comme nous venons de le voir, le temps d’exécution
n’a pas vraiment d’importance. Il est donc nécessaire que l’application soit efficace avec un minimum
d’interaction (pour ne pas augmenter le temps de fonctionnement). L’envoie d’un fichier image via un
protocole Internet de liaison ; un traitement de l’information par notre programme relié à une interface
web (en PHP par exemple). Toutes ces informations seraient stockées sur le serveur. Un traitement
plus poussé et simultané des résultats de notre logiciel permettrait de renvoyer le résultat par email
ou de le mettre a disposition sur Internet en les stockant dans une base de donnée de type SQL.
L’énorme avantage que nous aurions serait, que tout ordinateur muni d’une connexion Internet,
pourrait accéder à notre travail de traitement de ticket de caisse et récupérer ses article pour différents
usages :
– Permettre à plusieurs utilisateur de comparer leurs courses et trouver l’enseigne la plus intéressante.
– Faire des statistiques sur l’évolution du cours de la vie.
– Faire une estimation de ces dépenses de façon plus précise et donc de prévoir son budget.
84
Neuvième partie
Bilan
85
Chapitre 19
La fin de l’aventure
19.1
Bilan sur les objectifs
Nous pouvons dire que nous avons réussi à atteindre la plus grande partie des objectifs demandés
par le client. Le planning initial a été, dans l’ensemble, correctement respecté, nous avons réalisé la
majeure partie des objectifs que nous nous étions fixés. Les résultats sont plutôt encourageant, mais
il faut préciser que cette réussite n’est valable que pour un certain type de tickets de caisses. Nos
algorithmes ne sont pas encore capables de s’adapter d’eux même à tous les types de ticket, il faut
aux préalable, passer par une phase d’apprentissage manuelle. Nous n’avons pas pu coder le module
de post-traitement qui est l’étape finale du projet en préférant finir les modules précédents.
19.2
Gestion du projet
Ce projet nous a permis de découvrir de nouveaux aspects du développement logiciel. Nous avons
non seulement dû développer nos propres parties mais surtout les adapter à une librairie existante. Nous
nous sommes rendus compte, que la réutilisation de code déjà existant n’est pas une simple opération.
Elle implique un certain nombre de problèmes et de phases de tests. Nous avons dû apprendre à
adapter notre planning en fonction des problèmes rencontrés. Nous avons dû également faire des choix
au niveau des possibilités de notre logiciel, en privilégiant des solutions simples tout en étant certain
de pouvoir les implémenter de façon satisfaisante.
Au niveau de la répartition des tâches, nous avons sous-estimé l’importance de l’imprévu dans le développement. Nous nous étions réservés une semaine dans le planning pour nous permettre d’assembler
tous nos modules et nous laisser le temps de régler tous les problèmes en découlant. Malheureusement,
cette semaine a été trop courte pour nous permettre de tous régler comme nous le désirions, mais il
clair que sans elle nous n’aurions pas pus obtenir un résultat fonctionnel.
Ce projet nous a montré la difficulté du travail en équipe, notamment lors du choix de l’implémentation où chacun a sa propre idée du logiciel.
19.3
De nouveaux outils de développement...
Nous nous sommes aussi très vite rendus compte de l’importance d’avoir une bonne plateforme de
développement pour pouvoir faire un travail collaboratif efficace. Au début de notre projet, nous ne
nous servions ni d’outils collaboratifs, ni d’outils de débugage, puis au fur à mesure de l’avancement
du projet, ils se sont très vite rendus indispensable.
L’une des plus grandes méthodes acquise lors de ce projet est l’utilisation systématique de ces
outils. Nous n’avions jusqu’à présent développé que des projets où leur utilisation n’était pas requise,
nous avons donc dû nous former rapidement pour nous en servir de manière efficace.
86
Bibliographie
[1] Finereader pro. Internetaddress : http : //www.abbyy.com/f inereader8/?param = 44890, Apr
2007.
[2] Gocr. Internetaddress : http : //jocr.sourcef orge.net/, Apr 2007.
[3] Libnetpbm. Internetaddress : http : //netpbm.sourcef orge.net/doc/libnetpbm.html, Apr 2007.
[4] Simpleocr. Internetaddress : http : //www.simpleocr.com/, Apr 2007.
[5] H.S. Baird. The skew angle of printed documents. In SPSE’s 4th Annual Conference and Symposium on hybrid Imagig Systems,, pages 21–24, New York.
[6] Abdel Belaïd and Yolande Belaïd. Reconnaissance des formes, chapter 6. InterEditions, Paris,
1992.
[7] Jens Gulden. Unpaper. Internetaddress : http : //unpaper.berlios.de/, Apr 2007.
[8] Susan Haigh. La reconnaissance optique de caractères (roc) en tant que technologie de numérisation. Technical report, Services de technologie de l’information Bibliothèque nationale du Canada,
1996.
[9] H.ZEN and S. Ozawa. Procedings of international conference on computer vision and pattern
recognition (cvpr). In Mixed Mode Manuscript., pages 544–549.
[10] R.G. Casey K.Y. Wong and F.M. Wahl. Document analysis system. IBM Journal Research
Development,, 26(6) :647–656.
[11] Henri MAITRE.
Filtres de débruitage.
Internetaddress
//perso.enst.f r/ maitre/BET I/f iltresl inn lin/perf.html, Feb 2007.
[12] Christian RONSE.
Le filtre médian.
Internetaddress
strasbg.f r/ ronse/T IDOC/F ILT ER/median.html, Feb 2007.
:
http
:
:
http
:
//arthur.u −
[13] Christian RONSE.
Transformée de distance.
Internetaddress : http : //arthur.u −
strasbg.f r/ ronse/T IDOC/GD/tf d.html, Feb 2007.
[14] J.P. Trincklin. Conception d’un système d’analyse de documents, étude et réalisation d’un module
d’extraction de la structure physiqye de documents à support visuel. PhD thesis, Thèse de Docteur
Ingénieur, Université de Franche-Compté, 1984.
[15] Wikipedia.
Résolution
numérique.
Internetaddress
//f r.wikipedia.org/wiki/R%C3%A9solutionn um%C3%A9rique, Feb 2007.
87
:
http
: