Guest Post: Microsoft OCR Library per Windows Runtime

 

Questo post è stato scritto da Massimo Bonanni di DomusDotNet, MVP di Visual Basic (twitter: @massimobonanni)

Qualche mese fa è stata rilasciata, in preview, una libreria per Windows Runtime che si occupa di OCR e in questo post vorrei mostrarvi come funziona.

Partiamo dalla pagina MSDN (http://msdn.microsoft.com/en-us/library/windows/apps/xaml/dn643522.aspx) che racchiude le Windows Preview API ovvero quelle librerie di Microsoft “sperimentali” non ancora entrate a far parte dell’SDK ufficiale.

In questa pagina troviamo l’insieme delle classi che vanno sotto il namespace WindowsPreview.Media.Ocr (da notare il namespace WindowsPreview che ci conferma il fatto che si tratta di una libreria che potrebbe subire dei cambiamenti in fase di rilascio definitivo).

La libreria funziona sulle piattaforme Windows Runtime, quindi Windows Phone e Windows Store App.

Per poter utilizzare l’OCR, dobbiamo referenziare il package utilizzando NuGet:

image

Nel package NuGet troviamo, oltre alla WindowsPreview.Media.Ocr.dll (per le differenti piattaforme supportate), anche l’OcrResourceGenerator, un eseguibile che ci servirà, più avanti, per abilitare le lingue di riconoscimento nella nostra applicazione.

La dll dell’Ocr utilizza la libreria Microsoft Visual C++ 2013 Runtime Package e, per questo motivo, non potremo utilizzare, all’interno del nostro progetto, la compilazione “Any CPU” ma dovremo scegliere la compilazione per un processore specifico (ARM se stiamo testando l’app su un device fisico, x86 se usiamo l’emulatore).

Se stiamo lavorando con le Windows Store App e vogliamo distribuire l’applicazione sia per x86 che per ARM, genereremo due package che sottometteremo singolarmente nello store come package della stessa app.

Il cuore della libreria dell’Ocr è la classe OcrEngine che si occupa dell’elaborazione vera e propria.

La classe è molto semplice ed espone, oltre al costruttore, il metodo RecognizeAsync che ci permette di eseguire l’analisi di un’immagine alla ricerca del testo.

clip_image002[7]

Per analizzare un’immagine alla ricerca del testo basta eseguire i passi:

1. Creare un’istanza della classe OcrEngine (passando la lingua che si desidera sia utilizzata per l’analisi del testo);

2. Richiamare il metodo asincrono RecognizeAsync passando le dimensioni e l’array di byte dell’immagine recuperando il risultato dell’analisi (di tipo OcrResult);

3. Utilizzare le proprietà del risultato per recuperare l’effettivo testo riconosciuto e la sua posizione nell’immagine.

Partiamo con ordine cominciando a vedere quali lingue sono supportate e cosa dobbiamo fare per poterle utilizzare.

Come possiamo vedere dall’immagine precedente, l’enumerazione OcrLanguage contiene 21 valori che rappresentano le lingue supportate al momento (fortunatamente per noi c’è anche l’italiano).

Quando referenziamo, tramite NuGet, il package della libreria, questo crea, all’interno del nostro progetto, una cartella denominata “OcrResources” al cui interno troviamo il file MsOcrRes.orp.

clip_image002[9]

Questo file contiene le risorse, localizzate nelle lingue che ci interessano, necessarie all’engine per analizzare il testo e, per default, contiene solo quelle relative all’inglese.

Per poter utilizzare altre lingue è necessario ricreare il file utilizzando l’OcrResourceGenerator di cui abbiamo parlato in precedenza.

Ci basta lanciarlo, selezionare le lingue che ci interessano, premere il tasto “Generate Resources” e salvare il file MsOcrRes.orp sostituendo quello di default presente nel progetto.

image

Ultima cosa di cui dobbiamo essere certi è che il file venga copiato nella cartella degli eseguibili (e aggiunto al package da distribuire nello store). Per fare questo è sufficiente selezionare il file di risorse all’interno del solution explorer, aprire le sue proprietà ed impostare la proprietà “Copy to Output Directory a “Copy Always” (o “Copy if Newer”).

clip_image002[11]

Se proviamo ad eseguire il metodo RecognizeAsync avendo impostato una lingua non presente nel file di risorse o se il file di risorse non è presente, otteniamo un’eccezione di tipo ArgumentException nel cui messaggio è chiaramente indicato il problema.

clip_image004[7]

Se il metodo RecognizeAsync non solleva eccezioni e se è stato rilevato del testo nell’immagine, otteniamo il risultato dell’analisi del testo come istanza della classe OcrResult.

clip_image006[6]

Il vincolo da tenere presente nell’utilizzo del metodo RecognizeAsync è che la dimensione maggiore dell’immagine non può superare i 2600 pixel mentre la minima è 40 pixel. Per questo motivo, nell’esempio allegato al post, se l’immagine selezionata ha una delle due dimensioni che supera i 2600 pixel, viene ridimensionata. Il formato dell’immagine deve essere BGRA.

Il testo riconosciuto viene suddiviso in linee (classe OcrLine) esposte dalla collezione Lines della classe OcrResult, e se non viene riconosciuto alcun testo, la collezione delle Lines sarà Nothing.

Ogni linea espone le parole riconosciute (classe OcrWord) nella collezione Words e, infine, ogni “word” contiene l’effettivo testo riconosciuto e i valori necessari a posizionarlo all’interno dell’immagine (punto in alto a sinistra del rettangolo che contiene la parole e dimensioni dello stesso).

La classe OcrResult espone anche l’angolo di inclinazione dell’intero testo (supponendo che tutte le linee siano parallele).

Nella versione attuale della libreria non è presente alcun indicatore di confidenza o attendibilità del risultato.

Per ricapitolare il tutto mostrando del codice, dobbiamo:

1) Creare l’istanza della classe OcrEngine:

