Mr et Azure 310 : détection d’objetsMr and Azure 310: Object detection

Notes

Les tutoriels Mixed Reality Academy ont été conçus pour les appareils HoloLens (1re génération) et les casques immersifs de réalité mixte.The Mixed Reality Academy tutorials were designed with HoloLens (1st gen) and Mixed Reality Immersive Headsets in mind. Nous estimons qu’il est important de laisser ces tutoriels à la disposition des développeurs qui recherchent encore des conseils pour développer des applications sur ces appareils.As such, we feel it is important to leave these tutorials in place for developers who are still looking for guidance in developing for those devices. Notez que ces tutoriels ne sont pas mis à jour avec les derniers ensembles d’outils ou interactions utilisés pour HoloLens 2.These tutorials will not be updated with the latest toolsets or interactions being used for HoloLens 2. Ils sont fournis dans le but de fonctionner sur les appareils pris en charge.They will be maintained to continue working on the supported devices. Une nouvelle série de didacticiels sera publiée à l’avenir qui vous montrera comment développer pour HoloLens 2.There will be a new series of tutorials that will be posted in the future that will demonstrate how to develop for HoloLens 2. Cet avis sera mis à jour avec un lien vers ces didacticiels lors de leur publication.This notice will be updated with a link to those tutorials when they are posted.


Dans ce cours, vous allez apprendre à reconnaître le contenu visuel personnalisé et sa position spatiale dans une image fournie, en utilisant les fonctionnalités de « détection d’objets » d’Azure Custom Vision dans une application de réalité mixte.In this course, you will learn how to recognize custom visual content and its spatial position within a provided image, using Azure Custom Vision "Object Detection" capabilities in a mixed reality application.

Ce service vous permet d’effectuer l’apprentissage d’un modèle de Machine Learning à l’aide d’images d’objet.This service will allow you to train a machine learning model using object images. Vous allez ensuite utiliser le modèle formé pour reconnaître des objets similaires et se rapprocher de leur emplacement dans le monde réel, tel que fourni par la capture de l’appareil photo de Microsoft HoloLens ou d’une caméra qui se connecte à un PC pour les casques immersifs (VR).You will then use the trained model to recognize similar objects and approximate their location in the real world, as provided by the camera capture of Microsoft HoloLens or a camera connect to a PC for immersive (VR) headsets.

résultat du cours

Azure Custom vision, la détection d’objets est un service Microsoft qui permet aux développeurs de créer des classifieurs d’images personnalisées.Azure Custom Vision, Object Detection is a Microsoft Service which allows developers to build custom image classifiers. Ces classifieurs peuvent ensuite être utilisés avec de nouvelles images pour détecter des objets dans cette nouvelle image, en fournissant des limites de zone au sein de l’image elle-même.These classifiers can then be used with new images to detect objects within that new image, by providing Box Boundaries within the image itself. Le service fournit un portail en ligne simple et facile à utiliser pour simplifier ce processus.The Service provides a simple, easy to use, online portal to streamline this process. Pour plus d’informations, consultez les liens suivants :For more information, visit the following links:

À la fin de ce cours, vous disposerez d’une application de réalité mixte qui sera en mesure d’effectuer les opérations suivantes :Upon completion of this course, you will have a mixed reality application which will be able to do the following:

  1. L’utilisateur sera en mesure de pointer vers un objet, qu’il a formé à l’aide du service vision personnalisée Azure, détection d’objet.The user will be able to gaze at an object, which they have trained using the Azure Custom Vision Service, Object Detection.
  2. L’utilisateur utilise le geste Tap pour capturer une image de ce qu’il examine.The user will use the Tap gesture to capture an image of what they are looking at.
  3. L’application enverra l’image à la Service Vision personnalisée Azure.The app will send the image to the Azure Custom Vision Service.
  4. Il y aura une réponse du service qui affichera le résultat de la reconnaissance sous forme de texte d’espace universel.There will be a reply from the Service which will display the result of the recognition as world-space text. Pour ce faire, vous devez utiliser le suivi spatial de Microsoft HoloLens, afin de comprendre la position universelle de l’objet reconnu, puis l’utilisation de la balise associée à ce qui est détecté dans l’image, pour fournir le texte de l’étiquette.This will be accomplished through utilizing the Microsoft HoloLens' Spatial Tracking, as a way of understanding the world position of the recognized object, and then using the Tag associated with what is detected in the image, to provide the label text.

Ce cours couvre également le chargement manuel d’images, la création de balises et l’apprentissage du service, pour reconnaître différents objets (dans l’exemple fourni, une tasse), en définissant la zone limite dans l’image que vous envoyez.The course will also cover manually uploading images, creating tags, and training the Service, to recognize different objects (in the provided example, a cup), by setting the Boundary Box within the image you submit.

Important

À la suite de la création et de l’utilisation de l’application, le développeur doit revenir à la Service Vision personnalisée Azure, identifier les prédictions effectuées par le service et déterminer si elles sont correctes ou non (en marquant tout ce que le service a manqué et en ajustant les zones englobantes).Following the creation and use of the app, the developer should navigate back to the Azure Custom Vision Service, and identify the predictions made by the Service, and determine whether they were correct or not (through tagging anything the Service missed, and adjusting the Bounding Boxes). Le service peut ensuite être réformé, ce qui augmente la probabilité qu’il reconnaisse les objets réels.The Service can then be re-trained, which will increase the likelihood of it recognizing real world objects.

Ce cours vous apprend à obtenir les résultats de la Service Vision personnalisée Azure, la détection d’objets, dans un exemple d’application à base d’Unity.This course will teach you how to get the results from the Azure Custom Vision Service, Object Detection, into a Unity-based sample application. Il vous faudra appliquer ces concepts à une application personnalisée que vous pouvez générer.It will be up to you to apply these concepts to a custom application you might be building.

Prise en charge des appareilsDevice support

CoursCourse HoloLensHoloLens Casques immersifsImmersive headsets
Réalité mixte - Azure - Cours 310 : Détection d’objetsMR and Azure 310: Object detection ✔️✔️

PrérequisPrerequisites

Notes

Ce didacticiel est conçu pour les développeurs qui ont une expérience de base avec Unity et C#.This tutorial is designed for developers who have basic experience with Unity and C#. Sachez également que les conditions préalables et les instructions écrites dans ce document représentent les éléments qui ont été testés et vérifiés au moment de la rédaction (juillet 2018).Please also be aware that the prerequisites and written instructions within this document represent what has been tested and verified at the time of writing (July 2018). Vous êtes libre d’utiliser le logiciel le plus récent, tel qu’indiqué dans l’article installer les outils , bien qu’il ne soit pas supposé que les informations de ce cours correspondent parfaitement à ce que vous trouverez dans les logiciels plus récents que ceux répertoriés ci-dessous.You are free to use the latest software, as listed within the install the tools article, though it should not be assumed that the information in this course will perfectly match what you will find in newer software than what is listed below.

Nous vous recommandons d’utiliser le matériel et les logiciels suivants pour ce cours :We recommend the following hardware and software for this course:

Avant de commencerBefore you start

  1. Pour éviter de rencontrer des problèmes lors de la création de ce projet, il est fortement recommandé de créer le projet mentionné dans ce didacticiel dans un dossier racine ou dans un dossier racine (les chemins de dossiers longs peuvent entraîner des problèmes au moment de la génération).To avoid encountering issues building this project, it is strongly suggested that you create the project mentioned in this tutorial in a root or near-root folder (long folder paths can cause issues at build-time).
  2. Configurez et testez votre HoloLens.Set up and test your HoloLens. Si vous avez besoin de la prise en charge de la configuration de votre HoloLens, consultez l’article Configuration de hololens.If you need support setting up your HoloLens, make sure to visit the HoloLens setup article.
  3. Il est judicieux d’effectuer un réglage de l’étalonnage et du capteur au début du développement d’une nouvelle application HoloLens (parfois, il peut être utile d’effectuer ces tâches pour chaque utilisateur).It is a good idea to perform Calibration and Sensor Tuning when beginning developing a new HoloLens App (sometimes it can help to perform those tasks for each user).

Pour obtenir de l’aide sur l’étalonnage, veuillez suivre ce lien vers l’article d’étalonnage HoloLens.For help on Calibration, please follow this link to the HoloLens Calibration article.

Pour obtenir de l’aide sur le réglage du capteur, veuillez suivre ce lien vers l’article sur le paramétrage du capteur HoloLens.For help on Sensor Tuning, please follow this link to the HoloLens Sensor Tuning article.

Chapitre 1-portail Custom VisionChapter 1 - The Custom Vision Portal

