PiTextReader est un projet sous licence libre (GPL V3.0) de l’américain Russell Grokett qui utilise un micro-ordinateur Raspberry Pi3 pour convertir du texte imprimé en parole.

L’ambition du lecteur est de permettre aux personnes malvoyantes – dans le cas de Russell Grokett, il s’agissait d’un parent atteint de dégénérescence maculaire – de déclencher la lecture d’un texte imprimé. En permettant de transcrire phonétiquement un texte, ce projet offre la possibilité aux non-voyants ou malvoyants d’accéder à des documents de natures diverses. Il y a aussi un intérêt pour les personnes ayant des difficultés de lecture ou en apprentissage de la lecture. Enfin, le dispositif peut être utilisé dans le cadre de l’apprentissage des langues étrangères.

Il a été conçu pour être aussi bon marché (moins de 100 €) et aussi simple que possible : il n’a pas besoin d’une connexion internet et il n’a pas d’interface graphique. Il n’y a qu’un seul bouton. Il suffit donc de placer le document à lire sur le support, d’appuyer sur le bouton et, en quelques secondes, PiTextReader commence à lire à haute voix le texte, par l’intermédiaire d’un haut-parleur ou d’un casque. Son auteur admet cependant qu’il présente quelques inconvénients : la reconnaissance optique des caractères (OCR) peut échouer plus ou moins selon la police, la taille du texte, les couleurs et le contraste, et le rendu de la synthèse vocale fait très robotique. Sur ce dernier point, la version française que je propose est quelque peu améliorée grâce au module TTS (Text to Speech) Pico2wave, créé à l’origine par SVOX, qui offre une qualité de parole supérieure à la plupart des solutions open source.

Matériels :

  • Raspberry Pi3 Carte micro SD 8GB min
  • Alimentation pour Raspberry
  • Raspberry Pi Camera V2 Nappe 30 ou 50cm pour la caméra Outils d’ajustement de l’objectif de la caméra imprimable grâce à jwaldron232
  • Casque ou Haut-parleur amplifié avec fiche stéréo standard de 3,5 mm
  • Bouton poussoir avec LED intégrée Résistance de 330 ohm (en fonction du type de LED)
  • des câbles Dupont mâle/femelle
  • quelques mètres de filaments PLA pour imprimer un stand et un bras pour la caméra*
  • Ecrous, boulons et rondelles (3mm de diamètre) pour le bras de la caméra
PiTextReader


*Rien ne vous empêche bien sûr de prendre une caisse en bois comme R. Grokett. Attention à prévoir suffisamment de hauteur pour le bras caméra qui, s’il n’a pas d’articulation, offrira moins de souplesse en terme de cadrage.

Support 3D (Prototype)

Concernant le support 3D, afin d’aboutir rapidement à un prototype fonctionnel, j’ai combiné plusieurs design 3D en licence libre dont le support de caméra qui est l’œuvre de Scruffy-3D associé à l’extension de Suadanwar. Problème : les éléments sont sous licence Creative Commons by-nc-nd/4.0. La mention ND permet la réutilisation/modifications mais pas la diffusion. Je ne peux donc pas partager le fichier 3d avec le stand mais il est assez facile d’en réaliser un, par exemple, avec Tinkercad. J’ai, de toute façon, l’intention de modéliser un chassis plus stable avec certainement quelques améliorations comme l’intégration d’un HP. Je partagerai alors sous une licence réellement libre…

Branchements

Branchez le module caméra pendant que le Raspberry Pi est éteint dans le port dédié (CSI). La nappe doit être insérée sans la plier dans le connecteur en position ouverte, connecteur qu’il faut ensuite refermer pour verrouiller la nappe.

Le bouton est à brancher sur la prise GPIO* 24 et GPIO 18 pour la partie LED. les deux câbles restants iront à la terre (GND). Vous devrez souder les fils aux connecteurs du bouton. Pour limiter le courant arrivant sur la LED, il faut mettre une résistance, (pour ma petite LED, 220 ohm suffisent) entre le GPIO 18 et la LED (Cf. schéma de R. Grokett). Si vous n’avez pas de bouton avec LED intégré, vous pouvez bien-sûr utiliser une LED déportée. C’est ce que j’ai fait sur mon prototype avec un orifice dédié dans le support de bouton.

