Arquitectura, buenas prácticas y desarrollo sobre la nueva herramienta de Microsoft SharePoint 2016

Proyecto Oxford: desglosando las API Visión

Continuando con el post de la semana pasada, vamos a seguir comentando todas las posibilidades que tiene el proyecto Oxford para hacer más inteligente nuestras aplicaciones. Vamos a ver las características de la API de Visión.

¿Qué características tiene la API Visión?

API Visión está formada por múltiples algoritmos para procesar imágenes y devolver la información de la misma. Por ejemplo, si la imagen contiene contenido no propio para menores y no debe de publicarse en nuestra aplicación. Hay otras características como el color dominante y la categoría de la imagen. Por ejemplo cuando nos suban en nuestro sistema una foto de cualquier tipo de comida categorizar esta foto como gastronomía. Otra de las posibilidades que trae esta API es la de actuar como OCR. Es decir, lee el texto que hay en una imagen. Otra característica que tiene es generar la imagen de miniatura (thumbmail) adecuada para la interfaz de pantalla donde se esta representando. 

¿Cómo empezar?

Igual que en el anterior post (para la detección de caras teníamos que instalar el servicio FACE API), ahora tendremos que instalarnos el servicio de Visión. Para ello Nuevo-> Tienda y buscamos Visión nocturna

Captura

 

Una vez tenemos el servicio incluido dentro de nuestra suscripción de Azure (el uso de esta API es de momento totalmente gratuito e ilimitado), obtenemos los Keys que necesitaremos para que funcione esta API dentro de nuestro desarrollo.

keys

Analizando la imagen

En primer lugar nos vamos a crear una clase para que nos facilite el desarrollo y en la que encapsulemos los métodos a la API Visión. En primer lugar, implementaremos un constructor donde se le pasará la Primera Key (valor que es necesario para poder utilizar la API).

 public class VisionHelper
    {
        /// <summary>
        /// The vision service client.
        /// </summary>
        private readonly IVisionServiceClient visionClient;

        /// <summary>
        /// Initializes a new instance of the <see cref="VisionHelper"/> class.
        /// </summary>
        /// <param name="subscriptionKey">The subscription key.</param>
        public VisionHelper(string subscriptionKey)
        {
            this.visionClient = new VisionServiceClient(subscriptionKey);
        }
}

El siguiente paso es dar una dirección (ya sea local o una dirección de internet) que se analice en la foto en cuestión. Para ello implementamos el siguiente método:

  /// <summary>
        /// Analyze the given image.
        /// </summary>
        /// <param name="imagePathOrUrl">The image path or url.</param>
        public void AnalyzeImage(string imagePathOrUrl)
        {
            this.ShowInfo("Analyzing");
            AnalysisResult analysisResult = null;

            string resultStr = string.Empty;
            try
            {
                if (File.Exists(imagePathOrUrl))
                {
                    using (FileStream stream = File.Open(imagePathOrUrl, FileMode.Open))
                    {
                        analysisResult = this.visionClient.AnalyzeImageAsync(stream).Result;
                    }
                }
                else if (Uri.IsWellFormedUriString(imagePathOrUrl, UriKind.Absolute))
                {
                    analysisResult = this.visionClient.AnalyzeImageAsync(imagePathOrUrl).Result;
                }
                else
                {
                    this.ShowError("Invalid image path or Url");
                }
            }
            catch (ClientException e)
            {
                if (e.Error != null)
                {
                    this.ShowError(e.Error.Message);
                }
                else
                {
                    this.ShowError(e.Message);
                }

                return;
            }
            catch (Exception)
            {
                this.ShowError("Some error happened.");
                return;
            }
            this.ShowAnalysisResult(analysisResult);
        }

El siguiente paso es crear una aplicación que instancie al objeto VisionHelper (con la key de nuestra API) y analicemos una imagen. Un ejemplo :

            string subscriptionKey = ConfigurationManager.AppSettings["subscriptionKey"];
            if (string.IsNullOrWhiteSpace(subscriptionKey))
            {
                Console.ForegroundColor = ConsoleColor.DarkGreen;
                Console.WriteLine("To play this sample, you should firstly get a subscription key and put it into the App.Config file.");
                Console.WriteLine("If you don't have one, please access");
                Console.WriteLine("http://www.projectoxford.ai/doc/general/subscription-key-mgmt");
                Console.ResetColor();
                Console.WriteLine();
                Console.WriteLine("Please enter any key......");
                Console.ReadLine();
                return;
            }
            var vision = new VisionHelper(subscriptionKey);
           var  imagePathorUrl = GetValidImagePathorUrl();
            vision.AnalyzeImage(imagePathorUrl);
            Console.ReadLine();

Si introducimos la siguiente imagen:

Los resultados son los siguientes:

Resultados

Espectacular los resultados y toda la inteligencia que podemos hacer en nuestros desarrollos. Sin pensar mucho se me ocurre que cada vez que subamos una imagen a nuestro Gestor Documental preferido (SharePoint) se introduzca un metadato sobre a que categoría pertenece, de tal forma que posteriormente podamos buscar las imágenes según su categoría. Otro uso que se le podría dar es, por ejemplo, para evitar subir contenido para adultos. En caso de que se intente subir una de estas imágenes, el sistema no lo suba.

Reconocer Texto (OCR)

