elaborazione delle immagini per migliorare la precisione dell'OCR tesseract


146

Ho usato tesseract per convertire documenti in testo. La qualità dei documenti varia enormemente e sto cercando consigli su quale tipo di elaborazione delle immagini potrebbe migliorare i risultati. Ho notato che il testo altamente pixellato - ad esempio quello generato dai fax - è particolarmente difficile da elaborare per tesseract - presumibilmente tutti quei bordi frastagliati rispetto ai caratteri confondono gli algoritmi di riconoscimento delle forme.

Quale tipo di tecniche di elaborazione delle immagini migliorerebbe l'accuratezza? Ho usato una sfocatura gaussiana per appianare le immagini pixellate e ho visto alcuni piccoli miglioramenti, ma spero che esista una tecnica più specifica che produca risultati migliori. Pronuncia un filtro adattato alle immagini in bianco e nero, che appianerebbe i bordi irregolari, seguito da un filtro che aumenterebbe il contrasto per rendere i caratteri più distinti.

Qualche consiglio generale per chi è alle prime armi nell'elaborazione delle immagini?

Risposte:


104
  1. correggere DPI (se necessario) 300 DPI è minimo
  2. correggi la dimensione del testo (es. 12 pt dovrebbe essere ok)
  3. prova a correggere le righe di testo (inclina e scompone il testo)
  4. prova a correggere l'illuminazione dell'immagine (ad es. nessuna parte scura dell'immagine)
  5. binarizzare e de-noise immagine

Non esiste una riga di comando universale adatta a tutti i casi (a volte è necessario sfocare e rendere più nitida l'immagine). Ma puoi provare TEXTCLEANER dagli script ImageMagick di Fred .

Se non sei fan della riga di comando, forse puoi provare a utilizzare opensource scantailor.sourceforge.net o bookrestorer commerciale .


6
E c'è una guida illustrata su come farlo: code.google.com/p/tesseract-ocr/wiki/ImproveQuality
iljau,

2
Nota, lo script collegato sembra essere solo linux.
Zoran Pavlovic,

1
Questo non è vero - questo è uno script bash. Se hai installato bash e ImageMagick funzionerà anche su Windows. Bash potrebbe essere installato come parte di altri software utili, ad esempio git o msys2 ...
user898678

6
@iljau Da quando si è trasferito su github. pagina wiki è a: github.com/tesseract-ocr/tesseract/wiki/ImproveQuality
hometoast

2
I documenti di Tesseract sono stati spostati di nuovo su tesseract-ocr.github.io/tessdoc/ImproveQuality
Nessuno il

73

Non sono affatto un esperto OCR. Ma questa settimana avevo bisogno di convertire il testo da un jpg.

Ho iniziato con un jpg colorato 445x747 pixel RGB. Ho subito provato tesseract su questo, e il programma non ha convertito quasi nulla. Sono quindi entrato in GIMP e ho fatto quanto segue. immagine> modalità> immagine in scala di grigi> scala immagine> filtri 1191x2000 pixel> migliora> maschera di contrasto con valori di raggio = 6,8, quantità = 2,69, soglia = 0 Ho quindi salvato come nuovo jpg al 100% di qualità.

Quindi Tesseract è stato in grado di estrarre tutto il testo in un file .txt

Gimp è tuo amico.


11
+1 Ho seguito i tuoi passi e ho ottenuto un grande miglioramento. Grazie
onof

1
Ho anche l'impressione che Tesseract funzioni meglio se converti l'input in un file TIFF e dai a Tesseract il TIFF (invece di chiedere a Tesseract di fare la conversione per te). ImageMagick può fare la conversione per te. Questa è la mia impressione aneddotica, ma non l'ho testato attentamente, quindi potrebbe essere sbagliato.
DW

+1 Il filtro "maschera di contrasto" ha davvero reso la mia giornata. Un altro passo che mi ha aiutato: usando lo strumento "selezione fuzzy" seleziona lo sfondo e poi premi Canc per forzarlo
Davide

Sono bloccato su questo problema di elaborazione delle immagini prima del riconoscimento tesseract stackoverflow.com/questions/32473095/… Puoi aiutarmi qui?
Hussain,

no. ho provato a renderlo più grande e impostarlo in scala di grigi sembra che nulla mi dia risultati positivi. Sospiro :( Controlla questo obiettivo: freesms4us.com/…
gumuruh

30

Tre punti per migliorare la leggibilità dell'immagine: 1) Ridimensionare l'immagine con altezza e larghezza variabili (moltiplicare 0,5 e 1 e 2 per altezza e larghezza dell'immagine). 2) Convertire l'immagine in formato scala di grigi (bianco e nero). 3) Rimuovere i pixel di disturbo e rendere più chiaro (Filtra l'immagine).

Fare riferimento al codice seguente:

//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;
        }

IMMAGINE IN INGRESSO
IMMAGINE IN INGRESSO

IMMAGINE DI USCITA IMMAGINE DI USCITA


Sì. Dobbiamo passare il parametro richiesto al metodo di ridimensionamento, eseguirà il ridimensionamento anticipato, l'operazione SetGrayscale e RemoveNoise, quindi restituirà l'immagine di output con una migliore leggibilità.
Sathyaraj Palanisamy,

Ho provato questo approccio su una serie di file e confrontato con il risultato iniziale. In alcuni casi limitati fornisce risultati migliori, principalmente si è verificata una leggera riduzione della qualità del testo di output. Quindi, non sembra una soluzione universale.
Bryn,

Questo in realtà ha funzionato abbastanza bene per me. Certamente fornisce un punto di partenza per la pre-elaborazione delle immagini che rimuove la quantità di incomprensibile ritorno da Tesseract.
SES

22

Come regola generale, di solito applico le seguenti tecniche di pre-elaborazione delle immagini usando la libreria OpenCV:

  1. Riscalare l'immagine (è consigliabile se lavori con immagini che hanno un DPI inferiore a 300 dpi):

    img = cv2.resize(img, None, fx=1.2, fy=1.2, interpolation=cv2.INTER_CUBIC)
    
  2. Convertire l'immagine in scala di grigi:

    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
  3. Applicazione di dilatazione ed erosione per rimuovere il rumore (è possibile giocare con le dimensioni del kernel in base al set di dati):

    kernel = np.ones((1, 1), np.uint8)
    img = cv2.dilate(img, kernel, iterations=1)
    img = cv2.erode(img, kernel, iterations=1)
    
  4. L'applicazione della sfocatura, che può essere eseguita utilizzando una delle seguenti righe (ognuna delle quali ha i suoi pro e contro, tuttavia, la sfocatura mediana e il filtro bilaterale di solito funzionano meglio della sfocatura gaussiana.):

    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)
    

Di recente ho scritto una guida piuttosto semplice a Tesseract, ma dovrebbe permetterti di scrivere il tuo primo script OCR e chiarire alcuni ostacoli che ho riscontrato quando le cose erano meno chiare di quanto mi sarebbe piaciuto nella documentazione.

Nel caso in cui desideri verificarli, qui condivido i link con te:


perché convertiamo l'immagine in scala di grigi? Per essere più specifici, ho visto nel processo di rilevamento delle immagini, l'immagine viene prima convertita in scala di grigi, quindi sobel-> MSER -> SWT. potresti per favore elaborarlo? Sono nuovo nel campo IP.
OnePunchMan

Per quanto riguarda la mia comprensione, dipende dall'algoritmo, alcuni potrebbero non aver bisogno di convertirli affatto. Pensa ai pixel come a pochi valori di colore memorizzati digitalmente, nel caso di RGB, rosso, verde e blu-. Quando un pixel viene convertito nella scala B / N, l'algoritmo deve funzionare solo su 2 dimensioni anziché su 3. Ciò comporta evidenti vantaggi in termini di velocità quando si esegue l'algoritmo su pixel uno per uno. Inoltre, alcuni potrebbero anche dire che è più facile rimuovere il disturbo e rilevare i bordi di un'immagine quando viene convertita in scala di grigi.
bkaankuguoglu,

Grazie per la risposta. E riguardo al tuo blog, potresti scriverne uno su COME COSTRUIRE L'OCR DA SCRATCH UTILIZZANDO TESSERACT per la scrittura non romana. Ho cercato ovunque, tutto ciò che è disponibile giusto non è chiaro.
OnePunchMan,

16

Questo è un po 'di tempo fa, ma potrebbe ancora essere utile.

La mia esperienza dimostra che il ridimensionamento dell'immagine in memoria prima di passarlo a tesseract a volte aiuta.

Prova diverse modalità di interpolazione. Il post https://stackoverflow.com/a/4756906/146003 mi ha aiutato molto.


15

Ciò che mi è stato di ALTISSIMO aiuto in questo modo sono i codici sorgente per il progetto Capture2Text. http://sourceforge.net/projects/capture2text/files/Capture2Text/ .

BTW: Complimenti al suo autore per aver condiviso un algoritmo così scrupoloso.

Presta particolare attenzione al file Capture2Text \ SourceCode \ leptonica_util \ leptonica_util.c - questa è l'essenza della preelaborazione delle immagini per questa utility.

Se eseguirai i binari, puoi verificare la trasformazione dell'immagine prima / dopo il processo nella cartella Capture2Text \ Output \.

La soluzione citata da PS utilizza Tesseract per OCR e Leptonica per la preelaborazione.


1
Grazie per lo strumento Capture2Text. Risolve perfettamente tutti i problemi di OCR nel mio progetto!
Lê Quang Duy,

12

Versione Java per il codice di Sathyaraj sopra:

// 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;
}

Qual è la tua classe per Bitmap? Bitmap non si trova in Java (è nativo in Android).
Siamo Borg il

Questo metodo genera un'eccezione: Causato da: java.lang.IllegalArgumentException: y deve essere <bitmap.height ()
Nativ

9

La documentazione di Tesseract contiene alcuni buoni dettagli su come migliorare la qualità OCR attraverso le fasi di elaborazione delle immagini.

In una certa misura, Tesseract li applica automaticamente. È anche possibile dire a Tesseract di scrivere un'immagine intermedia per l'ispezione, vale a dire verificare il funzionamento dell'elaborazione interna dell'immagine (cercare tessedit_write_imagesnel riferimento sopra).

Ancora più importante, il nuovo sistema di rete neurale in Tesseract 4 produce risultati OCR molto migliori - in generale e soprattutto per le immagini con un po 'di rumore. È abilitato con --oem 1, ad esempio come in:

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

(questo esempio seleziona la lingua tedesca)

Pertanto, ha senso verificare prima di quanto si arriva con la nuova modalità LSTM di Tesseract prima di applicare alcune fasi di elaborazione delle immagini di pre-elaborazione personalizzate.


6

La soglia adattativa è importante se l'illuminazione non è uniforme sull'immagine. La mia preelaborazione con GraphicsMagic è menzionata in questo post: https://groups.google.com/forum/#!topic/tesseract-ocr/jONGSChLRv4

GraphicsMagic ha anche la funzione -lat per la soglia adattiva del tempo lineare che proverò presto.

Un altro metodo di soglia con OpenCV è descritto qui: http://docs.opencv.org/trunk/doc/py_tutorials/py_imgproc/py_thresholding/py_thresholding.html


2
Il collegamento OpenCV è stato modificato. Nella documentazione di OpenCV si tratta di OpenCV-Python Tutorials> Elaborazione di immagini in OpenCV> Soglia di immagine
richk

2

Ho fatto questi per ottenere buoni risultati da un'immagine che non ha testo molto piccolo.

  1. Applica sfocatura all'immagine originale.
  2. Applica soglia adattiva.
  3. Applica l'effetto di nitidezza.

E se i risultati non sono ancora buoni, ridimensiona l'immagine al 150% o al 200%.


2

La lettura di testo da documenti di immagini mediante qualsiasi motore OCR presenta numerosi problemi per ottenere una buona precisione. Non esiste una soluzione fissa per tutti i casi, ma qui ci sono alcune cose che dovrebbero essere considerate per migliorare i risultati OCR.

1) Presenza di rumore a causa della scarsa qualità dell'immagine / elementi / chiazze indesiderate nella regione di sfondo. Ciò richiede alcune operazioni di pre-elaborazione come la rimozione del rumore che può essere facilmente eseguita utilizzando il filtro gaussiano o i normali metodi di filtro mediano. Questi sono disponibili anche in OpenCV.

