Dela via


HoloLens (första generationen) och Azure 310: Objektidentifiering

Kommentar

Självstudierna för Mixed Reality Academy har utformats med HoloLens (första generationen) och Mixed Reality Immersive Headsets i åtanke. Därför anser vi att det är viktigt att låta de här självstudierna vara kvar för utvecklare som fortfarande letar efter vägledning för att utveckla för dessa enheter. De här självstudierna uppdateras inte med de senaste verktygsuppsättningarna eller interaktionerna som används för HoloLens 2. De underhålls för att fortsätta arbeta med de enheter som stöds. Det kommer att finnas en ny serie självstudier som kommer att publiceras i framtiden som visar hur du utvecklar för HoloLens 2. Det här meddelandet uppdateras med en länk till de självstudierna när de publiceras.


I den här kursen får du lära dig hur du känner igen anpassat visuellt innehåll och dess rumsliga position i en angivet bild med hjälp av azure Custom Vision-funktioner för objektidentifiering i ett program för mixad verklighet.

Med den här tjänsten kan du träna en maskininlärningsmodell med hjälp av objektbilder. Du kommer sedan att använda den tränade modellen för att identifiera liknande objekt och ungefärlig deras plats i den verkliga världen, enligt kamerainspelningen av Microsoft HoloLens eller en kameraanslutning till en dator för uppslukande (VR) headset.

kursresultat

Azure Custom Vision, Objektidentifiering är en Microsoft-tjänst som gör det möjligt för utvecklare att skapa anpassade bildklassificerare. Dessa klassificerare kan sedan användas med nya bilder för att identifiera objekt i den nya bilden genom att ange Box Boundaries i själva bilden. Tjänsten är en enkel onlineportal som är enkel att använda för att effektivisera den här processen. Mer information finns på följande länkar:

När kursen är klar har du ett program för mixad verklighet som kan göra följande:

  1. Användaren kommer att kunna titta på ett objekt som de har tränat med hjälp av Azure Custom Vision Service, Object Detection.
  2. Användaren använder Tap-gesten för att avbilda en bild av vad de tittar på.
  3. Appen skickar avbildningen till Azure Custom Vision Service.
  4. Det kommer att finnas ett svar från tjänsten som visar resultatet av igenkänningen som text i världsrymden. Detta uppnås genom användning av Microsoft HoloLens spatial spårning, som ett sätt att förstå det identifierade objektets världsposition och sedan använda taggen som är associerad med det som identifieras i bilden, för att tillhandahålla etiketttexten.

Kursen går även igenom hur du laddar upp bilder manuellt, skapar taggar och tränar tjänsten att identifiera olika objekt (i det angivna exemplet en kopp) genom att ange gränsrutan i den bild som du skickar in.

Viktigt!

När appen har skapats och använts bör utvecklaren gå tillbaka till Azure Custom Vision Service och identifiera de förutsägelser som gjorts av tjänsten och avgöra om de var korrekta eller inte (genom att tagga något som tjänsten missade och justera avgränsningsrutorna). Tjänsten kan sedan tränas om, vilket ökar sannolikheten för att den identifierar verkliga objekt.

Den här kursen lär dig hur du hämtar resultaten från Azure Custom Vision Service, Object Detection, till ett Unity-baserat exempelprogram. Det är upp till dig att tillämpa dessa begrepp på ett anpassat program som du kanske skapar.

Stöd för enheter

Kurs HoloLens Uppslukande headset
MR och Azure 310: Objektidentifiering ✔️

Förutsättningar

Kommentar

Den här självstudien är utformad för utvecklare som har grundläggande erfarenhet av Unity och C#. Tänk också på att kraven och de skriftliga instruktionerna i det här dokumentet representerar det som har testats och verifierats i skrivande stund (juli 2018). Du är fri att använda den senaste programvaran, som anges i artikeln installera verktyg , men det bör inte antas att informationen i den här kursen perfekt matchar vad du hittar i nyare programvara än vad som anges nedan.

Vi rekommenderar följande maskinvara och programvara för den här kursen:

Innan du börjar

  1. För att undvika problem med att skapa det här projektet rekommenderar vi starkt att du skapar projektet som nämns i den här självstudien i en rotmapp eller nära rotmapp (långa mappsökvägar kan orsaka problem vid byggtid).
  2. Konfigurera och testa dina HoloLens. Om du behöver stöd för detta kan du gå till installationsartikeln för HoloLens.
  3. Det är en bra idé att utföra kalibrering och sensorjustering när du börjar utveckla en ny HoloLens-app (ibland kan det hjälpa till att utföra dessa uppgifter för varje användare).

