Totali 192
Anonimi 192
Registrati 0

Username
Password
   
Ricordami
  Ho perso i dati   Nuovo utente        
Cerca
























 



 

Sistema sperimentale di OCR in C#
     
 

I sistemi di riconoscimento ottico dei caratteri, detti anche OCR (dall'inglese optical character recognition) sono programmi dedicati alla conversione di un'immagine contenente testo, solitamente acquisite tramite scanner, in testo digitale modificabile con un normale editor. Il testo può essere convertito in formato ASCII semplice, Unicode o, nel caso dei sistemi più avanzati, in un formato contenente anche l'impaginazione del documento.

 Nel giugno del 1916, la rivista scientifica italia "La scienza per tutti" pubblicò un articolo nel quale descriveva una macchina, alquanto verosimile capace di leggere e battere a macchina uno scritto. Si sa solo che si trattava di un brevetto sudamericano, ma la "macchina che legge e che scrive" si basava su un principio semplice quanto geniale; consisteva nella constatazione che ogni lettera dell’alfabeto ha nella sua forma un punto caratteristico che non si confonde con nessun altra lettera. Quindi, sovrapponendo tutte le lettere una sull’altra, era sempre possibile trovare almeno un punto che contraddistingueva ognuna delle lettere. L' utilità dell'invenzione, se pur molto audace, già all'epoca, era discutibile a causa di problemi quali le varie dimensioni e stili di caratteri.

Oggi, grazie a dispositivi di calcolo performanti il problema del matching dei caratteri è un problema secondario e risolvibile. Inoltre è possibile utilizzare anche metodi più sofisticati e perfomanti, come le reti neurali, la Support Vector Machine etc...
A scopo sperimentale ho scelto di intraprendere la strada storica per valutare le problematiche del sistema e trovare possibili soluzioni.

A scopo di facilitare la spiegazione di un tipico "use case"  ed il funzionamento a blocchi del sistema, allego la seguente immagine:

 

Il processo di estrazione del testo da un documento può essere rappresentato dai seguenti passi:

1-2) Acquisizione delle immagini tramite scanner.
3) Documento in formato immagine, scannerizzato. L'immagine è in formato colore (es RGB) e nella maggior parte dei casi è ruotata di qualche grado a causa di mal posizionamento 
del documento all'interno dello scanner
4) Processo di: - conversione in bianco e nero del documento (secondo una soglia), ridimensionamento del documento, ottimizzazione del contrasto. Deskew del documento, ovvero raddrizzamento dello stesso
in modo tale che le righe siano esattamente orizzontali e non ruotate. Leggero ritaglio o sbiadimento dei bordi.
5) Documento ottimizzato pronto per essere analizzato. Il documento viene trasformato in matrice bidimensionale.
6) Riconoscimento delle righe valutando per ogni riga di pixel il numeor di pixel non bianchi e valutando la continuità della riga rispetto alle precedenti (questo perchè le righe non possono essere di altezza troppo piccola)
7) Estrazione delle righe dal documento.
8) Individuazione di una riga (ciclo di processo delle righe)
9) Analisi delle lettere nella singola riga
10) Indivisuazione del singolo carattere della riga. Nel medesimo modo in cui sono analizzate e riconosciute le righe si individuano i caratteri che formano la riga.
11) Analisi del singolo carattere. Si ritaglia il carattere in modo che non vi sia margine (pixel bianchi). Si valutano tutti i possibili caratteri di un set di font (Arial, Times New Roman, etc..), e dopo averlo ritagliati, si ridimensionano in modo tale che abbiano la medesima dimensione del carattere da riconoscere. Si effettua il confronto e si calcola un valore di "fit" che valuta il grado di similitudine tra il carattere in esame e quelli del set di confronto.
12) Estrazione del testo utilizzando i dati dello step precdente.
13) Documento in formato elettronico (es txt, doc, pdf)

La parte preliminare del processo di riconoscimento ed estrazione del testo è il raddrizzamento (eventuale) della pagina, detto Deskew.
Il processo può essere rappresentato schematicamente dalla seguente immagine:
 

Si procede attraverso un ciclo a ruotare da -10 gradi a + 10 gradi l'immagine e a calcolare un valore di per ogni immagine ruotata.