Pour utiliser le service vision personnalisée Azure, vous devez configurer une instance de ce dernier pour qu’il soit mis à la disposition de votre application.To use the Azure Custom Vision Service, you will need to configure an instance of it to be made available to your application.

  1. Accédez à la page principale de service vision personnalisée.Navigate to the Custom Vision Service main page.

  2. Cliquez sur prise en main.Click on Getting Started.

  3. Connectez-vous au portail Custom Vision.Sign in to the Custom Vision Portal.

  4. Si vous n’avez pas encore de compte Azure, vous devez en créer un.If you do not already have an Azure account, you will need to create one. Si vous suivez ce didacticiel dans une situation de classe ou de laboratoire, demandez à votre formateur ou à l’un des prostructors de vous aider à configurer votre nouveau compte.If you are following this tutorial in a classroom or lab situation, ask your instructor or one of the proctors for help setting up your new account.

  5. Une fois que vous êtes connecté pour la première fois, vous êtes invité à entrer dans le panneau des conditions d’accès .Once you are logged in for the first time, you will be prompted with the Terms of Service panel. Cochez la case pour accepter les termes du contrat.Click the checkbox to agree to the terms. Cliquez ensuite sur J’accepte.Then click I agree.

  6. Après avoir accepté les termes, vous vous trouvez maintenant dans la section mes projets .Having agreed to the terms, you are now in the My Projects section. Cliquez sur nouveau projet.Click on New Project.

  7. Un onglet s’affiche sur le côté droit, ce qui vous invite à spécifier certains champs pour le projet.A tab will appear on the right-hand side, which will prompt you to specify some fields for the project.

    1. Insérer un nom pour votre projetInsert a name for your project

    2. Insérer une description pour votre projet (facultatif)Insert a description for your project (Optional)

    3. Choisissez un groupe de ressources ou créez-en un.Choose a Resource Group or create a new one. Un groupe de ressources permet de surveiller, de contrôler l’accès, de configurer et de gérer la facturation d’un regroupement de ressources Azure.A resource group provides a way to monitor, control access, provision and manage billing for a collection of Azure assets. Il est recommandé de conserver tous les services Azure associés à un seul projet (par exemple, ces cours) dans un groupe de ressources commun.It is recommended to keep all the Azure services associated with a single project (e.g. such as these courses) under a common resource group).

    4. Définissez les types de projets en tant que détection d’objet ( préversion).Set the Project Types as Object Detection (preview).

  8. Une fois que vous avez terminé, cliquez sur créer un projet. vous êtes alors redirigé vers la page du projet service vision personnalisée.Once you are finished, click on Create project, and you will be redirected to the Custom Vision Service project page.

Chapitre 2-formation de votre projet Custom VisionChapter 2 - Training your Custom Vision project

Une fois dans le portail Custom Vision, votre objectif principal est de former votre projet à la reconnaissance d’objets spécifiques dans les images.Once in the Custom Vision Portal, your primary objective is to train your project to recognize specific objects in images.

Vous avez besoin d’au moins quinze (15) images pour chaque objet que vous souhaitez que votre application reconnaisse.You need at least fifteen (15) images for each object that you would like your application to recognize. Vous pouvez utiliser les images fournies avec ce cours (une série de tasses).You can use the images provided with this course (a series of cups).

Pour former votre projet Custom Vision :To train your Custom Vision project:

  1. Cliquez sur le + bouton en regard de balises.Click on the + button next to Tags.

  2. Ajoutez un nom pour la balise qui sera utilisée pour associer vos images à.Add a name for the tag that will be used to associate your images with. Dans cet exemple, nous utilisons des images de tasses pour la reconnaissance, vous avez donc nommé la balise pour This, Cup.In this example we are using images of cups for recognition, so have named the tag for this, Cup. Cliquez sur Enregistrer une fois l’opération terminée.Click Save once finished.

  3. Vous remarquerez que votre balise a été ajoutée (vous devrez peut-être recharger votre page pour qu’elle apparaisse).You will notice your Tag has been added (you may need to reload your page for it to appear).

  4. Cliquez sur Ajouter des images au centre de la page.Click on Add images in the center of the page.

  5. Cliquez sur Parcourir les fichiers locaux et recherchez les images que vous souhaitez télécharger pour un objet, avec un minimum de 15 (15).Click on Browse local files, and browse to the images you would like to upload for one object, with the minimum being fifteen (15).

    Conseil

    Vous pouvez sélectionner plusieurs images à la fois, à télécharger.You can select several images at a time, to upload.

  6. Appuyez sur charger les fichiers une fois que vous avez sélectionné toutes les images avec lesquelles vous souhaitez former le projet.Press Upload files once you have selected all the images you would like to train the project with. Le chargement des fichiers va commencer.The files will begin uploading. Une fois que vous avez confirmé le téléchargement, cliquez sur terminé.Once you have confirmation of the upload, click Done.

  7. À ce stade, vos images sont chargées, mais elles ne sont pas marquées.At this point your images are uploaded, but not tagged.

  8. Pour baliser vos images, utilisez la souris.To tag your images, use your mouse. Au fur et à mesure que vous pointez sur votre image, une sélection de sélection vous aidera en dessinant automatiquement une sélection autour de votre objet.As you hover over your image, a selection highlight will aid you by automatically drawing a selection around your object. Si ce n’est pas exact, vous pouvez créer les vôtres.If it is not accurate, you can draw your own. Pour ce faire, vous devez maintenir le bouton gauche de la souris enfoncé et faire glisser la région de sélection pour englober votre objet.This is accomplished by holding left-click on the mouse, and dragging the selection region to encompass your object.

  9. Après la sélection de votre objet dans l’image, une petite invite vous demande d’ajouter une balise de région.Following the selection of your object within the image, a small prompt will ask for you to Add Region Tag. Sélectionnez votre balise créée précédemment (« Cup », dans l’exemple ci-dessus), ou si vous ajoutez d’autres balises, tapez ce qui se trouve dans et cliquez sur le bouton + (plus) .Select your previously created tag ('Cup', in the above example), or if you are adding more tags, type that in and click the + (plus) button.

  10. Pour baliser l’image suivante, vous pouvez cliquer sur la flèche à droite du panneau ou fermer le panneau des balises (en cliquant sur le X dans le coin supérieur droit du panneau), puis sur l’image suivante.To tag the next image, you can click the arrow to the right of the blade, or close the tag blade (by clicking the X in the top-right corner of the blade) and then click the next image. Une fois que l’image suivante est prête, répétez la même procédure.Once you have the next image ready, repeat the same procedure. Procédez ainsi pour toutes les images que vous avez chargées, jusqu’à ce qu’elles soient toutes marquées.Do this for all the images you have uploaded, until they are all tagged.

    Notes

    Vous pouvez sélectionner plusieurs objets dans la même image, comme l’image ci-dessous :You can select several objects in the same image, like the image below:

  11. Une fois que vous les avez balisées, cliquez sur le bouton balisé , à gauche de l’écran, pour afficher les images avec balises.Once you have tagged them all, click on the tagged button, on the left of the screen, to reveal the tagged images.

  12. Vous êtes maintenant prêt à former votre service.You are now ready to train your Service. Cliquez sur le bouton former et la première itération d’apprentissage commencera.Click the Train button, and the first training iteration will begin.

  13. Une fois qu’elle est créée, vous pouvez voir deux boutons nommés créer par défaut et URL de prédiction.Once it is built, you will be able to see two buttons called Make default and Prediction URL. Cliquez d’abord sur créer par défaut , puis sur URL de prédiction.Click on Make default first, then click on Prediction URL.

    Notes

    Le point de terminaison fourni à partir de ce est défini sur l' itération qui a été marquée comme valeur par défaut.The endpoint which is provided from this, is set to whichever Iteration has been marked as default. Par conséquent, si vous effectuez ultérieurement une nouvelle itération et la mettez à jour par défaut, vous n’aurez pas besoin de modifier votre code.As such, if you later make a new Iteration and update it as default, you will not need to change your code.

  14. Une fois que vous avez cliqué sur URL de prédiction, ouvrez le bloc-notes, puis copiez et collez l' URL (également appelée « prédiction-point de terminaison ») et la clé de prédiction de service, afin de pouvoir la récupérer lorsque vous en aurez besoin ultérieurement dans le code.Once you have clicked on Prediction URL, open Notepad, and copy and paste the URL (also called your Prediction-Endpoint) and the Service Prediction-Key, so that you can retrieve it when you need it later in the code.

Chapitre 3-configurer le projet UnityChapter 3 - Set up the Unity project