Om du behöver hjälp med kalibrering kan du följa den här länken till artikeln HoloLens-kalibrering.

Om du vill ha hjälp med sensorjustering följer du den här länken till artikeln HoloLens Sensor Tuning.

Kapitel 1 – Custom Vision-portalen

Om du vill använda Azure Custom Vision Service måste du konfigurera en instans av den som ska göras tillgänglig för ditt program.

  1. till huvudsidan för Custom Vision Service.

  2. Klicka på Komma igång.

    Skärmbild som visar knappen Komma igång.

  3. Logga in på Custom Vision-portalen.

    Skärmbild som visar knappen Logga in.

  4. Om du inte redan har ett Azure-konto måste du skapa ett. Om du följer den här självstudien i en klassrums- eller labbsituation ber du din instruktör eller någon av rektorerna om hjälp med att konfigurera ditt nya konto.

  5. När du är inloggad för första gången uppmanas du att använda panelen Användarvillkor . Klicka på kryssrutan för att godkänna villkoren. Klicka sedan på Jag godkänner.

    Skärmbild som visar panelen Användarvillkor.

  6. Efter att ha gått med på villkoren finns du nu i avsnittet Mina projekt . Klicka på Nytt projekt.

    Skärmbild som visar var du väljer Nytt projekt.

  7. En flik visas till höger, vilket uppmanar dig att ange några fält för projektet.

    1. Infoga ett namn för projektet

    2. Infoga en beskrivning för projektet (valfritt)

    3. Välj en resursgrupp eller skapa en ny. En resursgrupp är ett sätt att övervaka, kontrollera åtkomst, etablera och hantera fakturering för en samling Azure-tillgångar. Vi rekommenderar att du behåller alla Azure-tjänster som är associerade med ett enskilt projekt (t.ex. dessa kurser) under en gemensam resursgrupp).

      Skärmbild som visar var du kan lägga till information för det nya projektet.

    4. Ange Projekttyper som objektidentifiering (förhandsversion).

  8. När du är klar klickar du på Skapa projekt så omdirigeras du till projektsidan för Custom Vision Service.

Kapitel 2 – Träna ditt Custom Vision-projekt

I Custom Vision-portalen är ditt primära mål att träna projektet att identifiera specifika objekt i bilder.

Du behöver minst femton (15) bilder för varje objekt som du vill att programmet ska känna igen. Du kan använda bilderna som medföljer den här kursen (en serie koppar).