*Les ports GPIO sont des ports physiques, en l’occurrence sur notre Raspberry ce sont des picots métalliques carrés qui permettent de transmettre un signal électrique binaire en 3.3 V et environ 20 mA à l’aide de câbles Dupont mâle/femelle.

Fixez le Raspberry sur le stand sur lequel est monté le bras de la caméra. Prévoyez, en plus des écrous, des rondelles qui permettront de maintenir l’articulation tout en assurant sa mobilité. Branchez enfin un clavier, une souris, un écran avec le port HDMI et un câble Ethernet (ou connectez-vous en wifi). Ces périphériques ne sont nécessaires que pour l’installation puisque le projet final n’a ni interface, ni écran, ni connexion internet.

Installation

Insérez la carte SD sur laquelle vous aurez au préalable installé Raspberry Pi OS : https://raspberry-pi.fr/telechargements/

Ouvrez un terminal et saisissez les lignes suivantes :

$ sudo apt install git –y

Installez le logiciel de R. Grokett :

$ cd /home/pi
$ git clone https://github.com/rgrokett/PiTextReader.git
$ cd PiTextReader
$ sh install.sh

Tous les fichiers nécessaires à ce projet sont dans le répertoire PiTextReader. Pensez-donc, en ligne de commande, à bien vous placer dans ce répertoire (cd PiTextReader). « Install.sh » installe le module OCR Tesseract ainsi que le programme « cronfile » qui permet de planifier des tâches au démarrage. Ce dernier va ainsi lancer au démarrage le programme de la machine à lire : « pitextreader.py » de manière autonome. Plus besoin de clavier, ni souris, ni écran !

Pensez à relancer « install.sh » à chaque fois que vous modifiez le fichier « pitextreader.py ». Autrement cela fonctionnera en ligne de commande mais pas au redémarrage en mode automatique car le fichier « pitextreader.py » doit être rechargé dans le CronTab. Notez aussi que si vous modifiez le fichier « pitextreader.py » en ligne de commande, il y a des chances que le processus soit déjà lancé au démarrage avec le cronfile. Pour éviter les processus doublons, vous pouvez arrêter tous les scripts Python avec la commande « killall python » tandis que la combinaison <CTRL+C> vous permet d’arrêter un processus en cours, par exemple, « pitextreader.py ».

Mise au point

La commande suivante va lancer une prise de vue continue qui vous permettra de régler manuellement la mise au point, soit à l’aide de l’outil livré avec la caméra, soit avec celui que vous imprimerez grâce à jwaldron232 :

 $ raspistill -t 0 

La combinaison de touches <CTRL+C> vous permet d’arrêter le processus. Un quart de tour à peine devrait suffire à sortir du réglage d’usine, qui privilégie l’hyperfocale (environ 50cm à l’infini), alors que l’on aurait besoin d’une mise au point vers 14-18 cm. Une fois la caméra réglée, vous pouvez d’ores et déjà, après avoir placé un texte (quelques lignes de texte noir sur fond blanc pour tester rapidement) lancer en ligne de commande le fichier pitextreader.py. Si le texte est bien lu, félicitations, vous avez reproduit le projet pitextreader de R.Grokett, une machine à lire autonome en version anglaise (Moteur TTS Flite) ! Vous pouvez redémarrer pour vous assurer que le service « Cron » fonctionne bien :

$ sudo reboot 

Pour avoir un lecteur en langue française, j’ai dû faire quelques modifications que je vais expliquer ci-dessous. Vous pouvez télécharger les fichiers pitextreader.py et txtpico.sh sur ce fork* Github :

https://github.com/mediaenlab/PiTextReader

Il faudra donc remplacer le fichier pitextreader.py et ajouter le fichier txtpico.sh dans le répertoire PiTextReader.

*Un fork est une copie d’un dépôt. Forker un dépôt vous permet d’expérimenter librement des modifications sans toucher au projet original. Plus de détails ici.

Options de caméra