Ce qui suit est une configuration classique pour le développement avec une réalité mixte, et, par conséquent, est un bon modèle pour d’autres projets.The following is a typical set up for developing with mixed reality, and as such, is a good template for other projects.

  1. Ouvrez Unity et cliquez sur nouveau.Open Unity and click New.

  2. Vous devez maintenant fournir un nom de projet Unity.You will now need to provide a Unity project name. Insérez CustomVisionObjDetection.Insert CustomVisionObjDetection. Assurez-vous que le type de projet est défini sur 3D et définissez l’emplacement sur un emplacement approprié (n’oubliez pas que les répertoires racine sont plus proches).Make sure the project type is set to 3D, and set the Location to somewhere appropriate for you (remember, closer to root directories is better). Ensuite, cliquez sur créer un projet.Then, click Create project.

  3. Si Unity est ouvert, il est conseillé de vérifier que l' éditeur de script par défaut est défini sur Visual Studio.With Unity open, it is worth checking the default Script Editor is set to Visual Studio. Accédez à modifier les > Préférences , puis à partir de la nouvelle fenêtre, accédez à outils externes.Go to Edit > Preferences and then from the new window, navigate to External Tools. Remplacez l' éditeur de script externe par Visual Studio.Change External Script Editor to Visual Studio. Fermez la fenêtre Préférences .Close the Preferences window.

  4. Accédez ensuite à fichier > paramètres de build et basculez la plateforme sur plateforme Windows universelle, puis cliquez sur le bouton changer de plateforme .Next, go to File > Build Settings and switch the Platform to Universal Windows Platform, and then clicking on the Switch Platform button.

  5. Dans la même fenêtre paramètres de build , vérifiez que les éléments suivants sont définis :In the same Build Settings window, ensure the following are set:

    1. L' appareil cible est défini sur HoloLensTarget Device is set to HoloLens

    2. Le type de build est D3DBuild Type is set to D3D

    3. Le SDK est configuré sur le dernier installéSDK is set to Latest installed

    4. Version de Visual Studio définie sur le dernier installéVisual Studio Version is set to Latest installed

    5. La génération et l’exécution sont définies sur l' ordinateur localBuild and Run is set to Local Machine

    6. Les paramètres restants, dans paramètres de build, doivent être laissés par défaut pour le moment.The remaining settings, in Build Settings, should be left as default for now.

  6. Dans la même fenêtre paramètres de build , cliquez sur le bouton Paramètres du lecteur pour ouvrir le panneau correspondant dans l’espace où se trouve l' inspecteur .In the same Build Settings window, click on the Player Settings button, this will open the related panel in the space where the Inspector is located.

  7. Dans ce volet, quelques paramètres doivent être vérifiés :In this panel, a few settings need to be verified:

    1. Sous l’onglet autres paramètres :In the Other Settings tab:

      1. La version du runtime de script doit être expérimentale (.net 4,6 équivalent), ce qui déclenche la nécessité de redémarrer l’éditeur.Scripting Runtime Version should be Experimental (.NET 4.6 Equivalent), which will trigger a need to restart the Editor.

      2. Le serveur principal de script doit être .net.Scripting Backend should be .NET.

      3. Le niveau de compatibilité de l’API doit être .net 4,6.API Compatibility Level should be .NET 4.6.

    2. Dans l’onglet paramètres de publication , sous fonctionnalités, activez la case à cocher :Within the Publishing Settings tab, under Capabilities, check:

      1. InternetClientInternetClient

      2. WebcamWebcam

      3. SpatialPerceptionSpatialPerception

    3. Plus bas dans le volet, dans les paramètres XR (situés sous paramètres de publication), cochez la réalité virtuelle prise en charge, puis assurez-vous que le Kit de développement logiciel (SDK) Windows Mixed Reality est ajouté.Further down the panel, in XR Settings (found below Publish Settings), tick Virtual Reality Supported, then make sure the Windows Mixed Reality SDK is added.

  8. De retour dans les paramètres de build, les # projets Unity C ne sont plus grisés : cochez la case en regard de celle-ci.Back in Build Settings, Unity C# Projects is no longer greyed out: tick the checkbox next to this.

  9. Fermez la fenêtre Build Settings.Close the Build Settings window.

  10. Dans l' éditeur, cliquez sur modifier les > paramètres du projet > graphiques.In the Editor, click on Edit > Project Settings > Graphics.

  11. Dans le panneau Inspecteur , les paramètres graphiques sont ouverts.In the Inspector Panel the Graphics Settings will be open. Faites défiler jusqu’à ce que vous voyez un tableau appelé toujours inclure des nuanceurs.Scroll down until you see an array called Always Include Shaders. Ajoutez un emplacement en accroissant la variable de taille d’une unité (dans cet exemple, il s’agissait de 8, nous l’avons rendu 9).Add a slot by increasing the Size variable by one (in this example, it was 8 so we made it 9). Un nouvel emplacement s’affiche à la dernière position du tableau, comme indiqué ci-dessous :A new slot will appear, in the last position of the array, as shown below:

  12. Dans l’emplacement, cliquez sur le petit cercle cible en regard de l’emplacement pour ouvrir une liste de nuanceurs.In the slot, click on the small target circle next to the slot to open a list of shaders. Recherchez les nuanceurs hérités/transparent/diffus , puis double-cliquez dessus.Look for the Legacy Shaders/Transparent/Diffuse shader and double-click it.

Chapitre 4-importation du package CustomVisionObjDetection UnityChapter 4 - Importing the CustomVisionObjDetection Unity package

Pour ce cours, vous disposez d’un package d’actifs Unity appelé Azure-Mr-310. pour Unity.For this course you are provided with a Unity Asset Package called Azure-MR-310.unitypackage.

ACCÉLÉRATRICE Tous les objets pris en charge par Unity, y compris les scènes entières, peuvent être empaquetés dans un fichier . pour Unity , et exportés/importés dans d’autres projets.[TIP] Any objects supported by Unity, including entire scenes, can be packaged into a .unitypackage file, and exported / imported in other projects. Il s’agit du moyen le plus sûr et le plus efficace de déplacer des éléments multimédias entre différents projets Unity.It is the safest, and most efficient, way to move assets between different Unity projects.

Vous trouverez le package Azure-Mr-310 que vous devez télécharger ici.You can find the Azure-MR-310 package that you need to download here.

  1. Avec le tableau de bord Unity devant vous, cliquez sur ressources dans le menu en haut de l’écran, puis sur importer le package > package personnalisé.With the Unity dashboard in front of you, click on Assets in the menu at the top of the screen, then click on Import Package > Custom Package.

  2. Utilisez le sélecteur de fichiers pour sélectionner le package Azure-Mr-310. pour Unity , puis cliquez sur ouvrir.Use the file picker to select the Azure-MR-310.unitypackage package and click Open. La liste des composants de cet élément multimédia vous est présentée.A list of components for this asset will be displayed to you. Confirmez l’importation en cliquant sur le bouton Importer .Confirm the import by clicking the Import button.

  3. Une fois l’importation terminée, vous remarquerez que les dossiers du package ont été ajoutés à votre dossier de ressources .Once it has finished importing, you will notice that folders from the package have now been added to your Assets folder. Ce type de structure de dossiers est courant pour un projet Unity.This kind of folder structure is typical for a Unity project.

    1. Le dossier Materials contient les éléments utilisés par le curseur en regard.The Materials folder contains the material used by the Gaze Cursor.

    2. Le dossier plug-ins contient la dll Newtonsoft utilisée par le code pour désérialiser la réponse Web du service.The Plugins folder contains the Newtonsoft DLL used by the code to deserialize the Service web response. Les deux (2) différentes versions contenues dans le dossier, et sous-dossier, sont nécessaires pour permettre à la bibliothèque d’être utilisée et construite à la fois par l’éditeur Unity et par la build UWP.The two (2) different versions contained in the folder, and sub-folder, are necessary to allow the library to be used and built by both the Unity Editor and the UWP build.

    3. Le dossier Prefabs contient le Prefabs contenu dans la scène.The Prefabs folder contains the prefabs contained in the scene. Il s’agit des suivants :Those are:

      1. GazeCursor, le curseur utilisé dans l’application.The GazeCursor, the cursor used in the application. Fonctionne conjointement avec le Prefab SpatialMapping pour pouvoir être placé dans la scène au-dessus des objets physiques.Will work together with the SpatialMapping prefab to be able to be placed in the scene on top of physical objects.
      2. L' étiquette, qui est l’objet d’interface utilisateur utilisé pour afficher la balise d’objet dans la scène, si nécessaire.The Label, which is the UI object used to display the object tag in the scene when required.
      3. SpatialMapping, qui est l’objet qui permet à l’application d’utiliser la création d’une carte virtuelle, à l’aide du suivi spatial Microsoft HoloLens.The SpatialMapping, which is the object that enables the application to use create a virtual map, using the Microsoft HoloLens' spatial tracking.
    4. Le dossier scenes qui contient actuellement la scène prédéfinie pour ce cours.The Scenes folder which currently contains the pre-built scene for this course.

  4. Ouvrez le dossier scenes , dans le panneau Projet, puis double-cliquez sur ObjDetectionScene pour charger la scène que vous allez utiliser pour ce cours.Open the Scenes folder, in the Project Panel, and double-click on the ObjDetectionScene, to load the scene that you will use for this course.

    Notes

    Aucun code n’est inclus, vous allez écrire le code en suivant ce cours.No code is included, you will write the code by following this course.

Chapitre 5-créer la classe CustomVisionAnalyser.Chapter 5 - Create the CustomVisionAnalyser class.

À ce stade, vous êtes prêt à écrire du code.At this point you are ready to write some code. Vous allez commencer par la classe CustomVisionAnalyser .You will begin with the CustomVisionAnalyser class.

Notes

Les appels à la service vision personnalisée, effectués dans le code ci-dessous, sont effectués à l’aide de l' API REST Custom vision.The calls to the Custom Vision Service, made in the code shown below, are made using the Custom Vision REST API. À l’aide de ce guide, vous allez apprendre à implémenter et à utiliser cette API (utile pour comprendre comment implémenter des éléments similaires).Through using this, you will see how to implement and make use of this API (useful for understanding how to implement something similar on your own). Sachez que Microsoft propose un Kit de développement logiciel (SDK) Custom vision qui peut également être utilisé pour effectuer des appels au service.Be aware, that Microsoft offers a Custom Vision SDK that can also be used to make calls to the Service. Pour plus d’informations, consultez l' article du kit de développement logiciel (SDK) Custom vision.For more information visit the Custom Vision SDK article.

Cette classe est chargée des opérations suivantes :This class is responsible for:

  • Chargement de la dernière image capturée sous la forme d’un tableau d’octets.Loading the latest image captured as an array of bytes.

  • Envoi du tableau d’octets à votre instance Azure service vision personnalisée à des fins d’analyse.Sending the byte array to your Azure Custom Vision Service instance for analysis.

  • Réception de la réponse sous forme de chaîne JSON.Receiving the response as a JSON string.

  • La désérialisation de la réponse et le passage de la prédiction résultante à la classe SceneOrganiser , qui s’occupera du mode d’affichage de la réponse.Deserializing the response and passing the resulting Prediction to the SceneOrganiser class, which will take care of how the response should be displayed.

