NETTOYAGE (aka scanickel.sh)

Maintenant on a un dossier de photos sur la clé USB, qui s'appelle tout simplement images. Pour en tirer un PDF pas trop lourd ou du texte brut réutilisable, on a besoin de les traiter.

Vu qu'on va avoir des dizaines voire des centaines de photos par livre, l'idéal serait de faire un script qui automatise ce travail.

On pensait faire ce traitement (nettoyer les images, réduire leur poids, les OCRiser, les mettre en PDF) directement sur le RPi mais une fois que pi-scan est installé on ne peut plus rien mettre sur la carte SD (elle est en lecture seule). On ne peut pas installer de logiciel de traitement.

On va devoir le faire sur un autre ordinateur, et ça sera un script bash. C'est le langage du terminal dans macOS et la plupart des distributions Linux. Ça permet d'effectuer des actions simples (renommer, déplacer un fichier…) ou d'utiliser des logiciels compatibles avec la ligne de commande sur beaucoup de fichiers par exemple.

Les outils possibles pour les différentes étapes:

  • Nettoyer les scans (recadrer, contraster, enlever les taches, redresser…)
    • ScanTailor: pas vraiment maintenu et pas utilisable en ligne de commande (apparemment il y a un paquet scantailor-universal-cli mais à compiler soi-même)
    • gscan2pdf: utilise unpaper pour cleaner les scans, output DejaVu et PDF automatique OCR intégré, (choix entre gocr, tesseract ou cuneiform), mais PAS D'INTERFACE EN LIGNE DE COMMANDE -> seulement 2-3 commandes pour ouvrir des images directement dans le logiciel
    • ImageMagick
    • unpaper
  • Réduire le poids des images
    • ImageMagick
  • Assembler en PDF
    • img2pdf
    • png2pdf
    • tiff2pdf
    • ghostscript
  • OCR
  • un peu tout en même temps
    • gscan2pdf
    • OCRmyPDF

En passant les images dans unpaper puis tesseract on devrait pouvoir refaire en ligne de commande ce que fait gscan2pdf (qui utilise les deux logiciels sous le capot).

ImageMagick peut être utile pour recadrer et faire un premier traitement + convertir en format lisible par unpaper (pbm/N&B, pgm/niveaux de gris 8-bit, ppm/RGB 24-bit ou tiff)

Documentation de unpaper:

Exemple de processus avec unpaper

Essais

ImageMagick

Commande testées :

  • grayscale: convert input -colorspace Gray ouptut
  • threshold-dithering: convert input +dither -colors 2 -colorspace Gray output
  • brightness-contrast: convert input -brightness-contrast +20 -colorspace Gray ouptut

Avec l'output en pbm pour que unpaper le nettoie mieux ensuite.

  • avoir toutes les infos de l'image -> identify -verbose image (choper seulement la résolution -> identify -format "%x x %y" image)

Unpaper

Unpaper ne supporte que les formats d'images pbm/pgm/pnm (qui sont très lourds), c'est pas très pratique.

