web-dev-qa-db-fra.com

traitement des images pour améliorer la précision de l'OCR tesseract

J'utilise tesseract pour convertir des documents en texte. La qualité des documents varie énormément et je cherche des conseils sur le type de traitement d'image susceptible d'améliorer les résultats. J'ai remarqué qu'un texte hautement pixellisé - par exemple celui généré par des télécopieurs - est particulièrement difficile à traiter par tesseract - vraisemblablement tous ces bords irréguliers des caractères confondent les algorithmes de reconnaissance de forme. 

Quels types de techniques de traitement d'images pourraient améliorer la précision? J'ai utilisé un flou gaussien pour adoucir les images pixellisées et j'ai constaté quelques améliorations, mais j'espère qu'il existe une technique plus spécifique qui donnerait de meilleurs résultats. Dites un filtre adapté aux images en noir et blanc, qui lisserait les bords irréguliers, suivi d'un filtre qui augmenterait le contraste pour rendre les caractères plus distincts.

Avez-vous des conseils généraux pour un novice en traitement d’image? 

114
user364902
  1. correction DPI (si nécessaire) 300 DPI minimum
  2. correction de la taille du texte (par exemple, 12 points devraient suffire)
  3. essayez de corriger les lignes de texte (texte de redressement et de réduction)
  4. essayez de corriger l'éclairage de l'image (par exemple, aucune partie sombre de l'image)
  5. binariser et atténuer l'image

Il n’existe pas de ligne de commande universelle qui conviendrait à tous les cas (vous devez parfois flouter et accentuer l’image). Mais vous pouvez essayer TEXTCLEANER à partir des scripts ImageMagick de Fred .

Si vous n'êtes pas fan de ligne de commande, vous pouvez peut-être essayer d'utiliser opensource scantailor.sourceforge.net ou commercial bookrestorer .

79
user898678

Je ne suis en aucun cas un expert en OCR. Mais cette semaine, j’avais besoin de convertir du texte en jpg.

J'ai commencé avec un jpg colorisé, RVB 445x747 pixels . J'ai tout de suite essayé sur tesseract, et le programme n'a presque rien converti . Je suis ensuite allé dans GIMP et ai procédé comme suit. améliorer> masque flou avec des valeurs de rayon = 6.8, montant = 2.69, seuil = 0 J'ai ensuite enregistré sous un nouveau jpg à 100% de qualité.

Tesseract a ensuite pu extraire tout le texte dans un fichier .txt

Gimp est ton ami.

65
John

Trois points pour améliorer la lisibilité de l'image: 1) Redimensionnez l'image avec une hauteur et une largeur variables (multipliez 0.5 et 1 et 2 par la hauteur et la largeur de l'image) . 2) Convertissez l'image au format échelle de gris ( Noir et blanc) . 3) Supprimez les pixels parasites et rendez-vous plus clair (filtrez l'image).

Voir le code ci-dessous: 

//Resize
  public Bitmap Resize(Bitmap bmp, int newWidth, int newHeight)
        {

                Bitmap temp = (Bitmap)bmp;

                Bitmap bmap = new Bitmap(newWidth, newHeight, temp.PixelFormat);

                double nWidthFactor = (double)temp.Width / (double)newWidth;
                double nHeightFactor = (double)temp.Height / (double)newHeight;

                double fx, fy, nx, ny;
                int cx, cy, fr_x, fr_y;
                Color color1 = new Color();
                Color color2 = new Color();
                Color color3 = new Color();
                Color color4 = new Color();
                byte nRed, nGreen, nBlue;

                byte bp1, bp2;

                for (int x = 0; x < bmap.Width; ++x)
                {
                    for (int y = 0; y < bmap.Height; ++y)
                    {

                        fr_x = (int)Math.Floor(x * nWidthFactor);
                        fr_y = (int)Math.Floor(y * nHeightFactor);
                        cx = fr_x + 1;
                        if (cx >= temp.Width) cx = fr_x;
                        cy = fr_y + 1;
                        if (cy >= temp.Height) cy = fr_y;
                        fx = x * nWidthFactor - fr_x;
                        fy = y * nHeightFactor - fr_y;
                        nx = 1.0 - fx;
                        ny = 1.0 - fy;

                        color1 = temp.GetPixel(fr_x, fr_y);
                        color2 = temp.GetPixel(cx, fr_y);
                        color3 = temp.GetPixel(fr_x, cy);
                        color4 = temp.GetPixel(cx, cy);

                        // Blue
                        bp1 = (byte)(nx * color1.B + fx * color2.B);

                        bp2 = (byte)(nx * color3.B + fx * color4.B);

                        nBlue = (byte)(ny * (double)(bp1) + fy * (double)(bp2));

                        // Green
                        bp1 = (byte)(nx * color1.G + fx * color2.G);

                        bp2 = (byte)(nx * color3.G + fx * color4.G);

                        nGreen = (byte)(ny * (double)(bp1) + fy * (double)(bp2));

                        // Red
                        bp1 = (byte)(nx * color1.R + fx * color2.R);

                        bp2 = (byte)(nx * color3.R + fx * color4.R);

                        nRed = (byte)(ny * (double)(bp1) + fy * (double)(bp2));

                        bmap.SetPixel(x, y, System.Drawing.Color.FromArgb
                (255, nRed, nGreen, nBlue));
                    }
                }



                bmap = SetGrayscale(bmap);
                bmap = RemoveNoise(bmap);

                return bmap;

        }


//SetGrayscale
  public Bitmap SetGrayscale(Bitmap img)
        {

            Bitmap temp = (Bitmap)img;
            Bitmap bmap = (Bitmap)temp.Clone();
            Color c;
            for (int i = 0; i < bmap.Width; i++)
            {
                for (int j = 0; j < bmap.Height; j++)
                {
                    c = bmap.GetPixel(i, j);
                    byte gray = (byte)(.299 * c.R + .587 * c.G + .114 * c.B);

                    bmap.SetPixel(i, j, Color.FromArgb(gray, gray, gray));
                }
            }
            return (Bitmap)bmap.Clone();

        }
//RemoveNoise
   public Bitmap RemoveNoise(Bitmap bmap)
        {

            for (var x = 0; x < bmap.Width; x++)
            {
                for (var y = 0; y < bmap.Height; y++)
                {
                    var pixel = bmap.GetPixel(x, y);
                    if (pixel.R < 162 && pixel.G < 162 && pixel.B < 162)
                        bmap.SetPixel(x, y, Color.Black);
                    else if (pixel.R > 162 && pixel.G > 162 && pixel.B > 162)
                        bmap.SetPixel(x, y, Color.White);
                }
            }

            return bmap;
        }

IMAGE ENTRÉE
INPUT IMAGE

IMAGE DE SORTIE OUTPUT IMAGE

28

C'est un peu plus tôt mais cela pourrait quand même être utile.

Mon expérience montre qu'il est parfois utile de redimensionner l'image en mémoire avant de la transmettre à tesseract.

Essayez différents modes d'interpolation. Le message https://stackoverflow.com/a/4756906/146003 m'a beaucoup aidé.

16
Atmocreations

Les codes sources du projet Capture2Text ont été extrêmement utiles pour moi . http://sourceforge.net/projects/capture2text/files/Capture2Text/ .

BTW: Félicitations à son auteur pour avoir partagé un algorithme aussi laborieux.

Portez une attention particulière au fichier Capture2Text\SourceCode\leptonica_util\leptonica_util.c - c’est l’essence de la préprocession d’images pour cet utilitaire.

Si vous voulez exécuter les fichiers binaires, vous pouvez vérifier la transformation de l'image avant/après le processus dans le dossier Capture2Text\Output \.

P.S. La solution mentionnée utilise Tesseract pour l’OCR et Leptonica pour le prétraitement.

13
Wiseman

Version Java pour le code de Sathyaraj ci-dessus:

// Resize
public Bitmap resize(Bitmap img, int newWidth, int newHeight) {
    Bitmap bmap = img.copy(img.getConfig(), true);

    double nWidthFactor = (double) img.getWidth() / (double) newWidth;
    double nHeightFactor = (double) img.getHeight() / (double) newHeight;

    double fx, fy, nx, ny;
    int cx, cy, fr_x, fr_y;
    int color1;
    int color2;
    int color3;
    int color4;
    byte nRed, nGreen, nBlue;

    byte bp1, bp2;

    for (int x = 0; x < bmap.getWidth(); ++x) {
        for (int y = 0; y < bmap.getHeight(); ++y) {

            fr_x = (int) Math.floor(x * nWidthFactor);
            fr_y = (int) Math.floor(y * nHeightFactor);
            cx = fr_x + 1;
            if (cx >= img.getWidth())
                cx = fr_x;
            cy = fr_y + 1;
            if (cy >= img.getHeight())
                cy = fr_y;
            fx = x * nWidthFactor - fr_x;
            fy = y * nHeightFactor - fr_y;
            nx = 1.0 - fx;
            ny = 1.0 - fy;

            color1 = img.getPixel(fr_x, fr_y);
            color2 = img.getPixel(cx, fr_y);
            color3 = img.getPixel(fr_x, cy);
            color4 = img.getPixel(cx, cy);

            // Blue
            bp1 = (byte) (nx * Color.blue(color1) + fx * Color.blue(color2));
            bp2 = (byte) (nx * Color.blue(color3) + fx * Color.blue(color4));
            nBlue = (byte) (ny * (double) (bp1) + fy * (double) (bp2));

            // Green
            bp1 = (byte) (nx * Color.green(color1) + fx * Color.green(color2));
            bp2 = (byte) (nx * Color.green(color3) + fx * Color.green(color4));
            nGreen = (byte) (ny * (double) (bp1) + fy * (double) (bp2));

            // Red
            bp1 = (byte) (nx * Color.red(color1) + fx * Color.red(color2));
            bp2 = (byte) (nx * Color.red(color3) + fx * Color.red(color4));
            nRed = (byte) (ny * (double) (bp1) + fy * (double) (bp2));

            bmap.setPixel(x, y, Color.argb(255, nRed, nGreen, nBlue));
        }
    }

    bmap = setGrayscale(bmap);
    bmap = removeNoise(bmap);

    return bmap;
}

// SetGrayscale
private Bitmap setGrayscale(Bitmap img) {
    Bitmap bmap = img.copy(img.getConfig(), true);
    int c;
    for (int i = 0; i < bmap.getWidth(); i++) {
        for (int j = 0; j < bmap.getHeight(); j++) {
            c = bmap.getPixel(i, j);
            byte gray = (byte) (.299 * Color.red(c) + .587 * Color.green(c)
                    + .114 * Color.blue(c));

            bmap.setPixel(i, j, Color.argb(255, gray, gray, gray));
        }
    }
    return bmap;
}

// RemoveNoise
private Bitmap removeNoise(Bitmap bmap) {
    for (int x = 0; x < bmap.getWidth(); x++) {
        for (int y = 0; y < bmap.getHeight(); y++) {
            int pixel = bmap.getPixel(x, y);
            if (Color.red(pixel) < 162 && Color.green(pixel) < 162 && Color.blue(pixel) < 162) {
                bmap.setPixel(x, y, Color.BLACK);
            }
        }
    }
    for (int x = 0; x < bmap.getWidth(); x++) {
        for (int y = 0; y < bmap.getHeight(); y++) {
            int pixel = bmap.getPixel(x, y);
            if (Color.red(pixel) > 162 && Color.green(pixel) > 162 && Color.blue(pixel) > 162) {
                bmap.setPixel(x, y, Color.WHITE);
            }
        }
    }
    return bmap;
}
12
Fábio Silva

En règle générale, j'applique généralement les techniques de prétraitement d'images suivantes à l'aide de la bibliothèque OpenCV:

  1. Rééchelonnage de l'image (il est recommandé si vous travaillez avec des images dont le DPI est inférieur à 300 dpi):

    img = cv2.resize(img, None, fx=1.2, fy=1.2, interpolation=cv2.INTER_CUBIC)
    
  2. Conversion de l'image en niveaux de gris:

    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
  3. Appliquez une dilatation et une érosion pour éliminer le bruit (vous pouvez jouer avec la taille du noyau en fonction de votre ensemble de données):

    kernel = np.ones((1, 1), np.uint8)
    img = cv2.dilate(img, kernel, iterations=1)
    img = cv2.erode(img, kernel, iterations=1)
    
  4. L'application du flou, ce qui peut être fait en utilisant l'une des lignes suivantes (chacune ayant ses avantages et ses inconvénients, toutefois, le flou médian et le filtre bilatéral fonctionnent généralement mieux que le flou gaussien):

    cv2.threshold(cv2.GaussianBlur(img, (5, 5), 0), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
    
    cv2.threshold(cv2.bilateralFilter(img, 5, 75, 75), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
    
    cv2.threshold(cv2.medianBlur(img, 3), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
    
    cv2.adaptiveThreshold(cv2.GaussianBlur(img, (5, 5), 0), 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 2)
    
    cv2.adaptiveThreshold(cv2.bilateralFilter(img, 9, 75, 75), 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 2)
    
    cv2.adaptiveThreshold(cv2.medianBlur(img, 3), 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 2)
    

J'ai récemment écrit un guide assez simple pour Tesseract, mais il devrait vous permettre d'écrire votre premier script OCR et d'éliminer certains obstacles que j'ai rencontrés lorsque les choses étaient moins claires que ce que j'aurais souhaité dans la documentation.

Au cas où vous souhaiteriez les consulter, je partage ici les liens avec vous:

9
bkaankuguoglu

Le seuillage adaptatif est important si l'éclairage est inégal sur l'image . Mon prétraitement à l'aide de GraphicsMagic est mentionné dans ce message: https://groups.google.com/forum/#!topic/tesseract-ocr/jONGSChLRv4

GraphicsMagic a également la fonctionnalité -lat pour Seuil adaptatif à temps linéaire que je vais essayer bientôt.

Une autre méthode de seuillage utilisant OpenCV est décrite ici: http://docs.opencv.org/trunk/doc/py_tutorials/py_imgproc/py_thresholding/py_thresholding.html

6
rleir

La documentation de Tesseract contient de bons détails sur comment améliorer la qualité de l'OCR via les étapes de traitement des images.

Dans une certaine mesure, Tesseract les applique automatiquement. Il est également possible de dire à Tesseract d’écrire une image intermédiaire pour inspection, c’est-à-dire pour vérifier l’efficacité du traitement interne de l’image (recherchez tessedit_write_images dans la référence ci-dessus).

Plus important encore, le nouveau système de réseau neuronal dans Tesseract 4 donne de bien meilleurs résultats OCR - en général et en particulier pour les images avec un peu de bruit. Il est activé avec --oem 1, par exemple. un péché:

$ tesseract --oem 1 -l deu page.png result pdf

(cet exemple sélectionne la langue allemande)

Il est donc judicieux de commencer par tester le nouveau mode Tesseract LSTM avant d’appliquer certaines étapes de traitement d’image de prétraitement personnalisées.

(à la fin de 2017, Tesseract 4 n'est pas encore publié, mais la version de développement est utilisable)

5
maxschlepzig

La lecture de texte à partir de documents image à l'aide de n'importe quel moteur OCR pose de nombreux problèmes pour obtenir une bonne précision. Il n'y a pas de solution fixe à tous les cas, mais voici quelques éléments à prendre en compte pour améliorer les résultats de la ROC.

1) Présence de bruit en raison d'une qualité d'image médiocre/d'éléments indésirables/de gouttes dans la zone d'arrière-plan. Cela nécessite des opérations de prétraitement telles que la suppression du bruit, ce qui peut être facilement fait en utilisant un filtre gaussien ou des méthodes de filtrage médian normales. Ceux-ci sont également disponibles dans OpenCV.

2) Mauvaise orientation de l'image: à cause d'une mauvaise orientation, le moteur d'OCR ne parvient pas à segmenter correctement les lignes et les mots dans l'image, ce qui donne la plus mauvaise précision.

3) Présence de lignes: lorsque vous effectuez une segmentation de mots ou de lignes, le moteur d'OCR tente parfois de fusionner les mots et les lignes pour ainsi traiter un contenu erroné et donner ainsi des résultats erronés. Il y a aussi d'autres problèmes, mais ceux-ci sont fondamentaux.

Cette application post- OCR est un exemple de cas dans lequel certains pré-prétraitements et post-traitements d’images sur des résultats OCR peuvent être appliqués pour obtenir une meilleure précision de l’OCR.

2
flamelite

Je les ai réalisées pour obtenir de bons résultats avec une image dont le texte n’est pas très petit.

  1. Appliquez un flou à l'image d'origine.
  2. Appliquer le seuil adaptatif.
  3. Appliquez un effet de netteté.

Et si vous n'obtenez toujours pas de bons résultats, ajustez l'image à 150% ou 200%.

2
Hamza Iqbal

La reconnaissance de texte dépend de divers facteurs pour produire une sortie de bonne qualité. La sortie OCR dépend fortement de la qualité de l'image d'entrée. C'est pourquoi chaque moteur d'OCR fournit des directives concernant la qualité de l'image d'entrée et sa taille. Ces directives aident le moteur de ROC à produire des résultats précis.

J'ai écrit un article détaillé sur le traitement d'images en python. Veuillez suivre le lien ci-dessous pour plus d'explications. Également ajouté le code source Python pour implémenter ces processus.

S'il vous plaît écrivez un commentaire si vous avez une suggestion ou une meilleure idée à ce sujet pour l'améliorer.

https://medium.com/cashify-engineering/improve-accuracy-of-ocr-using-image-preprocessing-8df29ec3a033

0
Brijesh Gupta