Pour créer cette classe :To create this class:

  1. Cliquez avec le bouton droit sur le dossier Asset, situé dans le panneau Projet, puis cliquez sur créer un > dossier.Right-click in the Asset Folder, located in the Project Panel, then click Create > Folder. Appelez le dossier scripts.Call the folder Scripts.

  2. Double-cliquez sur le dossier nouvellement créé pour l’ouvrir.Double-click on the newly created folder, to open it.

  3. Cliquez avec le bouton droit dans le dossier, puis cliquez sur créer un > # script C.Right-click inside the folder, then click Create > C# Script. Nommez le script CustomVisionAnalyser.Name the script CustomVisionAnalyser.

  4. Double-cliquez sur le nouveau script CustomVisionAnalyser pour l’ouvrir avec Visual Studio.Double-click on the new CustomVisionAnalyser script to open it with Visual Studio.

  5. Assurez-vous que les espaces de noms suivants sont référencés en haut du fichier :Make sure you have the following namespaces referenced at the top of the file:

    using Newtonsoft.Json;
    using System.Collections;
    using System.IO;
    using UnityEngine;
    using UnityEngine.Networking;
    
  6. Dans la classe CustomVisionAnalyser , ajoutez les variables suivantes :In the CustomVisionAnalyser class, add the following variables:

        /// <summary>
        /// Unique instance of this class
        /// </summary>
        public static CustomVisionAnalyser Instance;
    
        /// <summary>
        /// Insert your prediction key here
        /// </summary>
        private string predictionKey = "- Insert your key here -";
    
        /// <summary>
        /// Insert your prediction endpoint here
        /// </summary>
        private string predictionEndpoint = "Insert your prediction endpoint here";
    
        /// <summary>
        /// Bite array of the image to submit for analysis
        /// </summary>
        [HideInInspector] public byte[] imageBytes;
    

    Notes

    Veillez à insérer votre clé de prédiction de service dans la variable PredictionKey et votre point de terminaison de prédiction dans la variable predictionEndpoint .Make sure you insert your Service Prediction-Key into the predictionKey variable and your Prediction-Endpoint into the predictionEndpoint variable. Vous les avez copiées précédemment dans le bloc-notes, au chapitre 2, étape 14.You copied these to Notepad earlier, in Chapter 2, Step 14.

  7. Le code pour éveillé () doit maintenant être ajouté pour initialiser la variable d’instance :Code for Awake() now needs to be added to initialize the Instance variable:

        /// <summary>
        /// Initializes this class
        /// </summary>
        private void Awake()
        {
            // Allows this instance to behave like a singleton
            Instance = this;
        }
    
  8. Ajoutez la Coroutine (avec la méthode statique GetImageAsByteArray () en dessous de celle-ci), qui obtiendra les résultats de l’analyse de l’image, capturés par la classe ImageCapture .Add the coroutine (with the static GetImageAsByteArray() method below it), which will obtain the results of the analysis of the image, captured by the ImageCapture class.

    Notes

    Dans la Coroutine AnalyseImageCapture , il y a un appel à la classe SceneOrganiser que vous êtes encore en cours de création.In the AnalyseImageCapture coroutine, there is a call to the SceneOrganiser class that you are yet to create. Par conséquent, laissez ces lignes commentées pour l’instant.Therefore, leave those lines commented for now.

        /// <summary>
        /// Call the Computer Vision Service to submit the image.
        /// </summary>
        public IEnumerator AnalyseLastImageCaptured(string imagePath)
        {
            Debug.Log("Analyzing...");
    
            WWWForm webForm = new WWWForm();
    
            using (UnityWebRequest unityWebRequest = UnityWebRequest.Post(predictionEndpoint, webForm))
            {
                // Gets a byte array out of the saved image
                imageBytes = GetImageAsByteArray(imagePath);
    
                unityWebRequest.SetRequestHeader("Content-Type", "application/octet-stream");
                unityWebRequest.SetRequestHeader("Prediction-Key", predictionKey);
    
                // The upload handler will help uploading the byte array with the request
                unityWebRequest.uploadHandler = new UploadHandlerRaw(imageBytes);
                unityWebRequest.uploadHandler.contentType = "application/octet-stream";
    
                // The download handler will help receiving the analysis from Azure
                unityWebRequest.downloadHandler = new DownloadHandlerBuffer();
    
                // Send the request
                yield return unityWebRequest.SendWebRequest();
    
                string jsonResponse = unityWebRequest.downloadHandler.text;
    
                Debug.Log("response: " + jsonResponse);
    
                // Create a texture. Texture size does not matter, since
                // LoadImage will replace with the incoming image size.
                //Texture2D tex = new Texture2D(1, 1);
                //tex.LoadImage(imageBytes);
                //SceneOrganiser.Instance.quadRenderer.material.SetTexture("_MainTex", tex);
    
                // The response will be in JSON format, therefore it needs to be deserialized
                //AnalysisRootObject analysisRootObject = new AnalysisRootObject();
                //analysisRootObject = JsonConvert.DeserializeObject<AnalysisRootObject>(jsonResponse);
    
                //SceneOrganiser.Instance.FinaliseLabel(analysisRootObject);
            }
        }
    
        /// <summary>
        /// Returns the contents of the specified image file as a byte array.
        /// </summary>
        static byte[] GetImageAsByteArray(string imageFilePath)
        {
            FileStream fileStream = new FileStream(imageFilePath, FileMode.Open, FileAccess.Read);
    
            BinaryReader binaryReader = new BinaryReader(fileStream);
    
            return binaryReader.ReadBytes((int)fileStream.Length);
        }
    
  9. Supprimez les méthodes Start () et Update () , car elles ne seront pas utilisées.Delete the Start() and Update() methods, as they will not be used.

  10. Veillez à enregistrer vos modifications dans Visual Studio avant de revenir à Unity.Be sure to save your changes in Visual Studio, before returning to Unity.

Important

Comme mentionné précédemment, ne vous inquiétez pas du code qui peut sembler avoir une erreur, car vous fournirez bientôt d’autres classes, ce qui les corrigera.As mentioned earlier, do not worry about code which might appear to have an error, as you will provide further classes soon, which will fix these.

Chapitre 6-créer la classe CustomVisionObjectsChapter 6 - Create the CustomVisionObjects class

La classe que vous allez créer est maintenant la classe CustomVisionObjects .The class you will create now is the CustomVisionObjects class.

Ce script contient un certain nombre d’objets utilisés par d’autres classes pour sérialiser et désérialiser les appels passés au Service Vision personnalisée.This script contains a number of objects used by other classes to serialize and deserialize the calls made to the Custom Vision Service.

Pour créer cette classe :To create this class:

  1. Cliquez avec le bouton droit dans le dossier scripts , puis cliquez sur créer un > # script C.Right-click inside the Scripts folder, then click Create > C# Script. Appelez le script CustomVisionObjects.Call the script CustomVisionObjects.

  2. Double-cliquez sur le nouveau script CustomVisionObjects pour l’ouvrir avec Visual Studio.Double-click on the new CustomVisionObjects script to open it with Visual Studio.

  3. Assurez-vous que les espaces de noms suivants sont référencés en haut du fichier :Make sure you have the following namespaces referenced at the top of the file:

    using System;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.Networking;
    
  4. Supprimez les méthodes Start () et Update () à l’intérieur de la classe CustomVisionObjects , cette classe doit maintenant être vide.Delete the Start() and Update() methods inside the CustomVisionObjects class, this class should now be empty.

    Avertissement

    Il est important de suivre attentivement l’instruction suivante.It is important you follow the next instruction carefully. Si vous placez les nouvelles déclarations de classe à l’intérieur de la classe CustomVisionObjects , vous obtiendrez des erreurs de compilation dans le chapitre 10, indiquant que AnalysisRootObject et BoundingBox sont introuvables.If you put the new class declarations inside the CustomVisionObjects class, you will get compile errors in chapter 10, stating that AnalysisRootObject and BoundingBox are not found.

  5. Ajoutez les classes suivantes en dehors de la classe CustomVisionObjects .Add the following classes outside the CustomVisionObjects class. Ces objets sont utilisés par la bibliothèque Newtonsoft pour sérialiser et désérialiser les données de réponse :These objects are used by the Newtonsoft library to serialize and deserialize the response data:

    // The objects contained in this script represent the deserialized version
    // of the objects used by this application 
    
    /// <summary>
    /// Web request object for image data
    /// </summary>
    class MultipartObject : IMultipartFormSection
    {
        public string sectionName { get; set; }
    
        public byte[] sectionData { get; set; }
    
        public string fileName { get; set; }
    
        public string contentType { get; set; }
    }
    
    /// <summary>
    /// JSON of all Tags existing within the project
    /// contains the list of Tags
    /// </summary> 
    public class Tags_RootObject
    {
        public List<TagOfProject> Tags { get; set; }
        public int TotalTaggedImages { get; set; }
        public int TotalUntaggedImages { get; set; }
    }
    
    public class TagOfProject
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public int ImageCount { get; set; }
    }
    
    /// <summary>
    /// JSON of Tag to associate to an image
    /// Contains a list of hosting the tags,
    /// since multiple tags can be associated with one image
    /// </summary> 
    public class Tag_RootObject
    {
        public List<Tag> Tags { get; set; }
    }
    
    public class Tag
    {
        public string ImageId { get; set; }
        public string TagId { get; set; }
    }
    
    /// <summary>
    /// JSON of images submitted
    /// Contains objects that host detailed information about one or more images
    /// </summary> 
    public class ImageRootObject
    {
        public bool IsBatchSuccessful { get; set; }
        public List<SubmittedImage> Images { get; set; }
    }
    
    public class SubmittedImage
    {
        public string SourceUrl { get; set; }
        public string Status { get; set; }
        public ImageObject Image { get; set; }
    }
    
    public class ImageObject
    {
        public string Id { get; set; }
        public DateTime Created { get; set; }
        public int Width { get; set; }
        public int Height { get; set; }
        public string ImageUri { get; set; }
        public string ThumbnailUri { get; set; }
    }
    
    /// <summary>
    /// JSON of Service Iteration
    /// </summary> 
    public class Iteration
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public bool IsDefault { get; set; }
        public string Status { get; set; }
        public string Created { get; set; }
        public string LastModified { get; set; }
        public string TrainedAt { get; set; }
        public string ProjectId { get; set; }
        public bool Exportable { get; set; }
        public string DomainId { get; set; }
    }
    
    /// <summary>
    /// Predictions received by the Service
    /// after submitting an image for analysis
    /// Includes Bounding Box
    /// </summary>
    public class AnalysisRootObject
    {
        public string id { get; set; }
        public string project { get; set; }
        public string iteration { get; set; }
        public DateTime created { get; set; }
        public List<Prediction> predictions { get; set; }
    }
    
    public class BoundingBox
    {
        public double left { get; set; }
        public double top { get; set; }
        public double width { get; set; }
        public double height { get; set; }
    }
    
    public class Prediction
    {
        public double probability { get; set; }
        public string tagId { get; set; }
        public string tagName { get; set; }
        public BoundingBox boundingBox { get; set; }
    }
    
  6. Veillez à enregistrer vos modifications dans Visual Studio avant de revenir à Unity.Be sure to save your changes in Visual Studio, before returning to Unity.

