Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Remarque
Les tutoriels de la Mixed Reality Academy ont été conçus pour HoloLens (1re génération) et les casques immersifs Mixed Reality. Par conséquent, nous pensons qu’il est important de laisser ces tutoriels en place pour les développeurs qui sont toujours à la recherche d’instructions pour développer ces appareils. Ces didacticiels ne seront pas mis à jour avec les derniers ensembles d’outils ou interactions utilisés pour HoloLens 2. Ils seront maintenus pour continuer à fonctionner sur les appareils pris en charge. Une nouvelle série de didacticiels sera publiée à l’avenir et montrera comment développer pour HoloLens 2. Cet avis sera mis à jour avec un lien vers ces tutoriels lorsqu’ils seront publiés.
Dans ce cours, vous apprendrez à reconnaître du contenu visuel personnalisé dans une image fournie, à l’aide de fonctionnalités Azure Custom Vision dans une application de réalité mixte.
Ce service vous permettra d’entraîner un modèle d’apprentissage automatique à l’aide d’images d’objets. Vous allez ensuite utiliser le modèle entraîné pour reconnaître des objets similaires, tels que fournis par la capture photo de Microsoft HoloLens ou une caméra connectée à votre PC pour casques immersifs (VR).
Azure Custom Vision est un service cognitif Microsoft qui permet aux développeurs de créer des classifieurs d’images personnalisés. Ces classifieurs peuvent ensuite être utilisés avec de nouvelles images pour reconnaître ou classer des objets au sein de cette nouvelle image. Le Service fournit un portail en ligne simple et facile à utiliser pour simplifier le processus. Pour plus d’informations, consultez la page Service Azure Custom Vision.
A l’issue de ce cours, vous disposerez d’une application de réalité mixte qui pourra fonctionner en deux modes :
Mode d’analyse : configuration manuelle du service Custom Vision en chargeant des images, en créant des balises et en entraînant le service à reconnaître différents objets (dans ce cas, la souris et le clavier). Vous allez ensuite créer une application HoloLens qui capturera des images à l’aide de la caméra et tentera de reconnaître ces objets dans le monde réel.
Mode d’entraînement : vous allez implémenter du code qui activera un « mode d’entraînement » dans votre application. Le mode d’apprentissage vous permet de capturer des images à l’aide de la caméra HoloLens, de charger les images capturées vers le service et d’entraîner le modèle de vision personnalisé.
Ce cours vous apprendra à obtenir les résultats du service Custom Vision dans un exemple d’application basé sur Unity. Ce sera à vous d’appliquer ces concepts à une application personnalisée que vous pourriez créer.
Prise en charge des appareils
| Cours | HoloLens | Casques immersifs |
|---|---|---|
| MR et Azure 302b : Custom Vision | ✔️ | ✔️ |
Remarque
Bien que ce cours se concentre principalement sur HoloLens, vous pouvez également appliquer ce que vous apprenez dans ce cours aux casques immersifs (VR) Windows Mixed Reality. Étant donné que les casques immersifs (RV) n’ont pas de caméras accessibles, vous aurez besoin d’une caméra externe connectée à votre PC. Au fur et à mesure que vous suivez le cours, vous verrez des notes sur les modifications que vous pourriez avoir besoin d’utiliser pour prendre en charge les casques immersifs (VR).
Conditions préalables
Remarque
Ce tutoriel est conçu pour les développeurs qui ont une expérience de base avec Unity et C#. Veuillez également noter que les conditions préalables et les instructions écrites contenues dans ce document représentent ce qui a été testé et vérifié au moment de la rédaction (juillet 2018). Vous êtes libre d’utiliser les logiciels les plus récents, comme indiqué dans l’article installer les outils , bien qu’il ne faille pas supposer que les informations de ce cours correspondront parfaitement à ce que vous trouverez dans les logiciels plus récents que ceux énumérés ci-dessous.
Nous recommandons les matériels et logiciels suivants pour ce cours :
- Un PC de développement, compatible avec Windows Mixed Reality pour le développement de casque immersif (VR)
- Windows 10 Fall Creators Update (ou version ultérieure) avec le mode Développeur activé
- Le dernier SDK de Windows 10
- Unity 2017.4
- Visual Studio 2017
- Un casque immersif (RV) Windows Mixed Reality ou Microsoft HoloLens avec le mode Développeur activé
- Une caméra connectée à votre PC (pour le développement d’un casque immersif)
- Accès Internet pour la configuration d’Azure et la récupération d’API Custom Vision
- Une série d’au moins cinq (5) images (dix (10) recommandées) pour chaque objet que vous souhaitez que le service Custom Vision reconnaisse. Si vous le souhaitez, vous pouvez utiliser les images déjà fournies avec ce cours (une souris d’ordinateur et un clavier).
Avant de commencer
- Pour éviter de rencontrer des problèmes lors de la génération de ce projet, il est fortement suggéré de créer le projet mentionné dans ce tutoriel dans un dossier racine ou quasi-racine (les chemins d’accès aux dossiers longs peuvent causer des problèmes au moment de la génération).
- Configurer et tester votre HoloLens. Si vous avez besoin d’aide pour configurer votre HoloLens, consultez l’article consacré à la configuration de HoloLens.
- Il est recommandé d’effectuer l’étalonnage et le réglage du capteur lors du développement d’une nouvelle application HoloLens (parfois, il peut être utile d’effectuer ces tâches pour chaque utilisateur).
Pour obtenir de l’aide sur l’étalonnage, suivez ce lien vers l’article sur l’étalonnage HoloLens.
Pour obtenir de l’aide sur le réglage du capteur, suivez ce lien vers l’article sur le réglage du capteur HoloLens.
Chapitre 1 - Le portail de service Custom Vision
Pour utiliser le service Custom Vision dans Azure, vous devez configurer une instance du service qui sera mise à la disposition de votre application.
Tout d’abord, accédez à la page principale du service Custom Vision.
Cliquez sur le bouton Démarrer .
Connectez-vous au portail du service Custom Vision.
Remarque
Si vous n’avez pas encore de compte Azure, vous devez en créer un. Si vous suivez ce tutoriel en salle de classe ou en laboratoire, demandez à votre instructeur ou à l’un des surveillants de vous aider à configurer votre nouveau compte.
Une fois que vous êtes connecté pour la première fois, le panneau Conditions d’utilisation s’affiche . Cliquez sur la case à cocher pour accepter les conditions. Cliquez ensuite sur J’accepte.
Après avoir accepté les conditions, vous serez dirigé vers la section Projets du portail. Cliquez sur Nouveau projet.
Un onglet apparaîtra sur le côté droit, qui vous invitera à spécifier des champs pour le projet.
Insérez un nom pour votre projet.
Insérez une description pour votre projet (facultatif).
Choisissez un groupe de ressources ou créez-en un. Un groupe de ressources permet de surveiller, de contrôler l’accès, de provisionner et de gérer la facturation d’une collection de ressources Azure. Il est recommandé de conserver tous les services Azure associés à un seul projet (par exemple, ces cours) sous un groupe de ressources commun).
Définir les types de projet sur Classification
Définissez les domaines comme Général.
Si vous souhaitez en savoir plus sur les groupes de ressources Azure, consultez l’article sur les groupes de ressources.
Une fois que vous avez terminé, cliquez sur Créer un projet, vous serez redirigé vers le service Custom Vision, page du projet.
Chapitre 2 - Entraînement de votre projet Custom Vision
Une fois dans le portail Custom Vision, votre objectif principal est d’entraîner votre projet à reconnaître des objets spécifiques dans les images. Vous avez besoin d’au moins cinq (5) images, bien que dix (10) soit préférable, pour chaque objet que vous souhaitez que votre application reconnaisse. Vous pouvez utiliser les images fournies avec ce cours (une souris d’ordinateur et un clavier).
Pour entraîner votre projet de service Custom Vision :
Cliquez sur le + bouton en regard des balises.
Ajoutez le nom de l’objet que vous voulez reconnaître. Cliquez sur Enregistrer.
Vous remarquerez que votre balise a été ajoutée (vous devrez peut-être recharger votre page pour qu’elle apparaisse). Cliquez sur la case à cocher à côté de votre nouvelle balise, si elle n’est pas déjà cochée.
Cliquez sur Ajouter des images au centre de la page.
Cliquez sur Parcourir les fichiers locaux, puis recherchez, puis sélectionnez, les images que vous souhaitez télécharger, le minimum étant de cinq (5). N’oubliez pas que toutes ces images doivent contenir l’objet que vous entraînez.
Remarque
Vous pouvez sélectionner plusieurs images à charger à la fois.
Une fois que vous pouvez voir les images dans l’onglet, sélectionnez la balise appropriée dans la zone Mes balises .
Cliquez sur Télécharger des fichiers. Le téléchargement des fichiers commence. Une fois que vous avez la confirmation du chargement, cliquez sur Terminé.
Répétez la même procédure pour créer une nouvelle balise nommée Clavier et chargez les photos appropriées. Veillez à décocherSouris une fois que vous avez créé les nouvelles balises, afin d’afficher la fenêtre Ajouter des images .
Une fois que vous avez configuré les deux balises, cliquez sur Entraîner, et la première itération de formation commencera.
Une fois qu’elle est générée, vous pouvez voir deux boutons appelés Définir par défaut et URL de prédiction. Cliquez d’abord sur Définir par défaut , puis cliquez sur URL de prédiction.
Remarque
L’URL de point de terminaison qui est fournie à partir de celle-ci est définie sur l’itération qui a été marquée comme étant par défaut. Par conséquent, si vous effectuez ultérieurement une nouvelle itération et que vous la mettez à jour par défaut, vous n’aurez pas besoin de modifier votre code.
Une fois que vous avez cliqué sur l’URL de prédiction, ouvrez le Bloc-notes, puis copiez-collez l’URL et la clé de prédiction, afin de pouvoir les récupérer lorsque vous en aurez besoin plus tard dans le code.
Cliquez sur la roue dentée en haut à droite de l’écran.
Copiez la Clé d’entraînement et collez-la dans un Bloc-notes, pour une utilisation ultérieure.
Copiez également votre ID de projet et collez-le dans votre fichier Bloc-notes, pour une utilisation ultérieure.
Chapitre 3 - Mettre en place le projet Unity
Ce qui suit est une configuration typique pour développer avec la réalité mixte et, en tant que tel, constitue un bon modèle pour d’autres projets.
Ouvrez Unity et cliquez sur Nouveau.
Vous devrez maintenant fournir un nom de projet Unity. Insérer AzureCustomVision. Assurez-vous que le modèle de projet est défini sur 3D. Définissez l’emplacement à un endroit approprié pour vous (rappelez-vous, plus près des répertoires racines est préférable). Cliquez ensuite sur Créer un projet.
Avec Unity ouvert, il est utile de vérifier que l’éditeur de script par défaut est défini sur Visual Studio. Accédez à >Modifier les préférences, puis dans la nouvelle fenêtre, naviguez vers Outils externes. Changez l’éditeur de script externe pour Visual Studio 2017. Fermez la fenêtre Préférences .
Ensuite, allez dans Paramètres de génération de fichier > et sélectionnez la plateforme Windows universelle, puis cliquez sur le bouton Changer de plateforme pour appliquer votre sélection.
Dans les paramètres de génération de fichier>, assurez-vous que :
L’appareil cible est défini sur HoloLens
Pour les casques immersifs, définissez Appareil cible sur n’importe quel appareil.
Le type de build est défini sur D3D
Le Kit de développement logiciel (SDK) est défini sur Dernière version installée
La version de Visual Studio est définie sur Dernière version installée
Build and Run est défini sur l’ordinateur local
Enregistrez la scène et ajoutez-la à la build.
Pour ce faire, sélectionnez Ajouter des scènes ouvertes. Une fenêtre d’enregistrement s’affiche.
Créez un dossier pour cette scène et toutes les futures, puis sélectionnez le bouton Nouveau dossier pour créer un nouveau dossier, nommez-le Scènes.
Ouvrez votre dossier Scenes nouvellement créé, puis dans le champ de texte Nom du fichier : , tapez CustomVisionScene, puis cliquez sur Enregistrer.
Attention, vous devez enregistrer vos scènes Unity dans le dossier Assets , car elles doivent être associées au projet Unity. La création du dossier scenes (et d’autres dossiers similaires) est une façon typique de structurer un projet Unity.
Les paramètres restants, dans les paramètres de build, doivent être laissés par défaut pour l’instant.
Dans la fenêtre Paramètres de build, cliquez sur le bouton Paramètres du lecteur, cela ouvrira le panneau associé dans l’espace où se trouve l’inspecteur.
Dans ce panneau, quelques paramètres doivent être vérifiés :
Dans l’onglet Other Settings (Autres paramètres ) :
La version du runtime de script doit être expérimentale (équivalent .NET 4.6), ce qui nécessitera un redémarrage de l’éditeur.
Le backend de script doit être .NET
Le niveau de compatibilité de l’API doit être de .NET 4.6
Dans l’onglet Paramètres de publication, sous Fonctionnalités, cochez la case activée :
Client Internet
Webcam
Microphone
Plus bas dans le panneau, dans Paramètres XR (sous Paramètres de publication), cochez Réalité virtuelle prise en charge, vérifiez que le Kit de développement logiciel (SDK) Windows Mixed Reality est ajouté.
De retour dans les paramètres de buildUnity C# Projects n’est plus grisé ; Cochez la case à côté de celle-ci.
Fermez la fenêtre Paramètres de build.
Enregistrez votre scène et votre projet (FILE > SAVE SCENE / > FILE SAVE PROJECT).
Chapitre 4 : Importation de la DLL Newtonsoft dans Unity
Importante
Si vous souhaitez ignorer le composant Unity Set up de ce cours et continuer directement dans le code, n’hésitez pas à télécharger ce package Azure-MR-302b.unity, à l’importer dans votre projet en tant que package personnalisé, puis à passer au chapitre 6.
Ce cours nécessite l’utilisation de la bibliothèque Newtonsoft , que vous pouvez ajouter en tant que DLL à vos ressources. Le package contenant cette bibliothèque peut être téléchargé à partir de ce lien. Pour importer la bibliothèque Newtonsoft dans votre projet, utilisez le package Unity fourni avec ce cours.
Ajoutez le .unitypackage à Unity à l’aide de l’option de menu Package>personnaliséd’importation de package>.
Dans la zone Importer le package Unity qui s’affiche, assurez-vous que tout ce qui se trouve sous (et y compris) les plugins est sélectionné.
Cliquez sur le bouton Importer pour ajouter les éléments à votre projet.
Allez dans le dossier Newtonsoft sous Plugins dans la vue du projet et sélectionnez le plugin Newtonsoft.Json.
Avec le plugin Newtonsoft.Json sélectionné, assurez-vous que Toute plate-forme est décochée, puis assurez-vous que WSAPlayer est également décoché, puis cliquez sur Appliquer. Il s’agit simplement de vérifier que les fichiers sont configurés correctement.
Remarque
Le marquage de ces plugins les configure pour qu’ils ne soient utilisés que dans l’éditeur Unity. Il existe un ensemble différent d’entre eux dans le dossier WSA qui sera utilisé après l’exportation du projet à partir de Unity.
Ensuite, vous devez ouvrir le dossier WSA , dans le dossier Newtonsoft . Vous verrez une copie du même fichier que vous venez de configurer. Sélectionnez le fichier, puis, dans l’inspecteur, vérifiez que
- Toute plateforme est décochée
- seulWSAPlayer est coché
- Le processus n’est pasvérifié
Chapitre 5 - Configuration de la caméra
Dans le panneau Hiérarchie, sélectionnez la caméra principale.
Une fois sélectionné, vous pourrez voir tous les composants de la caméra principale dans le panneau de l’inspecteur.
L’objet caméra doit être nommé Caméra principale (notez l’orthographe !)
La balise d’appareil photo principal doit être définie sur MainCamera (notez l’orthographe !)
Assurez-vous que la position de transformation est définie sur 0, 0, 0
Définissez Clear Flags (Effacer les drapeaux sur Couleur unie ) (ignorez cette option pour le casque immersif).
Définissez la couleur d’arrière-plan du composant de caméra sur noir, alpha 0 (code hexadécimal : #00000000) (ignorez cette valeur pour le casque immersif).
Chapitre 6 - Création de la classe CustomVisionAnalyser.
À ce stade, vous êtes prêt à écrire du code.
Vous commencerez par la classe CustomVisionAnalyser .
Remarque
Les appels au service Custom Vision effectués dans le code indiqué ci-dessous sont effectués à l’aide de l’API REST de Custom Vision. En l’utilisant, vous verrez comment implémenter et utiliser cette API (utile pour comprendre comment implémenter quelque chose de similaire par vous-même). N’oubliez pas que Microsoft propose un SDK de service Custom Vision qui peut également être utilisé pour effectuer des appels au service. Pour plus d’informations, consultez l’article sur le SDK du service Custom Vision.
Cette classe est responsable de :
Chargement de la dernière image capturée sous la forme d’un tableau d’octets.
Envoi du tableau d’octets à votre instance de service Azure Custom Vision pour analyse.
Réception de la réponse sous forme de chaîne JSON.
Désérialisation de la réponse et transmission de la prédiction résultante à la classe SceneOrganiser , qui s’occupera de la façon dont la réponse doit être affichée.
Pour créer cette classe :
Cliquez avec le bouton droit dans le dossier Ressource situé dans le panneau Projet, puis cliquez sur Créer > un dossier. Appelez le dossier Scripts.
Double-cliquez sur le dossier que vous venez de créer pour l’ouvrir.
Cliquez avec le bouton droit dans le dossier, puis cliquez sur Créer un>script C#. Nommez le script CustomVisionAnalyser.
Double-cliquez sur le nouveau script CustomVisionAnalyser pour l’ouvrir avec Visual Studio.
Mettez à jour les espaces de noms en haut de votre fichier pour qu’ils correspondent aux éléments suivants :
using System.Collections; using System.IO; using UnityEngine; using UnityEngine.Networking; using Newtonsoft.Json;Dans la classe CustomVisionAnalyser , ajoutez les variables suivantes :
/// <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> /// Byte array of the image to submit for analysis /// </summary> [HideInInspector] public byte[] imageBytes;Remarque
Assurez-vous d’insérer votre clé de prédiction dans la variable predictionKey et votre point de terminaison de prédiction dans la variable predictionEndpoint . Vous les avez copiés dans le Bloc-notes plus tôt dans le cours.
Il faut maintenant ajouter du code pour Awake() pour initialiser la variable d’instance :
/// <summary> /// Initialises this class /// </summary> private void Awake() { // Allows this instance to behave like a singleton Instance = this; }Supprimez les méthodes Start() et Update().
Ensuite, ajoutez la coroutine (avec la méthode statique GetImageAsByteArray() en dessous), qui obtiendra les résultats de l’analyse de l’image capturée par la classe ImageCapture .
Remarque
Dans la coroutine AnalyseImageCapture , il y a un appel à la classe SceneOrganiser que vous devez encore créer. Par conséquent, laissez ces lignes commentées pour l’instant.
/// <summary> /// Call the Computer Vision Service to submit the image. /// </summary> public IEnumerator AnalyseLastImageCaptured(string imagePath) { 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; // The response will be in JSON format, therefore it needs to be deserialized // The following lines refers to a class that you will build in later Chapters // Wait until then to uncomment these lines //AnalysisObject analysisObject = new AnalysisObject(); //analysisObject = JsonConvert.DeserializeObject<AnalysisObject>(jsonResponse); //SceneOrganiser.Instance.SetTagsToLastLabel(analysisObject); } } /// <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); }Veillez à enregistrer vos modifications dans Visual Studio avant de revenir à Unity.
Chapitre 7 - Créer la classe CustomVisionObjects
La classe que vous allez créer maintenant est la classe CustomVisionObjects .
Ce script contient un certain nombre d’objets utilisés par d’autres classes pour sérialiser et désérialiser les appels effectués au service Custom Vision.
Avertissement
Il est important de prendre note du point de terminaison que le service Custom Vision vous fournit, car la structure JSON ci-dessous a été configurée pour fonctionner avec Custom Vision Prediction v2.0. Si vous avez une autre version, vous devrez peut-être mettre à jour la structure ci-dessous.
Pour créer cette classe :
Cliquez avec le bouton droit dans le dossier Scripts , puis cliquez sur Créer un>script C#. Appelez le script CustomVisionObjects.
Double-cliquez sur le nouveau script CustomVisionObjects pour l’ouvrir avec Visual Studio.
Ajoutez les espaces de noms suivants en haut du fichier :
using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Networking;Supprimez les méthodes Start() et Update() à l’intérieur de la classe CustomVisionObjects ; Cette classe doit maintenant être vide.
Ajoutez les classes suivantes en dehors de la classe CustomVisionObjects . Ces objets sont utilisés par la bibliothèque Newtonsoft pour sérialiser et désérialiser les données de réponse :
// 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 /// </summary> [Serializable] public class AnalysisObject { public List<Prediction> Predictions { get; set; } } [Serializable] public class Prediction { public string TagName { get; set; } public double Probability { get; set; } }
Chapitre 8 : Créer la classe VoiceRecognizer
Cette classe reconnaîtra l’entrée vocale de l’utilisateur.
Pour créer cette classe :
Cliquez avec le bouton droit dans le dossier Scripts , puis cliquez sur Créer un>script C#. Appelez le script VoiceRecognizer.
Double-cliquez sur le nouveau script VoiceRecognizer pour l’ouvrir avec Visual Studio.
Ajoutez les espaces de noms suivants au-dessus de la classe VoiceRecognizer :
using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Windows.Speech;Ajoutez ensuite les variables suivantes dans la classe VoiceRecognizer , au-dessus de la méthode Start() :
/// <summary> /// Allows this class to behave like a singleton /// </summary> public static VoiceRecognizer Instance; /// <summary> /// Recognizer class for voice recognition /// </summary> internal KeywordRecognizer keywordRecognizer; /// <summary> /// List of Keywords registered /// </summary> private Dictionary<string, Action> _keywords = new Dictionary<string, Action>();Ajoutez les méthodes Awake() et Start(), cette dernière permettant de configurer les mots-clés de l’utilisateur pour qu’ils soient reconnus lors de l’association d’un tag à une image :
/// <summary> /// Called on initialization /// </summary> private void Awake() { Instance = this; } /// <summary> /// Runs at initialization right after Awake method /// </summary> void Start () { Array tagsArray = Enum.GetValues(typeof(CustomVisionTrainer.Tags)); foreach (object tagWord in tagsArray) { _keywords.Add(tagWord.ToString(), () => { // When a word is recognized, the following line will be called CustomVisionTrainer.Instance.VerifyTag(tagWord.ToString()); }); } _keywords.Add("Discard", () => { // When a word is recognized, the following line will be called // The user does not want to submit the image // therefore ignore and discard the process ImageCapture.Instance.ResetImageCapture(); keywordRecognizer.Stop(); }); //Create the keyword recognizer keywordRecognizer = new KeywordRecognizer(_keywords.Keys.ToArray()); // Register for the OnPhraseRecognized event keywordRecognizer.OnPhraseRecognized += KeywordRecognizer_OnPhraseRecognized; }Supprimez la méthode Update().
Ajoutez le gestionnaire suivant, qui est appelé chaque fois qu’une entrée vocale est reconnue :
/// <summary> /// Handler called when a word is recognized /// </summary> private void KeywordRecognizer_OnPhraseRecognized(PhraseRecognizedEventArgs args) { Action keywordAction; // if the keyword recognized is in our dictionary, call that Action. if (_keywords.TryGetValue(args.text, out keywordAction)) { keywordAction.Invoke(); } }Veillez à enregistrer vos modifications dans Visual Studio avant de revenir à Unity.
Remarque
Ne vous inquiétez pas du code qui pourrait sembler avoir une erreur, car vous fournirez bientôt d’autres cours, qui les corrigeront.
Chapitre 9 : Créer la classe CustomVisionTrainer
Cette classe enchaîne une série d’appels web pour entraîner le service Custom Vision. Chaque appel sera expliqué en détail juste au-dessus du code.
Pour créer cette classe :
Cliquez avec le bouton droit dans le dossier Scripts , puis cliquez sur Créer un>script C#. Appelez le script CustomVisionTrainer.
Double-cliquez sur le nouveau script CustomVisionTrainer pour l’ouvrir avec Visual Studio.
Ajoutez les espaces de noms suivants au-dessus de la classe CustomVisionTrainer :
using Newtonsoft.Json; using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; using UnityEngine; using UnityEngine.Networking;Ajoutez ensuite les variables suivantes dans la classe CustomVisionTrainer, au-dessus de la méthode Start().
Remarque
L’URL de formation utilisée ici est fournie dans la documentation de formation Custom Vision 1.2 et sa structure est la suivante :https://southcentralus.api.cognitive.microsoft.com/customvision/v1.2/Training/projects/{projectId}/
Pour plus d’informations, consultez l’API de référence de la formation Custom Vision v1.2.Avertissement
Il est important de prendre note du point de terminaison que le service Custom Vision vous fournit pour le mode d’entraînement, car la structure JSON utilisée (dans la classe CustomVisionObjects) a été configurée pour fonctionner avec la formation Custom Vision v1.2. Si vous avez une autre version, vous devrez peut-être mettre à jour la structure des objets .
/// <summary> /// Allows this class to behave like a singleton /// </summary> public static CustomVisionTrainer Instance; /// <summary> /// Custom Vision Service URL root /// </summary> private string url = "https://southcentralus.api.cognitive.microsoft.com/customvision/v1.2/Training/projects/"; /// <summary> /// Insert your prediction key here /// </summary> private string trainingKey = "- Insert your key here -"; /// <summary> /// Insert your Project Id here /// </summary> private string projectId = "- Insert your Project Id here -"; /// <summary> /// Byte array of the image to submit for analysis /// </summary> internal byte[] imageBytes; /// <summary> /// The Tags accepted /// </summary> internal enum Tags {Mouse, Keyboard} /// <summary> /// The UI displaying the training Chapters /// </summary> private TextMesh trainingUI_TextMesh;Importante
Assurez-vous d’ajouter la valeur de votre clé de service (clé d’entraînement) et la valeur de l’ID du projet, que vous avez notées précédemment ; Il s’agit des valeurs que vous avez collectées à partir du portail plus tôt dans le cours (chapitre 2, étape 10 et suivantes).
Ajoutez les méthodes Start() et Awake() suivantes. Ces méthodes sont appelées lors de l’initialisation et contiennent l’appel de configuration de l’interface utilisateur :
/// <summary> /// Called on initialization /// </summary> private void Awake() { Instance = this; } /// <summary> /// Runs at initialization right after Awake method /// </summary> private void Start() { trainingUI_TextMesh = SceneOrganiser.Instance.CreateTrainingUI("TrainingUI", 0.04f, 0, 4, false); }Supprimez la méthode Update(). Cette classe n’en aura pas besoin.
Ajoutez la méthode RequestTagSelection(). Cette méthode est la première à être appelée lorsqu’une image a été capturée et stockée dans l’appareil et est maintenant prête à être envoyée au service Custom Vision, pour l’entraîner. Cette méthode affiche, dans l’interface utilisateur d’entraînement, un ensemble de mots clés que l’utilisateur peut utiliser pour marquer l’image qui a été capturée. Il avertit également la classe VoiceRecognizer pour qu’elle commence à écouter l’utilisateur pour l’entrée vocale.
internal void RequestTagSelection() { trainingUI_TextMesh.gameObject.SetActive(true); trainingUI_TextMesh.text = $" \nUse voice command \nto choose between the following tags: \nMouse\nKeyboard \nor say Discard"; VoiceRecognizer.Instance.keywordRecognizer.Start(); }Ajoutez la méthode VerifyTag(). Cette méthode reçoit l’entrée vocale reconnue par la classe VoiceRecognizer , vérifie sa validité, puis lance le processus d’entraînement.
/// <summary> /// Verify voice input against stored tags. /// If positive, it will begin the Service training process. /// </summary> internal void VerifyTag(string spokenTag) { if (spokenTag == Tags.Mouse.ToString() || spokenTag == Tags.Keyboard.ToString()) { trainingUI_TextMesh.text = $"Tag chosen: {spokenTag}"; VoiceRecognizer.Instance.keywordRecognizer.Stop(); StartCoroutine(SubmitImageForTraining(ImageCapture.Instance.filePath, spokenTag)); } }Ajoutez la méthode SubmitImageForTraining(). Cette méthode lancera le processus d’entraînement du service Custom Vision. La première étape consiste à récupérer l’ID de balise du service qui est associé à l’entrée vocale validée de l’utilisateur. L’ID de balise sera ensuite téléchargé avec l’image.
/// <summary> /// Call the Custom Vision Service to submit the image. /// </summary> public IEnumerator SubmitImageForTraining(string imagePath, string tag) { yield return new WaitForSeconds(2); trainingUI_TextMesh.text = $"Submitting Image \nwith tag: {tag} \nto Custom Vision Service"; string imageId = string.Empty; string tagId = string.Empty; // Retrieving the Tag Id relative to the voice input string getTagIdEndpoint = string.Format("{0}{1}/tags", url, projectId); using (UnityWebRequest www = UnityWebRequest.Get(getTagIdEndpoint)) { www.SetRequestHeader("Training-Key", trainingKey); www.downloadHandler = new DownloadHandlerBuffer(); yield return www.SendWebRequest(); string jsonResponse = www.downloadHandler.text; Tags_RootObject tagRootObject = JsonConvert.DeserializeObject<Tags_RootObject>(jsonResponse); foreach (TagOfProject tOP in tagRootObject.Tags) { if (tOP.Name == tag) { tagId = tOP.Id; } } } // Creating the image object to send for training List<IMultipartFormSection> multipartList = new List<IMultipartFormSection>(); MultipartObject multipartObject = new MultipartObject(); multipartObject.contentType = "application/octet-stream"; multipartObject.fileName = ""; multipartObject.sectionData = GetImageAsByteArray(imagePath); multipartList.Add(multipartObject); string createImageFromDataEndpoint = string.Format("{0}{1}/images?tagIds={2}", url, projectId, tagId); using (UnityWebRequest www = UnityWebRequest.Post(createImageFromDataEndpoint, multipartList)) { // Gets a byte array out of the saved image imageBytes = GetImageAsByteArray(imagePath); //unityWebRequest.SetRequestHeader("Content-Type", "application/octet-stream"); www.SetRequestHeader("Training-Key", trainingKey); // The upload handler will help uploading the byte array with the request www.uploadHandler = new UploadHandlerRaw(imageBytes); // The download handler will help receiving the analysis from Azure www.downloadHandler = new DownloadHandlerBuffer(); // Send the request yield return www.SendWebRequest(); string jsonResponse = www.downloadHandler.text; ImageRootObject m = JsonConvert.DeserializeObject<ImageRootObject>(jsonResponse); imageId = m.Images[0].Image.Id; } trainingUI_TextMesh.text = "Image uploaded"; StartCoroutine(TrainCustomVisionProject()); }Ajoutez la méthode TrainCustomVisionProject(). Une fois l’image envoyée et marquée, cette méthode sera appelée. Il crée une nouvelle itération qui sera formée avec toutes les images précédentes envoyées au service, plus l’image qui vient de charger. Une fois l’apprentissage terminé, cette méthode appelle une méthode pour définir l’itération nouvellement créée comme valeur par défaut, de sorte que le point de terminaison que vous utilisez pour l’analyse soit la dernière itération entraînée.
/// <summary> /// Call the Custom Vision Service to train the Service. /// It will generate a new Iteration in the Service /// </summary> public IEnumerator TrainCustomVisionProject() { yield return new WaitForSeconds(2); trainingUI_TextMesh.text = "Training Custom Vision Service"; WWWForm webForm = new WWWForm(); string trainProjectEndpoint = string.Format("{0}{1}/train", url, projectId); using (UnityWebRequest www = UnityWebRequest.Post(trainProjectEndpoint, webForm)) { www.SetRequestHeader("Training-Key", trainingKey); www.downloadHandler = new DownloadHandlerBuffer(); yield return www.SendWebRequest(); string jsonResponse = www.downloadHandler.text; Debug.Log($"Training - JSON Response: {jsonResponse}"); // A new iteration that has just been created and trained Iteration iteration = new Iteration(); iteration = JsonConvert.DeserializeObject<Iteration>(jsonResponse); if (www.isDone) { trainingUI_TextMesh.text = "Custom Vision Trained"; // Since the Service has a limited number of iterations available, // we need to set the last trained iteration as default // and delete all the iterations you dont need anymore StartCoroutine(SetDefaultIteration(iteration)); } } }Ajoutez la méthode SetDefaultIteration(). Cette méthode définit l’itération précédemment créée et entraînée comme étant par défaut. Une fois cette méthode terminée, cette méthode devra supprimer l’itération précédente existante dans le Service. Au moment de la rédaction de ce cours, il y a une limite d’un maximum de dix (10) itérations autorisées à exister en même temps dans le service.
/// <summary> /// Set the newly created iteration as Default /// </summary> private IEnumerator SetDefaultIteration(Iteration iteration) { yield return new WaitForSeconds(5); trainingUI_TextMesh.text = "Setting default iteration"; // Set the last trained iteration to default iteration.IsDefault = true; // Convert the iteration object as JSON string iterationAsJson = JsonConvert.SerializeObject(iteration); byte[] bytes = Encoding.UTF8.GetBytes(iterationAsJson); string setDefaultIterationEndpoint = string.Format("{0}{1}/iterations/{2}", url, projectId, iteration.Id); using (UnityWebRequest www = UnityWebRequest.Put(setDefaultIterationEndpoint, bytes)) { www.method = "PATCH"; www.SetRequestHeader("Training-Key", trainingKey); www.SetRequestHeader("Content-Type", "application/json"); www.downloadHandler = new DownloadHandlerBuffer(); yield return www.SendWebRequest(); string jsonResponse = www.downloadHandler.text; if (www.isDone) { trainingUI_TextMesh.text = "Default iteration is set \nDeleting Unused Iteration"; StartCoroutine(DeletePreviousIteration(iteration)); } } }Ajoutez la méthode DeletePreviousIteration(). Cette méthode recherche et supprime l’itération précédente autre que celle par défaut :
/// <summary> /// Delete the previous non-default iteration. /// </summary> public IEnumerator DeletePreviousIteration(Iteration iteration) { yield return new WaitForSeconds(5); trainingUI_TextMesh.text = "Deleting Unused \nIteration"; string iterationToDeleteId = string.Empty; string findAllIterationsEndpoint = string.Format("{0}{1}/iterations", url, projectId); using (UnityWebRequest www = UnityWebRequest.Get(findAllIterationsEndpoint)) { www.SetRequestHeader("Training-Key", trainingKey); www.downloadHandler = new DownloadHandlerBuffer(); yield return www.SendWebRequest(); string jsonResponse = www.downloadHandler.text; // The iteration that has just been trained List<Iteration> iterationsList = new List<Iteration>(); iterationsList = JsonConvert.DeserializeObject<List<Iteration>>(jsonResponse); foreach (Iteration i in iterationsList) { if (i.IsDefault != true) { Debug.Log($"Cleaning - Deleting iteration: {i.Name}, {i.Id}"); iterationToDeleteId = i.Id; break; } } } string deleteEndpoint = string.Format("{0}{1}/iterations/{2}", url, projectId, iterationToDeleteId); using (UnityWebRequest www2 = UnityWebRequest.Delete(deleteEndpoint)) { www2.SetRequestHeader("Training-Key", trainingKey); www2.downloadHandler = new DownloadHandlerBuffer(); yield return www2.SendWebRequest(); string jsonResponse = www2.downloadHandler.text; trainingUI_TextMesh.text = "Iteration Deleted"; yield return new WaitForSeconds(2); trainingUI_TextMesh.text = "Ready for next \ncapture"; yield return new WaitForSeconds(2); trainingUI_TextMesh.text = ""; ImageCapture.Instance.ResetImageCapture(); } }La dernière méthode à ajouter dans cette classe est la méthode GetImageAsByteArray(), utilisée sur les appels web pour convertir l’image capturée en un tableau d’octets.
/// <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); }Veillez à enregistrer vos modifications dans Visual Studio avant de revenir à Unity.
Chapitre 10 - Créer la classe SceneOrganiser
Cette classe :
Créez un objet curseur à attacher à la caméra principale.
Créez un objet Étiquette qui s’affiche lorsque le Service reconnaît les objets du monde réel.
Configurez l’appareil photo principal en y attachant les composants appropriés.
En mode Analyse, générez les étiquettes au moment de l’exécution, dans l’espace du monde approprié par rapport à la position de la caméra principale, et affichez les données reçues du service Custom Vision.
En mode Entraînement, générez l’interface utilisateur qui affichera les différentes étapes du processus d’entraînement.
Pour créer cette classe :
Cliquez avec le bouton droit dans le dossier Scripts , puis cliquez sur Créer un>script C#. Nommez le script SceneOrganiser.
Double-cliquez sur le nouveau script SceneOrganiser pour l’ouvrir avec Visual Studio.
Vous n’aurez besoin que d’un seul espace de noms, supprimez les autres au-dessus de la classe SceneOrganiser :
using UnityEngine;Ajoutez ensuite les variables suivantes dans la classe SceneOrganiser , au-dessus de la méthode Start() :
/// <summary> /// Allows this class to behave like a singleton /// </summary> public static SceneOrganiser Instance; /// <summary> /// The cursor object attached to the camera /// </summary> internal GameObject cursor; /// <summary> /// The label used to display the analysis on the objects in the real world /// </summary> internal GameObject label; /// <summary> /// Object providing the current status of the camera. /// </summary> internal TextMesh cameraStatusIndicator; /// <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.5f;Supprimez les méthodes Start() et Update().
Juste en dessous des variables, ajoutez la méthode Awake(), qui initialisera la classe et configurera la scène.
/// <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 CustomVisionTrainer class to this GameObject gameObject.AddComponent<CustomVisionTrainer>(); // Add the VoiceRecogniser class to this GameObject gameObject.AddComponent<VoiceRecognizer>(); // Add the CustomVisionObjects class to this GameObject gameObject.AddComponent<CustomVisionObjects>(); // Create the camera Cursor cursor = CreateCameraCursor(); // Load the label prefab as reference label = CreateLabel(); // Create the camera status indicator label, and place it above where predictions // and training UI will appear. cameraStatusIndicator = CreateTrainingUI("Status Indicator", 0.02f, 0.2f, 3, true); // Set camera status indicator to loading. SetCameraStatus("Loading"); }Ajoutez maintenant la méthode CreateCameraCursor() qui crée et positionne le curseur de l’appareil photo principal, et la méthode CreateLabel(), qui crée l’objet Analysis Label .
/// <summary> /// Spawns cursor for the Main Camera /// </summary> private GameObject CreateCameraCursor() { // Create a sphere as new cursor GameObject newCursor = GameObject.CreatePrimitive(PrimitiveType.Sphere); // Attach it to the camera newCursor.transform.parent = gameObject.transform; // Resize the new cursor newCursor.transform.localScale = new Vector3(0.02f, 0.02f, 0.02f); // Move it to the correct position newCursor.transform.localPosition = new Vector3(0, 0, 4); // Set the cursor color to red newCursor.GetComponent<Renderer>().material = new Material(Shader.Find("Diffuse")); newCursor.GetComponent<Renderer>().material.color = Color.green; return newCursor; } /// <summary> /// Create the analysis label object /// </summary> private GameObject CreateLabel() { // Create a sphere as new cursor GameObject newLabel = new GameObject(); // Resize the new cursor newLabel.transform.localScale = new Vector3(0.01f, 0.01f, 0.01f); // Creating the text of the label TextMesh t = newLabel.AddComponent<TextMesh>(); t.anchor = TextAnchor.MiddleCenter; t.alignment = TextAlignment.Center; t.fontSize = 50; t.text = ""; return newLabel; }Ajoutez la méthode SetCameraStatus(), qui traitera les messages destinés au maillage de texte fournissant le status de la caméra.
/// <summary> /// Set the camera status to a provided string. Will be coloured if it matches a keyword. /// </summary> /// <param name="statusText">Input string</param> public void SetCameraStatus(string statusText) { if (string.IsNullOrEmpty(statusText) == false) { string message = "white"; switch (statusText.ToLower()) { case "loading": message = "yellow"; break; case "ready": message = "green"; break; case "uploading image": message = "red"; break; case "looping capture": message = "yellow"; break; case "analysis": message = "red"; break; } cameraStatusIndicator.GetComponent<TextMesh>().text = $"Camera Status:\n<color={message}>{statusText}..</color>"; } }Ajoutez les méthodes PlaceAnalysisLabel() et SetTagsToLastLabel(), qui généreront et afficheront les données du service Custom Vision dans la scène.
/// <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>(); } /// <summary> /// Set the Tags as Text of the last label created. /// </summary> public void SetTagsToLastLabel(AnalysisObject analysisObject) { lastLabelPlacedText = lastLabelPlaced.GetComponent<TextMesh>(); if (analysisObject.Predictions != null) { foreach (Prediction p in analysisObject.Predictions) { if (p.Probability > 0.02) { lastLabelPlacedText.text += $"Detected: {p.TagName} {p.Probability.ToString("0.00 \n")}"; Debug.Log($"Detected: {p.TagName} {p.Probability.ToString("0.00 \n")}"); } } } }Enfin, ajoutez la méthode CreateTrainingUI(), qui génère l’interface utilisateur affichant les différentes étapes du processus d’entraînement lorsque l’application est en mode Formation. Cette méthode sera également exploitée pour créer l’objet camera status.
/// <summary> /// Create a 3D Text Mesh in scene, with various parameters. /// </summary> /// <param name="name">name of object</param> /// <param name="scale">scale of object (i.e. 0.04f)</param> /// <param name="yPos">height above the cursor (i.e. 0.3f</param> /// <param name="zPos">distance from the camera</param> /// <param name="setActive">whether the text mesh should be visible when it has been created</param> /// <returns>Returns a 3D text mesh within the scene</returns> internal TextMesh CreateTrainingUI(string name, float scale, float yPos, float zPos, bool setActive) { GameObject display = new GameObject(name, typeof(TextMesh)); display.transform.parent = Camera.main.transform; display.transform.localPosition = new Vector3(0, yPos, zPos); display.SetActive(setActive); display.transform.localScale = new Vector3(scale, scale, scale); display.transform.rotation = new Quaternion(); TextMesh textMesh = display.GetComponent<TextMesh>(); textMesh.anchor = TextAnchor.MiddleCenter; textMesh.alignment = TextAlignment.Center; return textMesh; }Veillez à enregistrer vos modifications dans Visual Studio avant de revenir à Unity.
Importante
Avant de continuer, ouvrez la classe CustomVisionAnalyser et, dans la méthode AnalyseLastImageCaptured(),décommentez les lignes suivantes :
AnalysisObject analysisObject = new AnalysisObject();
analysisObject = JsonConvert.DeserializeObject<AnalysisObject>(jsonResponse);
SceneOrganiser.Instance.SetTagsToLastLabel(analysisObject);
Chapitre 11 - Création de la classe ImageCapture
La classe suivante que vous allez créer est la classe ImageCapture .
Cette classe est responsable de :
Capturer une image à l’aide de la caméra HoloLens et la stocker dans le dossier de l’application .
Gestion des mouvements de clic de l’utilisateur.
Conservant la valeur Enum qui détermine si l’application s’exécutera en mode Analyse ou Formation .
Pour créer cette classe :
Accédez au dossier Scripts que vous avez créé précédemment.
Cliquez avec le bouton droit dans le dossier, puis cliquez sur Créer un > script C#. Nommez le script ImageCapture.
Double-cliquez sur le nouveau script ImageCapture pour l’ouvrir avec Visual Studio.
Remplacez les espaces de noms situés en haut du fichier par les espaces de noms suivants :
using System; using System.IO; using System.Linq; using UnityEngine; using UnityEngine.XR.WSA.Input; using UnityEngine.XR.WSA.WebCam;Ajoutez ensuite les variables suivantes à l’intérieur de la classe ImageCapture , au-dessus de la méthode Start() :
/// <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> /// Loop timer /// </summary> private float secondsBetweenCaptures = 10f; /// <summary> /// Application main functionalities switch /// </summary> internal enum AppModes {Analysis, Training } /// <summary> /// Local variable for current AppMode /// </summary> internal AppModes AppMode { get; private set; } /// <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;Le code des méthodes Awake() et Start() doit maintenant être ajouté :
/// <summary> /// Called on initialization /// </summary> private void Awake() { Instance = this; // Change this flag to switch between Analysis Mode and Training Mode AppMode = AppModes.Training; } /// <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 HoloLens API gesture recognizer to track user gestures recognizer = new GestureRecognizer(); recognizer.SetRecognizableGestures(GestureSettings.Tap); recognizer.Tapped += TapHandler; recognizer.StartCapturingGestures(); SceneOrganiser.Instance.SetCameraStatus("Ready"); }Implémentez un gestionnaire qui sera appelé lorsqu’un mouvement de clic se produit.
/// <summary> /// Respond to Tap Input. /// </summary> private void TapHandler(TappedEventArgs obj) { switch (AppMode) { case AppModes.Analysis: if (!captureIsActive) { captureIsActive = true; // Set the cursor color to red SceneOrganiser.Instance.cursor.GetComponent<Renderer>().material.color = Color.red; // Update camera status to looping capture. SceneOrganiser.Instance.SetCameraStatus("Looping Capture"); // Begin the capture loop InvokeRepeating("ExecuteImageCaptureAndAnalysis", 0, secondsBetweenCaptures); } else { // The user tapped while the app was analyzing // therefore stop the analysis process ResetImageCapture(); } break; case AppModes.Training: if (!captureIsActive) { captureIsActive = true; // Call the image capture ExecuteImageCaptureAndAnalysis(); // Set the cursor color to red SceneOrganiser.Instance.cursor.GetComponent<Renderer>().material.color = Color.red; // Update camera status to uploading image. SceneOrganiser.Instance.SetCameraStatus("Uploading Image"); } break; } }Remarque
En mode Analyse , la méthode TapHandler agit comme un commutateur pour démarrer ou arrêter la boucle de capture de photos.
En mode Entraînement , il capture une image à partir de l’appareil photo.
Lorsque le curseur est vert, cela signifie que l’appareil photo est disponible pour prendre l’image.
Lorsque le curseur est rouge, cela signifie que l’appareil photo est occupé.
Ajoutez la méthode utilisée par l’application pour démarrer le processus de capture d’image et stocker l’image.
/// <summary> /// Begin process of Image Capturing and send To Azure Custom Vision Service. /// </summary> private void ExecuteImageCaptureAndAnalysis() { // Update camera status to analysis. SceneOrganiser.Instance.SetCameraStatus("Analysis"); // Create a label in world space using the SceneOrganiser 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(false, delegate (PhotoCapture captureObject) { photoCaptureObject = captureObject; CameraParameters camParameters = new CameraParameters { hologramOpacity = 0.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); }); }); }Ajoutez les gestionnaires qui seront appelés lorsque la photo aura été capturée et lorsqu’elle sera prête à être analysée. Le résultat est ensuite transmis à CustomVisionAnalyser ou CustomVisionTrainer selon le mode sur lequel le code est défini.
/// <summary> /// Register the full execution of the Photo Capture. /// </summary> void OnCapturedPhotoToDisk(PhotoCapture.PhotoCaptureResult result) { // Call StopPhotoMode once the image has successfully captured photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode); } /// <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; switch (AppMode) { case AppModes.Analysis: // Call the image analysis StartCoroutine(CustomVisionAnalyser.Instance.AnalyseLastImageCaptured(filePath)); break; case AppModes.Training: // Call training using captured image CustomVisionTrainer.Instance.RequestTagSelection(); break; } } /// <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; // Update camera status to ready. SceneOrganiser.Instance.SetCameraStatus("Ready"); // Stop the capture loop if active CancelInvoke(); }Veillez à enregistrer vos modifications dans Visual Studio avant de revenir à Unity.
Maintenant que tous les scripts ont été terminés, retournez dans l’éditeur Unity, puis cliquez et faites glisser la classe SceneOrganiser du dossier Scripts vers l’objet Caméra principale dans le panneau hiérarchique.
Chapitre 12 - Avant de construire
Pour effectuer un test approfondi de votre application, vous devez la charger sur votre HoloLens.
Avant de procéder, vérifiez les points suivants :
Tous les paramètres mentionnés dans le chapitre 2 sont correctement réglés.
Tous les champs de la caméra principale, panneau de l’inspecteur, sont attribués correctement.
Le script SceneOrganiser est attaché à l’objet Caméra principale .
Veillez à insérer votre clé de prédiction dans la variable predictionKey .
Vous avez inséré votre point de terminaison de prédiction dans la variable predictionEndpoint .
Vous avez inséré votre clé d’entraînement dans la variable trainingKey de la classe CustomVisionTrainer .
Vous avez inséré votre ID de projet dans la variable projectId de la classe CustomVisionTrainer .
Chapitre 13 : Générer et charger une version test de votre application
Pour commencer le processus de génération :
Accédez aux paramètres de génération de fichier>.
Cochez les projets Unity C#.
Cliquez sur Build. Unity lancera une fenêtre de l’explorateur de fichiers, dans laquelle vous devez créer, puis sélectionner un dossier dans lequel construire l’application. Créez ce dossier maintenant et nommez-le App. Ensuite, avec le dossier App sélectionné, cliquez sur Sélectionner un dossier.
Unity commencera à construire votre projet dans le dossier App .
Une fois que Unity a terminé la génération (cela peut prendre un certain temps), il ouvrira une fenêtre Explorateur de fichiers à l’emplacement de votre build (case activée votre barre des tâches, car elle n’apparaîtra peut-être pas toujours au-dessus de vos fenêtres, mais vous informera de l’ajout d’une nouvelle fenêtre).
Pour déployer sur HoloLens :
Vous aurez besoin de l’adresse IP de votre HoloLens (pour le déploiement à distance) et pour vous assurer que votre HoloLens est en mode développeur. Pour cela :
Lorsque vous portez votre HoloLens, ouvrez les Paramètres.
Accéder auxoptions avancéesdu réseau & duWi-Fi> Internet >
Notez l’adresse IPv4 .
Revenez ensuite à Paramètres, puis à Mise à jour & SecurityFor Developers>
Activez le mode développeur.
Accédez à votre nouvelle build Unity (le dossier App ) et ouvrez le fichier solution avec Visual Studio.
Dans la configuration de la solution, sélectionnez Déboguer.
Dans la plateforme de solutions, sélectionnez x86, Ordinateur distant. Vous serez invité à insérer l’adresse IP d’un appareil distant (le HoloLens, dans ce cas, que vous avez indiqué).
Accédez au menu Générer et cliquez sur Déployer la solution pour charger l’application sur votre HoloLens.
Votre application doit maintenant apparaître dans la liste des applications installées sur votre HoloLens, prête à être lancée !
Remarque
Pour déployer sur un casque immersif, définissez la plateforme de solution sur l’ordinateur local et définissez la configuration sur Déboguer, avec x86 comme plateforme. Déployez ensuite sur l’ordinateur local, à l’aide de l’élément de menu Générer , en sélectionnant Déployer la solution.
Pour utiliser l’application
Pour basculer la fonctionnalité de l’application entre le mode Entraînement et le mode Prédiction , vous devez mettre à jour la variable AppMode , située dans la méthode Awake() située dans la classe ImageCapture .
// Change this flag to switch between Analysis mode and Training mode
AppMode = AppModes.Training;
ou
// Change this flag to switch between Analysis mode and Training mode
AppMode = AppModes.Analysis;
En mode Entraînement :
Regardez la souris ou le clavier et utilisez le geste Appuyer.
Ensuite, un texte apparaîtra vous demandant de fournir une balise.
Disons Souris ou Clavier.
En mode prédiction :
Examinez un objet et utilisez le geste Appuyer.
Le texte s’affiche pour indiquer l’objet détecté, avec la probabilité la plus élevée (normalisé).
Chapitre 14 : Évaluer et améliorer votre modèle Custom Vision
Pour améliorer la précision de votre Service, vous devrez continuer à entraîner le modèle utilisé pour la prédiction. Cela se fait en utilisant votre nouvelle application, avec les modes d’entraînement et de prédiction , ce dernier vous obligeant à visiter le portail, ce qui est couvert dans ce chapitre. Préparez-vous à revisiter votre portail plusieurs fois pour améliorer continuellement votre modèle.
Rendez-vous à nouveau sur votre portail Azure Custom Vision, et une fois que vous êtes dans votre projet, sélectionnez l’onglet Prédictions (en haut au centre de la page) :
Vous verrez toutes les images qui ont été envoyées à votre service pendant l’exécution de votre application. Si vous survolez les images, elles vous fourniront les prédictions qui ont été faites pour cette image :
Sélectionnez l’une de vos images pour l’ouvrir. Une fois ouvert, vous verrez les prédictions effectuées pour cette image à droite. Si les prédictions étaient correctes et que vous souhaitez ajouter cette image au modèle d’entraînement de votre service, cliquez sur la zone de saisie Mes balises , puis sélectionnez la balise que vous souhaitez associer. Lorsque vous avez terminé, cliquez sur le bouton Enregistrer et fermer en bas à droite, puis passez à l’image suivante.
Une fois que vous êtes de retour à la grille d’images, vous remarquerez que les images auxquelles vous avez ajouté des balises (et que vous avez enregistrées) seront supprimées. Si vous trouvez des images qui, selon vous, ne contiennent pas votre élément tagué, vous pouvez les supprimer en cliquant sur la coche de cette image (peut le faire pour plusieurs images), puis en cliquant sur Supprimer dans le coin supérieur droit de la page de la grille. Dans la fenêtre contextuelle qui suit, vous pouvez cliquer sur Oui, supprimer ou Non, pour confirmer la suppression ou l’annuler, respectivement.
Lorsque vous êtes prêt à continuer, cliquez sur le bouton vert Entraîner en haut à droite. Votre modèle de service sera entraîné avec toutes les images que vous avez maintenant fournies (ce qui le rendra plus précis). Une fois l’apprentissage terminé, veillez à cliquer à nouveau sur le bouton Définir par défaut , afin que votre URL de prédiction continue d’utiliser l’itération la plus récente de votre service.
Votre application API Custom Vision terminée
Félicitations, vous avez créé une application de réalité mixte qui tire parti de l’API Azure Custom Vision pour reconnaître des objets du monde réel, entraîner le modèle de service et afficher la confiance dans ce qui a été vu.
Exercices bonus
Exercice 1
Entraîner votre service Custom Vision à reconnaître davantage d’objets.
Exercice 2
Pour approfondir ce que vous avez appris, effectuez les exercices suivants :
Émettre un signal sonore lorsqu’un objet est reconnu.
Exercice 3
Utilisez l’API pour réentraîner votre service avec les mêmes images que celles que votre application analyse, afin de rendre le service plus précis (effectuer simultanément la prédiction et l’entraînement).