Så här tränar du ditt Custom Vision-projekt:

  1. Klicka på + knappen bredvid Taggar.

    Skärmbild som visar knappen + bredvid Taggar.

  2. Lägg till ett namn för taggen som ska användas för att associera dina bilder med. I det här exemplet använder vi bilder av koppar för igenkänning, så har namngett taggen för den här cupen. Klicka på Spara när det är klart.

    Skärmbild som visar var du lägger till ett namn för taggen.

  3. Du kommer att märka att taggen har lagts till (du kan behöva läsa in sidan igen för att den ska visas).

    Skärmbild som visar var taggen läggs till.

  4. Klicka på Lägg till bilder i mitten av sidan.

    Skärmbild som visar var bilder ska läggas till.

  5. Klicka på Bläddra bland lokala filer och bläddra till de bilder som du vill ladda upp för ett objekt, med minst femton (15).

    Dricks

    Du kan välja flera bilder i taget för att ladda upp.

    Skärmbild som visar de bilder som du kan ladda upp.

  6. Tryck på Ladda upp filer när du har valt alla bilder som du vill träna projektet med. Filerna börjar laddas upp. När du har bekräftat uppladdningen klickar du på Klar.

    Skärmbild som visar förloppet för de uppladdade bilderna.

  7. Nu laddas dina bilder upp, men taggas inte.

    Skärmbild som visar en bild som inte är taggad.

  8. Om du vill tagga dina bilder använder du musen. När du hovra över bilden hjälper en markeringsmarkering dig genom att automatiskt rita en markering runt objektet. Om det inte är korrekt kan du rita dina egna. Detta uppnås genom att hålla vänsterklicka på musen och dra markeringsregionen för att omfatta objektet.

    Skärmbild som visar hur du taggar en bild.

  9. När du har valt objektet i bilden uppmanas du att lägga till regiontagg i en liten fråga. Välj taggen som du skapade tidigare ('Cup', i exemplet ovan) eller om du lägger till fler taggar skriver du in den och klickar på knappen + (plus).

    Skärmbild som visar taggen som du lade till i bilden.

  10. Om du vill tagga nästa bild kan du klicka på pilen till höger om bladet eller stänga taggbladet (genom att klicka på X i det övre högra hörnet på bladet) och sedan klicka på nästa bild. När du har nästa bild klar upprepar du samma procedur. Gör detta för alla bilder som du har laddat upp tills alla är taggade.

    Kommentar

    Du kan välja flera objekt i samma bild, till exempel bilden nedan:

    Skärmbild som visar flera objekt i en bild.

  11. När du har taggat dem alla klickar du på den taggade knappen till vänster på skärmen för att visa de taggade bilderna.

    Skärmbild som visar knappen Taggad.

  12. Nu är du redo att träna din tjänst. Klicka på knappen Träna så börjar den första tränings-iterationen.

    Skärmbild som visar knappen Träna.

    Skärmbild som visar den första tränings iterationen.

  13. När den har skapats kan du se två knappar med namnet Gör standard och Förutsägelse-URL. Klicka på Gör standard först och klicka sedan på Förutsägelse-URL.

    Skärmbild som visar knappen Gör standard.

    Kommentar

    Slutpunkten som tillhandahålls från detta är inställd på den iteration som har markerats som standard. Om du senare gör en ny iteration och uppdaterar den som standard behöver du inte ändra koden.

  14. När du har klickat på Förutsägelse-URL öppnar du Anteckningar och kopierar och klistrar in URL:en (kallas även din förutsägelseslutpunkt) och tjänstens förutsägelsenyckel, så att du kan hämta den när du behöver den senare i koden.

    Skärmbild som visar förutsägelseslutpunkten och preditionsnyckeln.

Kapitel 3 – Konfigurera Unity-projektet

Följande är en typisk konfiguration för utveckling med mixad verklighet och är därför en bra mall för andra projekt.

  1. Öppna Unity och klicka på Nytt.

    Skärmbild som visar knappen Nytt.

  2. Nu måste du ange ett Unity-projektnamn. Infoga CustomVisionObjDetection. Kontrollera att projekttypen är inställd på 3D och ange platsen till någonstans som passar dig (kom ihåg att närmare rotkataloger är bättre). Klicka sedan på Skapa projekt.

    Skärmbild som visar projektinformationen och var du väljer Skapa projekt.

  3. När Unity är öppet är det värt att kontrollera att standardskriptredigeraren är inställd på Visual Studio. Gå till Redigera>inställningar och gå sedan till Externa verktyg från det nya fönstret. Ändra extern skriptredigerare till Visual Studio. Stäng fönstret Inställningar .

    Skärmbild som visar var du vill ändra den externa skriptredigeraren till Visual Studio.

  4. Gå sedan till Inställningar för filbygge > och växla plattform till Universell Windows-plattform och klicka sedan på knappen Växla plattform.

    Skärmbild som visar knappen Växla plattform.

  5. I samma fönster för bygginställningar kontrollerar du att följande är inställda:

    1. Målenheten är inställd på HoloLens

    2. Byggtyp är inställd på D3D

    3. SDK är inställt på Senaste installerat

    4. Visual Studio-versionen är inställd på Senaste installerad

    5. Build and Run är inställt på Lokal dator

    6. De återstående inställningarna i Bygginställningar bör vara kvar som standard för tillfället.

      Skärmbild som visar konfigurationsalternativen För bygginställning.

  6. I samma fönster för bygginställningar klickar du på knappen Spelarinställningar. Då öppnas den relaterade panelen i det utrymme där kontrollanten finns.

  7. I den här panelen måste några inställningar verifieras:

    1. På fliken Andra inställningar :

      1. Skriptkörningsversionen bör vara experimentell (.NET 4.6-motsvarighet), vilket utlöser ett behov av att starta om redigeraren.

      2. Skriptserverdelen ska vara .NET.

      3. API-kompatibilitetsnivån ska vara .NET 4.6.

        Skärmbild som visar alternativet API-kompatibilitetsnivå inställt på .NET 4.6.

    2. På fliken Publiceringsinställningar går du till Funktioner och kontrollerar:

      1. InternetClient

      2. Webbkamera

      3. SpatialPerception

        Skärmbild som visar den övre halvan av konfigurationsalternativen För funktioner.Skärmbild som visar den nedre halvan av konfigurationsalternativen För funktioner.

    3. Längre ned på panelen, i XR-inställningar (som finns under Publiceringsinställningar), markerar du Virtual Reality Supported (Virtual Reality Supported) och kontrollerar sedan att Windows Mixed Reality SDK har lagts till.

      Skärmbild som visar att Windows Mixed Reality SDK läggs till.

  8. I Bygginställningar är Unity C# Projects inte längre nedtonat: markera kryssrutan bredvid detta.

  9. Stäng fönstret Build Settings (Bygginställningar).

  10. I redigeraren klickar du på Redigera>projektinställningar>Grafik.

    Skärmbild som visar menyalternativet Grafik valt.

  11. I Kontrollpanelen är grafikinställningarna öppna. Rulla nedåt tills du ser en matris med namnet Always Include Shaders. Lägg till ett fack genom att öka variabeln Storlek med en (i det här exemplet var det 8 så vi gjorde det till 9). Ett nytt fack visas i matrisens sista position enligt nedan:

    Skärmbild som visar matrisen Always Included Shaders.

  12. I facket klickar du på den lilla målcirkeln bredvid facket för att öppna en lista över skuggningar. Leta efter den äldre skuggningen /transparent/diffus skuggning och dubbelklicka på den.

    Skärmbild som visar skuggning av äldre skuggningar/transparent/diffus skuggning.

