Desarrollando un servicio web con WindowsPreview.Media.Ocr

/ / Blog, DevOps, Digital Development
Creando presentaciones con Markdown y herramientas Open Source
keensoft patrocina la Welcome Party de la BeeCon 2016

En este artículo vamos a hablar acerca de cómo generar un servicio web utilizando la biblioteca OCR de Windows. El artículo se va a distribuir en dos partes: en la primera parte introduciremos algunas de las características generales de la biblioteca OCR, y en la segunda parte entraremos en detalle a explicar como generar un servicio web que haga uso de este motor OCR.

 

Introducción

La biblioteca WindowsPreview.Media.Ocr proporciona una serie de clases para el reconocimiento óptico de caracteres. Esta biblioteca solo funciona en sistemas operativos Windows que permitan la instalación de aplicaciones metro (Windows 8, Windows 8.1, Windows 10, Windows Server 2012 y Windows Server 2016). En Windows 10 y en Windows Server 2016 la biblioteca se incluye de forma nativa en la plataforma aunque con el nombre de Windows.Media.Ocr.

El motor de OCR se encarga de leer imágenes con texto y devolver el texto reconocido junto a la información recopilada en el análisis, como por ejemplo las coordenadas en la cuales se ha encontrado dicho texto dentro de la imagen.

La biblioteca tiene una serie de limitaciones, como por ejemplo en las dimensiones de la imagen a tratar (estas deben ser como mínimo de 40 por 40 píxeles, y como máximo de 2600 por 2600 píxeles). Además la orientación del texto de la imagen debe ser horizontal, y las líneas deben tener la misma dirección (aunque el motor de OCR puede corregir una rotación de hasta ± 40 grados). Hay casos en los cuales se pueden obtener medidas inadecuadas, por ejemplo: en imágenes borrosas, texto escrito a mano o en cursiva, estilos de fuentes artísticas, tamaño de letra pequeño (menos de 15 píxeles para los idiomas occidentales, o menos de 20 píxeles para los idiomas de Asia oriental), fondos complejos, textos con sombras o reflejos, etc. El número de lenguajes soportados por el motor OCR son 21, aunque en la nueva actualización se esperan 25 lenguajes diferentes. Se debe tener en cuenta que no vienen todos instalados, y se deben marcar los deseados en el ejecutable ‘OcrResourceGenerator’ que viene incluido en la instalación de la biblioteca.

 

Incorporando Windows OCR a un servicio web:

El objetivo a realizar consiste en generar un servicio web de Windows al cual se le pasarán dos rutas como parámetros de entrada: la ruta de un fichero pdf origen (el cual será analizado mediante la biblioteca WindowsPreview.Media.Ocr para extraer su contenido) y la ruta de un fichero pdf destino (este contendrá el contenido del primer fichero junto al resultado obtenido en el reconocimiento OCR). Esto nos permitirá llevar a cabo búsquedas de contenido en el fichero resultante.

El problema que presenta la biblioteca OCR de Windows, es que está pensada para ser utilizada únicamente en aplicaciones Windows 8.1 y Windows Phone 8, lo cual dificulta su uso en servicios web, servicios de Windows o aplicaciones de consola.

Para solucionar este problema debemos de hacer uso de la clave de registro que se genera al instalar la aplicación. Para ello, lo primero a realizar es crear nuestra aplicación de Windows con Visual Studio 2013 o Visual Studio 2015 e incorporar la biblioteca OCR desde NuGet (PS> Install-Package Microsoft.Windows.Ocr).

La aplicación debe contener una función OnLaunched para poder referenciarla desde nuestro servicio web. Esta función se encargará de lanzar la aplicación utilizando el parámetro o parámetros de entrada introducidos.
protected async override void OnLaunched(LaunchActivatedEventArgs e)
Debemos tener en cuenta que desde una aplicación de Windows no se tiene acceso a directorios o ficheros ajenos a la propia aplicación, por tanto para poder tratar un fichero (en nuestro caso un fichero pdf al cual se le incorporará posteriormente el OCR), deberemos copiarlo previamente en la carpeta temporal de la aplicación:

        var folderTemp = ApplicationData.Current.TemporaryFolder;

También disponemos de la carpeta local de la aplicación, en la cual podemos almacenar por ejemplo el log de esta:

        var folder = ApplicationData.Current.Local

Cuando instanciamos el motor OCR, se debe indicar el lenguaje que debe utilizarse en el reconocimiento de texto:

        OcrEngine ocrEngine = null;
        OcrLanguage OcrLanguage = extractOcrLanguage(language);
        ocrEngine = new OcrEngine(OcrLanguage);

