Utilisation avancée de Tesseract avec Python 3



Dans un article précédent (cliquez ici) nous avons vu comment installer et utiliser tesseract dans des exemples simples. Malheureusement si Tesseract reste une bonne solution (gratuite qui plus est) elle a ses limites. Certaines peuvent être remédiées via certaines configurations ou pré-traitement, d’autres non !

Limites de Tesseract

Voici quelques limitations de Tesseract :

  • Évidemment Tesseract n’est pas aussi précis que certaines solutions commerciales (de type ABBYY par exemple)
  • Malheureusement Tesseract n’est pas capable de reconnaître l’écriture manuscrite.
  • Tesseract ne fonctionne pas vraiment bien avec les images qui ont subit certaines modifications (arrière-plan complexe ou brouillé, lignes, occlusion partielle, déformation, etc.)
  • Il peut parfois renvoyer du charabia (faux positifs)
  • Il est sensible à la langue précisée en argument. En gros, Si un document contient des langues autres que celles indiquées dans les arguments -l LANG, les résultats peuvent être vraiment très mauvais !
  • tesseract analyse dans l’ordre de lecture naturel des documents, ce qui n’est pas toujours la bonne méthode. Par exemple, il peut ne pas reconnaître qu’un document contient plusieurs colonnes et peut essayer de joindre le texte de ces colonnes comme une seule ligne.
  • Il a du mal à interpréter des numérisations de mauvaise qualité.
  • Il n’expose pas d’informations/métadonnées sur le texte (comme la police).

Nous allons voir dans cet article comment s’affranchir au mieux de certaines de ces limites.

Visualisons déjà ce qui est reconnu par Tesseract

Pour commencer il peut être très utile de pouvoir voir ce que tesseract a interprété. Que diriez-vous d’afficher l’image avec en surlignage les zones détectées et interprétées comme du texte ?

Pour cela nous allons utiliser une librairie très puissante et très utilisées dans la « Vision par Ordinateur » : OpenCV.

try:
    from PIL import Image
except ImportError:
    import Image
import pytesseract
from pytesseract import Output
import cv2

simage = r'/[Path to image...]/image_2.png'
img = cv2.imread(simage)
d = pytesseract.image_to_data(img, output_type=Output.DICT)

NbBoites = len(d['level'])
print ("Nombre de boites: " + str(NbBoites))
for i in range(NbBoites):
    # Récupère les coordonnées de chaque boite
    (x, y, w, h) = (d['left'][i], d['top'][i], d['width'][i], d['height'][i])
    # Affiche un rectangle
    cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)

cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Cette petite routine va afficher l’image originale. En se basant sur les cadres de texte détectés par Tesseract (récupéré via la méthode image_to_data), on va lui superposer des cadres verts sur les mots détectés. Un nouvel écran s’affiche :

Note: Pour fermer l’écran appuyez juste sur une touche du clavier. si vous voulez afficher la fenetre pendant un temps limité (en ms) changez la valeur de cv2.waitKey([temps en ms]).

Cette façon de visualiser les résultats de Tesseract est très pratique et pour être utiliser pour mieux comprendre le comportement de cet outil.

On peut aussi utiliser la librairie Python matplotlib pour visualiser les résultats :

from matplotlib import pyplot as plt
plt.imshow(img)

Retrouvez les sources complets (Notebook Jupyter) ici.

Modification de l’image source (pre-processing)

C’est l’un des principal défaut de Tesseract: si l’image n’est pas vraiment parfaite, celle-ci nécessitera certainement un pré-traitement avant de pouvoir la soumettre à l’OCR. Heureusement nous avons plusieurs librairies magiques à notre disposition, dont OpenCV que nous avons déjà utilisé au préalable.

Sur internet j’ai trouvé quelques petites fonctions bien utiles qui encapsulent cette librairie :

# grayscale
def grayscale(image):
    return cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# noise removal
def remove_noise(image):
    return cv2.medianBlur(image,5)
 
# thresholding
def thresholding(image):
    return cv2.threshold(image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

# dilation
def dilate(image):
    kernel = np.ones((5,5),np.uint8)
    return cv2.dilate(image, kernel, iterations = 1)
    
# erosion
def erode(image):
    kernel = np.ones((5,5),np.uint8)
    return cv2.erode(image, kernel, iterations = 1)

# opening - erosion followed by dilation
def opening(image):
    kernel = np.ones((5,5),np.uint8)
    return cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel)

# canny edge detection
def canny(image):
    return cv2.Canny(image, 100, 200)

# skew correction
def deskew(image):
    coords = np.column_stack(np.where(image > 0))
    angle = cv2.minAreaRect(coords)[-1]
    if angle < -45:
        angle = -(90 + angle)
    else:
        angle = -angle
    (h, w) = image.shape[:2]
    center = (w // 2, h // 2)
    M = cv2.getRotationMatrix2D(center, angle, 1.0)
    rotated = cv2.warpAffine(image, M, (w, h), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)
    return rotated

# template matching
def match_template(image, template):
    return cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED) 