2) Orientamento errato dell'immagine: a causa di un orientamento errato, il motore OCR non riesce a segmentare correttamente le linee e le parole nell'immagine, il che dà la peggiore precisione.

3) Presenza di linee: durante la segmentazione di parole o linee, il motore OCR a volte cerca anche di unire le parole e le linee e quindi elaborare contenuti sbagliati e quindi dare risultati errati. Ci sono anche altri problemi, ma questi sono quelli di base.

Questa applicazione post OCR è un caso esemplificativo in cui è possibile applicare alcune pre-preelaborazioni di immagini e post-elaborazione sul risultato OCR per ottenere una migliore precisione OCR.


1

Il riconoscimento del testo dipende da una varietà di fattori per produrre un output di buona qualità. L'output OCR dipende in larga misura dalla qualità dell'immagine in ingresso. Questo è il motivo per cui ogni motore OCR fornisce linee guida per quanto riguarda la qualità dell'immagine in ingresso e le sue dimensioni. Queste linee guida aiutano il motore OCR a produrre risultati accurati.

Ho scritto un articolo dettagliato sull'elaborazione delle immagini in Python. Si prega di seguire il link qui sotto per ulteriori spiegazioni. Aggiunto anche il codice sorgente di Python per implementare quel processo.

Scrivi un commento se hai un suggerimento o un'idea migliore su questo argomento per migliorarlo.

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


2
Aggiungi una risposta qui come riepilogo del tuo blog. In modo che anche se il collegamento è morto, la risposta non sarà resa inutile.
Nithin,

0

puoi fare la riduzione del rumore e quindi applicare la soglia, ma puoi farlo con la configurazione dell'OCR modificando i valori --psm e --oem

prova: --psm 5 --oem 2

puoi anche guardare il seguente link per ulteriori dettagli qui

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.