L'immagine che avrà il valore più basso sarà l'immagine raddrizzata.

        /// <summary>

        /// Calculates Row height

        /// </summary>

        /// <param name="sourceImage"></param>

        /// <param name="lineImage"></param>

        /// <param name="angle"></param>

        /// <returns></returns>

        private double calculateWhiteRows(Bitmap sourceImage, int angle)

        {

            ImageReader ir = new ImageReader();

            ImageData imageTable = ir.GetImageData(sourceImage);

 

            double weight = 0;

 

            double with = ((double)sourceImage.Width);

            double height = ((double)sourceImage.Height);

 

            double startx = with / 4;

            double endx = with * 3/ 4;

 

            double starty = height / 4;

            double endy = height * 3 / 4;

 

            for (double row = starty; row <= endy - 1; row++) //  cicla le righe dell immagine (in verticale)

            {

                int ri = (int)row;

                double blankCount = 0;

 

                for (double col = startx; col <= endx - 1; col++) // cicla le colonne dell'immagine (le celle della riga row esima)

                {

                    int ci = (int)col;

 

                    int sourceImagePixelValue = imageTable.Pixels[ci, ri].GS;

 

                    if (sourceImagePixelValue > 140)

                    {

                        blankCount++;

                    }

                }

 

                double normalizedBlank = blankCount / (endx-startx);

 

                // riga senza intersezioni sotto soglia) overo pixel bianchi

                if (normalizedBlank >= 0.7d) // 0.8

                {

                    weight += 1;

                }

            }

 

            weight = weight / (endy-starty);

 

            return weight;

        }

 

All'immagine ottimizzata ì applicato un sistema di filtri che ne ottimizza il contrasto e la trasforma in bianco e nero

/// <summary>

        /// Optimize image

        /// </summary>

        /// <param name="sourceBitmap"></param>

        /// <returns></returns>

        public Bitmap Process(Bitmap sourceBitmap)

        {

            ImageReader ir = new ImageReader();

            ImageData imageTable = ir.GetImageData(sourceBitmap);

 

            double with = ((double)sourceBitmap.Width);

            double height = ((double)sourceBitmap.Height);

 

            int maxValue = 0;

            int minValue = 255;

 

            // DETERMINAZIONE MASSIMO E MINIMO VALORE

            for (double row = 0; row <= height - 1; row++) //  cicla le righe dell immagine (in verticale)

            {

                int ri = (int)row;

 

                for (double col = 0; col <= with - 1; col++) // cicla le colonne dell'immagine (le celle della riga row esima)

                {

                    int ci = (int)col;

 

                    int sourceImagePixelValue = imageTable.Pixels[ci, ri].GSM;

 

                    if (sourceImagePixelValue > maxValue)

                        maxValue = sourceImagePixelValue;

 

                    if (sourceImagePixelValue < minValue)

                        minValue = sourceImagePixelValue;

                }

            }

 

            double qp = 1;

            double qd = 20;

 

            double startx = with / qd;

            double endx = with * (qd-qp) / qd;

 

            double starty = height / qd;

            double endy = height * (qd - qp) / qd;

 

            for (double row = 0; row <= height - 1; row++) //  cicla le righe dell immagine (in verticale)

            {

                int ri = (int)row;

 

                for (double col = 0; col <= with - 1; col++) // cicla le colonne dell'immagine (le celle della riga row esima)

                {

                    int ci = (int)col;

 

                    double sourceImagePixelValue = imageTable.Pixels[ci, ri].GSM;

 

                    // SCALAMENTO FITTOTALEXURSION

                    sourceImagePixelValue = sourceImagePixelValue - minValue;

                    sourceImagePixelValue = sourceImagePixelValue * 255d/maxValue;

 

                    // SOGLIE B/N

 

                    if (sourceImagePixelValue > 190)

                        sourceImagePixelValue = 255;

 

                    if (sourceImagePixelValue < 80)

                        sourceImagePixelValue = 0;

 

                   

                    // TRATTAMENTO QUADRO

                    double sumq = 0;

 

                    if (row < starty && col < startx) // angolo l-u

                    {

                        sumq = Math.Abs(starty-row)/starty * 255d/1d;

                        sumq += Math.Abs(startx-col)/startx * 255d / 1d;

                    }

                   

                    if (row < starty && col > endx) // angolo r-u

                    {

                        sumq = Math.Abs(starty-row)/starty * 255d/1d;

                        sumq += Math.Abs(col-endx) / (with-endx) * 255d / 1d;

                    }

                   

                    if (row > endy && col < startx) // angolo l-b

                    {

                        sumq = Math.Abs(row - endy) / (height - endy) * 255d / 1d;

                        sumq += Math.Abs(startx - col) / startx * 255d / 1d;

                    }

                    if (row > endy && col > endx) // angolo r-b

                    {

                        sumq = Math.Abs(row - endy) / (height - endy) * 255d / 1d;

                        sumq += Math.Abs(col - endx) / (with - endx) * 255d / 1d;

                    }

                    if (row <= starty && col <= endx && col >= startx) // lato alto

                    {

                        sumq += Math.Abs(starty - row) / starty * 255d / 1d;

                    }

                    if (row >= endy && col <= endx && col >= startx) // lato basso

                    {

                        sumq += Math.Abs(row - endy) / (height - endy) * 255d / 1d;

                    }

                    if (col <= startx && row >= starty && row <= endy) // lato sinistra

                    {

                        sumq += Math.Abs(startx - col) / startx * 255d / 1d;

                    }

                    if (col >= endx && row >= starty && row <= endy) // lato destra

                    {

                        sumq += Math.Abs(col - endx) / (with - endx) * 255d / 1d;

                    }

 

                    sourceImagePixelValue += sumq;

 

                    // ASSEGNAZIONE E CONTROLLO

                    if (sourceImagePixelValue > 255)

                        sourceImagePixelValue = 255;

                    if (sourceImagePixelValue < 0)

                        sourceImagePixelValue = 0;

 

                    imageTable.Pixels[ci, ri].GS = (int)Math.Floor(sourceImagePixelValue);

                }

            }

 

 

            Bitmap optimizedImage = ir.GetImageFromData(imageTable,true);

            return optimizedImage;

        }

 