Chapitre 7-créer la classe SpatialMappingChapter 7 - Create the SpatialMapping class

Cette classe définit le conflit de mappage spatial dans la scène afin de pouvoir détecter les collisions entre les objets virtuels et les objets réels.This class will set the Spatial Mapping Collider in the scene so to be able to detect collisions between virtual objects and real objects.

Pour créer cette classe :To create this class:

  1. Cliquez avec le bouton droit dans le dossier scripts , puis cliquez sur créer un > # script C.Right-click inside the Scripts folder, then click Create > C# Script. Appelez le script SpatialMapping.Call the script SpatialMapping.

  2. Double-cliquez sur le nouveau script SpatialMapping pour l’ouvrir avec Visual Studio.Double-click on the new SpatialMapping script to open it with Visual Studio.

  3. Assurez-vous que les espaces de noms suivants sont référencés au-dessus de la classe SpatialMapping :Make sure you have the following namespaces referenced above the SpatialMapping class:

    using UnityEngine;
    using UnityEngine.XR.WSA;
    
  4. Ensuite, ajoutez les variables suivantes à l’intérieur de la classe SpatialMapping , au-dessus de la méthode Start () :Then, add the following variables inside the SpatialMapping class, above the Start() method:

        /// <summary>
        /// Allows this class to behave like a singleton
        /// </summary>
        public static SpatialMapping Instance;
    
        /// <summary>
        /// Used by the GazeCursor as a property with the Raycast call
        /// </summary>
        internal static int PhysicsRaycastMask;
    
        /// <summary>
        /// The layer to use for spatial mapping collisions
        /// </summary>
        internal int physicsLayer = 31;
    
        /// <summary>
        /// Creates environment colliders to work with physics
        /// </summary>
        private SpatialMappingCollider spatialMappingCollider;
    
  5. Ajoutez les éléments éveillé () et Start ():Add the Awake() and Start():

        /// <summary>
        /// Initializes this class
        /// </summary>
        private void Awake()
        {
            // Allows this instance to behave like a singleton
            Instance = this;
        }
    
        /// <summary>
        /// Runs at initialization right after Awake method
        /// </summary>
        void Start()
        {
            // Initialize and configure the collider
            spatialMappingCollider = gameObject.GetComponent<SpatialMappingCollider>();
            spatialMappingCollider.surfaceParent = this.gameObject;
            spatialMappingCollider.freezeUpdates = false;
            spatialMappingCollider.layer = physicsLayer;
    
            // define the mask
            PhysicsRaycastMask = 1 << physicsLayer;
    
            // set the object as active one
            gameObject.SetActive(true);
        }
    
  6. Supprimez la méthode Update () .Delete the Update() method.

  7. Veillez à enregistrer vos modifications dans Visual Studio avant de revenir à Unity.Be sure to save your changes in Visual Studio, before returning to Unity.

Chapitre 8-créer la classe GazeCursorChapter 8 - Create the GazeCursor class

Cette classe est chargée de configurer le curseur à l’emplacement approprié dans l’espace réel, en utilisant le SpatialMappingCollider créé dans le chapitre précédent.This class is responsible for setting up the cursor in the correct location in real space, by making use of the SpatialMappingCollider, created in the previous chapter.

Pour créer cette classe :To create this class:

  1. Cliquez avec le bouton droit dans le dossier scripts , puis cliquez sur créer un > # script C.Right-click inside the Scripts folder, then click Create > C# Script. Appeler le script GazeCursorCall the script GazeCursor

  2. Double-cliquez sur le nouveau script GazeCursor pour l’ouvrir avec Visual Studio.Double-click on the new GazeCursor script to open it with Visual Studio.

  3. Assurez-vous que l’espace de noms suivant est référencé au-dessus de la classe GazeCursor :Make sure you have the following namespace referenced above the GazeCursor class:

    using UnityEngine;
    
  4. Ajoutez ensuite la variable suivante à l’intérieur de la classe GazeCursor , au-dessus de la méthode Start () .Then add the following variable inside the GazeCursor class, above the Start() method.

        /// <summary>
        /// The cursor (this object) mesh renderer
        /// </summary>
        private MeshRenderer meshRenderer;
    
  5. Mettez à jour la méthode Start () avec le code suivant :Update the Start() method with the following code:

        /// <summary>
        /// Runs at initialization right after the Awake method
        /// </summary>
        void Start()
        {
            // Grab the mesh renderer that is on the same object as this script.
            meshRenderer = gameObject.GetComponent<MeshRenderer>();
    
            // Set the cursor reference
            SceneOrganiser.Instance.cursor = gameObject;
            gameObject.GetComponent<Renderer>().material.color = Color.green;
    
            // If you wish to change the size of the cursor you can do so here
            gameObject.transform.localScale = new Vector3(0.01f, 0.01f, 0.01f);
        }
    
  6. Mettez à jour la méthode Update () avec le code suivant :Update the Update() method with the following code:

        /// <summary>
        /// Update is called once per frame
        /// </summary>
        void Update()
        {
            // Do a raycast into the world based on the user's head position and orientation.
            Vector3 headPosition = Camera.main.transform.position;
            Vector3 gazeDirection = Camera.main.transform.forward;
    
            RaycastHit gazeHitInfo;
            if (Physics.Raycast(headPosition, gazeDirection, out gazeHitInfo, 30.0f, SpatialMapping.PhysicsRaycastMask))
            {
                // If the raycast hit a hologram, display the cursor mesh.
                meshRenderer.enabled = true;
                // Move the cursor to the point where the raycast hit.
                transform.position = gazeHitInfo.point;
                // Rotate the cursor to hug the surface of the hologram.
                transform.rotation = Quaternion.FromToRotation(Vector3.up, gazeHitInfo.normal);
            }
            else
            {
                // If the raycast did not hit a hologram, hide the cursor mesh.
                meshRenderer.enabled = false;
            }
        }
    

    Notes

    Ne vous inquiétez pas de l’erreur pour la classe SceneOrganiser introuvable, vous allez la créer dans le chapitre suivant.Do not worry about the error for the SceneOrganiser class not being found, you will create it in the next chapter.

  7. Veillez à enregistrer vos modifications dans Visual Studio avant de revenir à Unity.Be sure to save your changes in Visual Studio, before returning to Unity.

Chapitre 9-créer la classe SceneOrganiserChapter 9 - Create the SceneOrganiser class

Cette classe effectuera les opérations suivantes :This class will:

  • Configurez la caméra principale en y joignant les composants appropriés.Set up the Main Camera by attaching the appropriate components to it.

  • Lorsqu’un objet est détecté, il est chargé de calculer sa position dans le monde réel et de placer une étiquette de balise près de celui-ci avec le nom de balise approprié.When an object is detected, it will be responsible for calculating its position in the real world and place a Tag Label near it with the appropriate Tag Name.