Nous allons maintenant les utiliser pour retravailler nos images qui ont quelques défauts bien sur :

Si nous utilisons tesseract directement sur cette image, nous obtenons un résultat plutôt faux:

simage = r'/home/benoit/git/python_tutos/tesseract/image_4.png'
image_original = cv2.imread(simage)
print(pytesseract.image_to_string(image_original, lang='fra'))
plt.imshow(image_original,'gray')
jaur ceci est un test

En retirant les « bruits » de l’image on a quelque chose de bien mieux :

retouche3 = remove_noise(image_original)
print(pytesseract.image_to_string(retouche3, lang='fra'))
plt.imshow(retouche3)

Faisons un autre essai avec une image encore plus brouillée (notament en rajoutant un trait transversal sur le texte) :

Ce qui est intéressant ici c’est que tesseract ne détecte rien du tout : aucun texte !!!

Essayons de réduire le bruit sur l’image comme précédemment :

retouche3 = remove_noise(image_original)
print(pytesseract.image_to_string(retouche3, lang='fra'))
plt.imshow(retouche3)

Nous obtenons un résultat certes mieux mais incorrect :

Bonjour cect est un test

Si maintenant on joue sur l’atténuation et la mise en niveau de gris :

retouche4 = thresholding(grayscale(remove_noise(image_original)))
print(pytesseract.image_to_string(retouche4, lang='fra'))
plt.imshow(retouche4,'gray')

Le résultat devient alors correct …

Et si vous avez des fichiers pdf à traiter ?

C’est en effet un cas bien réel. Les fichiers à traiter ne sont pas au format image (png, jpg, etc.). Bien souvent vous avez des fichiers de type pdf à traiter, et manque de chance Tesseract ne sait pas directement les traiter ! encore une fois nous allons devoir faire un pré-traitement ou plus précisément une conversion afin de convertir notre fichier pdf dans un format image que tesseract pourra gérer. Attention car une légère nuance va s’ajouter : la pagination. En effet un pdf est bien souvent sur plusieurs pages alors qu’une image elle non. Il faudra donc bien prendre en compte cette nuance.

Heureusement Python possède un écosystème de librairie vraiment incroyable. Pour ce qui est de la conversion PDF -> PNG/JPG je vous suggère d’utiliser pyPI/pdf2image.

Pour celà l’installation se fait en deux étapes :

installation de la librairie Python :

pip install pdf2image

Installation de l’utilitaire de conversion (binaire). sur Linux c’est plutot simple (Ubuntu) :

$ sudo apt-get install poppler-utils

Sur windows c’est un peu plus complexe:

  1. Téléchargez tout d’abord le dernier binaire sur le site de Poppler http://blog.alivate.com.au/poppler-windows/
  2. Dézippez les fichiers avec les répertoires
  3. AJoutez une entrée dans votre PATH (plus particulièrement le répertoire bin dézippé).

Cela fait vous pouvez utiliser la librairie et en quelques lignes convertir votre fichier PDF :

from pdf2image import convert_from_path, convert_from_bytes
try:
    from PIL import Image
except ImportError:
    import Image
import pytesseract
from pytesseract import Output
images = convert_from_path('Facture.pdf')
print ("Nombre de pages: " + str(len(images)))
for i in range (len(images)):
    print ("Page N°" + str(i+1) + "\n")
    print(pytesseract.image_to_string(images[i]))

Cette portion de code va effectuer une interprétation de chaque page de votre pdf.

Conclusion

Comme nous l’avons vu en introduction si Tesseract est un outil performant et surtout gratuit, son défaut est qu’il ne peut pas tout gérer de manière simple. Comme bien souvent pour les projets Open Source et gratuit on peut faire beaucoup de choses mais moyennant des efforts réels d’intégration. Restent néanmoins plusieurs limites qui peuvent devenir vite contraignantes comme l’interprétation d’écriture manuscrite. Mais ceci est une autre histoire et pour cela il existe bien entendu d’autres solutions que nous tâcherons d’aborder ultérieurement.

Comme d’habitude vous trouverez les sources de cet article ici.



A propos de Benoit Cayla

En plus d'être un spécialiste des données et processus d'entreprise, j'aime passer le plus clair de mon temps à l'extérieur, à jardiner par exemple ou simplement à aller dans le bois avec mes enfants. J'adore aussi le VTT que je pratique chaque semaine avec mes amis. Quand je dois rester à l'intérieur, j'aime améliorer ma maison. Bricoler me permet ainsi de libérer ma créativité d'ingénieur. Et, si je n'ai rien à réparer ou créer, je passe mon temps libre à explorer les dernières avancées technologiques dans le monde de l'IA et du RPA.

Laissez un commentaire

Votre adresse de messagerie ne sera pas publiée.

trois × un =

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.

3 commentaires sur “Utilisation avancée de Tesseract avec Python