Il seguente codice permette di  individuare le righe:

       /// <summary>

        /// Create document by Bitmap

        /// </summary>

        /// <param name="sourceBitmap"></param>

        /// <returns></returns>

        public DocumentData OCRDocument(Bitmap sourceBitmap)

        {

            ImageReader ir = new ImageReader();

            ImageData imageTable = ir.GetImageData(sourceBitmap);

 

            DocumentData document = new DocumentData();

 

            double with = ((double)sourceBitmap.Width);

            double height = ((double)sourceBitmap.Height);

 

            List<int> blackRows = new List<int>();

            for (double row = 0; row <= height - 1; row++) //  cicla le righe dell immagine (in verticale)

            {

                int ri = (int)row;

 

                // CONTROLLO LINEA BIANCA O NO (interlinea)

                double totalPixels = 0;

                double maxPixels = 255 * with;

                for (double col = 0; col <= with - 1; col++) // cicla le colonne dell'immagine (le celle della riga row esima)

                {

                    int ci = (int)col;

 

                    int sourceImagePixelValue = imageTable.Pixels[ci, ri].GS;

 

                    totalPixels += sourceImagePixelValue;

                }

 

                double rappBlank = totalPixels / maxPixels;

 

                if (rappBlank < 0.95d)

                {

                    blackRows.Add(ri);

                }

            }

 

            // Creo la lista di oggetti riga

            List<RowData> rowDataList = new List<RowData>();

 

            double endy = 0;

            double starty = 0;

 

            int prevRow = -1;

    

            foreach (int row in blackRows) // cicla sulle righe "buone"

            {

                int ri = (int)row;

 

                bool isFirst = row == blackRows[0];

                bool isLast = row == blackRows[blackRows.Count-1];

 

                if (isFirst)

                {

                    // memorizza l'inizio riga

                    starty = row;

                    prevRow = row;

                    continue;

                }

 

                if (row == (prevRow + 1) && !isLast)

                {

                    prevRow = row;

                    continue;

                }

 

                endy = row;

 

                Point startPoint = new Point(0, (int)starty-4);

                if (startPoint.Y < 0)

                    startPoint.Y = 0;

 

                Point endPoint = new Point((int)with, (int)prevRow+4);

                if (endPoint.Y > height)

                    endPoint.Y = (int)height;

 

                RowData newRow = new RowData();

 

                newRow.StartingPoint = startPoint;

                newRow.EndingPoint = endPoint;

                rowDataList.Add(newRow);

 

                starty = row;

                prevRow = row;         

            }

 

 

            // cancella righe più basse di 3 px

            List<RowData> rowDataListToRemove = new List<RowData>();

            foreach (RowData rowData in rowDataList) // cicla sulle righe

            {

                Point startPoint = rowData.StartingPoint;

                Point endPoint = rowData.EndingPoint;

 

                int rowHeight = endPoint.Y - startPoint.Y;

 

                if (rowHeight < (3+8))

                    rowDataListToRemove.Add(rowData);

            }

            foreach (RowData rowData in rowDataListToRemove) // cicla sulle righe

            {

                rowDataList.Remove(rowData);

            }

 

            // riconosce l'inizio e la fine delle colonne delle varie righe

            foreach (RowData rowData in rowDataList) // cicla sulle righe

            {

                Point startPoint = rowData.StartingPoint;

                Point endPoint = rowData.EndingPoint;

 

                List<int> blackCols = new List<int>();

 

                int cpx = 0;

                for (double col = startPoint.X; col < endPoint.X; col++)

                {

 

                    int ci = (int)col;

                    int cpy = 0;

 

                    double sumColonna = 0;

                    double maxPixels = 0;

                    for (double row = startPoint.Y; row <= endPoint.Y; row++)

                    {

                        int ri = (int)row;

 

                        ImagePixelData ippd = imageTable.Pixels[ci, ri];

                       

                        int gs = ippd.GS;

                        if (gs < 25)

                            gs = 0;

 

                        sumColonna += gs;

                        maxPixels += 255;

                        cpx++;

                    }

 

                    double rappBlank = sumColonna / maxPixels;

 

                    if (rappBlank < 0.93d)

                    {

                        blackCols.Add(ci);

                    }

 

                    cpy++;

                }

 

                if (blackCols.Count > 0)

                {

                    int startCol = blackCols[0]-2;

                    if (startCol < 0)

                        startCol = 0;

                    rowData.StartingPoint = new Point(startCol, rowData.StartingPoint.Y);

                }

                if (blackCols.Count > 1)

                {

                    int endCol = blackCols[blackCols.Count - 1] + 2;

                    if (endCol > with)

                        endCol = (int)with;

                    rowData.EndingPoint = new Point(endCol, rowData.EndingPoint.Y);

                }

 

            }

 

            // crea le imageData dalle righe IMAGEDATA di valori dei  punti individuati

            foreach (RowData rowData in rowDataList) // cicla sulle righe

            {

                Point startPoint = rowData.StartingPoint;

                Point endPoint = rowData.EndingPoint;

 

                ImageData imageData = new ImageData();

                int dimx = endPoint.X-startPoint.X;

                int dimy = endPoint.Y-startPoint.Y;

                ImagePixelData[,] ipd = new ImagePixelData[dimx, dimy+1];

 

                int cpy = 0;

                for (double row = startPoint.Y; row <= endPoint.Y; row++)

                {

                    int ri = (int)row;

 

                    int cpx = 0;

                    for (double col = startPoint.X; col < endPoint.X; col++)

                    {

                        int ci = (int)col;

                       

                        ImagePixelData ippd = imageTable.Pixels[ci, ri];

                        ipd[cpx, cpy] = ippd;

 

                        cpx++;

                    }

 

                    cpy++;

                }

 

                imageData.Pixels = ipd;

                rowData.ImageData = imageData;

            }

 

            // Crea le parole (words)

 

            // riconosce l'inizio e la fine delle colonne delle varie righe per le parole come per i bound di riga

            foreach (RowData rowData in rowDataList) // cicla sulle righe

            {

                Point startPoint = rowData.StartingPoint;

                Point endPoint = rowData.EndingPoint;

 

                List<int> blackCols = new List<int>();

 

                int cpx = 0;

                for (double col = startPoint.X; col < endPoint.X; col++)

                {

 

                    int ci = (int)col;

                    int cpy = 0;

 

                    double sumColonna = 0;

                    double maxPixels = 0;

                    for (double row = startPoint.Y; row <= endPoint.Y; row++)

                    {

                        int ri = (int)row;

 

                        ImagePixelData ippd = imageTable.Pixels[ci, ri];

                       

                        int gs = ippd.GS;

                        if (gs < 25)

                            gs = 0;

 

                        sumColonna += gs;

                        maxPixels += 255;

                        cpx++;

                    }

 

                    double rappBlank = sumColonna / maxPixels;

 

                    if (rappBlank < 0.96d)

                    {

                        blackCols.Add(ci);

                    }

                    cpy++;

                }

 

               // devo riconoscere le singole parole

                List<WordData> wordList = new List<WordData>();

 

                double endx = 0;

                double startx = 0;

 

                int prevCol = -1;

 

                foreach (int col in blackCols) // cicla sulle righe "buone"

                {

                    int rii = (int)col;

 

                    bool isFirst = rii == blackCols[0];

                    bool isLast = rii == blackCols[blackCols.Count - 1];

 

                    if (isFirst)

                    {

                        // memorizza l'inizio riga

                        startx = col;

                        prevCol = col;

                        continue;

                    }

 

                    if (col == (prevCol + 1) && !isLast)

                    {

                        prevCol = col;

                        continue;

                    }

 

                    // Salta gli spazi piccoli tra le lettere e non gli spazi

                    if ((col - prevCol) < 5 && !isLast)

                    {

                        prevCol = col;

                        continue;

                    }

 

                    endx = col;

 

                    Point startWordPoint = new Point((int)startx - 1, startPoint.Y);

                    if (startWordPoint.X < startPoint.X)

                        startWordPoint.X = startPoint.X;

 

                    Point endWordPoint = new Point((int)prevCol + 3, endPoint.Y);

                    if (endWordPoint.X > endPoint.X)

                        endWordPoint.X = (int)endPoint.X;

 

                    WordData newWord = new WordData();

 

                    newWord.StartingPoint = startWordPoint;

                    newWord.EndingPoint = endWordPoint;

                    wordList.Add(newWord);

 

                    startx = col;

                    prevCol = col;

                }

 

                rowData.WordList = wordList;

            }

 

            // crea le imageData dalle righe IMAGEDATA di valori dei  punti individuati

            foreach (RowData rowData in rowDataList) // cicla sulle righe

            {

                foreach (WordData wordData in rowData.WordList) // cicla sulle righe

                {

                    Point startPoint = wordData.StartingPoint;

                    Point endPoint = wordData.EndingPoint;

 

                    ImageData imageData = new ImageData();

                    int dimx = endPoint.X - startPoint.X;

                    int dimy = endPoint.Y - startPoint.Y;

                    ImagePixelData[,] ipd = new ImagePixelData[dimx, dimy + 1];

 

                    int cpy = 0;

                    for (double row = startPoint.Y; row <= endPoint.Y; row++)

                    {

                        int ri = (int)row;

 

                        int cpx = 0;

                        for (double col = startPoint.X; col < endPoint.X; col++)

                        {

                            int ci = (int)col;

 

                            ImagePixelData ippd = imageTable.Pixels[ci, ri];

                            ipd[cpx, cpy] = ippd;

 

                            cpx++;

                        }

 

                        cpy++;

                    }

 

                    imageData.Pixels = ipd;

                    wordData.ImageData = imageData;

                }  

            }

 

 

            // Crea i singoli caratteri (Chars)

 

            // riconosce l'inizio e la fine delle colonne delle varie righe per le parole come per i bound di riga

            foreach (RowData rowData in rowDataList) // cicla sulle righe

            {

                foreach (WordData wordData in rowData.WordList) // cicla sulle parole

                {

                    Point startPoint = wordData.StartingPoint;

                    Point endPoint = wordData.EndingPoint;

 

                    double wordHeight = endPoint.Y - startPoint.Y;

 

                    List<int> blackCols = new List<int>();

 

                    int cpx = 0;

                    double sumSoglia = 0; // voglio che valga 1 quando la larghezza è uguale alla altezza (wordHeight) della riga

                    double sumRiga = 0;

 

                    double charRef = startPoint.X;

                    for (double col = startPoint.X; col < endPoint.X; col++)

                    {

                        sumRiga++;

                        double charlenght = (col - charRef) / wordHeight;

                        sumSoglia = sumRiga / wordHeight;

 

                        int ci = (int)col;

                        int cpy = 0;

 

                        // INIZIO CALCOLO PIXEL NELLA COLONNA DI INTERESSE

                        double sumColonna = 0;

                        double maxPixels = 0;

                        for (double row = startPoint.Y; row <= endPoint.Y; row++)

                        {

                            int ri = (int)row;

 

                            ImagePixelData ippd = imageTable.Pixels[ci, ri];

                            int gs = ippd.GS;

                            if (gs > 180)

                                gs = 255;

                            if (gs < 180)

                                gs = 0;

                            sumColonna += gs;

                            maxPixels += 255;

                            cpx++;

                        }

                        // FINE CALCOLO PIXEL NELLA COLONNA DI INTERESSE

 

                        double rappBlank = sumColonna / maxPixels;

 

                        //double rm = (1-charlenght*0.9d);

                        //rappBlank *= rm;

 

                        double soglia = 0.94d + charlenght * 0.02d; //(aggiunge 1 quando siamo alla fine)

                        if (rappBlank < soglia)

                        {

                            charRef = col;

                            sumRiga = 0;

                            blackCols.Add(ci);

                        }

                        cpy++;

                    }

 

                    // devo riconoscere i singoli caratteri

                    List<CharData> charList = new List<CharData>();

 

                    double endx = 0;

                    double startx = 0;

 

                    int prevCol = -1;

 

                    foreach (int col in blackCols) // cicla sulle righe "buone"

                    {

                        int rii = (int)col;

 

                        bool isFirst = rii == blackCols[0];

                        bool isLast = rii == blackCols[blackCols.Count - 1];

 

                        if (isFirst)

                        {

                            // memorizza l'inizio riga

                            startx = col;

                            prevCol = col;

                            continue;

                        }

 

                        double charWith = (col - prevCol) + 3;// vedi sotto per il 3;

 

                        if (!isLast)

                        {

                            if (col == (prevCol + 1))

                            {

                                prevCol = col;

                                continue;

                            }

 

                        }

                        endx = col;

 

                        Point startWordPoint = new Point((int)startx - 1, startPoint.Y);

                        if (startWordPoint.X < startPoint.X)

                            startWordPoint.X = startPoint.X;

 

                        Point endWordPoint = new Point((int)prevCol + 2, endPoint.Y);

                        if (endWordPoint.X > endPoint.X)

                            endWordPoint.X = (int)endPoint.X;

 

                        CharData newChar = new CharData();

 

                        newChar.StartingPoint = startWordPoint;

                        newChar.EndingPoint = endWordPoint;

 

                        charList.Add(newChar);

 

                        startx = col;

                        prevCol = col;

                    }

 

                    wordData.CharList = charList;

                }

            }

 

            // crea le imageData dalle righe IMAGEDATA di valori dei  punti individuati

            foreach (RowData rowData in rowDataList) // cicla sulle righe

            {

                foreach (WordData wordData in rowData.WordList) // cicla sulle parole

                {

                    foreach (CharData charData in wordData.CharList) // cicla sui caratteri

                    {

                        Point startPoint = charData.StartingPoint;

                        Point endPoint = charData.EndingPoint;

 

                        ImageData imageData = new ImageData();

                        int dimx = endPoint.X - startPoint.X;

                        int dimy = endPoint.Y - startPoint.Y;

                        ImagePixelData[,] ipd = new ImagePixelData[dimx, dimy + 1];

 

                        int cpy = 0;

                        for (double row = startPoint.Y; row <= endPoint.Y; row++)

                        {

                            int ri = (int)row;

 

                            int cpx = 0;

                            for (double col = startPoint.X; col < endPoint.X; col++)

                            {

                                int ci = (int)col;

 

                                ImagePixelData ippd = imageTable.Pixels[ci, ri];

                                ipd[cpx, cpy] = ippd;

 

                                cpx++;

                            }

 

                            cpy++;

                        }

 

                        imageData.Pixels = ipd;

                        charData.ImageData = imageData;

                        charData.StartingPoint = startPoint;

                        charData.EndingPoint = endPoint;

                    }

                }

            }

 

            // NOTA

            // La soglia piuttosto elevata del processo precedente ha dato luogo a non perfetta divisione delle lettere

            // si provvede a dividere ancora successivamente (es parola 3 riga 2 scansione_3_0

 

            for (int ciclo = 0; ciclo < 50; ciclo++)

            {

                int nonriconosciuti = 0;

 

                // crea le imageData dalle righe IMAGEDATA di valori dei  punti individuati

                foreach (RowData rowData in rowDataList) // cicla sulle righe

                {

                    foreach (WordData wordData in rowData.WordList) // cicla sulle parole

                    {

                        List<CharData> modifiedChardataList = new List<CharData>();

 

                        foreach (CharData charData in wordData.CharList) // cicla sui caratteri

                        {

                            Point startPoint = charData.StartingPoint;

                            Point endPoint = charData.EndingPoint;

 

                            ImagePixelData[,] ipd = charData.ImageData.Pixels;

 

                            double charWith = ipd.GetLength(0);

                            double charHeight = ipd.GetLength(1);

 

                            if (charWith > (charHeight*0.9d))

                            {

                                // DEVO SPEZZETTARE ANCORA perchè il carettere è troppo lungo. Devo partire dall'inizio e trovare il punto di minimo.

 

                                bool devoInserire = true;

                                int cpy = 0;

                                for (double col = startPoint.X; col < endPoint.X; col++)

                                {

                                    int ci = (int)col;

 

                                    double sumColonna = 0;

                                    double maxPixels = 0;

 

                                    int cpx = 0;

                                    for (double row = startPoint.Y; row <= endPoint.Y; row++)

                                    {

                                        int ri = (int)row;

 

                                        ImagePixelData ippd = imageTable.Pixels[ci, ri];

 

                                        int gs = ippd.GS;

                                        if (gs > 180)

                                            gs = 255;

                                        if (gs < 180)

                                            gs = 0;

 

                                        sumColonna += ippd.GS;

                                        maxPixels += 255;

 

                                        cpx++;

                                    }

 

                                    double soglia = sumColonna / maxPixels;

 

                                    double rappxy = cpy /charHeight;

                                    double rappxyinv = Math.Abs((charHeight /(charHeight+cpy))*0.2d-0.2d)*1.5d;

 

                                    double contributoCiclo = ciclo / 10d;

 

                                    double sogliaPar = 0.80d + contributoCiclo;// +rappxyinv;

 

                                    if (soglia < sogliaPar && rappxy > 0.4d)

                                    {

                                        // spezzettamento parte sinistra

                                        Point strartX_SX = startPoint;

                                        Point endX_SX = new Point((int)col, endPoint.Y);

 

                                        CharData charData_SX = new CharData();

                                        charData_SX.StartingPoint = strartX_SX;

                                        charData_SX.EndingPoint = endX_SX;

                                        modifiedChardataList.Add(charData_SX);

 

                                        // spezzettamento parte destra

                                        Point strartX_DX = new Point((int)col, startPoint.Y);

                                        Point endX_DX = new Point(endPoint.X, endPoint.Y);

 

                                        CharData charData_DX = new CharData();

                                        charData_DX.StartingPoint = strartX_DX;

                                        charData_DX.EndingPoint = endX_DX;

                                        modifiedChardataList.Add(charData_DX);

 

                                        devoInserire = false;

                                        break;

                                    }

 

                                    cpy++;

                                }

 

                                if (devoInserire)

                                {

                                    modifiedChardataList.Add(charData);

                                    nonriconosciuti++;

                                }

                            }

                            else

                            {

                                modifiedChardataList.Add(charData);

                                nonriconosciuti++;

                            }

                        }

 

                        wordData.CharList = modifiedChardataList;

                    }

                }

           

                // crea le imageData dalle righe IMAGEDATA di valori dei  punti individuati

                foreach (RowData rowData in rowDataList) // cicla sulle righe

                {

                    foreach (WordData wordData in rowData.WordList) // cicla sulle parole

                    {

                        foreach (CharData charData in wordData.CharList) // cicla sui caratteri

                        {

                            Point startPoint = charData.StartingPoint;

                            Point endPoint = charData.EndingPoint;

 

                            ImageData imageData = new ImageData();

                            int dimx = endPoint.X - startPoint.X;

                            int dimy = endPoint.Y - startPoint.Y;

                            ImagePixelData[,] ipd = new ImagePixelData[dimx, dimy + 1];

 

                            int cpy = 0;

                            for (double row = startPoint.Y; row <= endPoint.Y; row++)

                            {

                                int ri = (int)row;

 

                                int cpx = 0;

                                for (double col = startPoint.X; col < endPoint.X; col++)

                                {

                                    int ci = (int)col;

 

                                    ImagePixelData ippd = imageTable.Pixels[ci, ri];

                                    ipd[cpx, cpy] = ippd;

 

                                    cpx++;

                                }

 

                                cpy++;

                            }

 

                            imageData.Pixels = ipd;

                            charData.ImageData = imageData;

                            charData.StartingPoint = startPoint;

                            charData.EndingPoint = endPoint;

                        }

                    }

                }

 

                if (nonriconosciuti == 0)

                    break;

            }

            document.Rows = rowDataList;

 

            return document;

        }