Kapitel 4 – Importera CustomVisionObjDetection Unity-paketet

För den här kursen får du ett Unity Asset Package med namnet Azure-MR-310.unitypackage.

[TIPS] Alla objekt som stöds av Unity, inklusive hela scener, kan paketeras i en .unitypackage-fil och exporteras/importeras i andra projekt. Det är det säkraste och mest effektiva sättet att flytta tillgångar mellan olika Unity-projekt.

Du hittar det Azure-MR-310-paket som du behöver ladda ned här.

  1. Med Unity-instrumentpanelen framför dig klickar du på Tillgångar i menyn överst på skärmen och klickar sedan på Importera paket > anpassat paket.

    Skärmbild som visar menyalternativet Anpassat paket.

  2. Använd filväljaren för att välja paketet Azure-MR-310.unitypackage och klicka på Öppna. En lista över komponenter för den här tillgången visas för dig. Bekräfta importen genom att klicka på knappen Importera .

    Skärmbild som visar listan över tillgångskomponenter som du vill importera.

  3. När importen är klar ser du att mappar från paketet nu har lagts till i mappen Tillgångar . Den här typen av mappstruktur är typisk för ett Unity-projekt.

    Skärmbild som visar innehållet i mappen Tillgångar.

    1. Mappen Material innehåller det material som används av blickmarkören.

    2. Mappen Plugins innehåller newtonsoft DLL som används av koden för att deserialisera tjänstens webbsvar. De två (2) olika versionerna som finns i mappen och undermappen är nödvändiga för att biblioteket ska kunna användas och byggas av både Unity-redigeraren och UWP-versionen.

    3. Prefabs-mappen innehåller de prefabs som finns i scenen. Dessa är:

      1. GazeCursor, markören som används i programmet. Kommer att arbeta tillsammans med SpatialMapping-prefab för att kunna placeras i scenen ovanpå fysiska objekt.
      2. Etiketten, som är det användargränssnittsobjekt som används för att visa objekttaggen i scenen vid behov.
      3. SpatialMapping, som är det objekt som gör att programmet kan använda för att skapa en virtuell karta, med hjälp av Microsoft HoloLens rumsliga spårning.
    4. Mappen Scener som för närvarande innehåller den färdiga scenen för den här kursen.

  4. Öppna mappen Scener i projektpanelen och dubbelklicka på ObjDetectionScene för att läsa in scenen som du ska använda för den här kursen.

    Skärmbild som visar ObjDetectionScene i mappen Scener.

    Kommentar

    Ingen kod ingår, du kommer att skriva koden genom att följa den här kursen.