Pour créer cette classe :To create this class:

  1. Cliquez avec le bouton droit dans le dossier scripts , puis cliquez sur créer un > # script C.Right-click inside the Scripts folder, then click Create > C# Script. Nommez le script SceneOrganiser.Name the script SceneOrganiser.

  2. Double-cliquez sur le nouveau script SceneOrganiser pour l’ouvrir avec Visual Studio.Double-click on the new SceneOrganiser script to open it with Visual Studio.

  3. Assurez-vous que les espaces de noms suivants sont référencés au-dessus de la classe SceneOrganiser :Make sure you have the following namespaces referenced above the SceneOrganiser class:

    using System.Collections.Generic;
    using System.Linq;
    using UnityEngine;
    
  4. Ajoutez ensuite les variables suivantes à l’intérieur de la classe SceneOrganiser , au-dessus de la méthode Start () :Then add the following variables inside the SceneOrganiser class, above the Start() method:

        /// <summary>
        /// Allows this class to behave like a singleton
        /// </summary>
        public static SceneOrganiser Instance;
    
        /// <summary>
        /// The cursor object attached to the Main Camera
        /// </summary>
        internal GameObject cursor;
    
        /// <summary>
        /// The label used to display the analysis on the objects in the real world
        /// </summary>
        public GameObject label;
    
        /// <summary>
        /// Reference to the last Label positioned
        /// </summary>
        internal Transform lastLabelPlaced;
    
        /// <summary>
        /// Reference to the last Label positioned
        /// </summary>
        internal TextMesh lastLabelPlacedText;
    
        /// <summary>
        /// Current threshold accepted for displaying the label
        /// Reduce this value to display the recognition more often
        /// </summary>
        internal float probabilityThreshold = 0.8f;
    
        /// <summary>
        /// The quad object hosting the imposed image captured
        /// </summary>
        private GameObject quad;
    
        /// <summary>
        /// Renderer of the quad object
        /// </summary>
        internal Renderer quadRenderer;
    
  5. Supprimez les méthodes Start () et Update () .Delete the Start() and Update() methods.

  6. Sous les variables, ajoutez la méthode éveillé () , qui initialisera la classe et configurera la scène.Underneath the variables, add the Awake() method, which will initialize the class and set up the scene.

        /// <summary>
        /// Called on initialization
        /// </summary>
        private void Awake()
        {
            // Use this class instance as singleton
            Instance = this;
    
            // Add the ImageCapture class to this Gameobject
            gameObject.AddComponent<ImageCapture>();
    
            // Add the CustomVisionAnalyser class to this Gameobject
            gameObject.AddComponent<CustomVisionAnalyser>();
    
            // Add the CustomVisionObjects class to this Gameobject
            gameObject.AddComponent<CustomVisionObjects>();
        }
    
  7. Ajoutez la méthode PlaceAnalysisLabel () , qui instanciera l’étiquette dans la scène (qui, à ce stade, est invisible pour l’utilisateur).Add the PlaceAnalysisLabel() method, which will Instantiate the label in the scene (which at this point is invisible to the user). Il place également le quadruple (également invisible) où l’image est placée et chevauche le monde réel.It also places the quad (also invisible) where the image is placed, and overlaps with the real world. Cela est important, car les coordonnées de la zone récupérées à partir du service après l’analyse sont retracées dans ce Quad pour déterminer l’emplacement approximatif de l’objet dans le monde réel.This is important because the box coordinates retrieved from the Service after analysis are traced back into this quad to determined the approximate location of the object in the real world.

        /// <summary>
        /// Instantiate a Label in the appropriate location relative to the Main Camera.
        /// </summary>
        public void PlaceAnalysisLabel()
        {
            lastLabelPlaced = Instantiate(label.transform, cursor.transform.position, transform.rotation);
            lastLabelPlacedText = lastLabelPlaced.GetComponent<TextMesh>();
            lastLabelPlacedText.text = "";
            lastLabelPlaced.transform.localScale = new Vector3(0.005f,0.005f,0.005f);
    
            // Create a GameObject to which the texture can be applied
            quad = GameObject.CreatePrimitive(PrimitiveType.Quad);
            quadRenderer = quad.GetComponent<Renderer>() as Renderer;
            Material m = new Material(Shader.Find("Legacy Shaders/Transparent/Diffuse"));
            quadRenderer.material = m;
    
            // Here you can set the transparency of the quad. Useful for debugging
            float transparency = 0f;
            quadRenderer.material.color = new Color(1, 1, 1, transparency);
    
            // Set the position and scale of the quad depending on user position
            quad.transform.parent = transform;
            quad.transform.rotation = transform.rotation;
    
            // The quad is positioned slightly forward in font of the user
            quad.transform.localPosition = new Vector3(0.0f, 0.0f, 3.0f);
    
            // The quad scale as been set with the following value following experimentation,  
            // to allow the image on the quad to be as precisely imposed to the real world as possible
            quad.transform.localScale = new Vector3(3f, 1.65f, 1f);
            quad.transform.parent = null;
        }
    
  8. Ajoutez la méthode FinaliseLabel () .Add the FinaliseLabel() method. Il est responsable de ce qui suit :It is responsible for:

    • Définition du texte de l' étiquette avec la balise de la prédiction avec la confiance la plus élevée.Setting the Label text with the Tag of the Prediction with the highest confidence.
    • Appel du calcul du cadre englobant sur l’objet quadruple, positionné précédemment, et placement de l’étiquette dans la scène.Calling the calculation of the Bounding Box on the quad object, positioned previously, and placing the label in the scene.
    • Ajustement de la profondeur des étiquettes à l’aide d’un Raycast vers le cadre englobant, qui doit entrer en conflit avec l’objet dans le monde réel.Adjusting the label depth by using a Raycast towards the Bounding Box, which should collide against the object in the real world.
    • Réinitialisation du processus de capture pour permettre à l’utilisateur de capturer une autre image.Resetting the capture process to allow the user to capture another image.
        /// <summary>
        /// Set the Tags as Text of the last label created. 
        /// </summary>
        public void FinaliseLabel(AnalysisRootObject analysisObject)
        {
            if (analysisObject.predictions != null)
            {
                lastLabelPlacedText = lastLabelPlaced.GetComponent<TextMesh>();
                // Sort the predictions to locate the highest one
                List<Prediction> sortedPredictions = new List<Prediction>();
                sortedPredictions = analysisObject.predictions.OrderBy(p => p.probability).ToList();
                Prediction bestPrediction = new Prediction();
                bestPrediction = sortedPredictions[sortedPredictions.Count - 1];
    
                if (bestPrediction.probability > probabilityThreshold)
                {
                    quadRenderer = quad.GetComponent<Renderer>() as Renderer;
                    Bounds quadBounds = quadRenderer.bounds;
    
                    // Position the label as close as possible to the Bounding Box of the prediction 
                    // At this point it will not consider depth
                    lastLabelPlaced.transform.parent = quad.transform;
                    lastLabelPlaced.transform.localPosition = CalculateBoundingBoxPosition(quadBounds, bestPrediction.boundingBox);
    
                    // Set the tag text
                    lastLabelPlacedText.text = bestPrediction.tagName;
    
                    // Cast a ray from the user's head to the currently placed label, it should hit the object detected by the Service.
                    // At that point it will reposition the label where the ray HL sensor collides with the object,
                    // (using the HL spatial tracking)
                    Debug.Log("Repositioning Label");
                    Vector3 headPosition = Camera.main.transform.position;
                    RaycastHit objHitInfo;
                    Vector3 objDirection = lastLabelPlaced.position;
                    if (Physics.Raycast(headPosition, objDirection, out objHitInfo, 30.0f,   SpatialMapping.PhysicsRaycastMask))
                    {
                        lastLabelPlaced.position = objHitInfo.point;
                    }
                }
            }
            // Reset the color of the cursor
            cursor.GetComponent<Renderer>().material.color = Color.green;
    
            // Stop the analysis process
            ImageCapture.Instance.ResetImageCapture();        
        }
    
  9. Ajoutez la méthode CalculateBoundingBoxPosition () , qui héberge un certain nombre de calculs nécessaires pour convertir les coordonnées du cadre englobant récupérées à partir du service et les recréer proportionnellement sur le quadruple.Add the CalculateBoundingBoxPosition() method, which hosts a number of calculations necessary to translate the Bounding Box coordinates retrieved from the Service and recreate them proportionally on the quad.

        /// <summary>
        /// This method hosts a series of calculations to determine the position 
        /// of the Bounding Box on the quad created in the real world
        /// by using the Bounding Box received back alongside the Best Prediction
        /// </summary>
        public Vector3 CalculateBoundingBoxPosition(Bounds b, BoundingBox boundingBox)
        {
            Debug.Log($"BB: left {boundingBox.left}, top {boundingBox.top}, width {boundingBox.width}, height {boundingBox.height}");
    
            double centerFromLeft = boundingBox.left + (boundingBox.width / 2);
            double centerFromTop = boundingBox.top + (boundingBox.height / 2);
            Debug.Log($"BB CenterFromLeft {centerFromLeft}, CenterFromTop {centerFromTop}");
    
            double quadWidth = b.size.normalized.x;
            double quadHeight = b.size.normalized.y;
            Debug.Log($"Quad Width {b.size.normalized.x}, Quad Height {b.size.normalized.y}");
    
            double normalisedPos_X = (quadWidth * centerFromLeft) - (quadWidth/2);
            double normalisedPos_Y = (quadHeight * centerFromTop) - (quadHeight/2);
    
            return new Vector3((float)normalisedPos_X, (float)normalisedPos_Y, 0);
        }
    
  10. Veillez à enregistrer vos modifications dans Visual Studio avant de revenir à Unity.Be sure to save your changes in Visual Studio, before returning to Unity.

    Important

    Avant de continuer, ouvrez la classe CustomVisionAnalyser et, dans la méthode AnalyseLastImageCaptured () , supprimez les marques de commentaire des lignes suivantes :Before you continue, open the CustomVisionAnalyser class, and within the AnalyseLastImageCaptured() method, uncomment the following lines:

    // Create a texture. Texture size does not matter, since 
    // LoadImage will replace with the incoming image size.
    Texture2D tex = new Texture2D(1, 1);
    tex.LoadImage(imageBytes);
    SceneOrganiser.Instance.quadRenderer.material.SetTexture("_MainTex", tex);
    
    // The response will be in JSON format, therefore it needs to be deserialized
    AnalysisRootObject analysisRootObject = new AnalysisRootObject();
    analysisRootObject = JsonConvert.DeserializeObject<AnalysisRootObject>(jsonResponse);
    
    SceneOrganiser.Instance.FinaliseLabel(analysisRootObject);
    