Concernant la caméra, beaucoup d’options de rendu sont disponibles via la commande « raspistill ». Sur ce point, après différents tests visant à améliorer la vitesse de détection du module OCR, j’ai finalement gardé le code original car si, dans certaines conditions de lumières basses, on gagne effectivement quelques secondes avec « -drc high », ou en éclaircissant avec un « -brightness 80 », on les perd dès que la lumière change… (https://www.raspberrypi.org/documentation/raspbian/applications/camera.md)

CAMERA  = "/usr/bin/raspistill -cfx 128:128 -awb auto -t 500 -o /tmp/image.jpg"

Pour information, “cfx 128 :128“ convertit l’image en niveau de gris, “awb“ opère une balance des blancs automatique et “–t 500“ déclenche la photo après une demi seconde. Dans mon cas, j’ai simplement enlevé le « -rot 180 » qui supprime la rotation de l’image de manière à pouvoir lire un document posé sur un support incliné. Il suffit donc de remettre cette commande pour inverser la lecture.

TTS Version FR

Vous trouverez quelques informations en français sur les logiciels de synthèse vocale libres sur cette page de la communauté Ubuntu. On y apprend d’ailleurs que Svoxpico, une autre dénomination pour Pico2wave, est disponible aussi sur LibreOffice via une extension ! Le moteur TTS Pico se trouve dans les dépôts Open Source d’Android. (Licence Apache 2.0) : https://pypi.org.project/ttspico/ Ce module nécessite l’installation du paquet « libttspico-utils » qui n’est plus disponible dans les dépôts officiels de notre OS Raspbian Buster mais que l’on trouve sur ceux de Debian. Merci à Jerrm pour les 3 lignes ci-dessous qui vous permettront d’installer Pico2wave :

wget http://ftp.us.debian.org/debian/pool/non-free/s/svox/libttspico0_1.0+git20130326-9_armhf.deb 
wget http://ftp.us.debian.org/debian/pool/non-free/s/svox/libttspico-utils_1.0+git20130326-9_armhf.deb
sudo apt-get install -f ./libttspico0_1.0+git20130326-9_armhf.deb ./libttspico-utils_1.0+git20130326-9_armhf.deb

Explications des 4 lignes en bash du fichier « txtpico.sh » à positionner dans le répertoire /home/pi/PiTextReader.

#!/bin/sh
tr "\n" " " < /tmp/text.txt > /tmp/text1line.txt
pico2wave -l fr-FR -w /tmp/text.wav < /tmp/text1line.txt
aplay /tmp/text.wav

Pour information, la première ligne indique qu’il s’agit d’un script Bash, la deuxième supprime tous les retours à la ligne de manière à éviter des pauses incongrues lors de la lecture, la troisième demande au module Pico2wave en mode « langue française » de créer un fichier son « text.wav » à partir du fichier précédemment créé « text1line ». Enfin la quatrième ligne, lance la lecture du fichier son.

Note : La raison pour laquelle je n’ai pas intégré ce code dans le fichier « PiTextReader.py » et que je sollicite donc un fichier supplémentaire en bash est que n’ai pas réussi à faire lire par Python le symbole de redirection d’entrée (<) qui permet de créer le fichier .WAV à partir du fichier texte.

Les messages d’introduction (« Je suis prêt pour une lecture » et «Traitement en cours ») sont à créer. Pico2wave contrairement aux autres moteurs TTS, ne peut pas déclencher la voix directement sur la sortie standard (stdout/haut-parleur) mais crée un fichier wav à partir du texte qu’il faut ensuite lire… Autant créer ces messages une fois pour toute et se contenter de les faire lire dans le fichier « pytextreader.py ». On va les créer en ligne de commande. Libre à vous d’adapter le texte ! Après vous être placé dans le répertoire du projet avec “cd PiTextReader“, lancez les 3 commandes suivantes :

pico2wave –l fr-FR –w pret.wav “je suis prête pour une lecture“
pico2wave –l fr-FR –w pret2.wav “je suis à nouveau prête pour une lecture“
pico2wave –l fr-FR –w traitement.wav “En cours de traitement“

Les fichiers sont ainsi créés dans le répertoire du projet et seront lus via le code en Python.

Remarque : Si vous n’avez pas de son en mode autonome mais que vous en avez en ligne de commande, c’est certainement qu’il vous faut préciser votre environnement utilisateur dans le fichier « cronfile » en y ajoutant cette ligne :

export XDG_RUNTIME_DIR=/run/user/1000

Votre identifiant utilisateur (1000 dans mon cas) peut être différent. Saisissez « echo $UID » pour le trouver.

Nettoyage de texte

Pour nettoyer le texte qui ressort brut après l’océrisation, R. Grokett, utilise la commande « sed » bien pratique pour remplacer des occurrences de caractères. J’ai modifié la ligne correspondante pour l’adapter au moteur de synthèse vocale Pico2wave qui lit la ponctuation.

cmd = "sed -i -e 's/</./g' -e 's/>/./g' -e 's/|/l/g' -e 's/*/./g' -e 's/-//g' -e 'G' /tmp/text.txt"

Note : Vous pouvez facilement remplacer ou supprimer d’autres caractères en suivant ce principe : chaine1 = Chaîne à remplacer. chaine2 = Nouvelle chaîne de caractères.

-e ‘s/chaines1/chaine2/g’

Tesseract OCR

Depuis 2005, le code de Tesseract est sous licence libre (Apache 2.0). Son développement est maintenu par des équipe de Google. Afin de supprimer le message d’erreur (Warning. Invalid resolution 0 dpi. Using 70 instead…), j’ai ajouté «–300dpi » ce qui indique à Tesseract la résolution de l’image à traiter.

Temps de traitement

Dans de bonnes conditions (qui dépendent du type de document mais aussi de l’environnement lumineux, de l’inclinaison du texte, de la présence d’aplats de couleurs ou d’ombres…), il faut compter entre une demi-seconde à 2 secondes par ligne. Le premier critère est certainement la source : la nature du document à lire. La lecture démarre entre 20 et 30 secondes pour une pleine page au format poche. Note : Ce temps de traitement est surtout le fait de l’océrisation très gourmande en ressource… Les machines à lire du commerce sont ainsi parfois à relier à un ordinateur, ou, dans le cas de modèles « tout en 1 », souvent très onéreux, disposent d’un processeur multi-cœur performant. De ce point de vue, remplacer le Rpi3 par un Rpi4 accélérera fortement le temps de traitement (Attention, en l’état, le projet n’est pas compatible avec le Rpi4).

ON/OFF

Le projet de M. Grokett est prévu pour fonctionner en continu. En effet, le Raspberry n’a pas d’interrupteur intégré et retirer le câble d’alimentation à chaque fois que l’on souhaite l’éteindre n’est pas prudent pour la carte SD… Il est toutefois possible de l’éteindre correctement en branchant un clavier afin d’ouvrir un terminal avec la combinaison de touches [Ctrl]+[Alt]+[t] qui permet d’écrire “sudo halt“. Il existe aussi et heureusement des solutions pour arrêter et démarrer votre Raspberry avec un simple bouton (En plus des solutions « DIY », il y a, par exemple, le Module On-Off SHIM) !

Traduire un texte en langue étrangère !

Le Pitextreader peut facilement devenir un puissant traducteur à condition de le relier au réseau internet. Notre Raspberry 3 a le wifi intégré, donc c’est une manipulation aisée surtout avec l’interface graphique de l’OS “Raspberry Pi Desktop”. 

Je vous propose d’installer le traducteur « translate-shell », en abrégé “trans”, et éventuellement de jeter un œil sur son Github : https://github.com/soimort/translate-shell 
Translate Shell (anciennement Google Translate CLI) est un traducteur en ligne de commande qui utilise par défaut le moteur Google Translate mais d’autres sont disponibles. Il peut traduire du texte en ligne de commande, des pages html et, ce qui nous intéresse ici, des fichiers. On va ainsi pouvoir traduire le fichier texte issu de la reconnaissance de caractère… Nous aurons maintenant ce processus : 
Photo d’un texte > Reconnaissance de caractères OCR > Traduction > Synthèse vocale  

Installation avec la commande wget : 
sudo wget -O /usr/local/bin/trans https://git.io/trans 
sudo chmod a+x /usr/local/bin/trans 

Vous pouvez vérifier que le traducteur est bien installé directement dans le terminal : 

trans fr "Hello, World !" 

Puis transformer le fichier “txtpico.sh” de cette manière : 

#!/bin/sh 
Trans :fr -b |-o /tmp/textfr.txt < /tmp/text.txt 
tr "\n" " " < /tmp/textfr.txt > /tmp/text1line.txt 
pico2wave -l fr-FR -w /tmp/text.wav < /tmp/text1line.txt 
aplay /tmp/text.wav 

Le traducteur translate-shell est ainsi sollicité au commencement avec une traduction vers le français (fr) en mode bref (-b).  Tel quel, cela fonctionne avec un très grand nombre de langues en alphabet latin. La langue source, à traduire, est détectée automatiquement. Pour d’autres alphabets, il faut modifier la partie OCR, c’est à dire la ligne concernant Tesseract dans le fichier « PiTextReader.py » : 

cmd = "/usr/bin/tesseract --dpi 300 -l fra /tmp/image.jpg /tmp/text" 

Par exemple, pour activer la reconnaissance de l’alphabet cyrillique ukrainien, il faudra remplacer le français “fra” par “ukr” dans la ligne ci-dessus. 
Liste des langues supportées par Tesseract avec le code à 3 lettres correspondant : 
https://github.com/tesseract-ocr/tessdoc/blob/main/Data-Files-in-different-versions.md 

Améliorations

Pour être une réelle aide à des personnes atteintes d’un trouble de la vision, l’ergonomie du dispositif, en particulier l’accès au bouton principal devra être amélioré ainsi que la stabilité du chassis. Un guide pour le positionnement du document par rapport à la caméra est aussi à considérer car un mauvais positionnement est source d’erreurs et augmente en plus le temps de traitement (R. Grokett a ainsi prévu une encoche…). De même, il serait utile d’avoir un dispositif pour maintenir les pages à plat dans le cas d’un livre.

Si l’ambition de ce lecteur est de lire des livres de tailles et d’épaisseurs différentes, la hauteur du bras et le positionnement de la caméra sont des points cruciaux. Il existe des caméras pour Raspberry avec focus motorisée contrôlable logiciellement susceptibles de résoudre ces problèmes de cadrage et de mise au point.

Des améliorations peuvent aussi être apportées pour ajouter des fonctionnalités, qui pour certaines sont déjà codées, telles qu’une commande de volume, de pause, de changement ou de ralentissement de voix, de langue et pourquoi pas un écran qui ferait défiler en gros caractères le texte lu.

Côté logiciel, pour la partie synthèse vocale, il existe des déclinaisons de Pico2wave qui offrent plus d’options car plus proches du moteur original de Svox. D’autres moteurs TTS comme le populaire Espeak offrent d’autres voix en français assez qualitatives à condition de le coupler avec Mbrola. Cependant, dans le cadre de ce projet hors-ligne, il me paraît difficile d’atteindre le niveau de sophistication des solutions TTS connectées qui pour les plus performantes arrivent désormais grâce à une forme d’apprentissage (deep learning) à produire une intonation constituée de changements de rythme et d’intensité bluffants car quasi humains (cf. prosodie) en fonction d’un contexte analysé en temps réel.

Pour la partie OCR, il serait intéressant de tester les différents modes du nouveau Tesseract, la version 4, (la version 5 ne semble pas disponible pour le Raspberry) avec son réseau de neurones récurrents LSTM. Concrètement, il pourrait aider à mieux lire les données complexes comme les mises en page élaborées de type magazine ou des tableaux car il fait notamment plus facilement la distinction entre les lignes et le texte. https://github.com/tesseract-ocr/tesseract/blob/master/doc/tesseract.1.asc

Même si le projet est porté par Google, Tesseract témoigne du dynamisme des communautés open source et de l’intérêt des licences libres, facteurs et vecteurs d’innovation. Il suffit, pour s’en convaincre, d’observer les milliers de « fork » autour du Github officiel : https://github.com/tesseract-ocr/tesseract

Ce projet, sous licence libre, merci encore à Russell Grokett, ne demande qu’à être reproduit et amélioré !

Jean Alvin Médiateur numérique. Médiathèque du Bois Fleuri LORMONT https://www.mediaenlab.com/