Kapitel 5 – Skapa klassen CustomVisionAnalyser.

Nu är du redo att skriva kod. Du börjar med klassen CustomVisionAnalyser .

Kommentar

Anropen till Custom Vision Service, som görs i koden som visas nedan, görs med hjälp av Custom Vision REST-API:et. Med hjälp av detta får du se hur du implementerar och använder det här API:et (användbart för att förstå hur du implementerar något liknande på egen hand). Tänk på att Microsoft erbjuder en Custom Vision SDK som också kan användas för att göra anrop till tjänsten. Mer information finns i artikeln om Custom Vision SDK.

Den här klassen ansvarar för:

  • Läser in den senaste avbildningen som tagits som en matris med byte.

  • Skicka bytematrisen till din Azure Custom Vision Service-instans för analys.

  • Tar emot svaret som en JSON-sträng.

  • Deserialisera svaret och skicka den resulterande förutsägelsen till klassen SceneOrganiser , som tar hand om hur svaret ska visas.

Så här skapar du den här klassen:

  1. Högerklicka i tillgångsmappen i projektpanelen och klicka sedan på Skapa>mapp. Anropa mappen Skript.

    Skärmbild som visar hur du skapar mappen Skript.

  2. Dubbelklicka på den nyligen skapade mappen för att öppna den.

  3. Högerklicka i mappen och klicka sedan på Skapa>C#-skript. Ge skriptet namnet CustomVisionAnalyser.

  4. Dubbelklicka på det nya CustomVisionAnalyser-skriptet för att öppna det med Visual Studio.

  5. Kontrollera att du har följande namnområden som refereras längst upp i filen:

    using Newtonsoft.Json;
    using System.Collections;
    using System.IO;
    using UnityEngine;
    using UnityEngine.Networking;
    
  6. Lägg till följande variabler i klassen CustomVisionAnalyser :

        /// <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;
    

    Kommentar

    Se till att du infogar din tjänstförutsägelsenyckel i variabeln predictionKey och förutsägelseslutpunkten i variabeln predictionEndpoint. Du kopierade dessa till Anteckningar tidigare, i kapitel 2, steg 14.

  7. Kod för Awake() måste nu läggas till för att initiera instansvariabeln:

        /// <summary>
        /// Initializes this class
        /// </summary>
        private void Awake()
        {
            // Allows this instance to behave like a singleton
            Instance = this;
        }
    
  8. Lägg till coroutine -metoden (med den statiska metoden GetImageAsByteArray() under den), som hämtar resultatet av analysen av bilden, som avbildas av klassen ImageCapture .

    Kommentar

    I coroutinen AnalyseImageCapture finns det ett anrop till klassen SceneOrganiser som du ännu inte har skapat. Lämna därför dessa rader kommenterade för tillfället.

        /// <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. Ta bort metoderna Start() och Update() eftersom de inte kommer att användas.

  10. Se till att spara ändringarna i Visual Studio innan du återvänder till Unity.

Viktigt!

Som tidigare nämnts bör du inte oroa dig för kod som kan verka ha ett fel, eftersom du kommer att tillhandahålla ytterligare klasser snart, vilket kommer att åtgärda dessa.

Kapitel 6 – Skapa klassen CustomVisionObjects

Klassen du skapar nu är klassen CustomVisionObjects .

Det här skriptet innehåller ett antal objekt som används av andra klasser för att serialisera och deserialisera anropen till Custom Vision Service.

Så här skapar du den här klassen:

  1. Högerklicka i mappen Skript och klicka sedan på Skapa>C#-skript. Anropa skriptet CustomVisionObjects.

  2. Dubbelklicka på det nya CustomVisionObjects-skriptet för att öppna det med Visual Studio.

  3. Kontrollera att du har följande namnområden som refereras längst upp i filen:

    using System;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.Networking;
    
  4. Ta bort metoderna Start() och Update() i klassen CustomVisionObjects. Den här klassen bör nu vara tom.

    Varning

    Det är viktigt att du följer nästa instruktion noggrant. Om du placerar de nya klassdeklarationerna i klassen CustomVisionObjects får du kompileringsfel i kapitel 10 som anger att AnalysisRootObject och BoundingBox inte hittas.

  5. Lägg till följande klasser utanför klassen CustomVisionObjects . Dessa objekt används av Newtonsoft-biblioteket för att serialisera och deserialisera svarsdata:

    // 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. Se till att spara ändringarna i Visual Studio innan du återvänder till Unity.