Exemple : unpaper --layout {single ou double pour le format de page de sortie} --input-pages {1 ou 2 pour le format de pages d'entrée} input output

On peut rajouter des options pour paramétrer les filtres de base (enlever le bruit, les artefacts de scans, recentrer, deskew…) ou en enlever certains (mais par défaut tous les filtres s'appliquent).

Le logiciel est assez puissant mais fonctionne vraiment bien pour les scans "traditionnels" venant de scanners à plat, et qui sont déjà en bitmap. Dans notre cas, on a besoin de recadrer l'image et les problèmes de redressement et de centrage de la page seront réglé en amont au moment de la prise de vue. Le gros du boulot sera fait par ImageMagick, alors unpaper n'est peut-être pas nécessaire (ça alourdirait aussi le traitement si on a beaucoup de pages).

Tesseract

C'est le logiciel d'OCR qui à l'air le plus compétent sous Linux (fonctionne avec du machine learning). Tesseract est entraîné pour plusieurs langues mais est par défaut configuré pour l'anglais. Si on veut utiliser une autre langue il faut télécharger et installer le dataset correspondant auparavant. - Manuel d'utilisation - tesseract supporte aussi les formats pbm/pnm/pgm, si on veut l'utiliser après unpaper - commande de base tesseract input output-base (SANS EXTENSION) -l {code de langue à 3 lettres} extension (txt, pdf, hocr…) - export direct en pdf, avec un calque de texte en arrière-plan tesseract input output-base (SANS EXTENSION) -l langue pdf

Tesseract possède des page segmentation mode (psm), qui permette de spécifier comment l'image doit être interprétée. Par défaut tesseract considère les images qu'on lui envoie comme des pages de texte avec une seule colonne, et va moins bien marcher s'il n'y a qu'un mot sur la page par exemple.

Tests de psm (page segmentation method dans tesseract)

Obtenu en faisant tesseract --help-psm :

Page segmentation modes:
  0    Orientation and script detection (OSD) only.
  1    Automatic page segmentation with OSD.
  2    Automatic page segmentation, but no OSD, or OCR. (not implemented)
  3    Fully automatic page segmentation, but no OSD. (Default)
  4    Assume a single column of text of variable sizes.
  5    Assume a single uniform block of vertically aligned text.
  6    Assume a single uniform block of text.
  7    Treat the image as a single text line.
  8    Treat the image as a single word.
  9    Treat the image as a single word in a circle.
 10    Treat the image as a single character.
 11    Sparse text. Find as much text as possible in no particular order.
 12    Sparse text with OSD.
 13    Raw line. Treat the image as a single text line,
       bypassing hacks that are Tesseract-specific.

Pour me rendre compte des différences de psm, j'ai lancé cette boucle en bash qui crée un fichier texte pour chaque psm : for i in {0..13} do tesseract base.tiff psm-$i -l fra --psm $i txt done J'ai pu comparer les différences d'OCR avec des psm différentes sur la même image d'internet en 72 dpi et passée en 300 dpi avec -density 300 d'ImageMagick grâce à cette commande qui compare, pour chaque psm, la version 72 dpi et la version 300 dpi: for i in {0..13} do diff version-72-$i version-300-$i.* > diffs/diff-$i.txt done Il n'y a que pour les psm 4, 11 et 12 que c'est différent, avec des différences très minimes. Étonnamment il y a plus de texte reconnu en 72 dpi avec --psm 4 Super site pour expliquer les psm

  • Pour un bloc de texte uniforme (même fonte, même corps), le psm 6 est le mieux (romans = nickel)
  • Pour qu'un bloc de texte soit reconnu comme une seule colonne (genre tableaux/tableurs et tout ça), le psm 4 est le mieux

Optimiser le scan pour l'OCR

  • La taille des capitales optimale (en pixel) se trouve entre 20 et 45 pixels en hauteur

  • le filtre lat (Local Adaptative Threshold) d'ImageMagick est super puissant, il permet d'enlever le bruit du fond, bien pratique pour cleaner du texte scanné. Il regarde pour chaque pixel de l'image s'il est plus ou moins clair que la moyenne de la fenêtre définie, + l'offset en % -> convert $in -lat 20x2-20% $out (20x2 c'est la taille de la fenêtre en pixel et l'offset est de -20%). Les valeurs optimales de la fenêtre et de l'offset doivent dépendre de la taille du corps du texte, de celle de l'image et de sa résolution. Vu qu'avec les appareils photos on a toujours la même résolution ça fait déjà une variable en moins à gérer

Nettoyage de base

Une fois qu'on a récupéré les photos dans le RPi il faut: - recadrer les images pour n'avoir plus ou moins que la page - nettoyer l'image (niveaux/seuil) - OCR avec tesseract (est-ce que passer en 300dpi améliore la qualité de l'OCR ? À tester)

La commande en-dessous semble être la meilleure pour nettoyer une page en noir et blanc (on peut rajouter un filtre lat pour du texte pur si le fond est trop dégueu)

convert dossier/* \
              -colorspace gray \
              -type grayscale \
              -level 25%,73% \
              -contrast-stretch 0 \
              -normalize \
              -unsharp 10x10 \
              /tmp/scanickel/%04d.tiff

Pour les pages avec des images en couleur on peut remplacer -level 25%,73% par -sigmoidal-contrast 10x50% qui marche apparemment mieux.

Pistes pour le recadrage

C'est la partie la plus complexe à automatiser.

  • Seam Carving ? Technique qui détecte les zones moins intéressantes (aplats) pour redimensionner en préservant le premier plan -> intégré dans IM pour redimensionner mais pas pour le recadrer
  • AUTRE SOLUTION: unpaper, qui est décidément très fort, peut recadrer direct au bon format si on lui indique la résolution de la photo et le format de la page. Ça marche pas terrible, même en indiquant les dpi de la photo il ne calcule pas bien et recadre trop.
  • Technique trouvée sur un forum : utiliser un profil de l'image en vertical et en horizontal pour déterminer les bords de la page et recadrer plus précisément. Le problème est que pour ça il faut être sûr·e que la page soit d'équerre avec les bords de l'image.

Script magique

Sur un forum on a trouvé un script ImageMagick qui recadre pas mal du tout de manière automatique, à tester avec plus de documents. Il utilise la technique d'un de faire une moyenne de l'image en verticla et en horizontal pour détecter les bords des pages. Sinon la solution reste de donner "à la main" les valeurs des bords et de les appliquer sur toutes les pages (gérer pages de gauche/de droite séparément). Il reste à le piper avec img2pdf puis ocrmypdf pour avoir en théorie un pdf bien propre bien optimisé bien OCRisé.

Le script marche mieux si on le lance une fois que l'image est déjà nettoyée (contrastes boostés + N&B). Pour les pages en couleur on pourrait utiliser le résultat du script sur une image temporaire optimale en N&B et l'appliquer à une image couleur.

Techniquement ce qui se passe quand on lance le script : - On fait la moyenne des valeurs de gris de l'image en vertical et en horizontal, ça donne des images comme ça:

À partir de ça:

  • On utilise ces images pour déterminer combien de pixels il faut enlever en hauteur/largeur
  • On recadre toutes les photos avec ces valeurs (attention, on traite les pages de droite et de gauche séparément puisqu'elles ne sont pas symétriques)

Révélation

En fait je pensais que pour rogner on calculait la moyenne de l'image en hauteur puis en largeur pour utiliser l'histogramme des valeurs pour trouver l'index des pixels qui délimitaient le passage du blanc au noir (et inversement), ce qui nous donnait des chiffres qu'on utilisait pour dire à ImageMagick de recadrer l'image en largeur du pixel x1 jusqu'au pixel x2 par exemple.

MAIS EN FAIT c'est pas du tout ce qu'il se passe. Si on reprend cette commande : convert $PAGEDROITE -scale 1x! -bordercolor black -border 1 -fuzz 30% -trim $PAGEDROITE_tmp2.jpg

Effectivement elle recadre l'image en une ligne de 1 pixel de haut (-scale 1x!). Mais elle la recadre également (-trim) en enlevant le noir aux extrémités (-bordercolor black) avec une marge de tolérance de 30% (-fuzz 30%) Ce qu'on utilise pour recadrer l'image c'est la LARGEUR de cette ligne recadrée (qui fait donc la même largeur que la page en théorie) et l'OFFSET de l'image, c'est-à-dire ce qui a été recadré à gauche de l'image. Ce qui fait qu'on peut dire ensuite "prend cette image de 4608 pixels de largeur et recadre-là à 2304 pixels en partant du 305e pixel à la gauche de l'image" (plus ou moins).

Ce qui est étonnant c'est que l'image temporaire de 1 pixel de haut garde en mémoire l'"offset", c'est-à-dire de commbien de pixels elle a été recadrée par la commande -trim. Ce qui est encore plus étonnant c'est que selon le format elle ne garde pas forcément ces métadonnées en mémoire. Le format jpg ne garde aucune de ces métadonnées, donc si on utilise un jpg pour les mesures le résultat ressemble à ça :

Avant:

Après:

La largeur et la hauteur sont correctes mais il recadre depuis le coin en haut à gauche : les "offsets" n'ont pas été enregistrés dans les métadonnées du jpeg.

Avec un png les offsets sont mémorisés, alors le recadrage donne ça :

Petits détails

Pour un meilleur recadrage il faut que le fond derrière le livre soit noir et que les bords de l'image soient aussi noirs. S'il a quelques pixels blancs le recadrage automatique sera faussé.

Vu qu'on a le même cadrage à chaque fois, qui prend toute la vitre en photo, on peut remplir en noir les bords de l'image pour cacher les reflets de la vitre + les barres en alu qui sont visible et sont trop claires.

Il faut noircir plus ou 150 pixels à chaque bords en largeur et de 460 en hauteur (en haut ou en bas selon la page).

Pour ça on utilise -extent LARGEURxHAUTEUR pour recadrer l'image avec l'option -gravity south pour recadrer tous les bords sauf celui du bas (le petit fond de la page, qui n'a pas besoin d'être recadré car déjà au bord de l'image). Ensuite on refait -extent pour remettre l'image à sa taille originale mais en ajoutant -background black pour que ce qui va être ajouté à l'image soit noir.

En une ligne ça donne ça : mogrify -background black -gravity south -extent 4300x3000 -extent 4608x3456 $image. Ici on utilise mogrify et pas convert car on modifie en l'écrasant, on ne veut pas créer un nouveau fichier temporaire.

Apparemment pour le nettoyage il faut suffisamment de RAM sinon ça fait planter ImageMagick (testé sur un ordi avec 4Go de RAM, pour seulement 3 double pages, donc 6 images). Ça craint pour un fichier de 100 pages ? 300 pages ?

Pistes pour le redressage (si besoin)

  • On peut théoriquement utiliser unpaper en désactivant tous les filtres sauf le deskewing/la rotation mais ça ne marche pas.
  • ImageMagick a une fonction -deskew. D'après leur doc une valeur de 40% convient à la plupart des documents (en théorie si les appareils sont bien calibrés on aura pas ou très peu besoin de deskewing) -> convert -set option:deskew:auto-crop true in.jpg -deskew 40% out.jpg
  • OCRmyPDF peut aussi redresser une image, mais il a l'air d'avoir du mal si l'image n'est pas bien crop avant

Après recadrage et redressage, on arrive à ça à peu près:

Fabriquer le pdf

  • Le tiff à l'air d'être le format qui produit les pdfs les mieux compressés avec tesseract
  • Si on n'utilise pas d'OCR (tesseract) pour générer le pdf, alors on peut utiliser soit ImageMagick soit tiff2pdf. tiff2pdf rajoute une dépendance, mais ImageMagick produit des pdf très mal compressés. Après ce sera un fichier temporaire qui sera supprimé, mais sur une grande quantité de page ça peut faire une différence ?
  • Avec tiff2pdf on peut ajouter des métadonnées (auteurice, titre, sujet, mots-clés…), mais je ne sais pas si elles sont conservées après compression texte qui ne sont pas dans les tiffs d'origine
  • Compresser le pdf avec Ghostscript -> gs -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/screen -sOutputFile=out in -> pas mal mais fait des artefacts autour du texte (même si assez discrets), à voir peut-être une autre méthode de compression

Une technique en 2 temps

Avec tesseract -c textonly_pdf=1 on peut faire un pdf du calque de texte seulement. On peut le combiner avec un pdf comportant juste les images qui seront mieux optimisées pour réduire leur taille, comme ça on a le meilleur des deux mondes. Étant donné que tesseract ignore ce qu'il ne reconnaît pas les images devraient passer inaperçues. -> Plus de fichiers temporaires mais une meilleure solution ?

Meilleur logiciel

J'ai trouvé OCRmyPDF, qui utilise unpaper et tesseract sous le capot + combine plein d'options dont on peut avoir besoin (sélectionner certaines pages seulement, ignorer les pages avec des images en repérant celles qui pèsent trop lourd, créer un fichier texte avec le résultat de l'OCR en parallèle, optimiser les pdf…). Il faut "juste" traiter les images de base pour enlever les marges + redresser les pages si besoin.

Structure du programme

Pour que l'utilisation du programme soit assez simple mais puisse donner toutes les subtilités des outils utilisés, il faudrait des options par défaut tout en gardant la possibilité de rajouter des paramètres plus précis por chaque outil intermédiaire utilisé (ImageMagick, img2pdf, OCRmypdf). Par exemple, rajouter plusieurs langues pour que tesseract OCRise bien, rajouter un format de sortie (pdf mais aussi epub ou hocr peut-être), modifier le degré d'optimisation du pdf, ajouter des exceptions pour certaines pages (qu'on ne veut pas OCRiser ou qu'on ne veut pas traiter de la même manière, pour des images en couleur par exemple).

Résumé

Pour l'instant l'enchaînement d'actions automatiques le plus efficace ressemble à ça :

scans photo -> ImageMagick              -> img2pdf               -> OCRmyPDF
               | niveaux de gris           | conversion en pdf      | OCR
               | contraster                                         | optimisation
               | recadrer                                           | fichier texte brut
               | redresser
               | affiner la netteté

Best of tests