Dim engine = New OcrEngine(OcrLanguage.Italian )

2) Eseguire il metodo RecognizeAsync:

Dim result = Await engine.RecognizeAsync

(CUInt(SelectedImage.Image.PixelHeight),                           CUInt(SelectedImage.Image.PixelWidth),

                            SelectedImage.Image.ToByteArray())

3) Analizzare il risultato:

If result.Lines IsNot Nothing andalso result.Lines.any() Then

    Dim newImage = SelectedImage.Image.Clone()    For Each line In result.Lines        Dim lineStr = line.Words _                      .Select(Function(a) a.Text) _                      .Aggregate(Function(a, b) a + " " + b)        lstResults.Items.Add(lineStr)        For Each word In line.Words

             newImage.FillRectangle(word.Left, word.Top,
                                    word.Left + word.Width,
                                    word.Top + word.Height,

                                   Colors.Yellow)        Next    Next

     imgSource.Source = newImage
 Else

    Await ShowMessageBoxDialog("No text detected!")End If

Nel progetto allegato al post, recuperiamo il risultato, visualizziamo le stringhe di testo riconosciute in una lista e evidenziamo, al di sopra dell’immagine utilizzata, la posizione delle stringhe stesse utilizzando dei rettangoli gialli.

Il risultato è mostrato nella seguente figura:

clip_image002[13]

La cosa interessante della libreria è che non necessita di connessione di rete per poter analizzare il testo e questo la rende sicuramente interessante per moltissime applicazioni.

La solution Visual Studio 2013 completa è disponibile all’indirizzo https://code.msdn.microsoft.com/Using-Ocr-in-Windows-Phone-15d872c2

Altre informazioni riguardo la libreria Ocr sono disponibili all’indirizzo http://blogs.windows.com/buildingapps/2014/09/18/microsoft-ocr-library-for-windows-runtime/ mentre all’indirizzo https://channel9.msdn.com/Blogs/DevRadio/Microsoft-DevRadio-Microsoft-OCR-Library-Enabling-Windows-apps-to-Recognize-Text-in-Images trovate un video in cui due membri del gruppo di sviluppo della libreria ne parlano.