Kapitel 7 – Skapa klassen SpatialMapping

Den här klassen anger Spatial Mapping Collider i scenen så att du kan identifiera kollisioner mellan virtuella objekt och verkliga objekt.

Så här skapar du den här klassen:

  1. Högerklicka i mappen Skript och klicka sedan på Skapa>C#-skript. Anropa skriptet SpatialMapping.

  2. Dubbelklicka på det nya SpatialMapping-skriptet för att öppna det med Visual Studio.

  3. Kontrollera att du har följande namnområden som refereras ovanför klassen SpatialMapping :

    using UnityEngine;
    using UnityEngine.XR.WSA;
    
  4. Lägg sedan till följande variabler i klassen SpatialMapping ovanför metoden Start( ):

        /// <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. Lägg till Awake () och 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. Ta bort metoden Update().

  7. Se till att spara ändringarna i Visual Studio innan du återvänder till Unity.

Kapitel 8 – Skapa klassen GazeCursor

Den här klassen ansvarar för att konfigurera markören på rätt plats i det verkliga utrymmet genom att använda SpatialMappingCollider, som skapades i föregående kapitel.

Så här skapar du den här klassen:

  1. Högerklicka i mappen Skript och klicka sedan på Skapa>C#-skript. Anropa skriptet GazeCursor

  2. Dubbelklicka på det nya GazeCursor-skriptet för att öppna det med Visual Studio.

  3. Kontrollera att du har följande namnområde som refereras ovanför klassen GazeCursor :

    using UnityEngine;
    
  4. Lägg sedan till följande variabel i klassen GazeCursor ovanför metoden Start().

        /// <summary>
        /// The cursor (this object) mesh renderer
        /// </summary>
        private MeshRenderer meshRenderer;
    
  5. Uppdatera metoden Start() med följande kod:

        /// <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. Uppdatera metoden Update() med följande kod:

        /// <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;
            }
        }
    

    Kommentar

    Oroa dig inte för att felet för klassen SceneOrganiser inte hittas. Du skapar det i nästa kapitel.

  7. Se till att spara ändringarna i Visual Studio innan du återvänder till Unity.

Kapitel 9 – Skapa klassen SceneOrganiser

Den här klassen kommer att:

  • Konfigurera huvudkameran genom att koppla lämpliga komponenter till den.

  • När ett objekt identifieras ansvarar det för att beräkna sin position i den verkliga världen och placera en taggetikett nära det med lämpligt taggnamn.

Så här skapar du den här klassen:

  1. Högerklicka i mappen Skript och klicka sedan på Skapa>C#-skript. Ge skriptet namnet SceneOrganiser.

  2. Dubbelklicka på det nya SceneOrganiser-skriptet för att öppna det med Visual Studio.

  3. Kontrollera att du har följande namnområden som refereras ovanför klassen SceneOrganiser :

    using System.Collections.Generic;
    using System.Linq;
    using UnityEngine;
    
  4. Lägg sedan till följande variabler i klassen SceneOrganiser ovanför metoden Start( ):

        /// <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. Ta bort metoderna Start() och Update().

  6. Under variablerna lägger du till metoden Awake(), som initierar klassen och konfigurerar scenen.

        /// <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. Lägg till metoden PlaceAnalysisLabel(), som instansierar etiketten i scenen (som i det här läget är osynlig för användaren). Den placerar också fyrhjulingen (också osynlig) där bilden placeras och överlappar den verkliga världen. Detta är viktigt eftersom de lådkoordinater som hämtats från tjänsten efter analysen spåras tillbaka till den här fyrhjulingen för att fastställa den ungefärliga platsen för objektet i den verkliga världen.

        /// <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. Lägg till metoden FinaliseLabel(). Den ansvarar för:

    • Ange etiketttexten med taggen förutsägelse med högsta konfidens.
    • Anropa beräkningen av avgränsningsrutan på quad-objektet, som placerats tidigare och placera etiketten i scenen.
    • Justera etikettdjupet med hjälp av en Raycast mot avgränsningslådan, som ska kollidera mot objektet i den verkliga världen.
    • Återställa avbildningsprocessen så att användaren kan avbilda en annan bild.
        /// <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. Lägg till metoden CalculateBoundingBoxPosition(), som är värd för ett antal beräkningar som krävs för att översätta avgränsningsrutans koordinater som hämtats från tjänsten och återskapa dem proportionellt på quaden.

        /// <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. Se till att spara ändringarna i Visual Studio innan du återvänder till Unity.

    Viktigt!

    Innan du fortsätter öppnar du klassen CustomVisionAnalyser och i metoden AnalyseLastImageCaptured() avkommentarer du följande rader:

    // 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);
    