Notes

Ne vous inquiétez pas du message « Impossible de trouver la classe ImageCapture », vous allez le créer dans le chapitre suivant.Do not worry about the ImageCapture class 'could not be found' message, you will create it in the next chapter.

Chapitre 10-créer la classe ImageCaptureChapter 10 - Create the ImageCapture class

La classe suivante que vous allez créer est la classe ImageCapture .The next class you are going to create is the ImageCapture class.

Cette classe est chargée des opérations suivantes :This class is responsible for:

  • Capture d’une image à l’aide de la caméra HoloLens et stockage dans le dossier de l' application .Capturing an image using the HoloLens camera and storing it in the App folder.
  • Gestion des gestes Tap de l’utilisateur.Handling Tap gestures from the user.

Pour créer cette classe :To create this class:

  1. Accédez au dossier scripts que vous avez créé précédemment.Go to the Scripts folder you created previously.

  2. Cliquez avec le bouton droit dans le dossier, puis cliquez sur créer un > # script C.Right-click inside the folder, then click Create > C# Script. Nommez le script ImageCapture.Name the script ImageCapture.

  3. Double-cliquez sur le nouveau script ImageCapture pour l’ouvrir avec Visual Studio.Double-click on the new ImageCapture script to open it with Visual Studio.

  4. Remplacez les espaces de noms en haut du fichier par les éléments suivants :Replace the namespaces at the top of the file with the following:

    using System;
    using System.IO;
    using System.Linq;
    using UnityEngine;
    using UnityEngine.XR.WSA.Input;
    using UnityEngine.XR.WSA.WebCam;
    
  5. Ajoutez ensuite les variables suivantes à l’intérieur de la classe ImageCapture , au-dessus de la méthode Start () :Then add the following variables inside the ImageCapture class, above the Start() method:

        /// <summary>
        /// Allows this class to behave like a singleton
        /// </summary>
        public static ImageCapture Instance;
    
        /// <summary>
        /// Keep counts of the taps for image renaming
        /// </summary>
        private int captureCount = 0;
    
        /// <summary>
        /// Photo Capture object
        /// </summary>
        private PhotoCapture photoCaptureObject = null;
    
        /// <summary>
        /// Allows gestures recognition in HoloLens
        /// </summary>
        private GestureRecognizer recognizer;
    
        /// <summary>
        /// Flagging if the capture loop is running
        /// </summary>
        internal bool captureIsActive;
    
        /// <summary>
        /// File path of current analysed photo
        /// </summary>
        internal string filePath = string.Empty;
    
  6. Vous devez maintenant ajouter le code des méthodes éveillés () et Start () :Code for Awake() and Start() methods now needs to be added:

        /// <summary>
        /// Called on initialization
        /// </summary>
        private void Awake()
        {
            Instance = this;
        }
    
        /// <summary>
        /// Runs at initialization right after Awake method
        /// </summary>
        void Start()
        {
            // Clean up the LocalState folder of this application from all photos stored
            DirectoryInfo info = new DirectoryInfo(Application.persistentDataPath);
            var fileInfo = info.GetFiles();
            foreach (var file in fileInfo)
            {
                try
                {
                    file.Delete();
                }
                catch (Exception)
                {
                    Debug.LogFormat("Cannot delete file: ", file.Name);
                }
            } 
    
            // Subscribing to the Microsoft HoloLens API gesture recognizer to track user gestures
            recognizer = new GestureRecognizer();
            recognizer.SetRecognizableGestures(GestureSettings.Tap);
            recognizer.Tapped += TapHandler;
            recognizer.StartCapturingGestures();
        }
    
  7. Implémentez un gestionnaire qui sera appelé quand un mouvement TAP se produit :Implement a handler that will be called when a Tap gesture occurs:

        /// <summary>
        /// Respond to Tap Input.
        /// </summary>
        private void TapHandler(TappedEventArgs obj)
        {
            if (!captureIsActive)
            {
                captureIsActive = true;
    
                // Set the cursor color to red
                SceneOrganiser.Instance.cursor.GetComponent<Renderer>().material.color = Color.red;
    
                // Begin the capture loop
                Invoke("ExecuteImageCaptureAndAnalysis", 0);
            }
        }
    

    Important

    Lorsque le curseur est vert, cela signifie que l’appareil photo est disponible pour prendre l’image.When the cursor is green, it means the camera is available to take the image. Lorsque le curseur est rouge, cela signifie que l’appareil photo est occupé.When the cursor is red, it means the camera is busy.

  8. Ajoutez la méthode utilisée par l’application pour démarrer le processus de capture d’image et stocker l’image :Add the method that the application uses to start the image capture process and store the image:

        /// <summary>
        /// Begin process of image capturing and send to Azure Custom Vision Service.
        /// </summary>
        private void ExecuteImageCaptureAndAnalysis()
        {
            // Create a label in world space using the ResultsLabel class 
            // Invisible at this point but correctly positioned where the image was taken
            SceneOrganiser.Instance.PlaceAnalysisLabel();
    
            // Set the camera resolution to be the highest possible
            Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending
                ((res) => res.width * res.height).First();
            Texture2D targetTexture = new Texture2D(cameraResolution.width, cameraResolution.height);
    
            // Begin capture process, set the image format
            PhotoCapture.CreateAsync(true, delegate (PhotoCapture captureObject)
            {
                photoCaptureObject = captureObject;
    
                CameraParameters camParameters = new CameraParameters
                {
                    hologramOpacity = 1.0f,
                    cameraResolutionWidth = targetTexture.width,
                    cameraResolutionHeight = targetTexture.height,
                    pixelFormat = CapturePixelFormat.BGRA32
                };
    
                // Capture the image from the camera and save it in the App internal folder
                captureObject.StartPhotoModeAsync(camParameters, delegate (PhotoCapture.PhotoCaptureResult result)
                {
                    string filename = string.Format(@"CapturedImage{0}.jpg", captureCount);
                    filePath = Path.Combine(Application.persistentDataPath, filename);          
                    captureCount++;              
                    photoCaptureObject.TakePhotoAsync(filePath, PhotoCaptureFileOutputFormat.JPG, OnCapturedPhotoToDisk);              
                });
            });
        }
    
  9. Ajoutez les gestionnaires qui seront appelés lorsque la photo a été capturée et le moment où elle sera prête à être analysée.Add the handlers that will be called when the photo has been captured and for when it is ready to be analyzed. Le résultat est ensuite transmis à CustomVisionAnalyser pour l’analyse.The result is then passed to the CustomVisionAnalyser for analysis.

        /// <summary>
        /// Register the full execution of the Photo Capture. 
        /// </summary>
        void OnCapturedPhotoToDisk(PhotoCapture.PhotoCaptureResult result)
        {
            try
            {
                // Call StopPhotoMode once the image has successfully captured
                photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
            }
            catch (Exception e)
            {
                Debug.LogFormat("Exception capturing photo to disk: {0}", e.Message);
            }
        }
    
        /// <summary>
        /// The camera photo mode has stopped after the capture.
        /// Begin the image analysis process.
        /// </summary>
        void OnStoppedPhotoMode(PhotoCapture.PhotoCaptureResult result)
        {
            Debug.LogFormat("Stopped Photo Mode");
    
            // Dispose from the object in memory and request the image analysis 
            photoCaptureObject.Dispose();
            photoCaptureObject = null;
    
            // Call the image analysis
            StartCoroutine(CustomVisionAnalyser.Instance.AnalyseLastImageCaptured(filePath)); 
        }
    
        /// <summary>
        /// Stops all capture pending actions
        /// </summary>
        internal void ResetImageCapture()
        {
            captureIsActive = false;
    
            // Set the cursor color to green
            SceneOrganiser.Instance.cursor.GetComponent<Renderer>().material.color = Color.green;
    
            // Stop the capture loop if active
            CancelInvoke();
        }
    
  10. Veillez à enregistrer vos modifications dans Visual Studio avant de revenir à Unity.Be sure to save your changes in Visual Studio, before returning to Unity.

Chapitre 11-configuration des scripts dans la scèneChapter 11 - Setting up the scripts in the scene