La parte che effettua il riconoscimento del caratetre è invece la seguente:

/// <summary>

        /// Complete process document

        /// </summary>

        /// <param name="documentData"></param>

        /// <param name="fontItems"></param>

        /// <returns></returns>

        public DocumentData ComputeRecognition(DocumentData documentData,FontItems fontItems)

        {

            FontReader fontReader = new FontReader();

 

            StringBuilder sb = new StringBuilder();

 

            int cntRow = 0;

            foreach (RowData row in documentData.Rows)

            {

                cntRow++;

 

                if (cntRow > 1)

                    sb.AppendLine();

                int cntWord = 0;

                foreach (WordData word in row.WordList)

                {

                    cntWord++;

 

                    if (cntWord > 1)

                        sb.Append(" ");

 

                    int numChar = 1;

                    string lastChar = string.Empty;

                    foreach (CharData chard in word.CharList)

                    {

                        bool isLast = word.CharList.Count == numChar;

                        CharDataRecognitionResult charDataRecognitionResult = fontReader.RecognizeCharacter(chard, fontItems, numChar, lastChar, isLast);

                        charDataRecognitionResult.FontItem = null;

                        charDataRecognitionResult.ImageData = null;

                        charDataRecognitionResult.ImageDataOriginalRetailed = null;

 

                        chard.RecognitionResult = charDataRecognitionResult;

                        chard.RecognizedChar = charDataRecognitionResult.RecognizedChar == string.Empty ? "#" : charDataRecognitionResult.RecognizedChar;

                       

                        lastChar = charDataRecognitionResult.RecognizedChar;

                        numChar++;

 

                        sb.Append(chard.RecognizedChar);

 

                        if (GetText != null)

                        {

                            string extractedTextPart = sb.ToString();

                            string operation = GetText.Invoke(extractedTextPart, cntWord, cntRow);

                            if (operation == "annulla")

                                return documentData;

                        }

                    }

                }

            }

 

            string extractedText = sb.ToString();

            documentData.ExtractedText = extractedText;

 

            return documentData;

        }


 

L'interfaccia grafica sviluppata è puramente a scopo di debug e perfezionamento del sistema.

 

Visualizzazione del documento originale, non perfettamente contrastato, leggermente sbiadito, con aloni, bordi mal scasionati e ruotato.

 

Documento ottimizzato e processato con il Deskew

Individuazione delle righe, riconoscimento dei caratteri per ogni singola riga ed analisi degli stessi tramite confronto con il set di caratteri (font).

Font browser.


 



Download

Scarica la soluzione completa (13 MB)
 
 

 
Commenti degli utenti:

Scrivi un commento
     
 
Autenticarsi per inserire un commento

Label