Una vez instanciado, para reconocer el texto de una imagen, se dispone de la función RecognizeAsync a la cual se debe pasar como parámetros el tamaño de la imagen junto al contenido de esta.

        var ocrResult2 = await ocrEngine.RecognizeAsync((uint)imagen.PixelHeight, (uint)imagen.PixelWidth, imagen.PixelBuffer.ToArray());

Una vez generada la aplicación Windows desde Visual Studio (Proyecto/Tienda/Crear paquete para aplicaciones ), podemos proceder a instalarla en nuestro equipo ejecutando para ello el fichero powershell “Add-AppDevPackage” el cual se nos generará automáticamente al crear el paquete de aplicaciones:

 

instalaApp

 

Tras instalar la aplicación Windows en el equipo, debemos buscar su clave de registro. Esta clave es única y no varía aunque se instale en equipos distintos, lo cual nos facilita su posterior uso (la clave podemos encontrarla en HKEY_CURRENT_USER\Software\Classes\ActivatableClasses\Package\Some_Sort_Of_Guid_For_Your_APP\Server\App.App….\AppUserModelId).

 

registro

 

En este punto, ya podemos proceder a crear un proyecto en Visual Studio que genere un servicio web, desde el cual accederemos a la aplicación de Windows creada previamente.
Para ello solo necesitamos utilizar la clase ApplicationActivationManager la cual nos permite acceder a la clave de registro y lanzar la aplicación de Windows correspondiente.
Para poder llamar a la función OnLaunched de la aplicación, debemos añadir a nuestro servicio web las clases: ‘ActivateOptions ‘y ‘ApplicationActivationManager ‘, junto a la interfaz ‘IapplicationActivationManager‘.

        public enum ActivateOptions
        {
            None = 0x00000000, // No flags set
            DesignMode = 0x00000001, // The application is being activated for design mode, and thus will not be able to create an immersive window.
            NoErrorUI = 0x00000002, // Do not show an error dialog if the app fails to activate.
            NoSplashScreen = 0x00000004 // Do not show the splash screen when activating the app.
        }
       
      [ComImport, Guid("2e941141-7f97-4756-ba1d-9decde894a3d"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]  
  
      interface IapplicationActivationManager
       {
           // Activates the specified immersive application for the "Launch" contract,
           //passing the provided arguments string into the application. Callers can
           //obtain the process Id of the application instance fulfilling this
           //contract.
           IntPtr ActivateApplication([In] String appUserModelId, [In] String arguments, [In] ActivateOpt ions options, [Out] out UInt32 processId);
           IntPtr ActivateForFile([In] String appUserModelId, [In] IntPtr itemArray, [In] String verb, [O ut] out UInt32 processId);
           IntPtr ActivateForProtocol([In] String appUserModelId, [In] IntPtr itemArray, [Out] out UInt32 processId);
      }
        //Application Activation Manager
        [ComImport, Guid("45BA127D-10A8-46EA-8AB7-56EA9078943C")]
        class ApplicationActivationManager : IApplicationActivationManager
        {
            [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
            public extern IntPtr ActivateApplication([In] String appUserModelId, [In] String arguments, [I n] ActivateOptions options, [Out] out UInt32 processId);
            [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
            public extern IntPtr ActivateForFile([In] String appUserModelId, [In] IntPtr itemArray, [In] S tring verb, [Out] out UInt32 processId);
            [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
            public extern IntPtr ActivateForProtocol([In] String appUserModelId, [In] IntPtr itemArray, [O ut] out UInt32 processId);
       }

Una vez añadidas, podemos hacer uso de ellas mediante la instrucción:

        appActiveManager.ActivateApplication(appUserModelId, arguments, ActivateOptions.NoSplashScreen, out pid);

Siendo appUserModelId nuestra clave de registro de la aplicación y arguments los parámetros que necesita la aplicación Windows (fichero origen y fichero destino en nuestro caso). Indicamos ActivateOptions.NoSplashScreen para que no se muestre el splash de la aplicación al iniciarse, y el parámetro pid nos devolverá el id de proceso que está ejecutando la aplicación.

Podemos buscar el proceso cuyo id coincide con el devuelto, y comprobar cuando este termina.

        foreach (var p in System.Diagnostics.Process.GetProcesses()) {
           if (p.Id == pid) {
              proc = p;
           }
        }

        if (proc != null && proc.Id != 0) {
           while (!proc.HasExited){
              proc.Refresh();
           }
        }

Una vez termine el proceso,  obtendremos un fichero pdf destino que incluirá su OCR, de esta manera podremos realizar búsquedas de contenido en dicho fichero.

 

Conclusiones

En este artículo hemos visto cómo incorporar la biblioteca WindowsPreview.Media.Ocr a un servicio web, resolviendo los problemas de compatibilidad que presenta esta biblioteca a la hora de ser importada a un proyecto Windows que difiera de una aplicación metro.

 


Referencias