Kommentar

Oroa dig inte för imagecapture-klassen "det gick inte att hitta" meddelandet, du skapar det i nästa kapitel.

Kapitel 10 – Skapa klassen ImageCapture

Nästa klass som du ska skapa är klassen ImageCapture .

Den här klassen ansvarar för:

  • Avbilda en bild med HoloLens-kameran och lagra den i mappen App .
  • Hantera tryckgester från användaren.

Så här skapar du den här klassen:

  1. Gå till mappen Skript som du skapade tidigare.

  2. Högerklicka i mappen och klicka sedan på Skapa>C#-skript. Ge skriptet namnet ImageCapture.

  3. Dubbelklicka på det nya ImageCapture-skriptet för att öppna det med Visual Studio.

  4. Ersätt namnrymderna överst i filen med följande:

    using System;
    using System.IO;
    using System.Linq;
    using UnityEngine;
    using UnityEngine.XR.WSA.Input;
    using UnityEngine.XR.WSA.WebCam;
    
  5. Lägg sedan till följande variabler i klassen ImageCapture ovanför metoden 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>
        /// 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. Kod för metoderna Awake() och Start() måste nu läggas till:

        /// <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. Implementera en hanterare som anropas när en Tryckgest inträffar:

        /// <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);
            }
        }
    

    Viktigt!

    När markören är grön betyder det att kameran är tillgänglig för att ta bilden. När markören är röd betyder det att kameran är upptagen.

  8. Lägg till den metod som programmet använder för att starta avbildningsprocessen och lagra avbildningen:

        /// <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. Lägg till de hanterare som anropas när fotot har avbildats och för när det är redo att analyseras. Resultatet skickas sedan till CustomVisionAnalyser för analys.

        /// <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. Se till att spara ändringarna i Visual Studio innan du återvänder till Unity.

Kapitel 11 – Konfigurera skripten i scenen

Nu när du har skrivit all kod som behövs för det här projektet är det dags att konfigurera skripten i scenen och på prefaberna så att de fungerar korrekt.

  1. I Unity-redigeraren går du till hierarkipanelen och väljer huvudkameran.

  2. I Kontrollpanelen, med huvudkameran markerad, klickar du på Lägg till komponent och söker sedan efter SceneOrganiser-skript och dubbelklickar för att lägga till det.

    Skärmbild som visar Skriptet SceneOrganizer.

  3. I projektpanelen öppnar du mappen Prefabs, drar prefab för etikett till området Etikett tom referensmålinmatning i skriptet SceneOrganiser som du just har lagt till i huvudkameran, som du ser i bilden nedan:

    Skärmbild som visar skriptet som du lade till i huvudkameran.

  4. I hierarkipanelen väljer du GazeCursor-underordnad till huvudkameran.

  5. I Kontrollpanelen, med GazeCursor markerat, klickar du på Lägg till komponent och söker sedan efter GazeCursor-skript och dubbelklickar för att lägga till det.

    Skärmbild som visar var du lägger till GazeCursor-skriptet.

  6. I hierarkipanelen väljer du spatialmapping-underordnad till huvudkameran.

  7. I Kontrollpanelen, med SpatialMapping markerat, klickar du på Lägg till komponent, söker sedan efter SpatialMapping-skript och dubbelklickar för att lägga till det.

    Skärmbild som visar var du lägger till SpatialMapping-skriptet.

Återstående skript som du inte har angett läggs till av koden i SceneOrganiser-skriptet under körningen.

Kapitel 12 - Innan du bygger

För att kunna utföra ett grundligt test av ditt program måste du läsa in det separat på Microsoft HoloLens.