Otra de las utilidades que hemos comentado que tiene la API es reconocer el texto que hay en una foto. La API detecta múltiples idiomas como: chino, alemán, español, portugués, ruso, francés, etc. Para añadir la funcionalidad de reconocer texto, agregaremos el siguiente método a nuestro helper:

    public void RecognizeText(string imagePathOrUrl, bool detectOrientation = true, string languageCode = LanguageCodes.AutoDetect)
        {
            this.ShowInfo("Recognizing");
            OcrResults ocrResult = null;
            string resultStr = string.Empty;

            try
            {
                if (File.Exists(imagePathOrUrl))
                {
                    using (FileStream stream = File.Open(imagePathOrUrl, FileMode.Open))
                    {
                        ocrResult = this.visionClient.RecognizeTextAsync(stream, languageCode, detectOrientation).Result;
                    }
                }
                else if (Uri.IsWellFormedUriString(imagePathOrUrl, UriKind.Absolute))
                {
                    ocrResult = this.visionClient.RecognizeTextAsync(imagePathOrUrl, languageCode, detectOrientation).Result;
                }
                else
                {
                    this.ShowError("Invalid image path or Url");
                }
            }
            catch (ClientException e)
            {
                if (e.Error != null)
                {
                    this.ShowError(e.Error.Message);
                }
                else
                {
                    this.ShowError(e.Message);
                }

                return;
            }
            catch (Exception)
            {
                this.ShowError("Some error happened.");
                return;
            }
            this.ShowRetrieveText(ocrResult);
        }

A continuación, creamos una nueva aplicación e introducimos la misma aplicación que en el anterior caso, pero modificamos el método. En este caso, le vamos a llamar la Función RecognizeText. Si a la aplicación le introducimos una imágen como la siguiente:

El resultado que se obtiene es el siguiente: RecognizeText

En este caso, no ha reconocido la totalidad del texto, pero es una de las ocasiones en la que no funciona al 100€. Según las especificaciones, variará. Por ejemplo si el tamaño es menor de 50×50, es posible que no detecte el texto.

Creando imágenes en miniatura

Para finalizar con todas las posibilidades que tiene la API Visión vamos a ver lo simple que es poder generar imágenes más pequeñas. Ésta es una utilidad que en más de una ocasión hemos tenido que implementar. En muchas ocasiones dejamos que determinadas imágenes se publiquen en la Web sin darle importancia a las medidas y dejando al usuario final la responsabilidad de que ponga una imagen que cumpla con los requisitos especificados. Además, ahora hay multitud de dispositivos y resoluciones en los que se tiene que visualizar nuestra web. Lo más normal es que se vea bien en escritorio y no en un móvil (y en el smartphone que se ve bien es porque tiene una resolución enorme y tarda mucho en cargarla con 3/4G). Para poder utilizarla, igual que en los anteriores ejemplos extendemos el Helper con el siguiente código:

 public void GetThumbnail(string imagePathOrUrl, int width, int height, bool smartCropping, string resultFolder)
        {
            this.ShowInfo("Get Thumbnail");
            byte[] thumbnailResult = null;
            string resultStr = string.Empty;
            try
            {
                if (File.Exists(imagePathOrUrl))
                {
                    using (FileStream stream = File.Open(imagePathOrUrl, FileMode.Open))
                    {
                        thumbnailResult = this.visionClient.GetThumbnailAsync(stream, width, height, smartCropping).Result;
                    }
                }
                else if (Uri.IsWellFormedUriString(imagePathOrUrl, UriKind.Absolute))
                {
                    thumbnailResult = this.visionClient.GetThumbnailAsync(imagePathOrUrl, width, height, smartCropping).Result;
                }
                else
                {
                    this.ShowError("Invalid image path or Url");
                }
            }
            catch (ClientException e)
            {
                if (e.Error != null)
                {
                    this.ShowError(e.Error.Message);
                }
                else
                {
                    this.ShowError(e.Message);
                }

                return;
            }
            catch (Exception)
            {
                this.ShowError("Some error happened.");
                return;
            }
            // Write the result to local file
            string filePath = string.Format("{0}\\thumbnailResult_{1}.jpg", resultFolder, DateTime.UtcNow.Ticks.ToString());
            using (BinaryWriter binaryWrite = new BinaryWriter(new FileStream(filePath, FileMode.Create, FileAccess.Write)))
            {
                binaryWrite.Write(thumbnailResult);
            }
            this.ShowResult(string.Format("The result file has been saved to {0}", Path.GetFullPath(filePath)));
        }

Conclusión

Uno de los grandes beneficios que tiene el Cloud es poder utilizarlo en entornos que anteriormente eran muy costosos (tanto de implementar como económicamente). Esto hace posible que surjan herramientas como Machine Learning para tener herramientas que doten de inteligencia nuestros desarrollos. El proyecto Oxford es un claro ejemplo de ello.

El código de ejemplo lo podéis descargar desde el siguiente repositorio de GitHub.

 

mm

Sobre Adrián Díaz

Adrián Díaz es Ingeniero Informático por la Universidad Politécnica de Valencia. Es MVP de Microsoft en la categoría Office Development desde 2014, MCPD de SharePoint 2010, Microsoft Active Profesional y Microsoft Comunity Contribuitor 2012. Cofundador del grupo de usuarios de SharePoint de Levante LevaPoint. Lleva desarrollando con tecnologías Microsoft más de 10 años y desde hace 3 años está centrado en el desarrollo sobre SharePoint. Actualmente es Software & Cloud Architect Lead en ENCAMINA.
Esta entrada ha sido publicada en Azure. Enlace permanente.

Suscríbete a Desarrollando sobre SharePoint

Recibe todas las actualizaciones semanalmente de nuestro blog

You have Successfully Subscribed!

ENCAMINA, piensa en colores