/ Cognitive Services

Iniciando con Cognitive Services Computer Vision API

Imaginemos que estás desarrollando en tu cochera la siguiente aplicación de fotografías (¡tiembla instagram!) y que ahora tienes el reto de obtener información de todas las imágenes de tus usuarios para brindarles mejores servicios y optimizar tus filtros, la cantidad de usuarios que ahora tienes ya es considerable y contratar a personas para revisar una a una las imágenes que agregan a tu aplicación no es una opción ya que ahora mismo tu rango de subida de imágenes es de cinco por segundo (además ya no estamos en el siglo XIX) ¿Qué hacer? Una buena opción sería seleccionar un servicio de Inteligencia Artificial de clasificación de imágenes de los que existen en el mercado como lo es Computer Vision que forma parte de los Cognitive Services de Microsoft de los que hablamos en un post anterior.

Computer Vision API

Computer Vision forma parte de las características de Vision de Cognitive Services que nos permiten realizar análisis sobre imágenes, este servicio se compone de tres características fundamentales:

  • Clasificación de imágenes e identificación.
  • Generación de thumbnails.
  • Reconocimiento de texto y extracción.

Para utilizar Computer Vision lo primero que necesitas es contratar el servicio en el portal de Microsoft Azure.

Creando nuestro servicio en el portal de Azure

Dar de alta este servicio es algo realmente fácil, solo debes navegar al portal y buscar Computer Vision como se muestra en la siguiente imagen.

Screen-Shot-2018-06-27-at-10.45.54-PM-1
Al seleccionar el servicio se mostrará la siguiente descripción, presiona el botón Create.
Screen-Shot-2018-06-27-at-10.46.19-PM
Proporciona el nombre con el que identificarás el servicio así como tu suscripción, la región donde quedará alojado el servicio y selecciona el nivel de servicio, en este último punto puedes seleccionar entre un servicio gratuito pero limitado o uno de paga.

Si quieres saber los precios y características de estos servicios visita el siguiente link

Screen-Shot-2018-06-27-at-10.46.57-PM

Por último obtendrás una url similar a la que se muestra a continuación guarda este dato ya que te será útil más adelante.
https://region.api.cognitive.microsoft.com/vision/v1.0
También guarda la llave del servicio, ya que la utilizaremos más adelante.
Screen-Shot-2018-06-27-at-11.56.03-PM

Ahora que ya tenemos nuestro servicio listo en el portal de Azure vamos a revisar las características de este servicio.

Análisis de imágenes.

En el análisis de las imágenes para clasificación e identificación Computer Vision reconoce más de 2000 objetos (personas, paisajes y acciones) y 86 conceptos de taxonomía (rostros, comida, naturaleza y abstractos) también es capaz de detectar si una imagen es blanco y negro, un dibujo lineal o una imagen a color.

Entre los usos principales de esta característica se encuentran los siguientes:

  • Categorización de contenido.
  • Detectar caras humanas.
  • Detectar esquemas de color.
  • Marcar contenido para adultos.
  • Identificar tipos de imágenes y asignarles una categoría.

Como ejemplo analizaremos la siguiente imagen.
IMG_20180617_185830_998

Escribiremos el siguiente código sin utilizar el SDK tan solo usaremos llamadas REST para conocer mejor el servicio. Es importante que notes la variable service del código ya que es en esta línea donde definimos que característica utilizaremos, en este caso es analyze.

private static async void SendToCognitiveServiceWithoutSdkAsync(string imagePath)
        {
            string service = "analyze";
            HttpClient httpClient = new HttpClient();
            httpClient.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", key);

            string requestParameters = "visualFeatures=Categories,Description&language=es";
            string uri = $"{ComputerVisionUri}{service}?{requestParameters}";

            HttpResponseMessage response;
            byte[] byteData = GetImageAsByteArrays(imagePath);

            using (ByteArrayContent content = new ByteArrayContent(byteData))
            {
                content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
                response = await httpClient.PostAsync(uri, content);
            }
            string computerVisionResponse = await response.Content.ReadAsStringAsync();
            dynamic jsonObject = JsonConvert.DeserializeObject(computerVisionResponse);
            Console.WriteLine(JsonConvert.SerializeObject(jsonObject, Formatting.Indented));
        }

        private static byte[] GetImageAsByteArrays(string imagePath)
        {
            FileStream fileStream = new FileStream(imagePath, FileMode.Open, FileAccess.Read);
            BinaryReader binaryReader = new BinaryReader(fileStream);
            return binaryReader.ReadBytes((int)fileStream.Length);
        }