Innan du gör det kontrollerar du att:

  • Alla inställningar som anges i kapitel 3 är korrekt inställda.

  • Skriptet SceneOrganiser är kopplat till main camera-objektet .

  • Skriptet GazeCursor är kopplat till GazeCursor-objektet .

  • Skriptet SpatialMapping är kopplat till SpatialMapping-objektet .

  • I kapitel 5, steg 6:

    • Se till att du infogar din tjänstförutsägelsenyckel i variabeln predictionKey .
    • Du har infogat förutsägelseslutpunkten i klassen predictionEndpoint.

Kapitel 13 – Skapa UWP-lösningen och läs in programmet separat

Nu är du redo att skapa ditt program som en UWP-lösning som du kommer att kunna distribuera till Microsoft HoloLens. Så här påbörjar du byggprocessen:

  1. Gå till Inställningar för filbygge>.

  2. Markera Unity C#-projekt.

  3. Klicka på Lägg till öppna scener. Då läggs den öppna scenen till i bygget.

    Skärmbild som visar knappen Lägg till öppna scener.

  4. Klicka på Skapa. Unity startar ett Utforskaren fönster där du behöver skapa och väljer sedan en mapp som appen ska byggas in i. Skapa mappen nu och ge den namnet App. Klicka sedan på Välj mapp med appmappen markerad.

  5. Unity börjar skapa projektet i mappen App .

  6. När Unity har skapats (det kan ta lite tid) öppnas ett Utforskaren fönster på platsen för bygget (kontrollera aktivitetsfältet eftersom det kanske inte alltid visas ovanför dina fönster, men meddelar dig om att ett nytt fönster har lagts till).

  7. För att distribuera vidare till Microsoft HoloLens behöver du IP-adressen för den enheten (för fjärrdistribution) och för att säkerställa att den även har konfigurerat utvecklarläge . Så här gör du:

    1. Öppna inställningarna när du använder HoloLens.

    2. Gå till Avancerade alternativ för nätverks- och Internet-Wi-Fi>>

    3. Observera IPv4-adressen.

    4. Gå sedan tillbaka till Inställningar och sedan till Uppdatera och säkerhet>för utvecklare

    5. Ange utvecklarläge .

  8. Gå till din nya Unity-version (appmappen) och öppna lösningsfilen med Visual Studio.

  9. I Lösningskonfiguration väljer du Felsök.

  10. I Lösningsplattformen väljer du x86, Fjärrdator. Du uppmanas att infoga IP-adressen för en fjärrenhet (Microsoft HoloLens, i det här fallet som du noterade).

    Skärmbild som visar var IP-adressen ska infogas.

  11. Gå till menyn Skapa och klicka på Distribuera lösning för att separat läsa in programmet till dina HoloLens.

  12. Din app bör nu visas i listan över installerade appar på Microsoft HoloLens, redo att startas!

Så här använder du programmet:

  • Titta på ett objekt som du har tränat med Azure Custom Vision Service, Objektidentifiering och använd gesten Tryck.
  • Om objektet har identifierats visas en etiketttext i världsrymden med taggnamnet.

Viktigt!

Varje gång du tar ett foto och skickar det till tjänsten kan du gå tillbaka till sidan Tjänst och träna om tjänsten med de nyligen insamlade bilderna. I början måste du förmodligen också korrigera avgränsningsrutorna för att vara mer exakta och träna om tjänsten.

Kommentar

Etiketttexten som placeras kanske inte visas i närheten av objektet när Microsoft HoloLens-sensorerna och/eller SpatialTrackingComponent i Unity inte kan placera lämpliga kolliderare i förhållande till verkliga objekt. Försök att använda programmet på en annan yta om så är fallet.

Ditt Custom Vision-program för objektidentifiering

Grattis, du har skapat en mixed reality-app som använder Azure Custom Vision, API för objektidentifiering, som kan identifiera ett objekt från en bild och sedan ge objektet en ungefärlig position i 3D-utrymme.

Skärmbild som visar en mixed reality-app som använder Azure Custom Vision, API för objektidentifiering.

Bonusövningar

Övning 1

Lägg till i textetiketten och använd en halvtransparent kub för att omsluta det verkliga objektet i en 3D-avgränsningsruta.

Övning 2

Träna Din Custom Vision Service att identifiera fler objekt.

Övning 3

Spela upp ett ljud när ett objekt känns igen.

Övning 4

Använd API:et för att träna om tjänsten med samma bilder som din app analyserar, så att tjänsten blir mer exakt (gör både förutsägelse och träning samtidigt).