Maintenant que vous avez écrit l’ensemble du code nécessaire pour ce projet, il est temps de configurer les scripts dans la scène et, sur le prefabs, pour qu’ils se comportent correctement.Now that you have written all of the code necessary for this project, is time to set up the scripts in the scene, and on the prefabs, for them to behave correctly.

  1. Dans l' éditeur Unity, dans le panneau hiérarchie, sélectionnez l' appareil photo principal.Within the Unity Editor, in the Hierarchy Panel, select the Main Camera.

  2. Dans le panneau Inspecteur, avec l' appareil photo principal sélectionné, cliquez sur Ajouter un composant, puis recherchez le script SceneOrganiser et double-cliquez dessus pour l’ajouter.In the Inspector Panel, with the Main Camera selected, click on Add Component, then search for SceneOrganiser script and double-click, to add it.

  3. Dans le panneau Projet, ouvrez le dossier Prefabs, faites glisser l' étiquette Prefab dans la zone d’entrée cible de référence de l' étiquette vide, dans le script SceneOrganiser que vous venez d’ajouter à la caméra principale, comme illustré dans l’image ci-dessous :In the Project Panel, open the Prefabs folder, drag the Label prefab into the Label empty reference target input area, in the SceneOrganiser script that you have just added to the Main Camera, as shown in the image below:

  4. Dans le volet hiérarchie, sélectionnez l’enfant GazeCursor de l' appareil photo principal.In the Hierarchy Panel, select the GazeCursor child of the Main Camera.

  5. Dans le volet de l’inspecteur, avec le GazeCursor sélectionné, cliquez sur Ajouter un composant, puis recherchez le script GazeCursor et double-cliquez dessus pour l’ajouter.In the Inspector Panel, with the GazeCursor selected, click on Add Component, then search for GazeCursor script and double-click, to add it.

  6. Là encore, dans le panneau hiérarchie, sélectionnez l’enfant SpatialMapping de l' appareil photo principal.Again, in the Hierarchy Panel, select the SpatialMapping child of the Main Camera.

  7. Dans le volet de l’inspecteur, avec le SpatialMapping sélectionné, cliquez sur Ajouter un composant, puis recherchez le script SpatialMapping et double-cliquez dessus pour l’ajouter.In the Inspector Panel, with the SpatialMapping selected, click on Add Component, then search for SpatialMapping script and double-click, to add it.

Les scripts restants que vous n’avez pas définis seront ajoutés par le code dans le script SceneOrganiser , pendant l’exécution.The remaining scripts thats you have not set will be added by the code in the SceneOrganiser script, during runtime.

Chapitre 12-avant générationChapter 12 - Before building

Pour effectuer un test minutieux de votre application, vous devez l’chargement sur votre Microsoft HoloLens.To perform a thorough test of your application you will need to sideload it onto your Microsoft HoloLens.

Avant cela, assurez-vous que :Before you do, ensure that:

  • Tous les paramètres mentionnés dans le chapitre 3 sont correctement définis.All the settings mentioned in the Chapter 3 are set correctly.

  • Le script SceneOrganiser est attaché à l’objet Camera principal .The script SceneOrganiser is attached to the Main Camera object.

  • Le script GazeCursor est attaché à l’objet GazeCursor .The script GazeCursor is attached to the GazeCursor object.

  • Le script SpatialMapping est attaché à l’objet SpatialMapping .The script SpatialMapping is attached to the SpatialMapping object.

  • Dans le Chapitre 5, étape 6 :In Chapter 5, Step 6:

    • Veillez à insérer votre clé de prédiction de service dans la variable predictionKey .Make sure you insert your Service Prediction Key into the predictionKey variable.
    • Vous avez inséré votre point de terminaison de prédiction dans la classe predictionEndpoint .You have inserted your Prediction Endpoint into the predictionEndpoint class.

Chapitre 13 : créer la solution UWP et chargement votre applicationChapter 13 - Build the UWP solution and sideload your application

Vous êtes maintenant prêt à créer votre application en tant que solution UWP que vous pourrez déployer sur Microsoft HoloLens.You are now ready to build you application as a UWP Solution that you will be able to deploy on to the Microsoft HoloLens. Pour commencer le processus de génération :To begin the build process:

  1. Accédez à fichier > paramètres de build.Go to File > Build Settings.

  2. # Projets Tick Unity C.Tick Unity C# Projects.

  3. Cliquez sur Ajouter des scènes ouvertes.Click on Add Open Scenes. Cette opération ajoute la scène actuellement ouverte à la Build.This will add the currently open scene to the build.

  4. Cliquez sur Générer.Click Build. Unity lance une fenêtre de l' Explorateur de fichiers , dans laquelle vous devez créer, puis sélectionner un dossier dans lequel générer l’application.Unity will launch a File Explorer window, where you need to create and then select a folder to build the app into. Créez ce dossier maintenant, puis nommez-le application.Create that folder now, and name it App. Ensuite, avec le dossier d' application sélectionné, cliquez sur Sélectionner un dossier.Then with the App folder selected, click Select Folder.

  5. Unity commence à générer votre projet dans le dossier de l' application .Unity will begin building your project to the App folder.

  6. Une fois la génération de Unity terminée (cela peut prendre un certain temps), une fenêtre de l' Explorateur de fichiers s’ouvre à l’emplacement de votre Build (Vérifiez la barre des tâches, car elle ne s’affiche pas toujours au-dessus de votre Windows, mais vous informera de l’ajout d’une nouvelle fenêtre).Once Unity has finished building (it might take some time), it will open a File Explorer window at the location of your build (check your task bar, as it may not always appear above your windows, but will notify you of the addition of a new window).

  7. Pour effectuer un déploiement sur Microsoft HoloLens, vous aurez besoin de l’adresse IP de cet appareil (pour le déploiement à distance) et pour vous assurer qu’il est également défini en mode développeur .To deploy on to Microsoft HoloLens, you will need the IP Address of that device (for Remote Deploy), and to ensure that it also has Developer Mode set. Pour ce faire :To do this:

    1. Tout en portant votre HoloLens, ouvrez les paramètres.Whilst wearing your HoloLens, open the Settings.

    2. Accéder au réseau & > Options avancées de Wi-Fi > InternetGo to Network & Internet > Wi-Fi > Advanced Options

    3. Notez l’adresse IPv4 .Note the IPv4 address.

    4. Ensuite, revenez aux paramètres, puis pour mettre à jour & sécurité > pour les développeursNext, navigate back to Settings, and then to Update & Security > For Developers

    5. Définissez le mode développeur sur.Set Developer Mode On.

  8. Accédez à votre nouvelle build Unity (le dossier de l' application ) et ouvrez le fichier solution avec Visual Studio.Navigate to your new Unity build (the App folder) and open the solution file with Visual Studio.

  9. Dans la configuration de la solution, sélectionnez Déboguer.In the Solution Configuration select Debug.

  10. Dans la plateforme de la solution, sélectionnez x86, ordinateur distant.In the Solution Platform, select x86, Remote Machine. Vous serez invité à insérer l' adresse IP d’un périphérique distant (le Microsoft HoloLens, dans ce cas, que vous avez noté).You will be prompted to insert the IP address of a remote device (the Microsoft HoloLens, in this case, which you noted).

  11. Accédez au menu générer , puis cliquez sur déployer la solution pour chargement l’application à votre HoloLens.Go to the Build menu and click on Deploy Solution to sideload the application to your HoloLens.

  12. Votre application doit maintenant apparaître dans la liste des applications installées sur votre Microsoft HoloLens, prête à être lancée.Your app should now appear in the list of installed apps on your Microsoft HoloLens, ready to be launched!

Pour utiliser l’application :To use the application:

  • Examinez un objet, que vous avez formé avec votre service vision personnalisée Azure, la détection d’objets et utilisez le geste TAP.Look at an object, which you have trained with your Azure Custom Vision Service, Object Detection, and use the Tap gesture.
  • Si l’objet est correctement détecté, un texte d’étiquette de l’espace universel apparaît avec le nom de la balise.If the object is successfully detected, a world-space Label Text will appear with the tag name.

Important

Chaque fois que vous capturez une photo et que vous l’envoyez au service, vous pouvez revenir à la page du service et reformer le service avec les images nouvellement capturées.Every time you capture a photo and send it to the Service, you can go back to the Service page and retrain the Service with the newly captured images. Au début, vous devrez probablement corriger les zones englobantes pour qu’elles soient plus précises et reformer le service.At the beginning, you will probably also have to correct the Bounding Boxes to be more accurate and retrain the Service.

Notes

Le texte de l’étiquette placée peut ne pas apparaître près de l’objet lorsque les capteurs Microsoft HoloLens et/ou SpatialTrackingComponent dans Unity ne peuvent pas placer les conflits appropriés, par rapport aux objets réels.The Label Text placed might not appear near the object when the Microsoft HoloLens sensors and/or the SpatialTrackingComponent in Unity fails to place the appropriate colliders, relative to the real world objects. Si c’est le cas, essayez d’utiliser l’application sur une autre surface.Try to use the application on a different surface if that is the case.

Votre Custom Vision, application de détection d’objetsYour Custom Vision, Object Detection application

Félicitations, vous avez créé une application de réalité mixte qui tire parti de l’API de détection d’objets Azure Custom Vision, qui peut reconnaître un objet d’une image, puis fournir une position approximative pour cet objet dans l’espace 3D.Congratulations, you built a mixed reality app that leverages the Azure Custom Vision, Object Detection API, which can recognize an object from an image, and then provide an approximate position for that object in 3D space.

Exercices bonusBonus exercises

Exercice 1Exercise 1

En ajoutant à l’étiquette de texte, utilisez un cube semi-transparent pour encapsuler l’objet réel dans un rectangle englobant 3D.Adding to the Text Label, use a semi-transparent cube to wrap the real object in a 3D Bounding Box.

Exercice 2Exercise 2

Formez votre Service Vision personnalisée pour qu’il reconnaisse plus d’objets.Train your Custom Vision Service to recognize more objects.

Exercice 3Exercise 3

Émettre un signal sonore lorsqu’un objet est reconnu.Play a sound when an object is recognized.

Exercice 4Exercise 4

Utilisez l’API pour reformer votre service avec les mêmes images que celles que votre application analyse, afin de rendre le service plus précis (effectuer la prédiction et la formation simultanément).Use the API to re-train your Service with the same images your app is analyzing, so to make the Service more accurate (do both prediction and training simultaneously).