En el código anterior podrás notar que es una llamada bastante sencilla, observa que necesitamos agregar el encabezado Ocp-Apim-Subscription-Key donde pasaremos el valor de la clave que obtuvimos del portal y enviaremos nuestra imagen al portal como un arreglo de bytes.

El código anterior nos regresará el siguiente JSON como respuesta con la imagen de ejemplo.

{
  "categories": [
    {
      "name": "planta_árbol",
      "score": 0.859375
    }
  ],
  "description": {
    "tags": [
      "exterior",
      "pasto",
      "campo",
      "árbol",
      "bosque",
      "parado",
      "cubierto#de#hierba",
      "pista",
      "verde",
      "país",
      "caminando",
      "viajando",
      "grande",
      "tren",
      "grupo",
      "blanco",
      "alto",
      "agua",
      "sucio",
      "hombre",
      "río"
    ],
    "captions": [
      {
        "text": "un bosque con arboles",
        "confidence": 0.64829311494828035
      }
    ]
  },
  "requestId": "e3370481-eeeb-4b9c-8e43-fd3fead85efc",
  "metadata": {
    "height": 2590,
    "width": 2590,
    "format": "Jpeg"
  }
}

El resultado maneja un campo "captions" que determina la confianza y se mostrará en un rango que está entre el 0 y 1 donde 1 es el máximo de certeza posible. Tengo que decir que me sorprendió (aún más) el hecho de que en los tags identifica un hombre (supongo que en el reflejo).

Este ha sido un pequeño ejemplo si deseas utilizar alguna otra de las características que he mencionado al inicio de esta sección visita la referencia del API en el siguiente link.

Restricciones

Toma en cuenta que el servicio tiene algunas limitantes, mismas que listaremos a continuación.

  • El tamaño de las imágenes debe encontrarse entre los 40x40 pixeles y los 3200 x 3200 pixeles.
  • El archivo no debe superar los 10 megapíxeles.
  • También son soportados los formatos jpeg, png, gif y BMP.
  • El tamaño del archivo no debe ser mayor de 4 MB.

Ahora pasemos a la siguiente característica de Computer Vision que es la generación de tumbnails.

Generación de thumbnails.

Thumbnail es la representación de una imagen a tamaño completo.
Retomemos ahora el escenario del inicio de este post ya tenemos el análisis de las imágenes de nuestros usuarios, solo que en algunas secciones de nuestra aplicación no es posible mostrar la imagen en tamaño completo, si bien podrías recortar las imágenes sin importar cuál sea el resultado final es mejor si realizas un recorte inteligente, para esto podemos utilizar las características de Computer Vision de generación de thumbnails con ROIs.

Con Computer Vision se puede realizar cortes inteligentes utilizando ROIs (Regions Of Interest) que nos permitirán obtener representaciones que se basan en un algoritmo que permite obtener las zonas más importantes de una imagen y también nos permite cambiar el radio de aspecto de la imagen.

Para probar esta característica utilizaremos la imagen del ejemplo anterior y el siguiente código. Es importante que revises nuevamente la variable service del código ya que es en esta línea donde definimos que característica utilizaremos, en este caso es generateThumbnail.

     private static async void GenerateThumbnailAsync(string imagePath, int width, int height, bool smart)
        {
            byte[] thumbnail = await GetThumbnailAsync(imagePath, width, height, smart);

            string thumbnailPath = $"{Path.GetDirectoryName(imagePath)}\\{Guid.NewGuid()}.jpg";

            using (BinaryWriter bw = new BinaryWriter(new FileStream(thumbnailPath, 
                FileMode.OpenOrCreate, 
                FileAccess.Write)))
            {
                bw.Write(thumbnail);
            }
        }

        private static async Task<byte[]> GetThumbnailAsync(string imagePath, int width, int height, bool smart)
        {
            string service = "generateThumbnail";
            HttpClient httpClient = new HttpClient();
            httpClient.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-key", key);

            string requestParameters = $"width={width}&height={height}&smartCropping={true}";
            string uri = $"{ComputerVisionUri}{service}?{requestParameters}";

            byte[] byteData = GetImageAsByteArrays(imagePath);

            using (ByteArrayContent content = new ByteArrayContent(byteData))
            {
                content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
                HttpResponseMessage responseMessage = await httpClient.PostAsync(uri, content);

                return await responseMessage.Content.ReadAsByteArrayAsync();
            }
        }        

