<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Microsoft DX España</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/esmsdn/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/esmsdn/feed.xml</link>
    <description>Blog del equipo de Evangelismo técnico de Microsoft España</description>
    <lastBuildDate>Wed, 18 Jul 2018 11:19:14 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>[Post Invitado] Gestionando grupos de personas con Azure Face API</title>
      <link>https://docs.microsoft.com/archive/blogs/esmsdn/post-invitado-gestionando-grupos-de-personas-con-azure-face-api</link>
      <pubDate>Wed, 18 Jul 2018 13:19:13 GMT</pubDate>
      <dc:creator><![CDATA[ilitia]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/esmsdn/?p=13505</guid>
      <description><![CDATA[Este artículo de Alberto Guerra Estévez, consultor de Ilitia Technologies, explica cómo gestionar...]]></description>
      <content:encoded><![CDATA[Este artículo de Alberto Guerra Estévez, consultor de Ilitia Technologies, explica cómo gestionar los límites que posee Face API a la hora de agregar personas al servicio.
<h3>Introducción</h3>
<a href="https://azure.microsoft.com/es-es/services/cognitive-services/face/">Face Api</a> es un conjunto de herramientas pertenecientes a los servicios cognitivos de Microsoft Azure ideales para poder identificar a las distintas personas dentro de un grupo. Podemos reconocer, a fecha de hoy, hasta un máximo de 1000 personas por grupo para la subscripción gratuita  y hasta 10.000 para la subscripción estándar<a href="#_edn1"><span>[i]</span></a>.

Con los límites actuales podremos añadir de manera sencilla a un volumen suficiente de personas como para cubrir la mayoría de casos que se nos presenten, pero<strong>… ¿y si necesitamos superar estos límites?</strong>

Face API proporciona las herramientas para poder superar los límites actuales del servicio, simplemente necesitaremos un poco más de lógica en nuestra aplicación para distribuir las distintas personas en nuevos grupos. Si antes el límite que teníamos era de 1.000 personas por grupo para la subscripción gratuita (10.000 en el caso de la subscripción estándar), ahora podremos multiplicar ese techo hasta por 1.000.000, 1.000 usuarios en 1.000 grupos disponibles por servicio, si sabemos gestionar adecuadamente ambos recursos. La ganancia es indudable y conseguirlo es una tarea relativamente sencilla.
<h3>1.    Requisitos antes de empezar</h3>
Para aprender a gestionar los grupos de personas de Face Api asumimos que conoces y tienes listos los siguientes puntos:
<ul>
 	<li>Tienes correctamente <strong>desplegado un servicio de Face Api en tu portal de Azure</strong> con una subscripción válida, sea gratuita o estándar y su clave correspondiente.</li>
 	<li>Conoces los fundamentos básicos y usos de Face API. No deben resultarte extraños conceptos asociados como Face, Person o Group.</li>
</ul>
<h3>2.    Conectando nuestra aplicación a Face API</h3>
Para integrar el ejemplo de este artículo vamos a elegir una aplicación UWP, ya que existen librerías que integran perfectamente toda la funcionalidad de la API y nos pueden facilitar el trabajo sin tener que implementar las llamadas mediante un cliente HTTP directamente (como cualquier otra API Rest).

En primer lugar, crearemos un nuevo proyecto Universal Windows (Blank App) desde Visual Studio:

<a href="https://msdnshared.blob.core.windows.net/media/2018/06/captura-1.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/06/captura-1.png" alt="" width="937" height="491" class="size-full wp-image-13545 aligncenter" /></a>

Añadimos al proyecto el NuGet Microsoft.ProjectOxford.Face, que nos dará las librerías de cliente que conecte con Face Api:

<a href="https://msdnshared.blob.core.windows.net/media/2018/06/Captura-2.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/06/Captura-2.png" alt="" width="602" height="163" class="size-full wp-image-13555 aligncenter" /></a>

Ya tendemos un punto de partida sobre el que empezar a implementar nuestro ejemplo.
<h3>3.    Inicializaciones</h3>
Necesitaremos los siguientes namespaces del SDK para las funciones que vamos a crear:
<pre class="brush: html line-numbers"><code>
using Microsoft.ProjectOxford.Face;
using Microsoft.ProjectOxford.Face.Contract;
</code></pre>
Para poder hacer todas las llamadas a la Api, debemos tener en cuenta las limitaciones que ofrece nuestra subscripción. Para ello haremos uso del siguiente algoritmo que gestiona la cola de peticiones con respecto a los límites de nuestra subscripción, esperando, si es necesario, hasta que el servicio vuelva a admitir nuevas llamadas, ya que, si superamos el límite de peticiones por segundo, nos disparará una excepción. Este algoritmo está disponible en la documentación de Microsoft:

<a href="https://docs.microsoft.com/en-us/azure/cognitive-services/face/face-api-how-to-topics/how-to-add-faces">https://docs.microsoft.com/en-us/azure/cognitive-services/face/face-api-how-to-topics/how-to-add-faces</a>
<pre>const int PersonCount = 10000;
const int CallLimitPerSecond = 10;
static Queue _timeStampQueue = new
Queue(CallLimitPerSecond);

private static async Task WaitCallLimitPerSecondAsync()
   {
     Monitor.Enter(_timeStampQueue);
     try
     {
        if (_timeStampQueue.Count &gt;= CallLimitPerSecond)
        {
          TimeSpan timeInterval = DateTime.UtcNow - _timeStampQueue.Peek();
          if (timeInterval &lt; TimeSpan.FromSeconds(1))
          {
              await Task.Delay(TimeSpan.FromSeconds(1) - timeInterval);
          } 
          _timeStampQueue.Dequeue();
        }
        _timeStampQueue.Enqueue(DateTime.UtcNow);
     }
     finally
     {
        Monitor.Exit(_timeStampQueue);
     }
   }
</pre>
Como haremos varias llamadas al servicio,  automatizaremos un poco más la gestión de estas llamadas mediente el siguiente método donde pasaremos nuestro código con la llamada a FaceApi que  invocará automáticamente al gestor de colas:
<pre>private async Task ExecuteFaceApiFunction(Func&lt;Task&gt; action)
        {
           try
           {
                await WaitCallLimitPerSecondAsync();
                await action();
           }
           catch (Exception e)
           {
               Debug.WriteLine(e.Message);
           }
        }
</pre>
Por último en nuestra clase definimos e instanciamos nuestra variable de nuestro servicio de Face Api y una variable que indique el límite de personas por grupo que permita nuestra subscripción:
<pre>private FaceServiceClient faceService = new FaceServiceClient("my_api_key", @"https://westeurope.api.cognitive.microsoft.com/face/v1.0");
private int maxPersonsPerGroup = 1000;
</pre>
&nbsp;
<h3>4.    Añadiendo a las personas en sus grupos correspondientes</h3>
&nbsp;

Ten lista  una carpeta con imágenes de caras individuales, distintas entre ellas, que será nuestro ejemplo de partida.

Para poder añadir cada una de las caras que vayamos procesando en nuestro grupo, usaremos alguna de las características de esa cara para poder generar un grupo de asignación. Una buena estrategia puede ser definir el grupo al que pertenece la cara por género.

Crearemos para ello la siguiente función que, para una nueva cara  y una lista de grupos <strong>ya existentes</strong> en nuestro servicio,  le asignará a nuestra cara un id de grupo donde debería insertarse que no esté lleno, y, en caso de estar todos llenos, creará un nuevo grupo incrementando el índice):
<pre>private async Task GetOrCreateProperGroupId(Face face, List grupos)
        {
               int subGroupIndex = 0;
               string resultGroupId;
               string subgroupName = "subgroup" + face.FaceAttributes.Gender;
               var compatibleGroups = grupos.Where(g =&gt; g.PersonGroupId.Contains(subgroupName));

               PersonGroup existentGroup = null;
               if (compatibleGroups != null &amp;&amp; compatibleGroups.Any())
               {
                  //si hay grupos compatibles con la cara dada (por genero) buscamos en el primero que este vacio
                  //y vamos incrementando el índice por cada grupo lleno que vayamos encontrando
                  foreach (var group in compatibleGroups)
                  {
                       PersonGroup[] personsInGroup = null;

                       await ExecuteFaceApiFunction(async () =&gt;;
                       {
                          personsInGroup = await faceService.ListPersonGroupsAsync();
                       });
                       if (personsInGroup?.Count() &gt;= maxPersonsPerGroup)
                       {
                           subGroupIndex++;
                       }
                       else
                       {
                           existentGroup = group;
                           break;
                       }
                  }
        }
        //el grupo donde insertar la nueva cara sera el resultado del género + el indice calculado
          resultGroupId = existentGroup?.PersonGroupId ?? (subgroupName + subGroupIndex);

       //si el grupo es nuevo para esta cara (no existe ningun grupo existente para ese groupID) generamos uno nuevo con el indice donde lo dejamos
         if (existentGroup == null)
         {
              Debug.WriteLine("No hay grupos libres para el subgrupo, Se genera nuevo grupo" + resultGroupId);

              await ExecuteFaceApiFunction(async () =&gt;;
              {
                     await faceService.CreatePersonGroupAsync(resultGroupId, resultGroupId);
               });
               grupos.Add(new PersonGroup() { PersonGroupId = resultGroupId });
         }
         
          return resultGroupId;
      }
</pre>
Con esta función ya podremos crear nuestro algoritmo para que inserte una serie de imágenes de un directorio en nuestro servicio de manera correcta, teniendo en cuenta los límites de capacidad por grupo:
<pre>            private async void LoadFotosFromDirectory()
         {
              var filePicker = new Windows.Storage.Pickers.FileOpenPicker()
              {
                   SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary,
                   ViewMode = Windows.Storage.Pickers.PickerViewMode.Thumbnail
               };

               var files = await filePicker.PickMultipleFilesAsync();

               Debug.WriteLine("Cargando fotos...\n");
               PersonGroup[] existentGroups = null;
               await ExecuteFaceApiFunction(async () =&gt;
                    {
                        existentGroups = await faceService.ListPersonGroupsAsync();
                    });
               foreach (var file in files)
               {
                    using (Stream imageFileStream = File.OpenRead(file.Path)) 
                    {
                          Face[] faces = null;
                          await ExecuteFaceApiFunction(async () =&gt;
                          {
                                faces = await faceService.DetectAsync(imageFileStream, true, true, new FaceAttributeType[] { FaceAttributeType.Gender});
                          });

                          foreach (var face in faces)
                          {
                               string properGroupid = await GetOrCreateProperGroupId(face, existentGroups.ToList());
                               CreatePersonResult newPerson=null;
                               await ExecuteFaceApiFunction(async () =&gt;
                               {
                                      newPerson = await faceService.CreatePersonAsync(properGroupid, face.FaceId.ToString());
                                });
                                await ExecuteFaceApiFunction(async () =&gt;
                                {
                                      await faceService.AddPersonFaceAsync(properGroupid, newPerson.PersonId, File.OpenRead(file.Path));
                                 });
                                 Debug.WriteLine("Persona " + newPerson.PersonId + " agregada a grupo " + properGroupid);
                          }
                     }
                }
                Debug.WriteLine("TODAS LAS FOTOS CARGADAS)");

                await TrainAllGroups();
          }

          private async Task TrainAllGroups ()
          {
                  PersonGroup[] grupos = null;
                  await ExecuteFaceApiFunction(async () =&gt;;
                  {
                        grupos = await faceService.ListPersonGroupsAsync();
                   });

                   foreach (var grupo in grupos)
                   {
                         await ExecuteFaceApiFunction(async () =&gt;;
                         {
                              await faceService.TrainPersonGroupAsync(grupo.PersonGroupId);
                          });
                   }
          }
</pre>
Cuando el proceso de entrenamiento haya concluido, tendremos todas las fotos cargadas en nuestro servicio superando el número de caras que el servicio nos permite por grupo.
<h3>Conclusiones</h3>
Con la técnica expuesta en este artículo podrás ir más allá de los límites que un grupo de Face Api permite. Sólo te queda aplicar las ideas y combinarlas con las diversas funcionalidades que el servicio cognitivo proporciona. Además, podrás mejorar y adaptar esta solución a tus necesidades concretas (permitir imágenes con varias fotos, identificar a las personas para no añadir dos personas repetidas al sistema, poder paralelizar varios de estos procesos para mejorar rendimiento…).
<h3>Referencias</h3>
Documentación oficial de Face Api:

<a href="https://docs.microsoft.com/es-es/azure/cognitive-services/face/overview">https://docs.microsoft.com/es-es/azure/cognitive-services/face/overview</a>

<a href="#_ednref1"></a>]]></content:encoded>
    </item>
    <item>
      <title>[Post invitado] Part 3. Step by step - How to train an objects classifier understanding Computer Vision techniques with Python and OpenCV</title>
      <link>https://docs.microsoft.com/archive/blogs/esmsdn/post-invitado-part-3-step-by-step-how-to-train-an-objects-classifier-understanding-computer-vision-techniques-with-python-and-opencv</link>
      <pubDate>Tue, 24 Apr 2018 03:00:46 GMT</pubDate>
      <dc:creator><![CDATA[esmsdn]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/esmsdn/?p=13465</guid>
      <description><![CDATA[In the previous post I explained how to create your own image detector with TensorFlow. It should be...]]></description>
      <content:encoded><![CDATA[In the previous post I explained <a href="https://blogs.msdn.microsoft.com/esmsdn/2018/04/09/post-invitado-part2-step-by-step-how-to-training-your-own-detector-classifier/">how to create your own image detector with TensorFlow</a>. It should be noted that you must differentiate between a classifier and an image detector. So, what is the difference between Object Detection and Object Recognition! Well, recognition simply implies establishing whether an image contains a specific object or not. while detection also requires the position of the object within the image. For example, there is an entry image that contains car, traffic lights, people, dogs, etc. The task is to be able to recognize which of the objects are contained in the image.

In this new post we will explain step by step how to create your own image classifier, not detection image as we did in the previous post. This time without using third-party technologies. If you have a computer with enough computer level and you also have a GPU like Nvidia GTX 650 or newer, you will not have any problems but also in the case that you do not have a very powerful computer you can follow this post and make a classifier without problems.
<h1>How to set up your virtual machine on Linux with Python and OpenCV</h1>
The classifier can be developed as we want, we can do it:
<h2>Data Science Virtual Machine for Linux(Ubuntu) on Azure</h2>
In the first place using virtual machines in Azure, already preconfigured. <a href="https://blogs.msdn.microsoft.com/esmsdn/2018/04/02/post-invited-analysis-and-object-detection-of- artworks-with-tensorflowgpu-on-windows-10/">If we have deployed our machine in Azure as in the first post.</a>

This time we can do the same but this time we will change OS instead of Windows, we will develop everything with Linux. Why? My intention is to show you that in machines with Windows 10 and Linux Ubuntu we can carry out our projects of machine learning with python without problems. Thanks to Anaconda we can create without problems our environment variables that will help us to carry out the same processes in both systems. You can see more information here:

<span><a href="https://azuremarketplace.microsoft.com/en-us/marketplace/apps/microsoft-ads.linux-data-science-vm-ubuntu">https://azuremarketplace.microsoft.com/en-us/marketplace/apps/microsoft-ads.linux-data-science-vm-ubuntu</a></span>

<span><a href="https://docs.microsoft.com/en-us/azure/machine-learning/data-science-virtual-machine/dsvm-tools-overview">https://docs.microsoft.com/en-us/azure/machine-learning/data-science-virtual-machine/dsvm-tools-overview</a></span>

<img src="https://artvisiondiag143.blob.core.windows.net/images/35.png" width="2704" height="1590" class="alignnone size-full" />

<em>Image Data Science Virtual Machine Linux (Ubuntu) summary</em>

We can see that this VM support many of the current deep learning environments and comes with CUDA and cuDNN installed, although many times without configuring paths. In the first post we explain how to configure these tools paths in our pc. For that case we only need to have Python environment 2.7, this VM default use 3.5 env. Also, we will not have to necessarily use a graphic card to develope this classifier of Artworks. Therefore, the idea of develope this classifier in a virtual machine of this dimension is not the best idea or profitable.

In the case that we want to use this option we have two options to develope the classifier project, one is connecting directly with PUTTY to the virtual machine and command line to organize and launch the commands to configure anaconda and the rest of the project. Another option is to connect by <span><a href="https://wiki.x2go.org/doku.php/doc:installation:x2goclient">X2GO</a></span>, it provides us UI rather friendly and close to a desktop interface. Be free to choose!!
<h2>Option X2GO</h2>
We need craate a new session first

<img src="https://artvisiondiag143.blob.core.windows.net/images/36.png" width="1360" height="729" class="alignnone size-full" />

Then we need to fill this form:
<ol>
 	<li><strong>Host</strong>: The host that azure give us to connect<img src="https://artvisiondiag143.blob.core.windows.net/images/37.png" width="1374" height="1339" class="alignnone size-full" /></li>
 	<li><strong>User: </strong>The user that you assigned when you create your machine on Azure</li>
 	<li><strong>Port SSH:</strong> 22</li>
 	<li><strong>Type of session:</strong> Change KDE to<span><a href="https://es.wikipedia.org/wiki/Xfce"> KFCE</a></span></li>
</ol>
<img src="https://artvisiondiag143.blob.core.windows.net/images/38.png" width="1514" height="521" class="alignnone size-full" />

Since we have X2GO with our session set up we can start to start our project!

The first thing we must do is configure our environment in python with the libraries that our project files need. To create the environment variable, we can launch this command:

<img src="https://artvisiondiag143.blob.core.windows.net/images/39.png" width="489" height="348" class="alignnone size-full" />

But there we are not assigning any library or what version of python we want, by default it will not assign the latest version and for the case of this project we do not need it but the 2.7. Therefore we need to write this command:

<img src="https://artvisiondiag143.blob.core.windows.net/images/40.png" width="490" height="349" class="alignnone size-full" />

Once we have our environment already configured in anaconda with python 2.7 we will be able to start running our .py files that will be explained later.
<h2>Our PC or Laptop</h2>
To carry out the project from your PC, first you must download and install Anaconda:

<span><a href="https://www.anaconda.com/download/">https://www.anaconda.com/download/</a></span>

It is very easy to start with python. Thanks to anaconda we can set up our environments easily and it's done in the same way that we explained earlier. Here you have a link to the anaconda documentation, it explains you how to configure your environments:

<span><a href="https://conda.io/docs/user-guide/tasks/manage-environments.html">https://conda.io/docs/user-guide/tasks/manage-environments.html</a></span>
<h1>Download this tutorial's repository from GitHub</h1>
Download the full repository located on this <span><a href="https://github.com/alexandergg/Project_Object_Detection_and_Classifier_Artworks">page</a></span>, scroll to the top and click Clone or Download and extract all the contents directly into the C:\ directory. This establishes a specific directory structure that will be used for the rest of the post. At this point, your project folder should looks like:

<img src="https://artvisiondiag143.blob.core.windows.net/images/41.png" width="1963" height="269" class="alignnone size-full" />

<em>Image Github repository solutions. API, App, Object Detection and Object classifer.</em>

<em>Solutions:</em>
<ul>
 	<li><span><a href="https://github.com/alexandergg/Project_Object_Detection_and_Classifier_Artworks/tree/master/Artworks_API">Artworks API with Flask</a></span></li>
 	<li><span><a href="https://github.com/alexandergg/Project_Object_Detection_and_Classifier_Artworks/tree/master/Artworks_app">Artworks Xamarin App</a></span></li>
 	<li><span><a href="https://github.com/alexandergg/Project_Object_Detection_and_Classifier_Artworks/tree/master/Project_Classifier_Artworks/Project_Classifier_Artworks"><em>Artworks Classifier</em></a></span></li>
</ul>
The first two solutions (API and App) we will explain in the last sections of this tutorial. The latter is the one that we will explain more in detail.
<h2>Artworks Classifier Solution</h2>
<img src="https://artvisiondiag143.blob.core.windows.net/images/41.jpg" width="2000" height="2000" class="alignnone size-full" />

<em>Image many artworks of the classsifier </em>

This folder contains the images of 20 artworks, python files needed to train artworks classifier. It also contains a <span><a href="https://developer.apple.com/documentation/coreml">CoreML</a></span> model if you wanted to use on iOS project with Xcode, it was generated with <span><a href="https://pypi.python.org/pypi/coremltools">Coremltools</a></span> library

If you want to practice training your own artwork classifer, you can leave all the files as they are. You can follow along with this tutorial to see how each of the files were generated, and then run the training. You will still need to generate the K-Means Cluster model and SVM model as described in nexts steps.

If you want to train your own object classifier, delete the following files (do not delete the folders):
<ul>
 	<li>All files in \images\train</li>
</ul>
Now, you're ready to start from scratch by training your own object classifier. This publication will assume that all the files listed above are unknown and will continue to explain how to use these files for your own training data set. Also, in this tutorial I explain important topics that are needed to complete this tutorial, knowing them we will be easier to understand the why of the processes. Such as:
<ul>
 	<li>Machine learning techniques</li>
 	<li>Algorithms</li>
 	<li>Image descriptors</li>
 	<li>Histograms and color histograms for artworks</li>
 	<li>K-Mean Clustering</li>
 	<li>Bag of Visual Words</li>
</ul>
<h1>Introduction to Machine Learning Techniques (Computer Vision)</h1>
Machine learning is related to computer science specifically with Artificial Intelligence. Within the latter the goal of machine learning is to create methods that allow computers to have the ability to learn.

<img src="https://artvisiondiag143.blob.core.windows.net/images/42.jpg" width="920" height="682" class="alignnone size-full" />

Within machine learning we find a subarea that we will use to create this project, called <em>Computer Vision</em>, it is an area that includes different functionalities such as acquiring, processing, analyzing and understanding real-world images in order to translate an image to numerical or symbolic information understandable to a computer. The acquisition of data is achieved by means such as camera, multidimensional data, scanners ... etc.

Today this discipline is used for different purposes, such as:
<ul>
 	<li><strong>Object detection</strong></li>
 	<li><strong>Video analysis</strong></li>
 	<li><strong>3D Vision</strong><strong> </strong></li>
</ul>
We will only focus on the first option, detection of objects in an image. The detection of objects is a part of the artificial vision that detects objects in an image based on their visual appearance. Within the detection of objects in an image we can distinguish two phases:
<ul>
 	<li><strong>Extraction of image characteristics</strong>
<ul>
 	<li>Consists of obtaining mathematical models that summarize the content of the image. These characteristics are also called descriptors, we will comment more in detail in next sections.</li>
</ul>
</li>
 	<li><strong>Search for objects based on these characteristics</strong>
<ul>
 	<li>For the object search process, we will have to elaborate the classification of said objects. There are several machine learning algorithms that will allow us to assign an identifier or label to an image. We will discuss it in future sections.</li>
</ul>
</li>
</ul>
Therefore, the classification of an image is the task of assigning a label to an image of a predefined set of categories. This means that, given an input image, our task is to analyze the image, return a label that categorizes the image. This tag is usually from a predefined set.

As, for example, if in our model classifier we do an inference with this image:

<img src="https://artvisiondiag143.blob.core.windows.net/images/43.jpg" width="513" height="599" class="alignnone size-full" />

<em>Image “Las Meninas”</em>

It would have to return:

<strong><em>Label: Las Meninas</em></strong>

In a more formal way, given the previous image of input W x H pixels, with three channels, R (Red) G (Green) B (Blue), respectively, our objective will be to take the pixels W x H x3 = N and find out how to accurately classify the contents of the image.

In addition, we must realize that in computer vision we must give value to semantics. For a human it is trivial that he knows that the previous image is a picture of art, specifically the Meninas. But in a computer, it is not trivial, nor does it have to know it. For the computer to analyze them, it will differentiate three main properties of every image:
<ul>
 	<li><strong>Spatial environment</strong></li>
 	<li><strong>Color</strong></li>
 	<li><strong>Texture</strong></li>
</ul>
These properties are encoded in a computer thanks to what we previously named as descriptors, each descriptor specializes in space, colors, textures, etc. Finally, based on the computer with these characterizations of space, textures and colors, you can apply automatic learning to learn how each type of image is, in our case, diverse types of art pictures of different artists.

For this we also must understand how computers represent images to analyze them. As for example if we insert the picture of the Mona Lisa or Mona Lisa in our classifier, it would represent it like this:

<img src="https://artvisiondiag143.blob.core.windows.net/images/44.jpg" width="275" height="410" class="alignnone size-full" />

<img src="https://artvisiondiag143.blob.core.windows.net/images/45.png" width="669" height="650" class="alignnone size-full" />

<em>Image Giocona and her features descriptors</em>

In addition, another section to investigate on how to build our classifier of art pictures is how the image or an object appears in an image. That is, the different points of view of a painting, different dimensions of the pictures, deformations of the image, lighting and occlusions.

<strong>1.4.2 Types of learning</strong>

When carrying out the research prior to the realization of the project we have encountered this problem, what kind of learning we want for our project. We have observed that there are three types of learning:
<ul>
 	<li><strong>Supervised</strong>
<ul>
 	<li>We have both image data (in image format or extracted feature vectors) along with the category label associated with each image so that we can teach our algorithm how each image category looks.</li>
</ul>
</li>
 	<li><strong>Non supervised</strong>
<ul>
 	<li>All we have is data from the image itself, we do not have labels or associated categories that we can use to teach our algorithm to make accurate predictions or classifications.</li>
</ul>
</li>
</ul>
<ul>
 	<li><strong>Semi-supervised</strong>
<ul>
 	<li>Try to be a middle ground between the previous two. We have a small group of our tagged image data and use that tagged information to generate more training data from the unlabelled data.</li>
</ul>
</li>
</ul>
In the end we opted to use the type of supervised learning since in our system we will have an image dataset of 20 tables where the category or label is the name of the table and each category contains 21 replications of that table. Something like our classifier would be:
<table>
<tbody>
<tr>
<td width="123"><strong>Label</strong></td>
<td width="106"><strong>Features Vector</strong></td>
</tr>
<tr>
<td width="123"><strong>Las Meninas</strong></td>
<td width="106">[…]</td>
</tr>
<tr>
<td width="123"><strong>3 de Mayo</strong></td>
<td width="106">[…]</td>
</tr>
<tr>
<td width="123"><strong>Maja desnuda</strong></td>
<td width="106">[…]</td>
</tr>
<tr>
<td width="123"><strong>Noche estrellada</strong></td>
<td width="106">[…]</td>
</tr>
<tr>
<td width="123"><strong>Mona Lisa</strong></td>
<td width="106">[…]</td>
</tr>
<tr>
<td width="123"><strong>         …</strong></td>
<td width="106">[…]</td>
</tr>
</tbody>
</table>
Features vector is something like this:
<ul>
 	<li><strong><em>Starry Night (Vincent Van Gogh):</em></strong></li>
</ul>
<img src="https://artvisiondiag143.blob.core.windows.net/images/46.png" width="575" height="353" class="alignnone size-full" />

<img src="https://artvisiondiag143.blob.core.windows.net/images/47.png" width="703" height="181" class="alignnone size-full" />

<strong>1.4.3 Pipeline of the image classifier</strong>

Once seen how to manipulate the images and how to face our learning we will have to investigate on how to divide in processes to build our classifier of images of art pictures. One of the most common pipelines is the following one in which we distinguish 5 phases that are:

<img src="https://artvisiondiag143.blob.core.windows.net/images/48.png" width="1135" height="171" class="alignnone size-full" />

<strong>Phase 1: Structure our initial dataset</strong>

It will be necessary to create our categories each with their images and specific labeling. In this project it would be like this:

<strong><em>Categories = {Las Meninas, Shooting 3 of May, Gioconda, Maja Desnuda, Starry Night, …}</em></strong>

<strong>Phase 2: Splitting our dataset (train and test)</strong>

Once we have our initial data set, we must divide it into two parts, training set and evaluation set or tests. Our classifier uses a training set to "learn" what each category looks like by making predictions about the input data and then correct it when the predictions are incorrect. After the classifier has been trained, we can evaluate its performance in a test set.

<img src="https://artvisiondiag143.blob.core.windows.net/images/49.jpg" width="1318" height="467" class="alignnone size-full" />

Algorithms like the ones we use in this Random Forest Classifier project, have many configurable parameters that will help us if we configure them well to obtain optimal performance. These parameters are called hyperparameters.

<strong>Phase 3: Extract features</strong>

Once we have our final data divisions, we will need to extract functions to quantify and abstract each image. The most common options according to the previous investigation are:
<ul>
 	<li><strong><em>Color descriptors</em></strong></li>
 	<li><strong><em>Histogram of gradients oriented (HOG)</em></strong></li>
 	<li><strong><em>Histograms with local binary patterns (BRIEF, ORB, BRISK, FREAK)</em></strong></li>
 	<li><strong><em>Local Invariant Descriptors (SIFT, SURF, RootSIFT)</em></strong><strong><em> </em></strong></li>
</ul>
<strong>Phase 4: Train our classification model</strong>

Given the feature vectors associated with the training data, we will be able to train our classifier. The objective here is for our classifier to learn how to recognize each of the categories in the data of our label. For this we have done an analytical study, such as Support Vector Machine, K-Nearest Neighbor.

<strong>Phase 5: Evaluate our classifier</strong>

Finally, we must evaluate our trained classifier. For each one of the vectors of characteristics in our test set, we present them to our classifier and we ask you to predict which is the label of the inserted image. Then we will have to tabulate the classifier's predictions for each point in the test set.

Finally, these classifier predictions are compared with the Ground-true label of our test set. The Ground-truth labels represent what the category really is. From there we can calculate the number of predictions that our classifier was successful and calculate aggregate reports such as Precision, Recall and F-Measure, which are used to quantify the performance of our classifier.
<h1>Classifications Algorithms (SVM and Random Forest)</h1>
We can find in the Machine Learning environment a great diversity of algorithms that we can use for our classification of art frames. After a series of tests between SVM, Decision three, K-Nearest Neighbor and Random Forests, we have chosen the latter due to the results we have found when we need to classify 5 pictorical styles. When our classifier classifies artworks, we used SVM.
<h2>Random Forest</h2>
We have investigated a bit how this algorithm works in detail. It was created and introduced into the scientific community by Leo Brieman in his 2001 article, Random Forests is one of those algorithms that many scientists still do not believe works, many say it is an elegant and straightforward way to perform a classification, others They say that there are multiple Decision trees but with a touch of randomness that clearly increases their accuracy.

From the satisfactory results and the good comments on it, it has been decided in this section to show the functioning of said algorithm. The Random Forests are a type of method of classification by sets, instead of using a single classifier as we have done in the tests with SVM and K-NN, this uses multiple classifiers that are added in one called goal classifier. In our case we will build multiple decision trees in a forest and then we will use our forest to make predictions.

<img src="https://artvisiondiag143.blob.core.windows.net/images/50.jpg" width="600" height="334" class="alignnone size-full" />

As you can see in the previous figure, our random forest consists of multiple grouped decision trees. Each decision tree "votes" on what it believes is the final classification. These votes are tabulated by the classifying goal, and the category with the most votes is the final classification.

You have to make an appointment to the "Jensen Inequality" to understand a large part of how Random Forests works. Dietterich's seminal work (2000) details the theory of why ensemble methods can generally obtain greater precision than a single model alone. This work depends on Jensen's Inequality, which is known as "diversity" or "decomposition of ambiguity" in the machine learning literature.

The formal definition of Jensen's Inequality states that the combined (average) convex set will have an error that is less than or equal to the average error of the individual models. It may be that an individual model has an error smaller than the average of all the models, but since there is no criterion that we can use to "select" this model, we can be sure that the average of all the models will not be worse than the Select any random individual model.

Another crucial factor is boostrapping or randomization injection. These classifiers train each individual decision tree in an initial sample of the original training data. Boostrapping is used to improve the accuracy of machine learning algorithms while reducing the risk of overfitting.

In the following figure we simulate the selection of "votes" created in the nodes of our classifier. We pass a feature input vector and through each of the decision trees, you will receive the votes of the class label of each of the trees, then a count of the votes will be done to show the final classification or prediction.

<img src="https://artvisiondiag143.blob.core.windows.net/images/51.jpg" width="587" height="348" class="alignnone size-full" />

In conclusion, we have used this classifier, since it is a set method that consists of multiple decision trees. The ensemble methods, such as the Random Forests, tend to obtain greater precision than other classifiers, since they averaged the results of each individual model. The reason why this average works is due to Jensen's Inequality.

Randomness is introduced in the selection of training data and in the selection of the characteristic column when training each tree in the forest. As Brieman discussed in his article Random forests, performing these two levels of random sampling helps (1) avoid overfitting and (2) generates a more precise classifier.
<h2>Support Vector Machine</h2>
The reason why SVMs are so popular is because they have quite solid theoretical foundations. The hyperparameters are still being improved, but in general, launching an SVM to a problem is an effective way to quickly obtain a prediction or a good result for a given problem. However, with what has given me problems with the adjustment of the parameters, if you want to obtain an optimal result you need to play with them and adjust it to the maximum to our problem, for this I found a Python library <em>GridSearchCV</em> that helps you to program the parameters that you want to touch and automate a workout executing all the possible combinations of said hyperparameters, with this we will save a lot of time in going testing different values ​​one by one.
<h2>Types of SVM</h2>
We will only explain how the linear SVM type worksLinear separability
<h3>Linear separability</h3>
In order to explain SVMs, we should first start with the concept of linear separability. A set of data is linearly separable if we can draw a straight line that clearly separates all data points in class #1 from all data points belonging to class #2:

<img src="https://artvisiondiag143.blob.core.windows.net/images/52.jpg" width="1000" height="850" class="alignnone size-full" />

<em>Image. Given our decision boundary, I am more confident that the highlighted square is indeed a square, because it is farther away from the decision boundary than the circle is.</em>

Take a few seconds and examine this plot and convince yourself that there is no way to draw a single straight line that cleanly divides the data points, so all blue squares are on one side of the line and all red circles on the other. Since we cannot do that, this is an example of data points that are not linear separable.

<em>*Note: As we’ll see later in this lesson, we’ll be able to solve this problem using the kernel trick.</em>

In the case of Plots, A and B, the line used to separate the data is called the separating hyperplane. In 2D space, this is just a simple line. In a 3D space, we end up with a plane. And in spaces &gt; 3 dimensions, we have a hyperplane.

Regardless of whether we have a line, plane, or a hyperplane, this separation is our decision boundary or the boundary we use to decide if a data point is a blue rectangle or a red circle. All data points for a given class will lay on one side of the decision boundary, and all data points for the second class on the other.

Keeping this in mind, wouldn’t it be nice if we could construct a classifier where the farther a point is from the decision boundary, the more confident we are about its prediction?
<h1>Images Descriptors</h1>
A very important part of our project, not to say the main one, is to know how to extract the descriptors of the images in the best way and efficiently possible. Before going into explaining the world of descriptors, basic concepts in the field of computer vision will be briefly explained and many professionals overlook such concepts, such as image descriptors, feature descriptors and feature vectors. All these terms are very similar, but nevertheless it is very important to understand the difference between them.

To begin with, a feature vector is simply a list of numbers used to abstractly quantify the content of an image. Characteristic vectors are transmitted to other computer vision programs, such as the creation of an automatic learning classifier to recognize the contents of the image using feature vectors or comparing feature vectors for similarity when constructing an image search engine. To extract feature vectors from an image, we can use image descriptors or feature descriptors.

An image descriptor quantifies the complete image and returns a vector of characteristics per image. The descriptions of the images tend to be simple and intuitive to understand but may lack the ability to distinguish between different objects in the images.

On the contrary, a feature descriptor quantifies many regions of an image, returning multiple feature vectors per image. Feature descriptions tend to be much more powerful than simple image descriptors and more robust to changes in rotation, translation and point of view of the input image.

An impediment that arose when extracting feature descriptors is that not only do we have to store several feature vectors per image, which increases our storage overhead, but we also need to apply methods such as Bag of Visual Words to take the multiple vectors of characteristics extracted from an image and condensed into a single vector of characteristics. This Bag Visual Words technique will be explained in another section.

Within the descriptors there are several types, but for our case we will use the local invariant descriptors, consisting mainly of:
<ul>
 	<li><em>SURF</em> (Speeded Up Robust Features), Keypoint detector DoG</li>
 	<li><em>SIFT</em> (Scale-invariant Feature Transform), Keypoint Detector Fast-Hessian</li>
</ul>
We will explain the operation of each one of them with an example of execution with the art pictures and we will explain the reason of our choice of SIFT through results represented in graphs.
<h2>Understanding Local features</h2>
The most usual when analyzing an image above is to use image descriptors to complete image, this leaves us with a global quantification of the image. However, a global quantification of the image means that each pixel of the image is included in the calculation of the vector characteristics and may not always be the most appropriate.

Suppose for a second that we were tasked with building a computer vision system to automatically identify the covers of books. Our system would take a photo of a artwork cover captured from a mobile device such as an iPhone or Android, extract features from the image, and then compare the features to a set of artworks covers in a database.

We can use HOG for resolve this problem? Or LBP’s?

The problem with these descriptors is that they are all global image, and if we use it we will end up by quantifying image regions that do not interest us, such as people who are in front of the frames. Including these regions in our vector calculation can dramatically divert the vector of output characteristics and we run the risk of not being able to correctly identify the art box.

The solution to this is to use local characteristics, where we only describe small local areas of the image that are considered interesting instead of the whole image. These regions must be unique, easily compared and carry some kind of semantic meaning in relation to the contents of the image.

<em>*Note: At the highest level, a “feature” is a region of an image that is both unique and easily recognizable.</em>
<h2>Keypoint detection and feature extraction</h2>
The process of finding and describing interesting regions of an image is broken down into two phases: keypoint detection and feature extraction.

The first phase is to find the “interesting” regions of an image. These regions could be edges, corners, “blobs”, or regions of an image where the pixel intensities are approximately uniform. There are many different algorithms that we’ll study that can find and detect these “interesting” regions — but in all cases, we call these regions keypoints. At the very core, keypoints are simply the (x, y)-coordinates of the interesting, salient regions of an image.

Then for each of our keypoints, we must describe and quantify the region of the image surrounding the keypoint by extracting a feature vector. This process of extracting multiple feature vectors, one for each keypoint, is called feature extraction. Again, there are many different algorithms we can use for feature extraction, and we’ll be studying many of them in this module.

However, up until this point, we have had a one-to-one correspondence between images and feature vectors. For each input image, we would receive one feature vector out. However, now we are inputting an image and receiving multiple feature vectors out. If we have multiple feature vectors for an image, how do we compare them? And how do we know which ones to compare?

As we’ll find out later in this tutorial, the answer is to use either keypoint matching or the <strong><em>bag-of-visual-words model</em></strong>
<h2>Research on SIFT and SURF</h2>
First, we explain what SIFT is. SIFT descriptor is a lot easier to understand than the Difference of Gaussian (DoG) keypoint detector also proposed by David Lowe in his 1999 ICCV paper, Object recognition from local scale-invariant features. The SIFT feature description algorithm requires a set of input keypoints. Then, for each of the input keypoints, SIFT takes the 16 x 16-pixel region surrounding the center pixel of the keypoint region.

The SURF descriptor is a characteristic vector extraction technique developed by Bay in its 2006 ECCV document. It is very similar to SIFT, but it has two main advantages with respect to SIFT.
<ol>
 	<li>The first advantage is that SURF is faster to calculate than SIFT, so it is more suitable for real-time applications.</li>
 	<li>The second advantage is that SURF is only half the size of the SIFT descriptor. SIFT returns a feature vector of 128-dim and SURF returns a vector of 64-dim.</li>
</ol>
Having understood its advantages, we have decided to set an objective and it is to understand why SURF works and because it obtains such satisfactory results, for this we have had to understand a keypoint descriptor called Fast Hessian. Today, both the SURF image description algorithm and the Fast Hessian keypoint descriptor ended up calling both by the scientific community "SURF" although they may be confusing.

Initially before SIFT and SURF were introduced there was a trend in which the proposed algorithms included a keypoint detector and an image descriptor, so sometimes we see both keypoint detectors and image descriptors share the same name.

But how does the Fast Hessian keypoint descriptor work?

The motivation of Fast Hessian and SURF came from the slowness of DoG and SIFT. The computer vision researchers wanted a faster keypoint detector and image descriptor.

The Fast Hessian is based on the same principles as DoG in that keypoints must be repeatable and recognizable at different scales of an image. However, instead of calculating the Gauss Difference explicitly as done in DoG, Bay proposed that instead we approximate the Gaasian Differences step using what is called Haar Wavelets and integral images. We will not go into detail about this technique, but in the following image you can see how it would become a theory:

<img src="https://artvisiondiag143.blob.core.windows.net/images/53.png" width="624" height="143" class="alignnone size-full" />

And from the previous result of building a matrix:

<img src="https://artvisiondiag143.blob.core.windows.net/images/54.png" width="193" height="123" class="alignnone size-full" />

A region will be marked with a keypoint if the candidate pixel score is 3 x 3 x 3 times greater than the neighbor. Unlike SIFT, this time we are only interested in maximums, not maximums or minimums. The use of Fast Hessian for our application is the best choice since it is very appropriate for a real-time approach. In the following image we have tested an image of "The persistence of memory" of Dali executing on this Fast Hessian and extracting its keypoints:

<img src="https://artvisiondiag143.blob.core.windows.net/images/55.png" width="1130" height="430" class="alignnone size-full" />

<em>Image Fast Hessian Keypoints of “Persistence of memory”</em>

Once these keypoints are extracted our image description algorithm, SURF, we will go through each one of the keypoints obtained and we will divide the keypoint region into 4 x 4 sub-areas, just like in SIFT.

<img src="https://artvisiondiag143.blob.core.windows.net/images/56.jpg" width="1203" height="585" class="alignnone size-full" />

From this step is when SIFT and SURF begin to differentiate. SURF for each of these sub-areas of 4 x 4 extracts by means of the Haar Wavelet sample points of 5 x 5.

<img src="https://artvisiondiag143.blob.core.windows.net/images/57.jpg" width="584" height="432" class="alignnone size-full" />

And for each extracted cell, both directions X and Y are processed by Haar Wavelets. This result is known as d_ {x} and d_ {y}.

<img src="https://artvisiondiag143.blob.core.windows.net/images/58.png" width="306" height="196" class="alignnone size-full" />

Now that we have d_ {x} and d_ {y}, we will extract their weights by means of what is called as Gaussian Kernel as in SIFT. The results furthest from the center of the keypoint will contribute less to the vector of final characteristics, however, the results that are closer to the center of the keypoint will contribute to the vector of final characteristics. And finally, SURF to finish its process and finally process its characteristics vector for each sub-area of ​​4 x 4 process this formula:

<img src="https://artvisiondiag143.blob.core.windows.net/images/59.png" width="656" height="424" class="alignnone size-full" />

Therefore, as we said at the beginning we have 4 x 4 = 16 subareas, returning a 4-dim vector to us. These characteristic vectors with 4-dim will concatenate with each other, giving rise to 16 x 4 = 64-dim of the characteristic vector of SURF. Below are results with paintings by Velázquez (Las Meninas) and DaVinci (The Last Supper).

<img src="https://artvisiondiag143.blob.core.windows.net/images/60.jpg" width="1344" height="612" class="alignnone size-full" />

<em>Image SURF keypoints of “Las Meninas”</em>

<img src="https://artvisiondiag143.blob.core.windows.net/images/61.jpg" width="1304" height="633" class="alignnone size-full" />

<em>Image SURF Keypoints of “The Last Supper"</em>
<h1>Histograms and color histograms for artworks</h1>
Within the fundamental concepts in computer vision we must give weight to the histograms. So, what exactly is a histogram? A histogram represents the distribution of pixel intensities (whether color or gray- scale) in an image. It can be visualized as a graph (or plot) that gives a high-level intuition of the intensity (pixel value) distribution. We are going to assume a RGB color space in this example, so these pixel values will be in the range of 0 to 255. And now what is a color histogram? Simply put, a color histogram counts the number of times a given pixel intensity (or range of pixel intensities) occurs in an image. Using a color histogram, we can express the actual distribution or “amount” of each color in an image. The counts for each color/color range are then used as our feature vector.

<img src="https://artvisiondiag143.blob.core.windows.net/images/63.jpg" width="213" height="235" class="alignnone size-full" />

<em>Image Athens School artwork</em>

<img src="https://artvisiondiag143.blob.core.windows.net/images/64.png" width="2733" height="1731" class="alignnone size-full" />

<em>Image Histogram of Athens School artwork</em>

We see there is a sharp peak in green and red histogram around bin 200, the end of the histogram represents that the men on the right with the toga with "green" and "red" tonality contain many pixels with that color range. We see that most of red, green, blue pixels are container in all the range except in final when blue decrease.

*Note: <em>I’m intentionally revealing which image I used to generate this histogram. I’m simply demonstrating my thought process as I look at a histogram. Being able to interpret and understand the data you are looking at, without necessarily knowing its source, is a good skill to have in computer vision.</em>

Now in the image below que can see a grapchic of computing 2d color histograms for each combination of the red, green, and blue channels. First is a 2D color histogram for the Green and Blue channels, the second for Green and Red, and the third for Blue and Red.

<img src="https://artvisiondiag143.blob.core.windows.net/images/65.png" width="2735" height="1745" class="alignnone size-full" />

<em>Image computing 2d color histograms for each combination of the red, green, and blue channels.</em>
<h1>K-Means Clustering</h1>
Once we understand how we extract the color histograms thanks to K-means we can group colors and know which the predominant ones in each painting, artist or pictorial style are. In addition to relating a feeling with the colors that we extract from a painting, such as <em>happy</em> or <em>sad</em>.

There are many clustering algorithms in ML but k-means after investigation I discovered that it is the most popular, most used and easiest to understand clustering algorithm. The K-Means algorithm is a type of unsupervised learning algorithm (no label/category information associated with the images/feature vector), I was explained this concept before in <em>Machine learning techniques chapter</em>.

Clustering algorithms seek to learn, from the properties of the data, an optimal division or discrete labeling of groups of points. Scientist call K-means because it finds “K” unique clusters where the center of each cluster(centroid) is the mean of all values in the cluster. The overall goal of use k-means is to put similar points in a cluster and dissimilar data points in a different cluster. For more information about clustering techniques enter in this <span><a href="https://www.mit.edu/~9.54/fall14/slides/Class13.pdf">link</a></span>.

Many clustering algorithms are available in Scikit-Learn and elsewhere, but perhaps the simplest to understand is an algorithm known as k-means clustering, which is implemented in <span><a href="https://scikit-learn.org/stable/modules/generated/sklearn.cluster.KMeans.html"><em>sklearn.cluster.KMeans</em></a></span>.
<h2>Applications of k-means in computer vision</h2>
In my project the k-means algorithm can also be used to extract the dominant colors of an artworks like extract the top 5 colours used by Ziem Felix in his 20 more relevant artworks. However, we can also extract features for only one artwork or a style of art, only changing the dataset.

<img src="https://artvisiondiag143.blob.core.windows.net/images/66.jpg" width="900" height="691" class="alignnone size-full" />

<em>Image example of artwork of Ziem Felix (Romanticism)</em>

Here I show you just one picture of Felix Ziem but in almost all we can see the same colors, intuitively we can distinguish the colors that predominate in this author but thanks to K-means we can get the correct colors like the following:

<img src="https://artvisiondiag143.blob.core.windows.net/images/67.png" width="1284" height="542" class="alignnone size-full" />

<em>Image Top 4 Colors predominant in Felix Ziem artworks</em>

You will think it is complicated but in a few lines of code knowing what we do with python we get it quickly. Only we need to take the raw pixel intensities of the image dataset as our data points, pass them on to k-means, and let the clustering algorithm determine the dominant colors. That’s all!

Example of extract Top 3 colors in only one artwork:

<img src="https://artvisiondiag143.blob.core.windows.net/images/68.png" width="1293" height="592" class="alignnone size-full" />

<em>Image Vincent Van Gogh artwork Top 3 [n_clusters] colors</em> <em>(Cafe Terrace)</em>

<img src="https://artvisiondiag143.blob.core.windows.net/images/69.jpg" width="2000" height="2000" class="alignnone size-full" />

<em>Image Salvador Dali artwork Top 5 [n_clusters] colors (Persistence of memory)</em>

In addition, reading a little about color analysis articles, several modern artists and current psychologists have in many cases managed to understand what feelings and emotions had the artists or pictorial styles. Graphics like these explain the feelings and aptitudes of some colors and with the extraction of these colors, thanks to K-Means, we can relate them to this table.

<img src="https://artvisiondiag143.blob.core.windows.net/images/70.png" width="1360" height="1153" class="alignnone size-full" />

<em>Image example plot of color emotions</em>

However, the most popular usage of k-means in computer vision/machine learning is the bag-of-visual words (BOVW) model where we:
<ul>
 	<li>Extract SIFT/SURF (or other local invariant feature vectors) from a dataset of images.</li>
 	<li>Cluster the SIFT/SURF features to form a “codebook”.</li>
 	<li>Quantize the SIFT/SURF features from each image into a histogram that counts the number of times each “visual word” appears.</li>
</ul>
<h1>Classification with Bag of Visual Words</h1>
This section is one of the most important! In the previous sections we have explained several very important concepts in computer vision to understand now the processes that are needed to carry out our image classifier, specifically artworks. But before I start to explain the classifier code, I will explain above what it means Bag of Visual words.
<h2>What is BOVW?</h2>
To begin explaining what this technique consists of I would like to mention the world of natural language processing or also known as NLP, where our intention is to compare and analyze multiple documents. Each document has a lot of different words and in a certain order. With this technique we ignore the order

and simply throw the words in a "bag". Then once all the words inside the bag we can analyze the occurrences of each word. Finally, with the NLP, each document becomes a histogram of word counts and can be used as characteristics for a ML process.
<h2>Visual Words</h2>
In the section on image descriptors, explain what they are and how they are generated (SIFT, SURF). If we think of an image as a document of "words" generated by SIFT, we can extend the Bag Visual Words model to classify images instead of text documents. We can imagine that a SIFT descriptor or "Visual Word" represents an object of the picture, as for example a descriptor can be the eye of the Mona Lisa.

These descriptors of SIFT have variations so we must have some grouping method to group words that represent the same. For example, all the characteristics of the eye of the Gioconda must go to the same cluster or container.

However, the characteristics of SIFT are not as literal as saying "eye of the Gioconda". We can not agrpar them according to a human definition but mathematically. For this the descriptions of SIFT are 128-dimensional vices so we can simply make a matrix with each SIFT descriptor in our training set as its own row, and 128 columns for each of the dimensions of the SIFT characteristics. Once all this has been done, we will have to connect that matrix to an aggregating algorithm, such as the K-Means explained above.

<img src="https://artvisiondiag143.blob.core.windows.net/images/71.jpg" width="1857" height="1051" class="alignnone size-full" />

<em>Image Example Bag Visual Words</em>

Next we go through each individual image, and assign all of its SIFT descriptors to the bin they belong in. All the “eye” SIFT descriptors will be converted from a 128-dimensional SIFT vector to a bin label like “eye” or “Bin number 4”. Finally we make a histogram for each image by summing the number of features for each codeword. For example, with K=3, we might get a total of 1 eye feature, 3 mouth features, and 5 bridge features for image number 1, a different distribution for image number 2, and so on.

<em>*Note: Remember, this is just a metaphor: real SIFT feature clusters won’t have such a human-meaningful definition.</em>

At this point we have converted images with varying numbers of SIFT features into K features. We can feed the matrix of M observations and K features into a classifier like Random Forest, AdaBoost or SVC as our X, image labels as our y, and it ought to be able to predict image labels from images with some degree of accuracy.

But how can I know how many bins I need? And what is K?

For this artworks classifier, I performed a grid search across a range of K values and compared the scores of classifiers for each K. The code is on <span><a href="https://github.com/alexandergg/Project_Object_Detection_and_Classifier_Artworks/tree/master/Project_Classifier_Artworks/Project_Classifier_Artworks">github</a></span> and references other files in the <span><a href="https://github.com/alexandergg/Project_Object_Detection_and_Classifier_Artworks">repo</a></span>.

<em>*Note: Determining what is K to use for K-Means depending on your project and how long it takes to do a grid search, you might want try differents methods.</em>
<h2>Prepare our classifier</h2>
Once we have understood the theory, we move on to the practical part to train our classifier and obtain our model to use it in our mobile application developed in Xamarin. The first thing we must do now is to understand each of the files that make up our solution of the object classifier.

<img src="https://artvisiondiag143.blob.core.windows.net/images/72.png" width="568" height="590" class="alignnone size-full" />

<em>Image repository files</em>

We can see that it consists of a folder images and three python files
<ul>
 	<li><em>py</em></li>
 	<li><em>py</em></li>
 	<li><em>py</em></li>
</ul>
We will not have to modify anything of the code, it will only depend on each one the modification of the dataset to change the thematic of images to classify.

<img src="https://artvisiondiag143.blob.core.windows.net/images/73.png" width="1723" height="504" class="alignnone size-full" />

<em>Image Dataset folder</em>
<h2>Training</h2>
From the project directory, issue the following command to begin training:
<pre>python  detector_K_gridsearch.py –train_path images/train</pre>
If everything has been set up correctly, python will initialize the training. When training begins, it will look like this:

<img src="https://artvisiondiag143.blob.core.windows.net/images/74.png" width="527" height="858" class="alignnone size-full" />

<em>Image Reading all files of differents folders of artworks</em>

Then after reading all the files, the program start to extract features descriptors for each image, in the image below I show you the example of Moulin Gallete Artwork, in which we see that the 21 images that we have of this picture now we have extracted their descriptors of characteristics:

<img src="https://artvisiondiag143.blob.core.windows.net/images/75.PNG" width="1287" height="994" class="alignnone size-full" />

<em>Image Array of descriptors of Moulin Gallete Artwork</em>

<img src="https://artvisiondiag143.blob.core.windows.net/images/76.png" width="331" height="274" class="alignnone size-full" />

When we have finished generating the descriptors of each image, we begin to group the SIFT descriptors of each group of images to then perform the clustering and start creating our word notebook. Our codebook will increase with K, thanks to <span><a href="https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GridSearchCV.html">GridSearchCV</a></span> we can do several tests with different values ​​of K to see which is the most optimal value for our bag of visual words. We will start with K = 50, K = 150, K = 300, K = 500 words.

<img src="https://artvisiondiag143.blob.core.windows.net/images/77.png" width="573" height="138" class="alignnone size-full" />

<img src="https://artvisiondiag143.blob.core.windows.net/images/78.PNG" width="514" height="834" class="alignnone size-full" />

<em>Image Codebook K=50</em>

<img src="https://artvisiondiag143.blob.core.windows.net/images/79.png" width="587" height="893" class="alignnone size-full" />

<em>Image Codebook K=150</em>

<img src="https://artvisiondiag143.blob.core.windows.net/images/80.png" width="504" height="854" class="alignnone size-full" />

<em>Image Codebook K=300</em>

<img src="https://artvisiondiag143.blob.core.windows.net/images/81.png" width="638" height="889" class="alignnone size-full" />

<em>Image Codebook K=500</em>

<img src="https://artvisiondiag143.blob.core.windows.net/images/82.png" width="558" height="101" class="alignnone size-full" />

<em>Image Comparison result of GridSearchCV for value “K” beetween SVM and AdaBoost</em>

At the end of the process we will automatically save, thanks to GridSearchCV, the best SVM model in this case, which gives us a result of accuracy 0.91 and also save the model of our cluster. Two very necessary files for our API.
<h1>Python App on Azure Web App with Flask</h1>
I try to explain you several steps that you must do it to build succesfully your Python Web App with Flask on Azure:
<ol>
 	<li>Look this doc: <span><a href="https://docs.microsoft.com/es-es/visualstudio/python/publishing-python-web-applications-to-azure-from-visual-studio">https://docs.microsoft.com/es-es/visualstudio/python/publishing-python-web-applications-to-azure-from-visual-studio</a></span></li>
 	<li>Look this doc: <span><a href="https://docs.microsoft.com/es-es/visualstudio/python/managing-python-on-azure-app-service">https://docs.microsoft.com/es-es/visualstudio/python/managing-python-on-azure-app-service</a></span></li>
</ol>
These docs will help you to do your build on azure succesfully, I attach you an image of my Flask API solution structure. But first, we need to create in visual studio a project with Flask

<img src="https://artvisiondiag143.blob.core.windows.net/images/83.png" width="945" height="655" class="alignnone size-full" />

<em>Image Flask Web Project template</em>

<img src="https://artvisiondiag143.blob.core.windows.net/images/84.png" width="324" height="596" class="alignnone size-full" />

<em>Image API Solution files</em>

As you can see, we have several files but many of them we only need for the start-up of our solution in our azure web app. If you look, we have a folder called “<em>pickle</em>” where we have the model of our classifier made by the SVM algorithm and we also have a model created by Bag of Visual Words technique with K-Means Clustering.

<img src="https://artvisiondiag143.blob.core.windows.net/images/85.png" width="300" height="593" class="alignnone size-full" />

<em>Image Python environment in VS17</em>

In addition, we can configure our solution in “Python Environment option” and add the version of python that we want from visual studio itself and add the libraries that are important for our project, such as:

<img src="https://artvisiondiag143.blob.core.windows.net/images/86.png" width="341" height="543" class="alignnone size-full" />

<em>Image requirements.txt file</em>

Do not worry about creating the requirement.txt file because you have everything in the <span><a href="https://github.com/alexandergg/Project_Object_Detection_and_Classifier_Artworks">github repository</a></span>. Once explained this you have a series of images where I show you the web app in Azure and the options that we must activate before uploading our solution to Azure.

<img src="https://artvisiondiag143.blob.core.windows.net/images/87.png" width="2731" height="1616" class="alignnone size-full" />

<em>Image Azure Python Web App with Flask</em>

<img src="https://artvisiondiag143.blob.core.windows.net/images/88.png" width="1630" height="1572" class="alignnone size-full" />

<em>Image set up python environment version</em>

<img src="https://artvisiondiag143.blob.core.windows.net/images/89.png" width="1642" height="1150" class="alignnone size-full" />

<em>Image Add pytho to extension to our web app</em>

<img src="https://artvisiondiag143.blob.core.windows.net/images/90.png" width="2632" height="1248" class="alignnone size-full" />

<em>Image Location of python extension with Kudu</em>

&nbsp;

As I commented before to correctly upload our solution to azure we must create a series of files, like the runtime.txt. In that one you only need to write: python-3.4 for example or your version that you wanna use.
<pre>python-3.4</pre>
Another one file is runserver.py, is our index.html, you only need to change in your web.config in this line:
<pre>&lt;add key="WSGI_HANDLER" value="runserver.app"/&gt;</pre>
<em>*Note: Remember that you need to set the file with .app extension.</em>
<pre>&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;!--This template is configured to use Python 3.5 on Azure App Service. To use a different version of Python,or to use a hosting service other than Azure, replace the scriptProcessor path below with the path givento you by wfastcgi-enable or your provider. 
For Python 2.7 on Azure App Service, the path is "D:\home\Python27\python.exe|D:\home\Python27\wfastcgi.py" 
The WSGI_HANDLER variable should be an importable variable or function (if followed by '()') that returnsyour WSGI object. 
See https://aka.ms/PythonOnAppService for more information.
--&gt;
&lt;configuration&gt;
  &lt;appSettings&gt;
    &lt;add key="PYTHONPATH" value="D:\home\site\wwwroot"/&gt;
    &lt;!-- The handler here is specific to Bottle; other frameworks vary. --&gt;
    &lt;add key="WSGI_HANDLER" value="runserver.app"/&gt;
    &lt;add key="WSGI_LOG" value="D:\home\LogFiles\wfastcgi.log"/&gt;
  &lt;/appSettings&gt;
  &lt;system.webServer&gt;
    &lt;httpErrors errorMode="Detailed"&gt;
&lt;/httpErrors&gt; 
   &lt;handlers&gt;
      &lt;add name="PythonHandler" path="*" verb="*" modules="FastCgiModule"           scriptProcessor="D:\home\Python362x64\python.exe|D:\home\Python362x64\wfastcgi.py"           resourceType="Unspecified" requireAccess="Script"/&gt;
    &lt;/handlers&gt;
  &lt;/system.webServer&gt;
&lt;/configuration&gt;</pre>
&nbsp;

Finally, you only need to add another web config like this image in a ProyectNameFolder/static/web.config and add lines of code like the image below:
<pre>&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;!--This template removes any existing handler so that the default handlers will be used for this directory.Only handlers added by the other web.config templates, or with the name set to PythonHandler, are removed. 
See https://aka.ms/PythonOnAppService for more information.
--&gt;
&lt;configuration&gt;
  &lt;system.webServer&gt;
    &lt;handlers&gt;
      &lt;remove name="PythonHandler"/&gt;
    &lt;/handlers&gt;
  &lt;/system.webServer&gt;
&lt;/configuration&gt;</pre>
When you have all these files correct in VS you need to publish and go to your azure web app and enter in development tools/Extension and install in your web app the env of Python that you would like to use.

Then go to kudu. In the <span><a href="https://docs.microsoft.com/es-es/visualstudio/python/managing-python-on-azure-app-service">second link</a></span> that I attach you it explains you very well how to finish your deployment, you need to install in your web app via Kudu all requirements in your requirements.txt. You only need to locate your Python env that you were install previously in your web app, in my case in kudu the location is:
<pre>D:\home\Python362x64\python.exe</pre>
In these lines of code, you can see that I start to import of libraries to my solution:
<pre>from flask import Flask, request, render_template, jsonify
from werkzeug import secure_filename
import logging
import sysimport cv2
import numpy as np
from sklearn.externals import joblib
import os
import pickle
import json</pre>
Here I start to declare variables, path to my models (SVM and cluster model) and console messages to see in execution:
<pre>app.logger.info('\n\n* * *\n\nOpenCV version is %s. should be at least 3.1.0, with nonfree installed.' % cv2.__version__) 
app = Flask(__name__)
ALLOWED_EXTENSIONS = ['png', 'jpg', 'jpeg'] 
APP_DIR = os.path.dirname(os.path.realpath(__file__)) 
MAX_PIXEL_DIM = 2000 
PICKLE_DIR = os.path.abspath(    os.path.join(    APP_DIR,    './pickles/')) 
LOG_PATH = os.path.abspath(os.path.join(APP_DIR,'../art_app.log')) 
logging.basicConfig(filename=LOG_PATH,level=logging.DEBUG,    format='%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]') 
app.logger.setLevel(logging.DEBUG)</pre>
In these lines of code, I write the sentences necessary to get my models in their respectives path of my solution:
<pre># TODO: make pickles for kmeans and single best classifier.
filename = os.path.join(PICKLE_DIR, 'svc\\svc.pickle')
model_svc = pickle.load(open(filename, 'rb'), encoding='latin1') 
filename = os.path.join(PICKLE_DIR, 'cluster_model\\cluster_model.pickle')
clust_model = pickle.load(open(filename, 'rb'), encoding='latin1') 
cluster_model = clust_model
clf = model_svc</pre>
Function to detect extension of input files of my API:
<pre>def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
 # upload image with curl using:
# curl -F 'file=@/home/' 'https://127.0.0.1:5000/'</pre>
Function to resize the input files:
<pre>def img_resize(img):
    height, width, _ = img.shape
    if height &gt; width:
        # too tall
        resize_ratio = float(MAX_PIXEL_DIM)/height
    else:
        # too wide, or a square which is too big
        resize_ratio = float(MAX_PIXEL_DIM)/width
     dim = (int(resize_ratio*width), int(resize_ratio*height))
     resized = cv2.resize(img, dim, interpolation=cv2.INTER_AREA)
    app.logger.debug('resized to %s' % str(resized.shape))
     return resized</pre>
This is the more complex function cause here when I get the file, resize them we need to get features of the picture and do an inference to our cluster model, and finally get a result that we send to prediction function, the one in charge of making the inference to our SVM model:

def img_to_vect(img_np):
<pre>"""
Given an image path and a trained clustering model (eg KMeans),    generates a feature vector representing that image.    Useful for processing new images for a classifier prediction.    
"""    
# img = read_image(img_path)
    height, width, _ = img_np.shape
    app.logger.debug('Color image size - H:%i, W:%i' % (height, width))
    if height &gt; MAX_PIXEL_DIM or width &gt; MAX_PIXEL_DIM:
        img_np = img_resize(img_np)
    gray = cv2.cvtColor(img_np, cv2.COLOR_BGR2GRAY)
    sift = cv2.xfeatures2d.SIFT_create()
    kp, desc = sift.detectAndCompute(gray, None)
     clustered_desc = cluster_model.predict(desc)
    img_bow_hist = np.bincount(clustered_desc, minlength=cluster_model.n_clusters)
     # reshape to an array containing 1 array: array[[1,2,3]]
    # to make sklearn happy (it doesn't like 1d arrays as data!)
    return img_bow_hist.reshape(1,-1)</pre>
Finally, prediction function gets the result of our img_to_vect function and inference our svm model and return the result to our client:
<pre>def prediction(img_str):
    nparr = np.fromstring(img_str, np.uint8)
    img_np = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
    # convert to K-vector of codeword frequencies
    img_vect = img_to_vect(img_np)
    prediction = clf.predict(img_vect)
    return prediction[0]</pre>
And here’s our unique API function:
<pre> @app.route('/predict', methods=['POST'])
def home():
    if request.method == 'POST':
        f = request.files['file'] 
       if f and allowed_file(f.filename):
            filename = secure_filename(f.filename)
            app.logger.debug('got file called %s' % filename)
            lb = prediction(f.read())
            print (lb)
            json_result = json.dumps(lb.decode("utf-8"))
            return json_result
        return 'Error. Something went wrong.'
    else:
        return render_template('img_upload.jnj')
  if __name__=="__main__":
    app.run(debug=True)</pre>
<h2>Demo with Postman (Azure Web App and local):</h2>
<iframe width="560" height="315" src="https://www.youtube.com/embed/JEFLYTcJ8vs" frameborder="0" allowfullscreen></iframe>
<h1>Create the mobile app on Azure Mobile App with Xamarin</h1>
La aplicación de Xamarin que he desarrollado no esta actualizada a .Net Standard, pero no creo que de problemas con la estrategia de PCL (Portable Class Library) <strong>😊</strong><strong>. </strong>La solución es muy sencilla, unicamente contaremos con una única pagina de interfaz de usuario y añadiremos por consiguiente los controles necesarios de los botones, que serán elegir imagen del carrete, tomar foto y clasificar.
<pre>&lt;?xml version="1.0" encoding="utf-8" ?&gt;
&lt;ContentPage xmlns="https://xamarin.com/schemas/2014/forms"             xmlns:x="https://schemas.microsoft.com/winfx/2009/xaml"             xmlns:local="clr-namespace:CustomVision"             x:Class="CustomVision.MainPage"&gt;
    &lt;StackLayout Padding="20"&gt;
        &lt;Image x:Name="Img" Source="ic_launcher.png" MinimumHeightRequest="100" MinimumWidthRequest="100"&gt;
&lt;/Image&gt;
        &lt;Button Text="Elegir imagen" Clicked="ElegirImage"&gt;
&lt;/Button&gt;
        &lt;Button Text="Sacar foto" Clicked="TomarFoto"&gt;
&lt;/Button&gt;
        &lt;Button Text="Clasificar" Clicked="Clasificar"&gt;
&lt;/Button&gt;
        &lt;Label x:Name="ResponseLabel"&gt;
&lt;/Label&gt;
        &lt;ProgressBar x:Name="Accuracy" HeightRequest="20"/&gt;
    &lt;/StackLayout&gt;
&lt;/ContentPage&gt;</pre>
Ademas en esta misma pagina de xaml tendremos nuestro manejadores de eventos o funciones de cada botón:
<pre>using Newtonsoft.Json;
using Plugin.Media;
using Plugin.Media.Abstractions;
using System;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using Xamarin.Forms;

namespace CustomVision
{
    public partial class MainPage : ContentPage
    {
        public const string ServiceApiUrl = "https://YOUR_WEB_APP_URL ";
        private MediaFile _foto = null;
        public MainPage()
        {
            InitializeComponent();
        }
         private async void ElegirImage(object sender, EventArgs e)
        {
            await CrossMedia.Current.Initialize(); 
            _foto = await Plugin.Media.CrossMedia.Current.PickPhotoAsync(new PickMediaOptions());
            Img.Source = FileImageSource.FromFile(_foto.Path);
        }
         private async void TomarFoto(object sender, EventArgs e)
        {
            await CrossMedia.Current.Initialize();
             if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
            {
                return;
            }
             var foto = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions()
            {
                PhotoSize = PhotoSize.Custom,                CustomPhotoSize = 10,                CompressionQuality = 92,                Name = "image.jpg"
            }); 
            _foto = foto;
             if (_foto == null)
                return;
             Img.Source = FileImageSource.FromFile(_foto.Path);
        }
         private async void Clasificar(object sender, EventArgs e)
        {
            using (Acr.UserDialogs.UserDialogs.Instance.Loading("Clasificando..."))
            {
                if (_foto == null) return;
                 var httpClient = new HttpClient();
                var url = ServiceApiUrl;
                var requestContent = new MultipartFormDataContent();
                var content = new StreamContent(_foto.GetStream());
                 content.Headers.ContentType =                    MediaTypeHeaderValue.Parse("image/jpg");
                 requestContent.Add(content, "file", "image.jpg");
                 var response = await httpClient.PostAsync(url, requestContent);
                 if (!response.IsSuccessStatusCode)
                {
                    Acr.UserDialogs.UserDialogs.Instance.Toast("Hubo un error en la deteccion...");
                    return;
                }
                 var json = await response.Content.ReadAsStringAsync();
                 var prediction = JsonConvert.DeserializeObject&lt;string&gt;(json); 
               if (prediction == null)
                {
                    Acr.UserDialogs.UserDialogs.Instance.Toast("Image no reconocida.");
                    return;
               }
                ResponseLabel.Text = $"{prediction}";
                //Accuracy.Progress = p.Probability;
            }
             Acr.UserDialogs.UserDialogs.Instance.Toast("Clasificacion terminada...");
        }
    }</pre>
&nbsp;
<h2>Demo Xamarin Forms App</h2>
<iframe width="560" height="315" src="https://www.youtube.com/embed/4lf5Bch0G84" frameborder="0" allowfullscreen="allowfullscreen"></iframe>
<em>Image Test mobile app on iOS 11.2 (iPhone 6 Plus)</em>
<iframe width="560" height="315" src="https://www.youtube.com/embed/s6IYhzAdIAk" frameborder="0" allowfullscreen="allowfullscreen"></iframe>

<em>Image Test mobile app on Android 7.1 Nougat or 8.0 Oreo (Nexus -API 25-26)</em>
<h1>Next Post!</h1>
Well, we have finished with the third post. Honestly, I thought that this post was going to be smaller but while I was writing it I saw that there was a lack of information to understand key concepts when carrying out projects or computer vision solutions. I hope these explanations and sample demonstrations have been very helpful and that you have shown them a very beautiful area such as computer vision, have the possibility to manipulate the images and extract information from them.

In the next post, we will dedicate to do a comparison between the use of TensorFlow and CNTK, the results obtained, the management and usability, points of interest and final conclusions about the research that has been carried out for the development of all the posts.

Kind regards,
Alexander González (<span><a href="https://twitter.com/glezglez96">@GlezGlez96</a></span>)
Microsoft Student Partner

&nbsp;

&nbsp;]]></content:encoded>
    </item>
    <item>
      <title>[Post Invitado] Part 2. Step by step - How to training your own detector classifier</title>
      <link>https://docs.microsoft.com/archive/blogs/esmsdn/post-invitado-part2-step-by-step-how-to-training-your-own-detector-classifier</link>
      <pubDate>Mon, 09 Apr 2018 17:34:33 GMT</pubDate>
      <dc:creator><![CDATA[esmsdn]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/esmsdn/?p=13395</guid>
      <description><![CDATA[Este articulo es una continuación de Analysis and Object Detection of Artworks with Tensorflow(GPU)...]]></description>
      <content:encoded><![CDATA[Este articulo es una continuación de <a href="https://blogs.msdn.microsoft.com/esmsdn/2018/04/02/post-invitado-analysis-and-object-detection-of-artworks-with-tensorflowgpu-on-windows-10/">Analysis and Object Detection of Artworks with Tensorflow(GPU) on Windows 10</a>

<hr />

Once we have installed Tensorflow, Cuda and CuDNN, we can pass to the next level! The purpose of this post is to explain how to train your own convolutional neural network object detection classifier for multiple objects. In this post we do not develop a nueronal network of zero, we will only take this github:

<a href="https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md">https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md</a>

It provide a collection of detection models pre-trained datasets of differents kind of objects. They are also useful for initializing your models when training on novel datasets like our dataset of artworks. At the end of this post, you will have the idea to develope your own model that can identify and program that can draw boxes around specific ítem in that case 12 items objects of different artworks, like <em>Gioconda, Athens School, Las Meninas, The Last Supper</em>.

<img width="2000" height="2000" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/32.jpg" />

There are several good tutorials available for how to use TensorFlow’s Object Detection API to train a classifier for a single object, like this one:

<a href="https://codelabs.developers.google.com/codelabs/tensorflow-for-poets/#0">https://codelabs.developers.google.com/codelabs/tensorflow-for-poets/#0</a>

The post is written for Windows 10. But without forgetting Linux, only emphasize that the development process is very similar and can also be used for Linux operating systems, but the file paths and package installation commands should be modified accordingly. Only tell you before start that TensorFlow-GPU allows your PC or VM to use the video card to provide extra processing power while training, so it will be used for this post, Regular Tensorflow not. In my experience doing this experiment, using TensorFlow-GPU instead of regular TensorFlow reduces training time by a factor of about 8hr using Fast-RCNN model, 3 hours to train instead of 8 hours with SSD-Mobilenet model for 3 objects, 21 hours for 12 objects. You can search differents models to tensorflow here:

<a href="https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md">https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md</a>

Regular TensorFlow can also be used for this post, I have tried it in my PC, but it will take longer. If you use regular TensorFlow, you do not need to install CUDA and cuDNN like <a href="https://blogs.msdn.microsoft.com/esmsdn/2018/04/02/post-invitado-analysis-and-object-detection-of-artworks-with-tensorflowgpu-on-windows-10/">in the first post</a><strong>.</strong>
<h1>Installing TensorFlow-GPU</h1>
Install Tensorflow-GPU following the instructions of the first post. Dowload CUDA 9.0 and CuDNN 7.1.1, but you will likely need to continue updating the CUDA and CuDNN versions to the latest supported version.
<h1>Setting up the Object Detection directory structure and Anaconda Virtual Environment</h1>
The TensorFlow Object Detection API requires using the specific directory structure provided in its GitHub repository. It also requires several additional Python packages, specific additions to the PATH and PYTHONPATH variables, and a few extra setup commands to get everything set up to run or train an object detection model. Stay tuned at each step because the processes are very meticulous and the minimum failure can give us error.
<h3>Download TensorFlow Object Detection API repository from GitHub</h3>
Create a folder directly in C: and name it “tensorflow” or whatever you want. This working directory will contain the full TensorFlow object detection framework, as well as your training images, training data, trained classifier, configuration files, and everything else needed for the object detection classifier.

Download the full TensorFlow object detection repository located at <a href="https://github.com/tensorflow/models">https://github.com/tensorflow/models</a> by clicking the “Clone or Download” button and downloading the zip file. Open the downloaded zip file and extract the “models-master” folder directly into your C:\DIRECTORY you just created. Rename “models-master” to just “models”.

<img width="1360" height="864" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/13.png" />

<em>Image of the principal directory</em>

<img width="1362" height="868" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/14.png" />

<em>Image of Tensorflow Object Detection API directory</em>

<img width="1912" height="543" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/7.png" />

<em>Image of Tensorflow Object Detection API, Research directory</em>
<h3>Download the Faster-RCNN and SSD-Mobilenet models</h3>
TensorFlow provides several object detection models (pre-trained classifiers with specific neural network architectures) in its <a href="https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md">model zoo</a>. If we look at the README of this github we can see that they tell us the importance within each model of three files.

<img width="1870" height="686" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/9.png" />

In the following sections we will explain what we should do with them, in the case of this project we have used <em>ssd_mobilenet_v1_coco</em> and <em>faster_rcnn_inception_v2_coco. </em>But before I would like to explain the importance of understanding the following table of models proposed by tensorflow. Especially understand well the speed, and the mAP column.

<img width="1728" height="1474" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/10.png" />

<em>*Note: What is column mAP? </em><em><a href="https://stackoverflow.com/questions/46094282/why-we-use-map-score-for-evaluate-object-detectors-in-deep-learning">https://stackoverflow.com/questions/46094282/why-we-use-map-score-for-evaluate-object-detectors-in-deep-learning</a></em>

&nbsp;

As we can see in the table SSD-Mobilenet has much more speed than the other models and smaller mAP, we could conclude that it is the best but once my project has been realized I have noticed that for example in this case with art paintings, it has given good results in terms of detection but in terms of accuracy it give me low level of success. On the other hand faster rcnn has surprised me because according to the table it has a very slow detection speed with respect to SSD-Mobilenet but it has gone very well on my MS Surface webcam. At the end to have been analyzing both models decided to train with both and extract their inference graph to use it both on tablets, laptops and mobile or Iot devices. You can choose which model to train your objection detection classifier on. If you are planning on using the object detector on a device with low computational like mobile, use the SDD-MobileNet model. If you will be running your object detector on a laptop or desktop PC, use one of the RCNN models.

In this post will use the Faster-RCNN-Inception-V2 model and ssd_mobilenet_v1_coco. Download the models and open the downloaded faster_rcnn_inception_v2_coco_2018_01_28.tar.gz and ssd_mobilenet_v1_coco_2017_11_17.tar.gz file with a file archiver such as WinZip or 7-Zip and extract the faster_rcnn_inception_v2_coco_2018_01_28 and ssd_mobilenet_v1_coco_2017_11_17 folder to the <em>C:\tensorflow1\models\research\object_detection</em> folder.

<em>*Note: The models date and versions will likely change in the future, but it should still work with this tutorial.</em>
<h3>Download this tutorial's repository from GitHub</h3>
Download the full repository located on this <a href="https://github.com/alexandergg/Project_Object_Detection_and_Classifier_Artworks">page</a>, scroll to the top and click Clone or Download and extract all the contents directly into the C:\tensorflow1\models\research\object_detection directory. This establishes a specific directory structure that will be used for the resto of the post. At this point, your \object_detection folder should look like:

<img width="821" height="1039" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/11.png" />

<em>Final directory with Object Detection API with 11 extra-files(selected)</em>

This repository contains the images, annotation data, .csv files, and TFRecords needed to train a "Artworks" objects detector. It also contains Python scripts that are used to generate the training data. It has scripts to test out the object detection classifier on images, videos, or a webcam feed.

If you want to practice training your own "Artwork" Detector, you can leave all the files as they are. You can follow along with this tutorial to see how each of the files were generated, and then run the training. You will still need to generate the TFRecord files train.record and test.record as described in nexts steps.

You can also use the frozen inference graph for my trained Artwork model detector from the I and extract the contents to \object_detection\inference_graph. This inference graph will work "out of the box". You can test it after all the setup instructions have been completed by running the Object_detection_image.py or video or webcam script.

If you want to train your own object detector, delete the following files (do not delete the folders):
<ul>
 	<li>All files in \object_detection\images\train and \object_detection\images\test</li>
 	<li>The “test_labels.csv” and “train_labels.csv” files in \object_detection\images</li>
 	<li>All files in \object_detection\training</li>
 	<li>All files in \object_detection\inference_graph</li>
</ul>
Now, you are ready to start from scratch in training your own object detector. This post will assume that all the files listed above were deleted, and will go on to explain how to generate the files for your own training dataset.
<h3>Set up new Anaconda virtual environment</h3>
Next, we'll work on setting up a virtual environment in Anaconda for tensorflow-gpu. From the Start menu in Windows, search for the Anaconda Prompt utility, right click on it, and click “Run as Administrator”.

Activate the environment of tensorflow by issuing:
<pre>C:\&gt; activate tensorflow</pre>
Install the other necessary packages by issuing the following commands:
<pre>(tensorflow) C:\&gt; conda install -c anaconda protobuf
(tensorflow) C:\&gt; pip install pillow
(tensorflow) C:\&gt; pip install lxml
(tensorflow) C:\&gt; pip install jupyter
(tensorflow) C:\&gt; pip install matplotlib
(tensorflow) C:\&gt; pip install pandas
(tensorflow) C:\&gt; pip install opencv-python</pre>
<em>*Note: The ‘pandas’ and ‘opencv-python’ packages are not needed by TensorFlow, but they are used in the Python scripts to generate TFRecords and to work with images, videos, and webcam feeds.</em>
<h3>Configure PATH and PYTHONPATH environment variables</h3>
The PATH variable must be configured to add the \models, \models\research, and \models\research\slim directories. bor some reason, you need to create a PYTHONPATH variable with these directories, and then add PYTHONPATH to the PATH variable. Otherwise, it will not work. Do this by issuing the following commands (from any directory):
<pre>(tensorflow) C:\&gt; set PYTHONPATH=C:\tensorflow1\models; C:\tensorflow1\models\research;C:\tensorflow1\models\research\slim
(tensorflow1) C:\&gt; set PATH=%PATH%; PYTHONPATH</pre>
<em>*Note: Every time the "tensorflow1" virtual environment is exited, the PATH and PYTHONPATH variables are reset and need to be set up again.</em>

<img width="1317" height="159" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/33.png" />
<h3>Compile Protobufs and run setup.py</h3>
Now we need to compile the Protobuf files, which are used by TensorFlow to configure model and training parameters. Unfortunately, the short protoc compilation command posted on TensorFlow’s Object Detection API <a href="https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/installation.md">installation page</a> does not work on Windows. Every .proto file in the \object_detection\protos directory must be called out individually by the command.

To understand what the protobuf files are (<em>Protocol Buffer, Mechanism for serializing structured data by Google</em>):
<ul>
 	<li><a href="https://www.tensorflow.org/extend/tool_developers/">https://www.tensorflow.org/extend/tool_developers/</a></li>
 	<li><a href="https://developers.google.com/protocol-buffers/?hl=en">https://developers.google.com/protocol-buffers/?hl=en</a></li>
</ul>
In the Anaconda Command Prompt, change directories to the \models\research directory and copy and paste the following command into the command line and press Enter:
<pre>protoc --python_out=. .\object_detection\protos\anchor_generator.proto .\object_detection\protos\argmax_matcher.proto .\object_detection\protos\bipartite_matcher.proto .\object_detection\protos\box_coder.proto .\object_detection\protos\box_predictor.proto .\object_detection\protos\eval.proto .\object_detection\protos\faster_rcnn.proto .\object_detection\protos\faster_rcnn_box_coder.proto .\object_detection\protos\grid_anchor_generator.proto .\object_detection\protos\hyperparams.proto .\object_detection\protos\image_resizer.proto .\object_detection\protos\input_reader.proto .\object_detection\protos\losses.proto .\object_detection\protos\matcher.proto .\object_detection\protos\mean_stddev_box_coder.proto .\object_detection\protos\model.proto .\object_detection\protos\optimizer.proto .\object_detection\protos\pipeline.proto .\object_detection\protos\post_processing.proto .\object_detection\protos\preprocessor.proto .\object_detection\protos\region_similarity_calculator.proto .\object_detection\protos\square_box_coder.proto .\object_detection\protos\ssd.proto .\object_detection\protos\ssd_anchor_generator.proto .\object_detection\protos\string_int_label_map.proto .\object_detection\protos\train.proto .\object_detection\protos\keypoint_box_coder.proto .\object_detection\protos\multiscale_anchor_generator.proto</pre>
This creates a name_pb2.py file from every name.proto file in the \object_detection\protos folder.

<em>*Note: TensorFlow occassionally adds new .proto files to the \protos folder. You may need to update the protoc command to include the new .proto files.</em>

<img width="1889" height="220" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/34.png" />

Finally, run the following commands from the C:\tensorflow1\models\research directory:
<pre>(tensorflow) C:\tensorflow1\models\research&gt; python setup.py build
(tensorflow) C:\tensorflow1\models\research&gt; python setup.py install</pre>
<h1>Labelling pictures</h1>
Now that the TensorFlow Object Detection API is all set up and ready to go, we need to provide the images it will use to train a new detection classifier.
<h3>Gather Pictures</h3>
TensorFlow needs hundreds of images of an object to train a good detection classifier. To train a robust classifier, the training images should have random objects in the image along with the desired objects, and should have a variety of backgrounds and lighting conditions. There should be some images where the desired object is partially obscured, overlapped with something else, or only halfway in the picture.

For my Artwork Detection classifier, I have twelve different objects I want to detect (Diego Velazquez, Maria Agustina, Infanta Margarita, Isaben de Velasco and Mari barbola part of “Las Meninas”, Platon, aristoteles and heraclito of “School of Athens”, Gioconda of “Mona Lisa” and finally Jesus, Judas and Mateo of “The Last Supper”).

I used Google Image Search to find about 80 pictures of each artwork. You can use your phone or download images of the objects from Google Image Search. I recommend having at least 200 pictures overall. I used 310 aprox, pictures to train my artworks detector. In addition to a better training has been cut the complete image of the art box focusing on the object we want to detect.

<img width="2000" height="2000" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/15.jpg" />

<em>Images of the different artworks cropped</em>

In the case of art paintings, we are lucky that they are static images that will always be presented to our model in the same way, they are not objects with movement or relevant changes of appearance such as an animal, cars, etc. With the images of the art pictures I played when choosing the images for the dataset with the main properties of computer vision, luminosity, size cause. They always appear in the same way, we can only extract real photos with people in front of the picture as another training case.

You can use the resizer.py script in this repository to reduce the size of the images cause they should be less than 200KB each, and their resolution shouldn’t be more than 720x1280. The larger the images are, the longer it will take to train the classifier.

After you have all the pictures you need, move 20% of them to the \object_detection\images\test directory, and 80% of them to the \object_detection\images\train directory. Make sure there are a variety of pictures in both the \test and \train directories.
<h3>Label Pictures</h3>
With all the pictures gathered, it’s time to label the desired objects in every picture. Labellimg is a great and new tool for me for labeling images, and its GitHub page has very clear instructions on how to install and use it.

<a href="https://github.com/tzutalin/labelImg">LabelImg GitHub link</a>

<a href="https://www.dropbox.com/s/tq7zfrcwl44vxan/windows_v1.6.0.zip?dl=1">LabelImg download link</a>

Download and install LabelImg, point it to your \images\train directory, and then draw a box around each object in each image. Repeat the process for all the images in the \images\test directory. This will take a while and a lot of patience…

<img width="2735" height="1739" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/16.png" />

<em>Image open file directory and observe what objects we want</em>

<em> <img width="2735" height="1741" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/17.png" /></em>

<em>Image about create RectBox around objects</em>
<h1>Generating training data</h1>
LabelImg saves a .xml file containing the label data for each image. These .xml files will be used to generate TFRecords, which are one of the inputs to the TensorFlow trainer. Once you have labeled and saved each image, there will be one .xml file for each image in the \test and \train directories.

<img width="1919" height="1039" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/19.png" />

<em>Image of XML file generate by LabelImg with the objects with their respectives coordinates.</em>

With the images labeled, it’s time to generate the TFRecords that serve as input data to the TensorFlow training model. This tutorial uses the xml_to_csv.py and generate_tfrecord.py scripts from <a href="https://github.com/datitran/raccoon_dataset">Dat Tran’s Raccoon Detector dataset</a>, with some slight modifications to work with our directory structure.

First, the image .xml data will be used to create .csv files containing all the data for the train and test images. From the \object_detection folder, issue the following command in the Anaconda command prompt:
<pre>(tensorflow) C:\tensorflow1\models\research\object_detection&gt; python xml_to_csv.py</pre>
This creates a train_labels.csv and test_labels.csv file in the \object_detection\images folder.

<img width="1486" height="1004" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/20.png" />

<em>Image of train and test csv files from xml file of LabelImg</em>

<img width="1046" height="846" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/21.png" />

<em>Image inside train and test .csv file</em>

Next, open the generate_tfrecord.py file in a text editor. Replace the label map starting at line 31 with your own label map, where each object is assigned an ID number. This same number assignment will be used when configuring the labelmap.pbtxt file.

You will replace the following code in generate_record.py:
<pre># TO-DO replace this with label map
def class_text_to_int(row_label):
    if row_label == 'Diego Velazquez':
        return 1
    elif row_label == 'Maria Agustina':
        return 2
    elif row_label == 'Infanta Margarita':
        return 3
    elif row_label == 'Isabel de Velasco':
        return 4
    elif row_label == 'Mari Barbola':
        return 5
    elif row_label == 'Platon':
        return 6
    elif row_label == 'Aristoteles':
        return 7
    elif row_label == 'Heraclito':
        return 8
    elif row_label == 'Gioconda':
        return 9
    elif row_label == 'Jesucristo':
        return 10
    elif row_label == 'Judas':
        return 11
    elif row_label == 'Mateo':
        return 12
    else:
        None</pre>
Then, generate the TFRecord files by issuing these commands from the \object_detection folder:
<pre>python generate_tfrecord.py --csv_input=images\train_labels.csv --image_dir=images\train --output_path=train.record
python generate_tfrecord.py --csv_input=images\test_labels.csv --image_dir=images\test --output_path=test.record</pre>
These generate a train.record and a test.record file in \object_detection. These will be used to train the new object detection classifier.
<h1>Creating a label map and configuring training</h1>
The last thing to do before training is to create a label map and edit the training configuration file.
<h3>Label map</h3>
The label map tells the trainer what each object is by defining a mapping of class names to class ID numbers. Use a text editor to create a new file and save it as labelmap.pbtxt in the C:\tensorflow1\models\research\object_detection\training folder. <em>Don’t forget that the file type is .pbtxt, not .txt</em>. In the text editor, copy or type in the label map in the format below. The example below is the label map for my Artworks Detector:
<pre>item {
  id: 1
  name:
 'Diego Velazquez'
} 
item {
  id: 2
  name: 'Maria Agustina'
}
 item {
  id: 3
  name: 'Infanta Margarita'
}
 item {
  id: 4
  name: 'Isabel de Velasco'
}
 item {
  id: 5
  name: 'Mari Barbola'
}
 item {
  id: 6
  name: 'Platon'
}
 item {
  id: 7
  name: 'Aristoteles'
}
 item {
  id: 8
  name: 'Heraclito'
}
 item {
  id: 9
  name: 'Gioconda'
}
  item {
  id: 10
  name:
 'Jesucristo'
}
 item {
  id: 11
  name: 'Judas'
}
 item {
  id: 12
  name: 'Mateo'
}</pre>
<h3>Configure training</h3>
Finally, the object detection training pipeline must be configured. It defines which model and what parameters will be used for training. This is the last step before running training 😊

In this step we can do the same with SSD-Mobilenet and Faster-RCNN. First we will do the training with Faster-RCNN and then I will explain how it would be done with the Mobilenet model.

Navigate to C:\tensorflow1\models\research\object_detection\samples\configs and copy the faster_rcnn_inception_v2_pets.config file into the \object_detection\training directory. Then, open the file with a text editor. There are several changes to make to the .config file, mainly changing the number of classes and examples, and adding the file paths to the training data.

Make the following changes to the faster_rcnn_inception_v2_pets.config file. Note: The paths must be entered with single forward slashes (NOT backslashes), or TensorFlow will give a file path error when trying to train the model.  Also, the paths must be in double quotation marks (“ ), not single quotation marks ( ' ).
<ul>
 	<li>Line 9. Change num_classes to the number of different objects you want the classifier to detect. For the above basketball, shirt, and shoe detector, it would be num_classes : 3 .</li>
 	<li>Line 110. Change fine_tune_checkpoint to:</li>
</ul>
<ul>
 	<li style="list-style-type: none">
<ul>
 	<li>fine_tune_checkpoint : "C:/tensorflow1/models/research/object_detection/faster_rcnn_inception_v2_coco_2018_01_28/model.ckpt"</li>
</ul>
</li>
</ul>
<ul>
 	<li>Lines 126 and 128. In the train_input_reader section, change input_path and label_map_path to:</li>
</ul>
<ul>
 	<li style="list-style-type: none">
<ul>
 	<li>input_path : "C:/tensorflow1/models/research/object_detection/train.record"</li>
 	<li>label_map_path: "C:/tensorflow1/models/research/object_detection/training/labelmap.pbtxt"</li>
</ul>
</li>
</ul>
<ul>
 	<li>Line 132. Change num_examples to the number of images you have in the \images\test directory.</li>
 	<li>Lines 140 and 142. In the eval_input_reader section, change input_path and label_map_path to:</li>
</ul>
<ul>
 	<li style="list-style-type: none">
<ul>
 	<li>input_path : "C:/tensorflow1/models/research/object_detection/test.record"</li>
 	<li>label_map_path: "C:/tensorflow1/models/research/object_detection/training/labelmap.pbtxt"</li>
</ul>
</li>
</ul>
Save the file after the changes have been made. Great! Now we can start the training job!

For Mobilenet of other model we have to do the same process, get the config file of each model and change the parameters that I explained before. The model of SSD-Mobilenet you can find it here like Faster-RCNN:

<img width="1483" height="1003" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/22.png" />

<em>Image directory of Tensorflow pre-trained models(Coco or Pets datasets)</em>
<h1>Training</h1>
From the \object_detection directory, issue the following command to begin training:
<pre>python train.py --logtostderr --train_dir=training/ --pipeline_config_path=training/faster_rcnn_inception_v2_pets.config</pre>
If we want to use SSD-Mobilenet to train your model, you only need to change the config_path:
<pre>python train.py --logtostderr --train_dir=training/ --pipeline_config_path=training/ssd_mobilenet_v1_pets.config</pre>
If everything has been set up correctly, TensorFlow will initialize the training. When training begins, it will look like this:

<img width="1904" height="1038" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/23.png" />

Image training with Faster RCNN.

Each step of training reports the loss. It will start set up our graphic card (Tesla K80) then get the parameters of our model.config. This step start with high los and get lower and lower as training progresses. For my training on the Faster-RCNN-Inception-V2 model, it started at about 3.0 and quickly dropped below 0.8. I recommend allowing your model to train until the loss consistently drops below 0.05, which will take about 40,000 steps, or about 2-3 hours depending on how powerful your CPU and GPU are.

<em>*Note: The loss numbers will be different if a different model is used. MobileNet-SSD starts with a loss of about 40, and should be trained until the loss is consistently under 2.</em>

<img width="1897" height="790" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/24.png" />

<em>Image Training SSD-Mobilenet</em>

You can view that FasterRCNN training los is more faster than SSD-Mobilenet. Also you can view progress of the training job by using TensorBoard. Thanks to tensorboard I have been able to explain in my final project through graphs like training has been from beginning to end, demonstrating also differences between both models.

<a href="https://www.tensorflow.org/programmers_guide/summaries_and_tensorboard">https://www.tensorflow.org/programmers_guide/summaries_and_tensorboard</a>

To do this, open a new instance of Anaconda Prompt, activate the tensorflow virtual environment, change to the C:\tensorflow1\models\research\object_detection directory, and issue the following command:
<pre>(tensorflow) C:\tensorflow1\models\research\object_detection&gt;tensorboard --logdir=training</pre>
This will create a webpage on your local machine at PCNAME:6006, which can be viewed through a web browser. The TensorBoard page provides information and graphs that show how the training is progressing. One important graph is the Loss graph, which shows the overall loss of the classifier over time.

<img width="4032" height="3024" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/31.png" />

<img width="4032" height="3024" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/30.png" />

<img width="4032" height="3024" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/29.png" />

<img width="4032" height="3024" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/28.png" />

The training routine periodically saves checkpoints about every five minutes. You can terminate the training by pressing Ctrl+C while in the command prompt window. I typically wait until just after a checkpoint has been saved to terminate the training. You can terminate training and start it later, and it will restart from the last saved checkpoint. The checkpoint at the highest number of steps will be used to generate the frozen inference graph.
<h1>Create and exporting the frozen inference graph</h1>
Now that training is complete, the last step is to generate the frozen inference graph (.pb file). From the \object_detection folder, issue the following command, where “XXXX” in “model.ckpt-XXXX” should be replaced with the highest-numbered .ckpt file in the training folder:
<pre>python export_inference_graph.py --input_type image_tensor --pipeline_config_path training/faster_rcnn_inception_v2_pets.config --trained_checkpoint_prefix training/model.ckpt-XXXX --output_directory inference_graph</pre>
This creates a frozen_inference_graph.pb file in the \object_detection\inference_graph folder. The. pb file contains the object detection classifier.
<h1>Testing and using your newly trained object detection classifier</h1>
Now that we have our classifier ready we can put it to the test. I enclosed them in the github repository Python scripts developed by Evan Juras to test it on an image, video or source of the webcam.

Before executing the Python scripts, we will have to change several code variables, then those changes are shown in the image:

<img width="1051" height="750" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/27.png" />

<em>Image Example of changes in Object_Detection_video.py</em>
<h1>Common errors throughout the Project</h1>
<ol>
 	<li><em> </em><em>Google may add more .proto files to the object_detection/protos folder, so it may be necessary to add more files to the "protoc" command at 13:13. </em><em>You can do this by adding ".\object_detection\protos\FILENAME.proto" to the end of the long command string for each new file.</em></li>
 	<li><em> </em><em>When running the "python train.py" command, if you get an error that says "TypeError: __init__() got an unexpected keyword argument 'dct_method'.", then remove the "dct_method=dct_method" argument from line 110 of the object_detection/data_decoders/tf_example_decoder.py file.</em></li>
 	<li><em> </em><em>When running "python train.py", if you get an error saying "google.protobuf.text_format.ParseError: 110:25 : Expected string but found: '“' ", try re-typing the quotation marks around each of the filepaths. </em><em>If you copied the filepaths over from my GitHub tutorial, the quotation marks sometimes copy over as a different character type, and TensorFlow doesn't like that.</em></li>
 	<li><em> </em><em>For train.py, if you get an error saying "TypeError: Expected int32, got rang </em><em>e(0, 3) of type 'range' instead.", it is likely an issue with the learning_schedules.py file. </em><em>In the \object_detection\utils\learning_schedules.py file, change line 153</em></li>
 	<li><em> </em><em>from "tf.constant(range(num_boundaries), dtype=tf.int32)," to "tf.constant(list(range(num_boundaries)), dtype=tf.int32),".</em></li>
</ol>
<h1>Next post!</h1>
<a href="https://blogs.msdn.microsoft.com/esmsdn/2018/04/24/post-invitado-part-3-step-by-step-how-to-train-an-objects-classifier-understanding-computer-vision-techniques-with-python-and-opencv/">The third post</a> will explain another way of recognizing and classifying images (20 artworks) using scikit learn and python without having to use models of TensorFlow, CNTK or other technologies which offer models of convolved neural networks. Moreover, we will explain how to set up your own web app with python. For this part a fairly simple API which will collect information about the captured image of our mobile application in Xamarin will be needed, so it will inference with our model made in python, and it will return the corresponding prediction. With that methodology we can get easy classification without heavy hardware like TensorFlow or CNTK.

<em>*Note: Also explain how I can prove my frozen inference model in Android!</em>]]></content:encoded>
    </item>
    <item>
      <title>[Post Invitado] Part 1. Analysis and Object Detection of Artworks with Tensorflow(GPU) on Windows 10</title>
      <link>https://docs.microsoft.com/archive/blogs/esmsdn/post-invitado-analysis-and-object-detection-of-artworks-with-tensorflowgpu-on-windows-10</link>
      <pubDate>Mon, 02 Apr 2018 12:27:17 GMT</pubDate>
      <dc:creator><![CDATA[esmsdn]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/esmsdn/?p=13385</guid>
      <description><![CDATA[En esta serie de artículos, Alexander Gonzalez, Microsoft Student Partner, te explica cómo utilizar...]]></description>
      <content:encoded><![CDATA[<span lang="ES-TRAD" style="margin: 0px;color: #212121;font-family: 'Segoe UI',sans-serif">En esta serie de artículos, Alexander Gonzalez, Microsoft Student Partner, te explica cómo utilizar Tensorflow en Azure para el análisis y detección de objetos utilizando como ejemplo obras de arte. Forma parte de su Trabajo Final de Grado para el Grado en Ingeniería Informática.</span>

<hr />

Nowadays, all major technology companies are committed to innovative projects using technologies such as Machine Learning, Deep Learning. Both will be the most important technologies for 2018, making a difference on the currently way of living and working. Machine learning and Deep learning are constantly making new business models, jobs, as well as, innovative and efficient industries.

Machine learning is around us more than we think. It can be found in mobile phones, cars, homes and in our own job, helping us to make better decisions, access to higher quality information and faster. According to several surveys and analytical companies, these technologies will be present in all the new software products in 2020.

From the business point of view, artificial intelligence (AI) will be used a stepping stone to improve and increase the effectiveness and efficiency of the services of any company as well as quality for the user. The trends indicate that most sectors will see radical changes in the way they use all their products, the only risk of such change is to completely ignore it. In the future, products that we currently know will change thanks to the AI . Furthermore, and it is calculated that in the next three years, around 1.2 trillion dollars will change hands thanks to this new field in computing. Consequently, this means that every year the AI is taking a lot of strength and support; therefore, it will leave a mark setting differences between companies in the up-coming years.

The objective of these post series is to show the possibilities that we currently have to perform machine learning and computer vision projects which they will be published in 4 parts:

1. The first part is dedicated to the installation and explanation of the software that we will need to take any project in this case, with TensorFlow, CUDA and CuDNN.

2. The second part will cover step by step the necessary processes to make our dataset, in this case artworks images, followed by training. Finally, the evaluation and obtaining of relevant graphics for the preparation of documentation will be explained. <em>[Link to be added]</em>

[embed]https://www.youtube.com/watch?v=wue_odBwAP8[/embed]

<em>Image of Fast-RCNN on surface webcam with python program</em>

[embed]https://www.youtube.com/watch?v=mO-FyZmkYhE[/embed]

<em>Image of Fast-RCNN on MS Surface webcam with python program</em>

[embed]https://www.youtube.com/watch?v=4mTmQ_hE9Tg[/embed]

<em>Image of SSD-Mobilenet on LG mobile</em>

3. The third post will explain another way of recognizing and classifying images (20 artworks) using scikit learn and python without having to use models of TensorFlow, CNTK or other technologies which offer models of convolved neural networks. Moreover, we will explain how to set up your own web app with python. For this part a fairly simple API which will collect information about the captured image of our mobile application in Xamarin will be needed, so it will inference with our model made in python, and it will return the corresponding prediction. With that methodology we can get easy classification without heavy hardware like TensorFlow or CNTK. <i>[Link to be added]</i>

<img width="1070" height="676" class="aligncenter size-full" src="https://artvisiondiag143.blob.core.windows.net/images/1.png" />

4. Finally, the last post is dedicated to a comparison between the use of TensorFlow and CNTK, the results obtained, the management and usability, points of interest and final conclusions about the research that has been carried out for the development of all the posts. <i>[Link to be added]</i>

&nbsp;
<h1>1. How to install Tensorflow 1.5 on Windows 10 with CUDA and CudaDNN</h1>
<h2>Prerequisites</h2>
• Nvidia GPU (GTX 650 or newer. The GTX1050 is a good entry level choice)
• Anaconda with Python 3.6(or 3.5)
• CUDA ToolKit(versión 9)
• CuDNN(7.1.1)

If we want to get results quickly the first thing to think about is with what hardware we will face our computer vision project, since the demands are high in terms of GPU and processing. My advice is to use Data Science Virtual Machine that Azure supports. They are complete virtual machines preconfigured for the modelling, development and implementation of science data. Below, I highlight several documents provided by Azure team that will help us understand the provisioning of these machines and prices:

<img width="1301" height="728" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/2.png" />

• <a href="https://azure.microsoft.com/es-es/services/virtual-machines/data-science-virtual-machines/">https://azure.microsoft.com/es-es/services/virtual-machines/data-science-virtual-machines/</a>
• <a href="https://docs.microsoft.com/es-es/azure/machine-learning/data-science-virtual-machine/provision-vm">https://docs.microsoft.com/es-es/azure/machine-learning/data-science-virtual-machine/provision-vm</a>
• <a href="https://docs.microsoft.com/es-es/azure/machine-learning/data-science-virtual-machine/overview">https://docs.microsoft.com/es-es/azure/machine-learning/data-science-virtual-machine/overview</a>
<em>*Note: We must install a Data Science virtual machine of NC6 or NV6 model with GPU.</em>
Once we have our machine created, we can start to install
<h2>Download CUDA 9.0</h2>
First of all, we will have to install CUDA Tool Kit:
• Download version 9.0 here: <a href="https://developer.nvidia.com/cuda-downloads">https://developer.nvidia.com/cuda-downloads</a>
• Currently version 9.0 is supported by Tensorflow 1.5

<img width="1664" height="1004" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/3.png" />

Installer type <strong>exe(network)</strong> is the lighter way one. More complete one is <strong>exe(local)</strong>
<h2>Set your environment Variables:</h2>
• Go to start and search “environment variables”
• Click th environmen variables button
• Add the following paths:
o C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\extras\CUPTI\libx64
o C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\libnvvp
o C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\bin

In data Science machine we have CUDA install and we can find this path already install
• <strong>Variable name</strong>: CUDA_PATH_V9_0
• <strong>Variable value</strong>: C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0
<h2>Download CUDNN 8.0</h2>
• Go to <a href="https://developer.nvidia.com/rdp/cudnn-download">https://developer.nvidia.com/rdp/cudnn-download</a>
• Create a user profile if needed
• Select CUDNN 7.1.1 for CUDA tool kit 9.0

<img width="1665" height="970" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/4.png" />

<img width="1228" height="754" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/5.png" />

• Extract the zip file and place them somewhere (<strong>C://</strong> directory for example)
• Add a in environment variable path to the bin folder: For example: <strong>C:/cuda/bin</strong>
<h2>Update GPU Driver</h2>
• Go to <a href="https://www.nvidia.com/Download/index.aspx">https://www.nvidia.com/Download/index.aspx</a>
• Select your version to download
• Install the drive

<img width="1296" height="769" class="alignnone size-full" src="https://artvisiondiag143.blob.core.windows.net/images/6.png" />
<h2>Install Anaconda 3.6</h2>
In these VM Anaconda environment has been installed. Howver, the installation steps of Anaconda were not explained in this post.
<h2>Install Tensorflow GPU on Windows 10</h2>
Open your cmd window and type the following command:
<p style="padding-left: 30px"><em>pip install –ignore-installed –upgrade tensorflow-gpu</em></p>
Notes: Tensorflow documentation: <a href="https://www.tensorflow.org/install/">https://www.tensorflow.org/install/</a>

Test your tensorflow install:

1. Open anaconda prompt

2. Write “<strong>python –-version</strong>”

3. Write “<strong>python</strong>”

4. Once the interpreter opens type the following:
<pre><em>import tensorflow as tf
</em><em>hello= tf.constant('Hello, Tensorflow!')</em> 
<em>Sess = tf.session()</em> 
<em>print(sess.run(hello))</em></pre>
In the next post, we will cover step by step the necessary processes to make our dataset. In this case, artwork images, followed by training and finally the evaluation and obtaining relevant graphics for document preparation. Index of the next post:
<a href="https://blogs.msdn.microsoft.com/esmsdn/2018/04/09/post-invitado-part-2-how-to-train-an-object-detector-classifier-for-multiple-objects-using-tensorflowgpu-on-windows-10/">1. Labelling pictures</a>
<a href="https://blogs.msdn.microsoft.com/esmsdn/2018/04/24/post-invitado-part-3-step-by-step-how-to-train-an-objects-classifier-understanding-computer-vision-techniques-with-python-and-opencv/">2. Generating training data</a>
<a href="https://blogs.msdn.microsoft.com/esmsdn/2018/04/24/post-invitado-part-3-step-by-step-how-to-train-an-objects-classifier-understanding-computer-vision-techniques-with-python-and-opencv/">3. Creating a label map and configuring training</a>
4. Training
5. Exporting the inference graph
6. Testing and using your newly trained object detection classifier

&nbsp;

Kind regards,
Alexander González (<a href="https://twitter.com/glezglez96">@GlezGlez96</a>)
Microsoft Student Partner]]></content:encoded>
    </item>
    <item>
      <title>[Post Invitado] Trabajando con conjuntos de datos en Power BI</title>
      <link>https://docs.microsoft.com/archive/blogs/esmsdn/post-invitado-trabajando-con-conjuntos-de-datos-en-power-bi</link>
      <pubDate>Thu, 26 Oct 2017 19:25:55 GMT</pubDate>
      <dc:creator><![CDATA[esmsdn]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/esmsdn/?p=13365</guid>
      <description><![CDATA[En este artículo, Javier Fernández Corres, consultor de ilitia Technologies, te explica paso a paso...]]></description>
      <content:encoded><![CDATA[En este artículo, <a href="https://www.linkedin.com/in/javier-fern%C3%A1ndez-corres-29988314/">Javier Fernández Corres</a>, consultor de <a href="https://www.ilitia.com/">ilitia Technologies</a>, te explica paso a paso cómo crear un conjunto de datos personalizado en Power BI para explotar después su información.

Como ejemplo, vamos a extraer la información de una colección de sitios de SharePoint Online, la añadiremos a un conjunto de datos y la mostraremos en una tabla en un informe de Power BI.

Para extraer la información y añadirla al conjunto de datos, debemos crear una aplicación de consola en Visual Studio y seguir los siguientes pasos:

1. Registrando la aplicación

2. Creación de un conjunto de datos

3. Añadiendo información al conjunto de datos

4. Explotando la información
<h3>1. Registrando la aplicación</h3>
Para poder acceder a nuestro entorno de Power BI y realizar las distintas operaciones con conjuntos de datos, es necesario registrar la aplicación en Azure AD.. Al realizar el registro obtendremos un id. de cliente, (Sin un id. de cliente, Azure AD no puede autenticar la aplicación de consola) que será el que utilicemos en nuestra aplicación de consola para crear el conjunto de datos y añadir la información.

Para ello accedemos a la herramienta de registro de aplicaciones del centro de desarrollo de Power BI:
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="605"><a href="https://dev.powerbi.com/apps">https://dev.powerbi.com/apps</a></td>
</tr>
</tbody>
</table>
En primer lugar, insertamos nuestro usuario y contraseña.

<a href="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image00238.jpg"><img width="244" height="46" title="clip_image002" style="margin: 0px" alt="clip_image002" src="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image002_thumb26.jpg" border="0" /></a>

En segundo lugar, completamos la información de nuestra aplicación:

· App Name, escribimos un nombre. Por ejemplo, “SharePoint Online Info”.

· App Type, seleccionamos Native app

· Redirect Url. El URI estándar para una aplicación cliente es:

https://login.live.com/oauth20_desktop.srf

<a href="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image00424.jpg"><img width="244" height="84" title="clip_image004" style="margin: 0px" alt="clip_image004" src="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image004_thumb19.jpg" border="0" /></a>

En tercer lugar, seleccionamos los permisos de la aplicación. En nuestro caso vamos a seleccionar Read and Write All Datasets.

<a href="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image00617.jpg"><img width="244" height="73" title="clip_image006" style="margin: 0px" alt="clip_image006" src="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image006_thumb14.jpg" border="0" /></a>

Por último, pulsamos el botón Register App y obtendremos nuestro id. de cliente, que utilizaremos posteriormente en nuestra aplicación de consola.

<a href="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image00819.jpg"><img width="244" height="71" title="clip_image008" style="margin: 0px" alt="clip_image008" src="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image008_thumb15.jpg" border="0" /></a>
<h3>2. Creación de un conjunto de datos</h3>
Como primer paso, creamos una aplicación de consola en Visual Studio 2015.

Una vez hecho esto, instalamos la librería de autenticación de Directorio Activo (ADAL, <a href="https://www.nuget.org/packages/Microsoft.IdentityModel.Clients.ActiveDirectory">https://www.nuget.org/packages/Microsoft.IdentityModel.Clients.ActiveDirectory</a>). Para ello, seleccionamos <i>Herramientas &gt; Administrador de paquetes de NuGet &gt; Consola de Administrador de Paquetes</i> y escribimos lo siguiente:
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="609">Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory -Version 2.21.301221612</td>
</tr>
</tbody>
</table>
Antes de crear nuestro conjunto de datos, necesitamos obtener un token de acceso de autenticación empleando el siguiente método, que recibirá como parámetro el id de cliente obtenido anteriormente.
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="609" valign="top">[code language="csharp"]public static string GetToken(string clientID)

{

string redirectUri = &quot;https://login.live.com/oauth20_desktop.srf&quot;;

string resourceUri = &quot;https://analysis.windows.net/powerbi/api&quot;;

string authorityUri = &quot;https://login.windows.net/common/oauth2/authorize&quot;;

AuthenticationContext authContext = new AuthenticationContext(authorityUri);

string token = authContext.AcquireToken(resourceUri, clientID, new Uri(redirectUri)).AccessToken;

return token;

}[/code]</td>
</tr>
</tbody>
</table>

Para la creación del conjunto de datos haremos una llamada REST a la siguiente dirección, enviando una cadena JSON. La dirección URL que identifica un conjunto de datos es:

<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="609">https://api.powerbi.com/v1.0/myorg/datasets</td>
</tr>
</tbody>
</table>

Si se va a crear dentro de un grupo, la dirección será:
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="609">https://api.PowerBI.com/v1.0/myorg/groups/{group_id}/datasets</td>
</tr>
</tbody>
</table>

Antes de nada, añadiremos las siguientes referencias a nuestra aplicación de consola:
[code language="csharp"]
using System.IO;
using System.Net;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
[/code]

Vamos a utilizar el siguiente método para crear el conjunto de datos:

[code language="csharp"]
public static string CreateDataset(string token, string json)
{

string statusCode = string.Empty;

string powerBIDatasetsApiUrl = &quot;https://api.powerbi.com/v1.0/myorg/datasets&quot;;

//Para grupos: https://api.PowerBI.com/v1.0/myorg/groups/{group_id}/datasets

HttpWebRequest request = System.Net.WebRequest.Create(powerBIDatasetsApiUrl) as System.Net.HttpWebRequest;

request.KeepAlive = true;

request.Method = &quot;POST&quot;;

request.ContentLength = 0;

request.ContentType = &quot;application/json&quot;;

request.Headers.Add(&quot;Authorization&quot;, String.Format(&quot;Bearer {0}&quot;, token));

byte[] byteArray = System.Text.Encoding.UTF8.GetBytes(json);

request.ContentLength = byteArray.Length;

using (Stream writer = request.GetRequestStream())

{

writer.Write(byteArray, 0, byteArray.Length);

var response = (HttpWebResponse)request.GetResponse();

statusCode = response.StatusCode.ToString();

}

return statusCode;

}
[/code]

Añadimos lo siguiente al método Main de nuestra aplicación de consola:
[code language="csharp"]
string token = GetToken(clientID);

Console.WriteLine(&quot;Creating DataSet&quot;);

string json = &quot;{\&quot;name\&quot;: \&quot;SiteCollection\&quot;, \&quot;tables\&quot;: &quot; +

&quot;[{\&quot;name\&quot;: \&quot;Info\&quot;, \&quot;columns\&quot;: &quot; +

&quot;[{ \&quot;name\&quot;: \&quot;Title\&quot;, \&quot;dataType\&quot;: \&quot;string\&quot;}, &quot; +

&quot;{ \&quot;name\&quot;: \&quot;Description\&quot;, \&quot;dataType\&quot;: \&quot;string\&quot;},&quot; +

&quot;{ \&quot;name\&quot;: \&quot;Created\&quot;, \&quot;dataType\&quot;: \&quot;DateTime\&quot;},&quot; +

&quot;{ \&quot;name\&quot;: \&quot;Size\&quot;, \&quot;dataType\&quot;: \&quot;Int64\&quot;}&quot; +

&quot;]}]}&quot;;

string statusCode = CreateDataset(token, json);

Console.WriteLine(string.Format(&quot;Dataset {0}&quot;, statusCode));

Console.ReadLine();
[/code]

Para poder extraer la información de la colección de sitios, vamos a crear un conjunto de datos “SiteCollection”, con una tabla “Info” y las columnas título, descripción, fecha de creación y tamaño en bytes.

Ejecutamos la aplicación. Nos va a pedir permisos para leer y escribir conjuntos de datos, y para iniciar sesión y leer nuestro perfil.

<a href="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image01012.jpg"><img width="244" height="180" title="clip_image010" style="margin: 0px" alt="clip_image010" src="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image010_thumb11.jpg" border="0" /></a>

Aceptamos y el conjunto de datos se creará en Power BI.
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="301" valign="top"><a href="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image0127.jpg"><img width="244" height="50" title="clip_image012" style="margin: 0px" alt="clip_image012" src="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image012_thumb7.jpg" border="0" /></a></td>
<td width="301" valign="top"><a href="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image0144.jpg"><img width="182" height="135" title="clip_image014" style="margin: 0px" alt="clip_image014" src="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image014_thumb4.jpg" border="0" /></a></td>
</tr>
</tbody>
</table>
<h3>3. Añadiendo información al conjunto de datos</h3>
Vamos a añadir a nuestro conjunto de datos la información de una colección de sitios de SharePoint Online.

Añadimos la siguiente referencia:

[code language="csharp"]
using Microsoft.SharePoint.Client;
[/code]

En primer lugar, tenemos que obtener la información y para ello necesitaremos el siguiente método:


[code language="csharp"]
public static SiteInfo GetSiteInfo(string siteUrl, string userName, string password)

{

SiteInfo info = new SiteInfo();

using (var ctx = new ClientContext(siteUrl))

{

ctx.RequestTimeout = 1000000;

ctx.Credentials = new SharePointOnlineCredentials(userName, ToSecureString(password));

Site site = ctx.Site;

ctx.Load(site, s =&amp;gt; s.RootWeb.Title, s =&amp;gt; s.RootWeb.Description, s =&amp;gt; s.RootWeb.Created, s =&amp;gt; s.Usage);

ctx.ExecuteQuery();

info.Title = site.RootWeb.Title;

info.Description = site.RootWeb.Description;

info.Created = site.RootWeb.Created;

info.Size = site.Usage.Storage;

}

return info;

}

public static SecureString ToSecureString(string plainString)

{

var secureString = new SecureString();

foreach (char c in plainString)

{

secureString.AppendChar(c);

}

return secureString;

}
[/code]


Previamente hemos definido la clase SiteInfo:
[code language="csharp"]
public class SiteInfo

{

public string Title { get; set; }

public string Description { get; set; }

public DateTime Created { get; set; }

public long Size { get; set; }

}
[/code]

Para la añadir filas a un conjunto de datos haremos una llamada REST a la siguiente dirección, enviando una cadena JSON. La dirección URL que identifica un conjunto de datos es:
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="609">https://api.powerbi.com/v1.0/myorg/datasets/{dataset_id}/tables/{table_name}/rows</td>
</tr>
</tbody>
</table>

Si se va a insertar datos dentro de un grupo, la dirección será:
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="609">https://api.powerbi.com/v1.0/myorg/groups/{group_id}/datasets/{dataset_id}/tables/{table_name}/rows</td>
</tr>
</tbody>
</table>
Antes de añadir filas a nuestro conjunto de datos, necesitaremos obtenerlo. Para eso utilizaremos el siguiente método:
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="609" valign="top">
[code language="csharp"]
private static dataset[] GetDatasets(string token)

{

string powerBIApiUrl = &quot;https://api.powerbi.com/v1.0/myorg/datasets&quot;;

//Para grupos: https://api.PowerBI.com/v1.0/myorg/groups/{group_id}/datasets

HttpWebRequest request = System.Net.WebRequest.Create(powerBIApiUrl) as System.Net.HttpWebRequest;

request.KeepAlive = true;

request.Method = &quot;GET&quot;;

request.ContentLength = 0;

request.ContentType = &quot;application/json&quot;;

request.Headers.Add(&quot;Authorization&quot;, String.Format(&quot;Bearer {0}&quot;, token));

using (HttpWebResponse httpResponse = request.GetResponse() as System.Net.HttpWebResponse)

{

using (StreamReader reader = new System.IO.StreamReader(httpResponse.GetResponseStream()))

{

string responseContent = reader.ReadToEnd();

JavaScriptSerializer jsonSerializer = new JavaScriptSerializer();

Datasets datasets = (Datasets)jsonSerializer.Deserialize(responseContent, typeof(Datasets));

return datasets.value;

}

}

}

[/code]
</td>
</tr>
</tbody>
</table>
Y las siguientes clases:
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="609" valign="top">
[code language="csharp"]
public class Datasets

{

public dataset[] value { get; set; }

}

public class dataset

{

public string Id { get; set; }

public string Name { get; set; }

}[/code]
</td>
</tr>
</tbody>
</table>
Vamos a utilizar el siguiente método para añadir filas al conjunto de datos:
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="609" valign="top">

[code language="csharp"]
public static void AddRows(string token, string json, string datasetName, string tableName)

{

dataset[] datasets = GetDatasets(token);

string datasetId = (from d in datasets where d.Name == datasetName select d).FirstOrDefault().Id;

string powerBIApiUrl = String.Format(&quot;https://api.powerbi.com/v1.0/myorg/datasets/{0}/tables/{1}/rows&quot;, datasetId, tableName);

//Para grupos: https://api.powerbi.com/v1.0/myorg/groups/{group_id}/datasets/{dataset_id}/tables/{table_name}/rows

HttpWebRequest request = System.Net.WebRequest.Create(powerBIApiUrl) as System.Net.HttpWebRequest;

request.KeepAlive = true;

request.Method = &quot;POST&quot;;

request.ContentLength = 0;

request.ContentType = &quot;application/json&quot;;

request.Headers.Add(&quot;Authorization&quot;, String.Format(&quot;Bearer {0}&quot;, token));

byte[] byteArray = System.Text.Encoding.UTF8.GetBytes(json);

request.ContentLength = byteArray.Length;

using (Stream writer = request.GetRequestStream())

{

writer.Write(byteArray, 0, byteArray.Length);

}

}
[/code]
</td>
</tr>
</tbody>
</table>
Añadimos lo siguiente al método Main de nuestra aplicación de consola. Vamos a añadir a nuestro conjunto de datos la información de dos colecciones de sitio de SharePoint Online.
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="609" valign="top">
[code language="csharp"]
string clientID = &quot;aa5b6616-8c17-45ba-9d8c-8f89c78f2de9&quot;;

string token = GetToken(clientID);

Console.WriteLine(&quot;Adding Rows&quot;);

SiteInfo info = SharePointOnline.GetSiteInfo(&quot;https://ilitia.sharepoint.com&quot;, &quot;xxx@ilitia.com&quot;, &quot;xxx&quot;);

SiteInfo info2 = SharePointOnline.GetSiteInfo(&quot;https://ilitia.sharepoint.com/sites/dev&quot;, &quot;xxx @ilitia.com&quot;, &quot;xxx&quot;);

string row1 = &quot;{\&quot;Title\&quot;:\&quot;&quot; + info.Title + &quot;\&quot;,\&quot;Description\&quot;:\&quot;&quot; + info.Description + &quot;\&quot;,\&quot;Created\&quot;:\&quot;&quot; + info.Created.ToString(&quot;MM/dd/yyyy&quot;) + &quot;\&quot;,\&quot;Size\&quot;:&quot; + info.Size + &quot;}&quot;;

string row2 = &quot;{\&quot;Title\&quot;:\&quot;&quot; + info2.Title + &quot;\&quot;,\&quot;Description\&quot;:\&quot;&quot; + info2.Description + &quot;\&quot;,\&quot;Created\&quot;:\&quot;&quot; + info2.Created.ToString(&quot;MM/dd/yyyy&quot;) + &quot;\&quot;,\&quot;Size\&quot;:&quot; + info2.Size + &quot;}&quot;;

string json = &quot;{\&quot;rows\&quot;:[&quot; + row1 + &quot;,&quot; + row2 + &quot;]}&quot;;

string datasetName = &quot;SiteCollection&quot;;

string tableName = &quot;Info&quot;;

AddRows(token, json, datasetName, tableName);

Console.WriteLine(&quot;Rows Added&quot;);

Console.ReadLine();
[/code]
</td>
</tr>
</tbody>
</table>
Si accedemos a nuestro sitio de Power BI, al conjunto de datos “SiteCollection”, seleccionamos las columnas de la tabla Info y el resultado obtenido es el siguiente:
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="301" valign="top"><a href="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image0164.jpg"><img width="244" height="55" title="clip_image016" style="margin: 0px" alt="clip_image016" src="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image016_thumb2.jpg" border="0" /></a></td>
<td width="301" valign="top"><a href="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image0182.jpg"><img width="183" height="134" title="clip_image018" style="margin: 0px" alt="clip_image018" src="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image018_thumb2.jpg" border="0" /></a></td>
</tr>
</tbody>
</table>
Si quisiéramos eliminar las filas del conjunto de datos, tendríamos que ejecutar el método siguiente:
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="609" valign="top">
[code language="csharp"]
public static void DeleteRows(string token, string datasetName, string tableName)

{

dataset[] datasets = GetDatasets(token);

string datasetId = (from d in datasets where d.Name == datasetName select d).FirstOrDefault().Id;

string powerBIApiUrl = String.Format(&quot;https://api.powerbi.com/v1.0/myorg/datasets/{0}/tables/{1}/Rows&quot;, datasetId, tableName);

//Para grupos: https://api.powerbi.com/v1.0/myorg/groups/{group_id}/datasets/{dataset_id}/tables/{table_name}/rows

HttpWebRequest request = System.Net.WebRequest.Create(powerBIApiUrl) as System.Net.HttpWebRequest;

request.KeepAlive = true;

request.Method = &quot;DELETE&quot;;

request.ContentLength = 0;

request.ContentType = &quot;application/json&quot;;

request.Headers.Add(&quot;Authorization&quot;, String.Format(&quot;Bearer {0}&quot;, token));

using (HttpWebResponse httpResponse = request.GetResponse() as System.Net.HttpWebResponse)

{

using (StreamReader reader = new System.IO.StreamReader(httpResponse.GetResponseStream()))

{

string responseContent = reader.ReadToEnd();

}

}

}
[/code]
</td>
</tr>
</tbody>
</table>
<h3>4. Explotando la información</h3>
Ahora vamos a explotar la información de nuestro conjunto de datos desde Power BI Desktop.

Abrimos Power BI Desktop y accedemos a las opciones:

<a href="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image0202.jpg"><img width="204" height="244" title="clip_image020" style="margin: 0px" alt="clip_image020" src="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image020_thumb2.jpg" border="0" /></a>

Seleccionamos “Características de la versión preliminar” y marcamos la opción “Conexión dinámica al servicio Power BI”:

<a href="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image0221.jpg"><img width="244" height="225" title="clip_image022" style="margin: 0px" alt="clip_image022" src="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image022_thumb1.jpg" border="0" /></a>

A continuación, vamos a Obtener datos y seleccionamos la opción “Servicio de Power BI” en la sección “Servicios en línea”.

<a href="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image0241.jpg"><img width="225" height="244" title="clip_image024" style="margin: 0px" alt="clip_image024" src="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image024_thumb1.jpg" border="0" /></a>

Seleccionamos nuestra área de trabajo o grupo y cargamos nuestro conjunto de datos:

<a href="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image0261.jpg"><img width="244" height="166" title="clip_image026" style="margin: 0px" alt="clip_image026" src="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image026_thumb1.jpg" border="0" /></a>

Ya podemos pintar nuestro informe:

<a href="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image0281.jpg"><img width="244" height="68" title="clip_image028" alt="clip_image028" src="https://msdnshared.blob.core.windows.net/media/2017/10/clip_image028_thumb1.jpg" border="0" /></a>
<h3>5. Conclusión</h3>
En este artículo hemos visto cómo crear un conjunto de datos de personalizado y cómo llenarlo de información, para explotarlo después desde nuestros informes de Power BI. Para el ejemplo hemos obtenido la información de nuestro sitio de SharePoint Online. Veremos en próximos posts cómo obtener esta información directamente desde Power BI, utilizando la API de REST de SharePoint.]]></content:encoded>
    </item>
    <item>
      <title>Xamarin Madrid: Comunidad renovada</title>
      <link>https://docs.microsoft.com/archive/blogs/esmsdn/xamarin-madrid-comunidad-renovada</link>
      <pubDate>Fri, 13 Oct 2017 08:49:17 GMT</pubDate>
      <dc:creator><![CDATA[esmsdn]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/esmsdn/?p=13115</guid>
      <description><![CDATA[Presentación:
Xamarin Madrid es un grupo para todos los interesados en una multiplataforma usando...]]></description>
      <content:encoded><![CDATA[<h1><a href="https://msdnshared.blob.core.windows.net/media/2017/10/xamarin_madrid.jpg"><img width="400" height="400" class="aligncenter size-full wp-image-13117" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/10/xamarin_madrid.jpg" /></a><strong>Presentación:</strong></h1>
Xamarin Madrid es un grupo para todos los interesados en una multiplataforma usando Xamarin.

Nuestra idea es realizar encuentros periódicos donde se puedan realizar laboratorios, demonstraciones de todas las posibilidades de Xamarin y encuentros, donde podamos intercambiar experiencias.

No importa el nivel ni la experiencia, solo es necesario tener ganas de aprender y disfrutar con el desarrollo de Software.
En nuestro Meetup podrás encontrar la agenda con nuestros próximos eventos:
• 7 de noviembre: <a href="https://www.meetup.com/es-ES/Xamarin-Madrid/events/244055802/">Experiencia de Xamarin en grandes proyectos reales</a>

De momento tenemos planificado uno, ¡pero solo es el primero de muchos!
<h1>Objetivo</h1>
El objetivo del grupo es compartir conocimiento y experiencia en el desarrollo multiplataforma con Xamarin y todo lo que esté relacionado, como puede ser IOS, Android, UWP, .Net, patrones y etc…

¡Anímate y únete, te esperamos!
<h1>Información de contacto y enlaces de interés</h1>
Email: <a href="mailto:xamarin-madrid@outlook.com">xamarin-madrid@outlook.com</a>
Twiter: <a href="https://twitter.com/xamarin_madrid">@Xamarin_Madrid</a>
Meetup: <a href="https://www.meetup.com/es-ES/Xamarin-Madrid/">https://www.meetup.com/es-ES/Xamarin-Madrid/</a>]]></content:encoded>
    </item>
    <item>
      <title>Construye aplicaciones rápidamente con Azure Serverless</title>
      <link>https://docs.microsoft.com/archive/blogs/esmsdn/construye-aplicaciones-rapidamente-con-azure-serverless</link>
      <pubDate>Tue, 22 Aug 2017 11:41:41 GMT</pubDate>
      <dc:creator><![CDATA[esmsdn]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/esmsdn/?p=13105</guid>
      <description><![CDATA[Los servicios Serverless de Azure permiten a los desarrolladores crear e implementar aplicaciones...]]></description>
      <content:encoded><![CDATA[<span lang="ES"><span style="color: #000000;font-family: Calibri">Los servicios Serverless de Azure permiten a los desarrolladores crear e implementar aplicaciones auto escalables m</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">á</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">s r</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">á</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">pido que nunca. La tecnolog</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">í</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">a Serverless permite a los desarrolladores centrarse directamente en sus aplicaciones en lugar de organizar, administrar y escalar la infraestructura. Azure proporciona las herramientas necesarias para acelerar este proceso de forma sencilla.</span></span>

<img width="723" height="331" class="aligncenter" alt="Resultado de imagen de azure serverless" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAAtqAAAAJDU5ZjUzMDgzLTUwYWEtNDIyNC04M2RiLTM2NzgxZTRmZTZkNg.png" />

<span lang="ES"><span style="color: #000000;font-family: Calibri">Tradicionalmente, la creaci</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">ó</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">n de una nueva aplicaci</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">ó</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">n no pod</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">í</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">a comenzar hasta que se hubiesen contestado algunas preguntas fundamentales relacionadas con la infraestructura - </span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">¿</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">D</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">ó</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">nde se ejecutar</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">á</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri"> la aplicaci</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">ó</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">n? </span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">¿</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">C</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">ó</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">mo escalar</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">á</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri"> la aplicaci</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">ó</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">n para satisfacer la demanda? </span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">¿</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">C</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">ó</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">mo puedo supervisar mi aplicaci</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">ó</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">n? Estas y muchas preguntas similares, ocupan una parte del desarrollo. Azure Serverless permite simplemente abstraer la infraestructura y hacer que el c</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">ó</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">digo de la aplicaci</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">ó</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">n y la l</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">ó</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">gica de negocio sean centrales.</span></span>

<span lang="ES"><span style="color: #000000;font-family: Calibri">La plataforma proporciona una serie de servicios completamente administrados que abarcan el c</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">á</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">lculo, almacenamiento, base de datos, monitoreo, an</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">á</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">lisis, inteligencia, etc. para ayudar a construir aplicaciones sin servidor para cualquier tipo de escenario. Centr</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">é</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">monos en dos piezas centrales para el desarrollo de aplicaciones sin servidor, Azure Functions y Azure Logic Apps.</span></span>

<span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">•</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri"><a href="https://docs.microsoft.com/en-us/azure/azure-functions/"> Azure Functions</a> proporciona Functions-as-a-Service (Funciones como Servicio), donde simplemente se proporciona c</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">ó</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">digo (ya sea C #, JavaScript, Python o muchos otros lenguajes soportados) y se ejecuta mediante peticiones. Las Azure Functions pueden ser creadas y depuradas localmente en la m</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">á</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">quina del desarrollador y pueden transmitir y recibir datos de otros servicios como Azure Storage o Event Hubs a trav</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">é</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">s de un concepto denominado <a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-triggers-bindings">bindings</a>. Las funciones se escalan para satisfacer las necesidades de la aplicaci</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">ó</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">n autom</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">á</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">ticamente, por lo que una funci</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">ó</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">n que se ejecuta correctamente a nivel local se ampliar</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">á</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri"> autom</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">á</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">ticamente hasta potencialmente procesar miles de millones de eventos en la nube.</span></span>

<span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">•</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri"><a href="https://docs.microsoft.com/en-us/azure/logic-apps/"> Azure Logic Apps</a> proporciona flujos de trabajo serverless en la nube. Por ejemplo, en una operaci</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">ó</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">n como agregar un nuevo cliente, puede haber varias funcionalidades a ejecutar. Es posible que haya que agregar al cliente a una base de datos, generar un correo electr</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">ó</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">nico de bienvenida, crear un nuevo inicio de sesi</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">ó</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">n de usuario y crear una entrada en el CRM. Logic Apps permite la orquestaci</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">ó</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">n de datos y procesamiento para llevar estos pasos aislados en un flujo de trabajo coherente. Logic Apps incluye m</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">á</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">s de 150 conectores para servicios como Visual Studio Team Services, Salesforce, SAP y muchos otros. Esto permite a los desarrolladores integrar f</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">á</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">cilmente datos dentro y fuera de sus aplicaciones serverless en lugar de escribir c</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">ó</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">digos complejos entre sistemas dispares.</span></span>

<img class="aligncenter" alt="Azure Functions" src="https://azurecomcdn.azureedge.net/mediahandler/acomblog/media/Default/blog/5e65fd67-fb69-4db9-a607-e89dead8c520.gif" />

<span lang="ES"><span style="color: #000000;font-family: Calibri">La plataforma Azure Serverless proporciona un marco f</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">á</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">cil de usar para crear pr</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">á</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">cticamente cualquier tipo de aplicaci</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">ó</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">n cloud combinando f</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">á</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">cilmente IoT, procesamiento de datos, automatizaci</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">ó</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">n, mensajer</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">í</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">a e inteligencia con mayor agilidad y potencia en la entrega de soluciones </span></span><span><span style="color: #000000;font-family: Calibri">“</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">end to end</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">”</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">. </span></span>

<span lang="ES"><span style="color: #000000;font-family: Calibri">Prueba algunas de las gu</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">í</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">as de iniciaci</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">ó</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">n de <a href="https://docs.microsoft.com/es-es/azure/azure-functions/">Serverless </a>para crear tu primera aplicaci</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">ó</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">n serverless en cuesti</span></span><span lang="ES" style="margin: 0px;font-family: 'Times New Roman',serif"><span style="color: #000000">ó</span></span><span lang="ES"><span style="color: #000000;font-family: Calibri">n de minutos.</span></span>

Daniel Mitchell

Technical Evangelist Intern

<a href="https://twitter.com/danymitb">@danymitb</a>]]></content:encoded>
    </item>
    <item>
      <title>Integración de Azure B2C en una aplicación de Xamarin Forms utilizando MSAL</title>
      <link>https://docs.microsoft.com/archive/blogs/esmsdn/integracion-de-azure-b2c-en-una-aplicacion-de-xamarin-forms-utilizando-msal</link>
      <pubDate>Mon, 14 Aug 2017 09:00:51 GMT</pubDate>
      <dc:creator><![CDATA[esmsdn]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/esmsdn/?p=13055</guid>
      <description><![CDATA[En este artículo veremos cómo crear una sencilla aplicación Xamarin Forms, que muestra cómo usar...]]></description>
      <content:encoded><![CDATA[En este artículo veremos cómo crear una sencilla aplicación Xamarin Forms, que muestra cómo usar MSAL para autenticar usuarios a través de Azure Active Directory B2C y acceder a una API Web ASP.NET con el token resultante. Para obtener más información sobre Azure B2C, consulta la <a href="https://docs.microsoft.com/azure/active-directory-b2c/active-directory-b2c-overview">documentación de Azure AD B2C</a>.
<h2>Cómo ejecutar un ejemplo simple</h2>
Para ejecutar este ejemplo necesitarás: - Visual Studio 2017 - Una conexión a Internet - Un tenant Azure AD B2C.
Si no tienes un tenant Azure AD B2C, puedes seguir las instrucciones para crear uno. Si sólo quieres ver este ejemplo, no necesitas crear tu propio tenant, ya que el proyecto viene con algunos ajustes asociados a un tenant de prueba y una aplicación; Sin embargo, es altamente recomendable que registres tu propia aplicación y pruebes los pasos de configuración a continuación.
<h3>Paso 1: clona o descarga el repositorio</h3>
Desde la línea de comandos:

&nbsp;
<pre class="prettyprint">git clone https://github.com/Azure-Samples/active-directory-b2c-xamarin-native.git
</pre>
<h3>[OPCIONAL] Paso 2: consigue tu propio tenant Azure B2C</h3>
También puedes modificar el ejemplo para usar tu propio tenant Azure AD B2C. Primero, necesitará crear un tenant Azure AD B2C siguiendo <a href="https://azure.microsoft.com/documentation/articles/active-directory-b2c-get-started">estas instrucciones</a>.
<em>IMPORTANTE: si eliges realizar uno de los pasos opcionales, tienes que hacer TODOS ellos para que el ejemplo funcione como se esperaba.</em>
<h3>[OPCIONAL] Paso 3: crea tus propias políticas</h3>
Este ejemplo utiliza tres tipos de políticas: una política de registro / inicio de sesión unificado y una directiva de edición de perfil. Crea una política de cada tipo siguiendo <a href="https://azure.microsoft.com/documentation/articles/active-directory-b2c-reference-policies">estas instrucciones</a>. Puede elegir incluir tantos o tan pocos proveedores de identidad como deses.
Si ya tienes políticas existentes en tu tenant Azure AD B2C, no dudes en volver a utilizarlas. No hay necesidad de crear nuevas sólo para este ejemplo.
<h3>[OPCIONAL] Paso 4: Crea tu propia Web API</h3>
Este ejemplo llama a una API en https://fabrikamb2chello.azurewebsites.net que tiene el mismo código que el ejemplo <a href="https://github.com/Azure-Samples/active-directory-b2c-javascript-nodejs-webapi">Node.js Web API con Azure AD B2C</a>. Necesitará su propia API o, como mínimo, tendrás que <a href="https://docs.microsoft.com/azure/active-directory-b2c/active-directory-b2c-app-registration#register-a-web-api">registrar una API Web con Azure AD B2C</a> para que puedas definir los ámbitos para los que su aplicación de una única página solicitará tokens de acceso.
El registro de tu API web debe incluir la siguiente información:
<ul>
 	<li>Habilitar la configuración de la aplicación web / Web API para la aplicación.</li>
 	<li>Establecer la URL de respuesta en el valor apropiado indicado en el ejemplo o proporcionar cualquier URL si sólo está realizando el registro de api web, por ejemplo https: // myapi.</li>
 	<li>Asegúrate de proporcionar también una URI de AppID, por ejemplo demoapi, que se utiliza para construir los scopes que están configurados en el código de la aplicación de una sola página.</li>
 	<li>(Opcional) Una vez que se haya creado la aplicación, abre Scopes públicos de la aplicación y agrega los scopes adicionales que desees.</li>
 	<li>Copia los valores de URI de AppID y Scopes públicos, para poder ingresarlos en el código de tu aplicación.</li>
</ul>
<h3>[OPCIONAL] Paso 5: Crea tu propia app nativa</h3>
Ahora debes <a href="https://docs.microsoft.com/azure/active-directory-b2c/active-directory-b2c-app-registration#register-a-mobilenative-application">registrar tu aplicación nativa en su tenant B2C</a>, para que tenga su propio ID de aplicación. No olvides proporcionar una API de Acceso a tu API web registrada en el paso anterior.
El registro nativo de tu aplicación debe incluir la siguiente información:
<ul>
 	<li>Activa la configuración Native Client para tu aplicación.</li>
 	<li>Una vez que se haya creado la aplicación, abre la pestaña de Propiedades de la aplicación y configura la URI de redirección para tu aplicación en msal &lt;Id de aplicación&gt;: // auth.</li>
 	<li>Una vez que se haya creado la aplicación, abre la pestaña de acceso a la API y agrega la API que creaste en el paso anterior.</li>
 	<li>Copia el ID de aplicación generado para su aplicación, de modo que puedas utilizarlo en el paso siguiente.</li>
</ul>
<h3>[OPCIONAL] Paso 6: Configura el proyecto de Visual Studio con los datos de tu aplicación</h3>
<ol>
 	<li>Abre la solución en Visual Studio.</li>
 	<li>Abre el archivo UserDetailsClient \ App.cs.</li>
 	<li>Busca la asignación para public static string Tenant y reemplaza el valor por su nombre de tenant.</li>
 	<li>Busca la asignación de ClientID de cadena estática pública y reemplaza el valor por el ID de aplicación del paso 2.</li>
 	<li>Busca la asignación para cada una de las políticas public static string PolicyX y reemplaza los nombres de las políticas que creaste en el paso 3.</li>
 	<li>Busca la asignación para los ámbitos public static string [] Scopes y reemplázalos con los que creaste en el Paso 4.</li>
</ol>
<h3>[OPCIONAL] Paso 6a: Configura el proyecto en iOS con la URI de retorno de la API</h3>
<ol>
 	<li>Abre el archivo UserDetailsClient.iOS \ info.plist en un editor de texto (abrirlo en Visual Studio no funcionará para este paso, ya que necesitas editar el texto)</li>
 	<li>En los tipos de URL, sección, agrega una entrada para el esquema de autorización utilizado en su redirectUri. Xml &lt;key&gt; CFBundleURLSchemes &lt;/ key&gt; &lt;array&gt; &lt;string&gt; msal [APPLICATIONID] &lt;/ string&gt; &lt;/ array&gt; donde [APPLICATIONID] es el identificador que copiaste en el paso 2. Guarda el archivo.</li>
</ol>
<h3>[OPCIONAL] Paso 6b: Configura el proyecto Android con la URI de retorno de la API</h3>
<ol>
 	<li>Abre el UserDetailsClient.Droid \ Properties \ AndroidManifest.xml</li>
 	<li>Agrega o modifica el elemento &lt;application&gt; como en el siguiente xml &lt;application&gt; &lt;activity android: name = "microsoft.identity.client.BrowserTabActivity"&gt; &lt;action android: name = "android.intent.action. VIEW "/&gt; &lt;categoría android: name =" android.intent.category.DEFAULT "/&gt; &lt;android: name =" android.intent.category.BROWSABLE "/&gt; &lt;data android: scheme =" msal [APPLICATIONID] Android: host = "auth" /&gt; &lt;/ intent-filter&gt; &lt;/ activity&gt; &lt;/ application&gt; donde [APPLICATIONID] es el identificador que copiaste en el paso 2. Guarda el archivo.</li>
</ol>
<h3>Paso 7: Ejecutar el ejemplo</h3>
<ol>
 	<li>Elige la plataforma en la que deseas trabajar estableciendo el proyecto de inicio en el Explorador de soluciones. Asegúrate de que la plataforma elegida esté marcada para la creación y la implementación en el Configuration Manager.</li>
 	<li>Limpia la solución, hazle un rebuild y ejecútala.</li>
 	<li>Haz clic en el botón de inicio de sesión en la parte inferior de la pantalla de la aplicación. El ejemplo funciona exactamente de la misma manera, independientemente del tipo de cuenta que elijas, y contará con algunas diferencias visuales en la experiencia de autenticación y consentimiento. Al acceder con éxito, la pantalla de la aplicación mostrará una información de perfil básica para el usuario autenticado y podrás ver botones que te permitirán editar tu perfil, llamar a una API y cerrar sesión.</li>
 	<li>Cierra la aplicación y vuelve a abrirla. Verás que la aplicación conserva el acceso a la API y recupera la información de usuario de inmediato, sin necesidad de volver a iniciar sesión.</li>
 	<li>Para salir, haz clic en el botón Cerrar sesión y confirma que pierde el acceso a la API hasta que se cierre la sesión.</li>
</ol>
<h3>Corriendo la app en el emulador</h3>
MSAL en Android requiere soporte para las pestañas personalizadas de Chrome, que muestran las indicaciones de autenticación. No todas las imágenes de los emuladores vienen con Chrome a bordo: consulta<a href="https://github.com/Azure-Samples/active-directory-general-docs/blob/master/AndroidEmulator.md"> este documento </a>para obtener instrucciones sobre cómo garantizar que tu emulador admita las funciones requeridas por MSAL.
<h2>Acerca del código</h2>
La estructura de la solución es sencilla. Toda la lógica de la aplicación y UX residen en UserDetailsClient (portable). Primitiva principal de MSAL para clientes nativos, PublicClientApplication, se inicializa como una variable estática en App.cs. En el inicio de la aplicación, la página principal intenta obtener un token sin mostrar ningún UX - sólo en caso de que un token adecuado ya esté presente en la caché de las sesiones anteriores. Este es el código que implementa esa lógica:

&nbsp;
<pre class="prettyprint">protected override async void OnAppearing()
{
    UpdateSignInState(false);

    // Check to see if we have a User in the cache already.
    try
    {
        AuthenticationResult ar = await App.PCA.AcquireTokenSilentAsync(App.Scopes, GetUserByPolicy(App.PCA.Users, App.PolicySignUpSignIn), App.Authority, false);
        UpdateUserInfo(ar);
        UpdateSignInState(true);
    }
    catch (Exception)
    {
        // Doesn't matter, we go in interactive mode
        UpdateSignInState(false);
    }
}

</pre>
Si el intento de obtener un token falla, no hacemos nada y visualizamos la pantalla con el botón de inicio de sesión. Cuando se pulsa el botón de inicio de sesión, ejecutamos la misma lógica, pero empleando un método que muestra UX interactivo:

&nbsp;
<pre class="prettyprint">AuthenticationResult ar = await App.PCA.AcquireTokenAsync(App.Scopes, GetUserByPolicy(App.PCA.Users, App.PolicySignUpSignIn), App.UiParent);
</pre>
El parámetro Scopes indica los permisos que la aplicación necesita para obtener acceso a los datos solicitados a través de la siguiente llamada de la API web (en este ejemplo, encapsulado en OnCallApi). El UiParent se utiliza en Android para vincular el flujo de autenticación a la actividad actual y se ignora en todas las demás plataformas.

La lógica de salida es muy simple. En este ejemplo tenemos sólo un usuario, sin embargo estamos mostrando una lógica de salida más genérica, que puede aplicar si tienes varios usuarios simultáneos y deseas borrar toda la memoria caché.

Csharp foreach (usuario var en App.PCA.Users) {App.PCA.Remove (usuario); }

&nbsp;
<h3>Consideraciones especiales para Android</h3>
Los proyectos específicos de la plataforma requieren sólo un par de líneas adicionales para adaptarse a las diferencias de plataforma individuales.
UserDetailsClient.Droid requiere dos líneas adicionales en el archivo MainActivity.cs. En OnActivityResult, necesitamos agregar:

&nbsp;
<pre class="prettyprint">AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs(requestCode, resultCode, data);
</pre>
Esa línea garantiza que el control vuelva a MSAL una vez que la parte interactiva del flujo de autenticación terminó.
En OnCreate, necesitamos agregar la siguiente asignación: csharp App.UiParent = new UIParent (Xamarin.Forms.Forms.Context as Activity); Ese código asegura que los flujos de autenticación ocurran en el contexto de la actividad actual.
<h3>Consideraciones específicas para iOS</h3>
UserDetailsClient.iOS sólo requiere una línea adicional, en AppDelegate.cs. Debe asegurarse de que el handler OpenUrl tiene un aspecto similar al siguiente: csharp public override bool OpenUrl (aplicación UIApplication, URL NSUrl, opciones NSDictionary) {AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs (url, ""); Devuelve verdadero; } Una vez más, esta lógica está destinada a asegurar que una vez que la parte interactiva del flujo de autenticación se concluye, el flujo vuelve a MSAL.
<h2>Más información</h2>
Para más información sobre Azure B2C, consulta la <a href="https://aka.ms/aadb2c">documentación oficial</a>.
<h3>Más ejemplos de Jean-Marc Prieur</h3>
<a href="https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-graphapi-directoryextensions-web/">Extending the Azure AD directory schema with custom properties </a>
<a href="https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-graphapi-web/">Calling the Azure AD Graph API in a web application </a>
<a href="https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-windows-store/">Integrating a Windows Universal application with Azure AD </a>
<a href="https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-native-desktop/">Integrating Azure AD into a Windows desktop application </a>
<a href="https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-daemon/">Calling web APIs in a daemon or long-running process </a>

&nbsp;

<a href="https://www.linkedin.com/in/daniel-ortiz-l%C3%B3pez/">Daniel Ortiz López</a>

Technical Evangelist Intern

<a href="https://twitter.com/ortizlopez91">@ortizlopez91</a>]]></content:encoded>
    </item>
    <item>
      <title>Nuevas características en Power BI Desktop II</title>
      <link>https://docs.microsoft.com/archive/blogs/esmsdn/nuevas-caracteristicas-en-power-bi-desktop-ii</link>
      <pubDate>Mon, 07 Aug 2017 09:00:35 GMT</pubDate>
      <dc:creator><![CDATA[esmsdn]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/esmsdn/?p=12986</guid>
      <description><![CDATA[La semana pasada veíamos algunas de las novedades de Power BI lanzadas en el mes de julio, como las...]]></description>
      <content:encoded><![CDATA[La semana pasada veíamos <a href="https://blogs.msdn.microsoft.com/esmsdn/?p=12925&amp;preview=true">algunas de las novedades de Power BI </a>lanzadas en el mes de julio, como las nuevas tablas y matrices, la personalización de los campos, la integración de visualizaciones personalizadas desde la Store, los filtros relativos de fecha y las visualizaciones reactivas. En este artículo descubriremos las nuevas opciones para las visualizaciones de cascada, las actualizaciones sobre visualizaciones personalizadas, medidas rápidas sugeridas por la comunidad, el cross filtering bidireccional, el conector Snowflake y la posibilidad de añadir columnas desde otros ejemplos.
<h1>Nueva opción en la visualización de cascada - desglose</h1>
<div>Una pregunta común al analizar los datos es querer entender por qué algo aumentó o disminuyó. Normalmente, para responder a esta pregunta, tendrías que hacer algunos cálculos complejos o pasar mucho tiempo probando diferentes filtros para tener una idea de qué valores fueron más influyentes en el cambio.</div>
<div>Para hacer este proceso más fácil, estamos incorporando una nueva opción a nuestro gráfico de cascada, llamado desglose. Cuando pones un campo de dimensión en este nuevo hueco, te mostraremos qué valores en ese campo fueron las grandes causas de aumento y disminución entre cada categoría vecina en el gráfico de cascada.</div>
<div></div>
<div><a href="https://msdnshared.blob.core.windows.net/media/2017/07/91.jpg"><img width="610" height="258" class="aligncenter size-full wp-image-12995" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/91.jpg" /></a></div>
<div></div>
<div>Por defecto, te mostraremos las 5 diferencias principales , junto con un hueco llamado 'Otro', que agrupa todos los demás valores juntos. Sin embargo, puedes ajustar cuántos valores deseas ver antes de agrupar en el cubo "Otros" del panel de formato.</div>
<div></div>
<div><a href="https://msdnshared.blob.core.windows.net/media/2017/07/101.jpg"><img width="610" height="250" class="aligncenter size-full wp-image-13005" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/101.jpg" /></a></div>
<div></div>
<div>Encuentra más detalles sobre las actualizaciones de los gráficos de cascadas en el siguiente video:</div>
<div></div>
<div></div>
<iframe width="600" height="338" src="https://www.youtube.com/embed/4X96ow7FnSY" frameborder="0" allowfullscreen="allowfullscreen"></iframe>
<h1>Actualizaciones de visualizaciones personalizadas</h1>
A partir de este mes, vamos a presentar algunas nuevas visualizaciones personalizadas y otras noticias relacionadas, en el blog oficial de Power BI, de modo que podrás estar al día con todas las gráficas que se están creando.
<h3>Drilldown Choropleth</h3>
En primer lugar, el nuevo <a href="https://store.office.com/en-us/app.aspx?assetid=WA104381044&amp;sourcecorrid=60d54e91-5dc0-4613-8db7-7702e8a943c8&amp;searchapppos=0&amp;ui=en-US&amp;rs=en-US&amp;ad=US&amp;appredirect=false">Drilldown Choropleth</a>, que muestra cada ubicación en un mapa con un color basado en el valor de cada ubicación. Es similar al mapa de forma, que estaba en preview, y en el que podías utilizar tus propios archivos de mapas personalizados. Esto te da un control total sobre cómo se ve el mapa. La parte más poderosa de esta visualización es que se puede utilizar el desglose para moverte entre jerarquías geográficas, mostrando diferentes archivos de mapas en cada nivel.

&nbsp;

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/1112.png"><img width="947" height="421" class="aligncenter size-full wp-image-13007" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/1112.png" /></a>
<h3>Drilldown Cartogram</h3>
Si te gusta cómo funciona <a href="https://store.office.com/en-us/app.aspx?assetid=WA104381045&amp;sourcecorrid=0b98dddb-43d3-4e3b-8358-0c2c06bc831b&amp;searchapppos=4&amp;ui=en-US&amp;rs=en-US&amp;ad=US&amp;appredirect=false">Drilldown Choropleth</a>, pero prefieres una apariencia de burbuja similar a nuestro mapa de Power BI por defecto, el Drilldown Cartogram es una buena visualización personalizada para que lo pruebes. Este mapa tiene los mismos campos personalizados y funciones, pero muestra un círculo para cada ubicación con el tamaño y el color en función de los valores especificados.

&nbsp;

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/1210.png"><img width="772" height="408" class="aligncenter size-full wp-image-13015" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/1210.png" /></a>
<h3>Drilldown Player</h3>
La visualización <a href="https://store.office.com/en-us/app.aspx?assetid=WA104381035&amp;sourcecorrid=484587fe-a7be-42da-b5fa-72a374dc60f3&amp;searchapppos=3&amp;ui=en-US&amp;rs=en-US&amp;ad=US&amp;appredirect=false">Drilldown Player </a>te permite filtrar o resaltar tu informe de forma automática. Puedes agregar una columna categórica o numérica a la visualización y presionar play para ver cada valor de la columna, aplicado al resto del informe. Esta visualización también admite jerarquías, por lo que puedes agregar varias columnas a la visualización para admitir la reproducción a través de varios campos. Mediante la función de edición de interacciones, puedes controlar si el reproductor filtrará o resaltará las otras imágenes de su informe.

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/13.gif"><img width="1448" height="800" class="aligncenter size-full wp-image-13016" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/13.gif" /></a>

Encuentra más detalles sobre cada uno de estos elementos visuales personalizados en el siguiente video:
<iframe width="600" height="338" src="https://www.youtube.com/embed/4X96ow7FnSY" frameborder="0" allowfullscreen="allowfullscreen"></iframe>
<div>Estos son sólo algunas de las visualizaciones personalizadas lanzadas en la tienda el mes pasado. Aunque hay otros que quizá te puedan interesar también:</div>
<ul>
 	<li>Histograma con puntos</li>
 	<li>Gráfico rotativo</li>
 	<li>Cuadrante gráfico</li>
</ul>
<h3>Visualizaciones personalizadas certificadas</h3>
<div>¡Las visualizaciones personalizadas ahora pueden ser certificadas! Los diseños visuales personalizados certificados son diseños que se ajustan a buenas prácticas de desarrollo visual personalizado, según lo definimos en Microsoft, y son verificados por el equipo de certificación de visualizaciones de Power BI. Estos van desde el rendimiento hasta la seguridad y la usabilidad. Una vez que una visualización ha sido certificada, puede ser exportada a PowerPoint y se mostrará en los correos electrónicos de suscripción.</div>
<div>Las siguientes imágenes han sido certificadas hasta el momento:</div>
<ul>
 	<li><a href="https://store.office.com/en-us/app.aspx?assetid=WA104380755&amp;sourcecorrid=48334e83-53cf-4bc0-a022-1da4d06618b9&amp;searchapppos=0">Bullet Chart</a></li>
 	<li><a href="https://store.office.com/chiclet-slicer-WA104380756.aspx?assetid=WA104380756&amp;sourcecorrid=83cc564f-4d8f-40f7-85bc-5488c1309a58&amp;searchapppos=0">Chiclet slicer</a></li>
 	<li><a href="https://store.office.com/dual-kpi-WA104380774.aspx?assetid=WA104380774&amp;sourcecorrid=b76ac0d1-a367-46f3-bc56-677102c9b784&amp;searchapppos=0">Dual KPI</a></li>
 	<li><a href="https://store.office.com/timeline-slicer-WA104380786.aspx?assetid=WA104380786&amp;sourcecorrid=a21a11dd-be97-4758-879c-0d6dc56d5657&amp;searchapppos=0">Timeline slicer</a></li>
</ul>
Hay incluso más <a href="https://powerbi.microsoft.com/documentation/powerbi-custom-visuals-certified/#list-of-custom-visuals-that-have-been-certified">visualizaciones en proceso de certificación</a>, así que estate atento de los próximos anuncios.
<h1>Analítica y modelado</h1>
<h2>Medidas rápidas propuestas por la comunidad</h2>
La gente de la comunidad ha estado presentando ideas para nuevas medidas rápidas (¡junto con su DAX!) a nuestra <a href="https://community.powerbi.com/t5/Quick-Measures-Gallery/bd-p/QuickMeasuresGallery">Galería de Medidas Rápidas </a>y hemos integrado dos de esas medidas en nuestro producto. Encontrarás estas dos nuevas medidas en la categoría "Texto" en el cuadro de diálogo Medidas rápidas.
<h3>Puntuación de estrellas</h3>
La medida rápida de la puntuación de estrellas visualizará tus datos como un número de estrellas entre un mínimo y un máximo dado. Gracias a Chris Webb (<a href="https://blog.crossjoin.co.uk/">Blog</a>, <a href="https://twitter.com/Technitrain">Twitter</a>), por sugerir esta medida rápida.

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/141.jpg"><img width="610" height="329" class="aligncenter size-full wp-image-13025" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/141.jpg" /></a>
<h3>Lista de valores concatenados</h3>
Esta medida rápida concatenará las dimensiones en una lista separada por comas. También puede decidir cuántos valores se concatenarán antes de truncarlo con un "etc." Gracias a Devin Knight (<a href="https://devinknightsql.com/">Blog</a>, <a href="https://twitter.com/knight_devin">Twitter</a>), por sugerir esta medida en nuestra galería de medidas rápidas
<div></div>
<div><a href="https://msdnshared.blob.core.windows.net/media/2017/07/152.jpg"><img width="610" height="183" class="aligncenter size-full wp-image-13026" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/152.jpg" /></a></div>
<div></div>
<div>
<div>¡Asegúrate de enviar cualquier idea que tengas a <a href="https://community.powerbi.com/t5/Quick-Measures-Gallery/bd-p/QuickMeasuresGallery">la galería</a>, y podrás ver cómo aparece en el cuadro de diálogo de medidas rápidas!</div>
<div>Encuentra más detalles sobre estas medidas rápidas en el siguiente video:</div>
<div></div>
</div>
<iframe width="600" height="338" src="https://www.youtube.com/embed/4X96ow7FnSY" frameborder="0" allowfullscreen="allowfullscreen"></iframe>
<h2>Ya está disponible Cross Filtering Bidireccional para DirectQuery</h2>
<div>Lanzamos como preview el filtrado cruzado bidireccional para los modelos de DirectQuery hace unos meses, y ahora lo estamos haciendo disponible para todos. Esto significa que el filtrado cruzado bidireccional está generalmente disponible para los modelos de importación y DirectQuery, incluido el uso del filtrado bidireccional para definir la seguridad a nivel de fila. Puedes obtener más información sobre el filtrado cruzado bidireccional en <a href="https://powerbi.microsoft.com/blog/bidirectional-cross-filtering-whitepaper-2/">el documento técnico</a>.</div>
<div>Al utilizar esta función, debes tener en cuenta las implicaciones de rendimiento, y se recomienda encarecidamente que:</div>
<ul>
 	<li>"Asumir integridad referencial" está ya establecido. Obten más información sobre esta función en <a href="https://powerbi.microsoft.com/documentation/powerbi-desktop-assume-referential-integrity/">nuestra documentación</a>.</li>
 	<li>Sólo los agregados simples se utilizan cuando se filtran por tales relaciones. Por ejemplo, DistinctCount o medidas más complejas que no son agregables, no deben utilizarse.</li>
</ul>
<h1>Conectividad de datos</h1>
<h2>Conector Snowflake</h2>
Con la actualización de este mes, lanzamos conector Snowflake. También funciona en Power BI Service mediante el Gateway de datos On-Prem. Permanece atento al anuncio de lanzamiento de este mes del Gateway de datos On-Prem, ya que necesitarás usar esa nueva versión para configurar las conexiones de actualización o de DirectQuery a Snowflake desde Power BI Service.

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/161.jpg"><img width="561" height="610" class="aligncenter size-full wp-image-13036" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/161.jpg" /></a>

El conector Snowflake se puede encontrar en la categoría Base de datos en el cuadro de diálogo Obtener datos. Permite crear informes basados en los datos de Snowflake, ya sea utilizando los modos Importación o DirectQuery.

<a href="https://msdnshared.blob.core.windows.net/media/2017/07/171.jpg"><img width="610" height="266" class="aligncenter size-full wp-image-13045" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/07/171.jpg" /></a>
<h1>Editor de Queries</h1>
<h2>Añadir columnas desde ejemplos</h2>
<div>Este mes hemos añadido soporte para algunas transformaciones de datos de tipo número, fecha, tiempo y zona horaria, cuando se utiliza "Añadir Columna desde Ejemplos". Para obtener una lista completa de las transformaciones de datos admitidas en esta función, consulta<a href="https://powerbi.microsoft.com/documentation/powerbi-desktop-add-column-from-example/"> esta documentación</a>.</div>
<div></div>
<div>¡Esto es todo para este mes! Esperamos que disfrutes de esta nueva actualización y continuéis enviándonos comentarios sobre Power BI. Por favor, <a href="https://ideas.powerbi.com/forums/265200-power-bi/category/91861-desktop">no olvides votar por otras características que lte gustaría ver en el Power BI Desktop, en el futuro</a>.</div>
<a href="https://powerbi.microsoft.com/desktop?WT.mc_id=Blog_Desktop_Update">DESCÁRGATE POWER BI DESKTOP</a>

&nbsp;

<a href="https://www.linkedin.com/in/daniel-ortiz-l%C3%B3pez/">Daniel Ortiz López</a>

Technical Evangelist Intern

<a href="https://twitter.com/ortizlopez91">@ortizlopez91</a>

&nbsp;

&nbsp;]]></content:encoded>
    </item>
    <item>
      <title>Machine Learning para principiantes - Bonus Track: visualización de datos con PowerBI e interacción desde un bot</title>
      <link>https://docs.microsoft.com/archive/blogs/esmsdn/machine-learning-para-principiantes-bonus-track-visualizacion-de-datos-con-powerbi-e-interaccion-desde-un-bot</link>
      <pubDate>Tue, 01 Aug 2017 12:05:47 GMT</pubDate>
      <dc:creator><![CDATA[Gorka Madariaga]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/esmsdn/?p=12755</guid>
      <description><![CDATA[Si después de crear los experimentos te quedaste con ganas de más, en este ultimo capítulo veremos...]]></description>
      <content:encoded><![CDATA[Si después de crear los experimentos te quedaste con ganas de más, en este ultimo capítulo veremos como exporter los datos resultantes para que puedan ser consumidos tanto desde PowerBI como desde un bot conversacional.

Lo primero que debemos hacer es cambiar el tipo de datos de la columna <strong>"Drafted"</strong> a entero, así que vamos a añadir otro modulo <strong>"Edit Metadata"</strong> y realizar esta transformación:

<a href="https://msdnshared.blob.core.windows.net/media/2017/06/ML55.png"><img width="1024" height="537" class="alignnone size-large wp-image-12765" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/06/ML55-1024x537.png" /></a>

&nbsp;

Después de esto, vamos a exportar el resultado de las predicciones a una base de datos SQL en Azure, de forma que tanto PowerBI como el bot puedan conectarse a los datos e interactuar con ellos. Para hacer esto, busca el modulo <strong>"Export Data"</strong> en el menú de la izquierda, dentro de la categoría <strong>"Data Input and Output"</strong>, arrástralo al canvas y conecta su entrada con la salida del último modulo <strong>"Edit Metadata"</strong>:

<a href="https://msdnshared.blob.core.windows.net/media/2017/06/ML56.png"><img width="1024" height="673" class="alignnone size-large wp-image-12766" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/06/ML56-1024x673.png" /></a>

&nbsp;

Con el modulo <strong>"Export Data"</strong> seleccionado, podrás configurar el servicio de almacenamiento en el que prefieras almacenar tus datos. En este caso, la opción elegida es una base de datos Azure SQL, que necesitarás desplegar de antemano. Si no lo has hecho nunca, en <strong><a href="https://docs.microsoft.com/en-us/azure/sql-database/sql-database-get-started-portal">este enlace</a></strong> puedes encontrar un tutorial paso a paso para hacerlo. Asegúrate de <strong>incluir tu IP en la whitelist del firewall del servidor SQL</strong>, o toda consulta será rechazada.

Una vez tengas la base de datos en funcionamiento, necesitarás el nombre del servidor, el nombre de la base de datos y las credenciales del usuario para acceder a ella (todo puedes encontrarlo en el portal de Azure). Para continuar, simplemente escribe la lista (separada por comas) de columnas que se seleccionarÃ¡n del dataset (en este caso: <em>Player, Position, Age, Team, Season, Drafted, PER</em>), indica el nombre de la tabla en la que quieres que se almacenen estos datos, y con otra lista de nombres de columnas separados por comas puedes elegir el nombre de las columnas en la base de datos (en este caso, se recomienda mantener los mismos nombres y usar la misma lista que antes):

<a href="https://msdnshared.blob.core.windows.net/media/2017/06/ML57.png"><img width="335" height="793" class="size-full wp-image-12776 aligncenter" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/06/ML57.png" /></a>

&nbsp;

&nbsp;
<div>Tras ejecutar de nuevo el experimento predictivo, todas las predicciones se almacenarán en la base de datos y estarán disponibles para que se consulten y consuman desde Power BI y cualquier otra aplicación que pueda leer desde una base de datos SQL.</div>
<div>Con los datos ya almacenados, puedes seguir estos pasos para conectarse a ellos desde Power BI y comenzar a crear visualizaciones interactivas que te ayuden a interactuar con tus datos fácilmente: <a href="https://powerbi.microsoft.com/en-us/documentation/powerbi-azure-sql-database-with-direct-connect/">https://powerbi.microsoft.com/en-us/documentation/powerbi-azure-sql-database-with-direct-connect/</a></div>
<div></div>
<div><a href="https://msdnshared.blob.core.windows.net/media/2017/06/ML58.png"><img width="1024" height="589" class="alignnone size-large wp-image-12785" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/06/ML58-1024x589.png" /></a></div>
&nbsp;
&nbsp;
<div>En este repo de Github (<a href="https://aka.ms/predict-nba-stats">https://aka.ms/predict-nba-stats</a>) también puedes encontrar el código de un bot que te permitirá interactuar con las predicciones almacenadas a través de una conversación real. Necesitarás, eso sí, configurar tu <a href="https://luis.ai">LUIS</a> con diferentes <em>intents</em>, para que el bot pueda entender lo que se le está pidiendo (en este caso: saludo, información sobre un jugador específico por nombre, draftear un jugador, lista de los mejores jugadores disponibles y lista los mejores jugadores disponibles por posición). También puedes encontrar el bot publicado en <a href="https://aka.ms/nba-draft-bot">https://aka.ms/nba-draft-bot</a>, incluyendo tarjetas con fotos y noticias sobre los jugadores gracias a los <a href="https://azure.microsoft.com/en-us/services/cognitive-services/">Cognitive Services</a>, que nos ayudan a añadir inteligencia a nuestras apps de una manera tan simple como añadir un par de peticiones HTTP:</div>
<div></div>
<div><a href="https://msdnshared.blob.core.windows.net/media/2017/06/ML59.png"><img width="1015" height="808" class="size-full wp-image-12795 aligncenter" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/06/ML59.png" /></a> <a href="https://msdnshared.blob.core.windows.net/media/2017/06/ML60.png"><img width="1014" height="805" class="size-full wp-image-12805 aligncenter" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/06/ML60.png" /></a></div>
&nbsp;
&nbsp;
<div>Esto es solo un ejemplo de cómo puedes interactuar fácilmente con los resultados de tus experimentos de Machine Learning y enriquecer los datos con algunas fuentes externas, ya que solo teníamos <em>cold hard facts</em> (números e información sobre el jugador), pero carecíamos de contexto específico sobre el jugador, que podría ayudarnos a entender el aumento/disminución en su valoración, o si deberíamos elegirlo o no debido a distintos factores (lesiones, cambios en el equipo, problemas con el entrenador o con algún compañero...). Es sólo cuestión de tener <strong>un objetivo claro en mente</strong> y encontrar los datos correctos para crear tu solución.</div>
&nbsp;
<div>Como aprendizaje durante la construcción de este experimento, me quedaría con las siguientes ideas:</div>
<ul>
 	<li><strong>Regla del 80/20:</strong> la preparación y limpieza de los datos es <strong>CLAVE</strong> en cualquier experimento de Machine Learning, y se lleva la mayor parte del tiempo empleado en ello.</li>
 	<li>Probar <strong>diferentes enfoques y ajustes</strong> suele mejorar bastante el rendimiento del modelo, y se trata de un proceso de <strong>prueba-error</strong> (lo de llamarse "experimentos" no es casualidad) en el que cada detalle cuenta...sigue probando hasta que consigas los resultados deseados!</li>
 	<li>El <strong>conocimiento que se tenga sobre el dominio de los datos a estudiar es crucial</strong> para determinar el éxito de una solución de Machine Learning, y no es suficiente con dominar las habilidades técnicas necesarias para crear soluciones de este estilo. Este conocimiento sobre el dominio nos ayudará a entender mejor las posibles <strong>correlaciones</strong> entre features y el<strong> impacto de los ajustes</strong> que hagamos, así como la importancia de filtrar los datos acorde a ciertos criterios, con el fin de mejorar la calidad de nuestro dataset.</li>
</ul>
&nbsp;
<div>Por último, algunos enlaces que pueden resultar útiles para continuar aprendiendo sobre Azure ML Studio y Data Science:</div>
<ul>
 	<li>Azure Machine Learning Studio tool (<a href="https://studio.azureml.net/">https://studio.azureml.net/</a>) y documentación sobre el servicio (<a href="https://docs.microsoft.com/en-us/azure/machine-learning/">https://docs.microsoft.com/en-us/azure/machine-learning/</a>)</li>
 	<li>Microsoft Professional Program for Data Science - <a href="https://academy.microsoft.com/en-us/professional-program/data-science/">https://academy.microsoft.com/en-us/professional-program/data-science/</a></li>
 	<li>Basketball reference (fuente de los datasets) - <a href="https://www.basketball-reference.com/leagues/NBA_2017_advanced.html">https://www.basketball-reference.com/leagues/NBA_2017_advanced.html</a></li>
 	<li>Kaggle (otra fuente gratuita de datasets) - <a href="https://www.kaggle.com/datasets">https://www.kaggle.com/datasets</a></li>
</ul>
&nbsp;
<div>Espero que hayas disfrutado de esta serie tanto como yo creándola :)</div>
&nbsp;
&nbsp;
<div>Un saludo,</div>
&nbsp;
<div>Gorka Madariaga (<a href="https://twitter.com/Gk_8">@Gk_8</a>)</div>
<div>Technical Evangelist</div>]]></content:encoded>
    </item>
  </channel>
</rss>