Como en el ejemplo anterior aún no utilizaremos el SDK y realizaremos una llamada al API REST, es necesario también que volvamos a enviar el encabezado Ocp-Apim-Subscription-key con nuestra llave del servicio y enviaremos nuestra imagen como un arreglo de bytes. Si envías el parámetro smartCropping habilitarás el recorte inteligente.

El resultado será parecido a la siguiente imagen, te recomiendo que realices pruebas con diferentes imágenes para observar mejor los resultados.

63cc63ce-56db-4a66-af2f-97689b1fd0b7

Restricciones

Para obtener una mejor experiencia de esta característica el alto y ancho de la imagen deben ubicarse entre 1 y 1024 con un mínimo recomendado de 50x50.

Por último pero no menos impresionante llegamos a la característica conocida como reconocimiento de texto y extracción

Reconocimiento de texto.

Esta característica nos permite obtener el texto que contenga una imagen por medio de OCR (Optical Character Recognition), el análisis de la imagen utilizando esta característica también puede detectar el idioma de lo escrito.
El reconocimiento de texto actualmente soporta 25 lenguajes y con el parámetro language le indicamos al servicio que ese es el idioma que debe detectar en la imagen el valor por default es "unk" que indicará al servicio que debe auto detectar el lenguaje.
También podemos utilizar el parámetro detectOrientation para indicar que trate de corregir la orientación de la imagen.
Está característica también permite reconoce texto escrito a mano en inglés.

Para trabajar con este servicio debemos agregar la palabra ocr en la url base que obtuvimos del portal en caso de que decidas utilizar el API REST.

En el caso de esta característica vamos a utilizar el SDK para mostrar como podemos reducir el esfuerzo en el desarrollo, para ello es necesario que agregues el siguiente paquete de nuget Microsoft.Azure.CognitiveServices.Vision.ComputerVision.

Después agrega el siguiente código.

private static async void RecognizePrintedTextWithSdkAsync(string imagePath,
            bool detectOrientation)
        {
            ServiceClientCredentials serviceClientCredentials = new ApiKeyServiceClientCredentials(key);

            Microsoft.Azure.CognitiveServices.Vision.ComputerVision.ComputerVisionAPI computerVisionApi =
                new Microsoft.Azure.CognitiveServices.Vision.ComputerVision.ComputerVisionAPI(
                    serviceClientCredentials)
                {
                    AzureRegion = AzureRegions.Southcentralus
                };
            HttpOperationResponse<OcrResult> result = null;
            using (var fileReader = new FileStream(imagePath, FileMode.Open))
            {
                result = await computerVisionApi.RecognizePrintedTextInStreamWithHttpMessagesAsync(detectOrientation, fileReader);
            }
            foreach (OcrRegion region in result.Body.Regions)
            {
                foreach (OcrLine line in region.Lines)
                {
                    foreach (OcrWord word in line.Words)
                    {
                        Console.WriteLine(word.Text);
                    }
                }
            }
        }

La imagen que utilizaremos será la siguiente.
IMG_20180711_000930
El resultado del análisis de la imagen es el siguiente.
Screen-Shot-2018-07-11-at-12.14.42-AM

Restricciones

Para trabajar con el OCR debes de tomar en cuentas que existen limitantes, como puedes imaginarlo gran parte de la precisión depende de la calidad de la imagen es decir las imágenes no deben de ser borrosas, tampoco deben de tener fuentes muy elaboradas, ser muy pequeñas o tener mezclas de letras mayúsculas y minúsculas, otro de los problemas que podrías tener es el hecho de que no obtendrás un buen resultado si las imágenes tienen un fondo muy elaborado o los caracteres tienen tachaduras.

Conclusiones

Es algo impresionante como la tecnología a avanzado tanto en estos últimos años tanto que ahora nos permite incluir características tan potentes de análisis de imágenes en una simple aplicación de consola como la que hemos realizado en los ejemplos mostrados en este post lo que abre muchas posibilidades en la mejora de nuestras aplicaciones.

Como siempre pueden encontrar el código de ejemplo en github solo debes cambiar las rutas de la imágenes y los parámetros del servicio :).

Antes de despedirnos cuéntame en los comentarios ¿Cómo utilizarías o utilizas Computer Vision en tus aplicaciones?
¡Saludos!
@saturpimentel

Links relacionados

Overview
Quickstarts
API Reference
Pluralsight

La imagen del post fue obtenida de Unsplash.

Saturnino Pimentel

Saturnino Pimentel

Microsoft MVP, consultor, blogger y conferencista. Saturnino es el cofundador de la comunidad de programadores c# y el meetup de c# de la ciudad de México además de participar con otras comunidades.

Read More

Suscríbete a la lista de correos :)

* indicates required