Delen via


HoloLens (1e generatie) en Azure 310: Objectdetectie

Notitie

De Mixed Reality Academy-zelfstudies zijn ontworpen met HoloLens (1e generatie) en Mixed Reality Immersive Headsets in gedachten. Daarom vinden we het belangrijk om deze zelfstudies te behouden voor ontwikkelaars die nog steeds op zoek zijn naar richtlijnen bij het ontwikkelen voor die apparaten. Deze zelfstudies worden niet bijgewerkt met de nieuwste toolsets of interacties die worden gebruikt voor HoloLens 2. Ze blijven behouden om door te gaan met het werken op de ondersteunde apparaten. Er is een nieuwe reeks zelfstudies die in de toekomst zullen worden gepost en die laten zien hoe u zich ontwikkelt voor HoloLens 2. Deze kennisgeving wordt bijgewerkt met een koppeling naar deze zelfstudies wanneer ze worden gepost.


In deze cursus leert u hoe u aangepaste visuele inhoud en de ruimtelijke positie binnen een opgegeven afbeelding kunt herkennen met behulp van de mogelijkheden van Azure Custom Vision 'Objectdetectie' in een mixed reality-toepassing.

Met deze service kunt u een machine learning-model trainen met behulp van objectinstallatiekopieën. Vervolgens gebruikt u het getrainde model om vergelijkbare objecten te herkennen en hun locatie in de echte wereld te benaderen, zoals wordt geleverd door de cameraopname van Microsoft HoloLens of een camera die verbinding maakt met een pc voor insluitende headsets (VR).

cursusresultaat

Azure Custom Vision, objectdetectie is een Microsoft-service waarmee ontwikkelaars aangepaste afbeeldingsclassificaties kunnen bouwen. Deze classificaties kunnen vervolgens worden gebruikt met nieuwe afbeeldingen om objecten in die nieuwe afbeelding te detecteren door Box Boundaries binnen de afbeelding zelf op te geven. De Service biedt een eenvoudige, gebruiksvriendelijke onlineportal om dit proces te stroomlijnen. Ga naar de volgende koppelingen voor meer informatie:

Na voltooiing van deze cursus hebt u een mixed reality-toepassing die het volgende kan doen:

  1. De gebruiker kan naar een object kijken , dat ze hebben getraind met behulp van de Azure Custom Vision-service, objectdetectie.
  2. De gebruiker gebruikt de tikbeweging om een afbeelding vast te leggen van wat ze bekijken.
  3. De app verzendt de afbeelding naar de Azure Custom Vision-service.
  4. Er wordt een antwoord van de Service weergegeven dat het resultaat van de herkenning als wereldruimtetekst weergeeft. Dit wordt bereikt door gebruik te maken van de ruimtelijke tracering van Microsoft HoloLens, als een manier om de wereldpositie van het herkende object te begrijpen en vervolgens de tag te gebruiken die is gekoppeld aan wat in de afbeelding wordt gedetecteerd, om de labeltekst op te geven.

De cursus behandelt ook het handmatig uploaden van afbeeldingen, het maken van tags en het trainen van de Service om verschillende objecten (in het opgegeven voorbeeld een kopje) te herkennen door het Begrenzingsvak in te stellen in de afbeelding die u verzendt.

Belangrijk

Na het maken en gebruiken van de app moet de ontwikkelaar teruggaan naar de Azure Custom Vision-service en de voorspellingen van de service identificeren en bepalen of ze correct zijn of niet (door iets te taggen dat de service heeft gemist en de begrenzingsvakken aan te passen). De service kan vervolgens opnieuw worden getraind, waardoor de kans op het herkennen van echte objecten wordt vergroot.

In deze cursus leert u hoe u de resultaten kunt ophalen van de Azure Custom Vision Service, Objectdetectie, in een op Unity gebaseerde voorbeeldtoepassing. Het is aan u om deze concepten toe te passen op een aangepaste toepassing die u mogelijk bouwt.

Ondersteuning voor apparaten

Cursus HoloLens Insluitende headsets
MR en Azure 310: Objectdetectie ✔️

Vereisten

Notitie

Deze zelfstudie is ontworpen voor ontwikkelaars die basiservaring hebben met Unity en C#. Houd er ook rekening mee dat de vereisten en schriftelijke instructies in dit document staan voor wat er op het moment van schrijven is getest en geverifieerd (juli 2018). U bent vrij om de nieuwste software te gebruiken, zoals vermeld in het artikel over de installatie van de hulpprogramma's , hoewel wordt ervan uitgegaan dat de informatie in deze cursus perfect overeenkomt met wat u vindt in nieuwere software dan hieronder wordt vermeld.

Voor deze cursus raden we de volgende hardware en software aan:

Voordat u begint

  1. Om problemen met het bouwen van dit project te voorkomen, wordt u sterk aangeraden het project te maken dat in deze zelfstudie wordt genoemd in een hoofdmap of in een near-rootmap (lange mappaden kunnen problemen veroorzaken tijdens de build).
  2. Uw HoloLens instellen en testen. Als u hiervoor ondersteuning nodig hebt, gaat u naar het installatieartikel van HoloLens.
  3. Het is een goed idee om kalibratie en sensorafstemming uit te voeren bij het ontwikkelen van een nieuwe HoloLens-app (soms kan het helpen om deze taken voor elke gebruiker uit te voeren).

Voor hulp bij kalibratie volgt u deze koppeling naar het HoloLens-kalibratieartikel.

Volg deze koppeling naar het artikel HoloLens Sensor Tuning voor hulp bij sensorafstemming.

Hoofdstuk 1 - De Custom Vision-portal

Als u de Azure Custom Vision-service wilt gebruiken, moet u een exemplaar configureren dat beschikbaar moet worden gemaakt voor uw toepassing.

  1. Navigeer naar de hoofdpagina van custom Vision Service.

  2. Klik op Aan de slag.

    Schermopname met de knop Aan de slag gemarkeerd.

  3. Meld u aan bij de Custom Vision-portal.

    Schermopname van de knop Aanmelden.

  4. Als u nog geen Azure-account hebt, moet u er een maken. Als u deze zelfstudie volgt in een leslokaal- of labsituatie, vraagt u uw docent of een van de proctors voor hulp bij het instellen van uw nieuwe account.

  5. Zodra u voor het eerst bent aangemeld, wordt u gevraagd om het deelvenster Servicevoorwaarden . Klik op het selectievakje om akkoord te gaan met de voorwaarden. Klik vervolgens op Ik ga akkoord.

    Schermopname van het deelvenster Servicevoorwaarden.

  6. Nadat u akkoord bent gegaan met de voorwaarden, bevindt u zich nu in de sectie Mijn projecten . Klik op Nieuw project.

    Schermopname van waar u Nieuw project kunt selecteren.

  7. Aan de rechterkant wordt een tabblad weergegeven, waarin u wordt gevraagd om enkele velden voor het project op te geven.

    1. Een naam voor uw project invoegen

    2. Een beschrijving voor uw project invoegen (optioneel)

    3. Kies een resourcegroep of maak een nieuwe. Een resourcegroep biedt een manier om de toegang te bewaken, te beheren, facturering in te richten en te beheren voor een verzameling Azure-assets. Het wordt aanbevolen om alle Azure-services die zijn gekoppeld aan één project (zoals deze cursussen) onder een gemeenschappelijke resourcegroep te houden.

      Schermopname die laat zien waar u details voor het nieuwe project kunt toevoegen.

    4. Stel de projecttypen in als objectdetectie (preview).

  8. Zodra u klaar bent, klikt u op Project maken en wordt u omgeleid naar de projectpagina van Custom Vision Service.

Hoofdstuk 2: Uw Custom Vision-project trainen

Eenmaal in de Custom Vision-portal is uw primaire doel om uw project te trainen om specifieke objecten in afbeeldingen te herkennen.

U hebt ten minste vijftien (15) installatiekopieën nodig voor elk object dat u wilt herkennen aan uw toepassing. U kunt de afbeeldingen van deze cursus (een reeks bekers) gebruiken.

Uw Custom Vision-project trainen:

  1. Klik op de + knop naast Tags.

    Schermopname van de knop + naast Tags.

  2. Voeg een naam toe voor de tag waaraan uw afbeeldingen worden gekoppeld. In dit voorbeeld gebruiken we afbeeldingen van bekers voor herkenning, dus hebben we het label voor deze cup genoemd. Klik op Opslaan nadat u klaar bent.

    Schermopname van waar een naam voor de tag moet worden toegevoegd.

  3. U ziet dat uw tag is toegevoegd (mogelijk moet u de pagina opnieuw laden om deze weer te geven).

    Schermopname van waar uw tag wordt toegevoegd.

  4. Klik op Afbeeldingen toevoegen in het midden van de pagina.

    Schermopname die laat zien waar afbeeldingen moeten worden toegevoegd.

  5. Klik op Lokale bestanden bladeren en blader naar de afbeeldingen die u voor één object wilt uploaden, met minimaal vijftien (15).

    Tip

    U kunt verschillende afbeeldingen tegelijk selecteren om te uploaden.

    Schermopname van de afbeeldingen die u kunt uploaden.

  6. Druk op Bestanden uploaden zodra u alle afbeeldingen hebt geselecteerd waarmee u het project wilt trainen. De bestanden worden geüpload. Zodra u de upload hebt bevestigd, klikt u op Gereed.

    Schermopname van de voortgang van de geüploade afbeeldingen.

  7. Op dit moment worden uw afbeeldingen geüpload, maar niet getagd.

    Schermopname van een afbeelding zonder vlag.

  8. Als u uw afbeeldingen wilt taggen, gebruikt u de muis. Wanneer u de muisaanwijzer over de afbeelding beweegt, kunt u met een selectiemarkering automatisch een selectie rond uw object tekenen. Als het niet nauwkeurig is, kunt u zelf tekenen. Dit wordt bereikt door met de linkermuisknop op de muis te klikken en het selectiegebied te slepen om uw object te omvatten.

    Schermopname van het taggen van een afbeelding.

  9. Na de selectie van uw object in de afbeelding wordt u gevraagd om regiotag toe te voegen. Selecteer de eerder gemaakte tag ('Cup', in het bovenstaande voorbeeld) of als u meer tags toevoegt, typt u deze in en klikt u op de knop + (plus ).

    Schermopname van de tag die u aan de afbeelding hebt toegevoegd.

  10. Als u de volgende afbeelding wilt taggen, klikt u op de pijl rechts van de blade of sluit u de tagblade (door op de X in de rechterbovenhoek van de blade te klikken) en vervolgens op de volgende afbeelding te klikken. Wanneer u de volgende afbeelding klaar hebt, herhaalt u dezelfde procedure. Doe dit voor alle afbeeldingen die u hebt geüpload, totdat ze allemaal zijn gelabeld.

    Notitie

    U kunt verschillende objecten in dezelfde afbeelding selecteren, zoals in de onderstaande afbeelding:

    Schermopname van meerdere objecten in een afbeelding.

  11. Zodra u ze allemaal hebt getagd, klikt u op de knop Getagd aan de linkerkant van het scherm om de getagde afbeeldingen weer te geven.

    Schermopname waarin de knop Getagd is gemarkeerd.

  12. U bent nu klaar om uw service te trainen. Klik op de knop Trainen en de eerste trainingsiteratie begint.

    Schermopname waarin de knop Trainen is gemarkeerd.

    Schermopname van de eerste trainingsiteratie.

  13. Zodra deze is gemaakt, kunt u twee knoppen zien met de naam Standaard maken en Voorspellings-URL. Klik eerst op Standaard maken en klik vervolgens op Voorspellings-URL.

    Schermopname met de knop Standaard maken gemarkeerd.

    Notitie

    Het eindpunt dat hieruit wordt verstrekt, wordt ingesteld op de iteratie die als standaard is gemarkeerd. Als u later een nieuwe iteratie maakt en deze als standaard bijwerkt , hoeft u uw code niet te wijzigen.

  14. Nadat u op de voorspellings-URL hebt geklikt, opent u Kladblok en kopieert en plakt u de URL (ook wel uw voorspellingseindpunt genoemd) en de servicevoorspellingssleutel, zodat u deze kunt ophalen wanneer u deze later in de code nodig hebt.

    Schermopname van het voorspellingseindpunt en de preditionsleutel.

Hoofdstuk 3 - Het Unity-project instellen

Hier volgt een typische opzet voor het ontwikkelen met mixed reality en is als zodanig een goede sjabloon voor andere projecten.

  1. Open Unity en klik op Nieuw.

    Schermopname met de knop Nieuw gemarkeerd.

  2. U moet nu een Unity-projectnaam opgeven. Voeg CustomVisionObjDetection in. Zorg ervoor dat het projecttype is ingesteld op 3D en stel de locatie in op een locatie die geschikt is voor u (vergeet niet, dichter bij hoofdmappen is beter). Klik vervolgens op Project maken.

    Schermopname van de projectdetails en de locatie waar u Project maken wilt selecteren.

  3. Als Unity is geopend, is het de moeite waard om te controleren of de standaardscripteditor is ingesteld op Visual Studio. Ga naar Voorkeuren bewerken>en navigeer vervolgens vanuit het nieuwe venster naar Externe hulpprogramma's. Wijzig de externe scripteditor in Visual Studio. Sluit het venster Voorkeuren .

    Schermopname die laat zien waar u de externe scripteditor kunt wijzigen in Visual Studio.

  4. Ga vervolgens naar > Instellingen voor het bouwen van bestanden en schakel het platform over naar Universeel Windows-platform en klik vervolgens op de knop Platform wisselen.

    Schermopname waarin de knop Switch Platform is gemarkeerd.

  5. Controleer in hetzelfde venster Build-instellingen of het volgende is ingesteld:

    1. Doelapparaat is ingesteld op HoloLens

    2. Buildtype is ingesteld op D3D

    3. SDK is ingesteld op Laatst geïnstalleerd

    4. Visual Studio-versie is ingesteld op Meest recent geïnstalleerd

    5. Bouwen en uitvoeren is ingesteld op lokale computer

    6. De overige instellingen, in Build-instellingen, moeten voorlopig standaard blijven staan.

      Schermopname van de configuratieopties voor build-instellingen.

  6. Klik in hetzelfde venster Build-instellingen op de knop Spelerinstellingen . Hiermee opent u het gerelateerde deelvenster in de ruimte waar de Inspector zich bevindt.

  7. In dit deelvenster moeten enkele instellingen worden geverifieerd:

    1. Op het tabblad Overige instellingen :

      1. Scripting Runtime-versie moet experimenteel (.NET 4.6 Equivalent) zijn, waardoor de editor opnieuw moet worden opgestart.

      2. Back-end voor scripts moet .NET zijn.

      3. API-compatibiliteitsniveau moet .NET 4.6 zijn.

        Schermopname van de optie API-compatibiliteitsniveau ingesteld op .NET 4.6.

    2. Schakel op het tabblad Publicatie-instellingen onder Mogelijkheden het volgende in:

      1. InternetClient

      2. Webcam

      3. SpatialPerception

        Schermopname van de bovenste helft van de configuratieopties voor mogelijkheden.Schermopname van de onderste helft van de configuratieopties voor mogelijkheden.

    3. Tik verderop in het deelvenster in XR-instellingen (onder Publicatie-instellingen) op Virtual Reality Ondersteund en controleer of de Windows Mixed Reality SDK is toegevoegd.

      Schermopname van het toevoegen van de Windows Mixed Reality SDK.

  8. In Build Settings wordt Unity C# Projects niet meer grijs weergegeven: schakel het selectievakje naast dit selectievakje in.

  9. Sluit het venster Build Settings.

  10. Klik in de editor op> Projectinstellingen>bewerken.

    Schermopname van de menuoptie Graphics geselecteerd.

  11. In het inspectorpaneel worden de grafische instellingen geopend. Schuif omlaag totdat u een matrix ziet met de naam Always Include Shaders. Voeg een site toe door de variabele Grootte met één te vergroten (in dit voorbeeld was het 8, dus we hebben deze 9 gemaakt). Er wordt een nieuwe site weergegeven, op de laatste positie van de matrix, zoals hieronder wordt weergegeven:

    Schermopname met de matrix Always Included Shaders gemarkeerd.

  12. Klik in de site op de kleine doelcirkel naast de site om een lijst met shaders te openen. Zoek naar de verouderde shaders/transparante/diffuse arcering en dubbelklik erop.

    Schermopname met de verouderde shaders/transparante/diffuse arcering gemarkeerd.

Hoofdstuk 4: Het CustomVisionObjDetection Unity-pakket importeren

Voor deze cursus krijgt u een Unity Asset Package met de naam Azure-MR-310.unitypackage.

[TIP] Alle objecten die worden ondersteund door Unity, inclusief volledige scènes, kunnen worden verpakt in een .unitypackage-bestand en geëxporteerd/geïmporteerd in andere projecten. Het is de veiligste en meest efficiënte manier om assets te verplaatsen tussen verschillende Unity-projecten.

U vindt het Azure-MR-310-pakket dat u hier moet downloaden.

  1. Klik met het Unity-dashboard voor u op Assets in het menu boven aan het scherm en klik vervolgens op Aangepast pakket > importeren.

    Schermopname met de menuoptie Aangepast pakket gemarkeerd.

  2. Gebruik de bestandskiezer om het Pakket Azure-MR-310.unitypackage te selecteren en klik op Openen. Er wordt een lijst met onderdelen voor deze asset weergegeven. Bevestig het importeren door op de knop Importeren te klikken.

    Schermopname van de lijst met assetonderdelen die u wilt importeren.

  3. Zodra het importeren is voltooid, ziet u dat mappen uit het pakket nu zijn toegevoegd aan de map Assets . Dit soort mapstructuur is typisch voor een Unity-project.

    Schermopname van de inhoud van de map Assets.

    1. De map Materialen bevat het materiaal dat wordt gebruikt door de cursor van de gaze.

    2. De map Plugins bevat de Newtonsoft DLL die door de code wordt gebruikt om het webantwoord van de service te deserialiseren. De twee (2) verschillende versies in de map en submap zijn nodig om toe te staan dat de bibliotheek kan worden gebruikt en gebouwd door zowel de Unity Editor als de UWP-build.

    3. De map Prefabs bevat de prefabs in de scène. Dit zijn:

      1. De GazeCursor, de cursor die in de toepassing wordt gebruikt. Werkt samen met de SpatialMapping-prefab om in de scène op fysieke objecten te kunnen worden geplaatst.
      2. Het label, dat is het UI-object dat wordt gebruikt om de objecttag in de scène weer te geven wanneer dat nodig is.
      3. De SpatialMapping, het object waarmee de toepassing een virtuele kaart kan maken, met behulp van de ruimtelijke tracering van Microsoft HoloLens.
    4. De map Scènes die momenteel de vooraf gemaakte scène voor deze cursus bevat.

  4. Open de map Scènes in het projectvenster en dubbelklik op objDetectionScene om de scène te laden die u voor deze cursus gaat gebruiken.

    Schermopname van objDetectionScene in de map Scènes.

    Notitie

    Er is geen code opgenomen. U schrijft de code door deze cursus te volgen.

Hoofdstuk 5: maak de klasse CustomVisionAnalyser.

Op dit moment bent u klaar om code te schrijven. U begint met de klasse CustomVisionAnalyser .

Notitie

De aanroepen naar de Custom Vision-service, gemaakt in de onderstaande code, worden uitgevoerd met behulp van de Custom Vision REST API. Door dit te gebruiken, ziet u hoe u deze API implementeert en gebruikt (handig om te begrijpen hoe u iets op uw eigen manier kunt implementeren). Houd er rekening mee dat Microsoft een Custom Vision SDK biedt die ook kan worden gebruikt om de service aan te roepen. Ga voor meer informatie naar het Artikel over de Custom Vision SDK.

Deze klasse is verantwoordelijk voor:

  • Het laden van de meest recente afbeelding die is vastgelegd als een matrix van bytes.

  • De bytematrix verzenden naar uw Azure Custom Vision Service-exemplaar voor analyse.

  • Het antwoord ontvangen als een JSON-tekenreeks.

  • Deserialiseren van het antwoord en het doorgeven van de resulterende voorspelling aan de klasse SceneOrganiser , die zorgt voor de weergave van het antwoord.

Ga als volgt te werk om deze klasse te maken:

  1. Klik met de rechtermuisknop in de assetmap in het deelvenster Project en klik vervolgens op Map maken>. Roep de mapscripts aan.

    Schermopname van het maken van de map Scripts.

  2. Dubbelklik op de zojuist gemaakte map om deze te openen.

  3. Klik met de rechtermuisknop in de map en klik vervolgens op C#-script maken>. Geef het script de naam CustomVisionAnalyser.

  4. Dubbelklik op het nieuwe CustomVisionAnalyser-script om het te openen met Visual Studio.

  5. Zorg ervoor dat boven aan het bestand naar de volgende naamruimten wordt verwezen:

    using Newtonsoft.Json;
    using System.Collections;
    using System.IO;
    using UnityEngine;
    using UnityEngine.Networking;
    
  6. Voeg in de klasse CustomVisionAnalyser de volgende variabelen toe:

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

    Notitie

    Zorg ervoor dat u uw servicevoorspellingssleutel invoegt in de variabele predictionKey en uw voorspellingseindpunt in de predictionEndpoint-variabele . U hebt deze eerder naar Kladblok gekopieerd in hoofdstuk 2, stap 14.

  7. Code for Awake() moet nu worden toegevoegd om de instantievariabele te initialiseren:

        /// <summary>
        /// Initializes this class
        /// </summary>
        private void Awake()
        {
            // Allows this instance to behave like a singleton
            Instance = this;
        }
    
  8. Voeg de coroutine (met de statische Methode GetImageAsByteArray() eronder toe , waarmee de resultaten van de analyse van de afbeelding worden verkregen, die is vastgelegd door de klasse ImageCapture .

    Notitie

    In de AnalyseImageCapture-coroutine is er een aanroep naar de ScèneOrganiser-klasse die u nog moet maken. Laat deze regels daarom voorlopig commentaar geven.

        /// <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. Verwijder de methoden Start() en Update(), omdat ze niet worden gebruikt.

  10. Sla uw wijzigingen op in Visual Studio voordat u terugkeert naar Unity.

Belangrijk

Zoals eerder vermeld, hoeft u zich geen zorgen te maken over code die mogelijk een fout bevat, omdat u binnenkort verdere klassen opgeeft, waardoor deze worden opgelost.

Hoofdstuk 6: de klasse CustomVisionObjects maken

De klasse die u nu maakt, is de klasse CustomVisionObjects .

Dit script bevat een aantal objecten die door andere klassen worden gebruikt om de aanroepen naar de Custom Vision-service te serialiseren en deserialiseren.

Ga als volgt te werk om deze klasse te maken:

  1. Klik met de rechtermuisknop in de map Scripts en klik vervolgens op C#-script maken>. Roep het script CustomVisionObjects aan.

  2. Dubbelklik op het nieuwe CustomVisionObjects-script om het te openen met Visual Studio.

  3. Zorg ervoor dat boven aan het bestand naar de volgende naamruimten wordt verwezen:

    using System;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.Networking;
    
  4. Verwijder de methoden Start() en Update() in de klasse CustomVisionObjects . Deze klasse moet nu leeg zijn.

    Waarschuwing

    Het is belangrijk dat u de volgende instructie zorgvuldig volgt. Als u de nieuwe klassedeclaraties in de klasse CustomVisionObjects plaatst, krijgt u compilatiefouten in hoofdstuk 10, waarin wordt aangegeven dat AnalysisRootObject en BoundingBox niet worden gevonden.

  5. Voeg de volgende klassen toe buiten de klasse CustomVisionObjects . Deze objecten worden gebruikt door de Newtonsoft-bibliotheek om de antwoordgegevens te serialiseren en deserialiseren:

    // 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. Sla uw wijzigingen op in Visual Studio voordat u terugkeert naar Unity.

Hoofdstuk 7 - De klasse SpatialMapping maken

Deze klasse stelt de Spatial Mapping Collider in de scène in, zodat conflicten tussen virtuele objecten en echte objecten kunnen worden gedetecteerd.

Ga als volgt te werk om deze klasse te maken:

  1. Klik met de rechtermuisknop in de map Scripts en klik vervolgens op C#-script maken>. Roep het script SpatialMapping aan.

  2. Dubbelklik op het nieuwe SpatialMapping-script om het te openen met Visual Studio.

  3. Zorg ervoor dat u de volgende naamruimten hebt waarnaar wordt verwezen boven de klasse SpatialMapping :

    using UnityEngine;
    using UnityEngine.XR.WSA;
    
  4. Voeg vervolgens de volgende variabelen toe in de klasse SpatialMapping, boven de methode 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. Voeg de Awake() en Start()toe:

        /// <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. Verwijder de methode Update().

  7. Sla uw wijzigingen op in Visual Studio voordat u terugkeert naar Unity.

Hoofdstuk 8 - De klasse GazeCursor maken

Deze klasse is verantwoordelijk voor het instellen van de cursor op de juiste locatie in echte ruimte, door gebruik te maken van spatialMappingCollider, gemaakt in het vorige hoofdstuk.

Ga als volgt te werk om deze klasse te maken:

  1. Klik met de rechtermuisknop in de map Scripts en klik vervolgens op C#-script maken>. Het script GazeCursor aanroepen

  2. Dubbelklik op het nieuwe GazeCursor-script om het te openen met Visual Studio.

  3. Zorg ervoor dat u de volgende naamruimte hebt waarnaar wordt verwezen boven de klasse GazeCursor :

    using UnityEngine;
    
  4. Voeg vervolgens de volgende variabele toe in de klasse GazeCursor, boven de Methode Start().

        /// <summary>
        /// The cursor (this object) mesh renderer
        /// </summary>
        private MeshRenderer meshRenderer;
    
  5. Werk de Methode Start() bij met de volgende code:

        /// <summary>
        /// Runs at initialization right after the Awake method
        /// </summary>
        void Start()
        {
            // Grab the mesh renderer that is on the same object as this script.
            meshRenderer = gameObject.GetComponent<MeshRenderer>();
    
            // Set the cursor reference
            SceneOrganiser.Instance.cursor = gameObject;
            gameObject.GetComponent<Renderer>().material.color = Color.green;
    
            // If you wish to change the size of the cursor you can do so here
            gameObject.transform.localScale = new Vector3(0.01f, 0.01f, 0.01f);
        }
    
  6. Werk de methode Update() bij met de volgende code:

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

    Notitie

    U hoeft zich geen zorgen te maken over de fout voor de ScèneOrganiser-klasse die niet wordt gevonden. U maakt deze in het volgende hoofdstuk.

  7. Sla uw wijzigingen op in Visual Studio voordat u terugkeert naar Unity.

Hoofdstuk 9 - De klasse SceneOrganiser maken

In deze klasse wordt het volgende weergegeven:

  • Stel de hoofdcamera in door de juiste onderdelen eraan te koppelen.

  • Wanneer een object wordt gedetecteerd, is het verantwoordelijk voor het berekenen van de positie in de echte wereld en plaatst u een labellabel bij het object met de juiste tagnaam.

Ga als volgt te werk om deze klasse te maken:

  1. Klik met de rechtermuisknop in de map Scripts en klik vervolgens op C#-script maken>. Geef het script de naam ScèneOrganiser.

  2. Dubbelklik op het nieuwe ScèneOrganiser-script om het te openen met Visual Studio.

  3. Zorg ervoor dat u de volgende naamruimten hebt waarnaar wordt verwezen boven de klasse SceneOrganiser :

    using System.Collections.Generic;
    using System.Linq;
    using UnityEngine;
    
  4. Voeg vervolgens de volgende variabelen toe in de klasse SceneOrganiser, boven de methode 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. Verwijder de methoden Start() en Update().

  6. Voeg onder de variabelen de methode Awake() toe, waarmee de klasse wordt geïnitialiseerd en de scène wordt ingesteld.

        /// <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. Voeg de methode PlaceAnalysisLabel() toe, waarmee het label in de scène wordt geïnstitueerd (die op dit moment onzichtbaar is voor de gebruiker). Het plaatst ook de quad (ook onzichtbaar) waar de afbeelding wordt geplaatst en overlapt met de echte wereld. Dit is belangrijk omdat de boxcoördinaten die zijn opgehaald uit de Service nadat de analyse is teruggetraceerd naar deze quad om de geschatte locatie van het object in de echte wereld te bepalen.

        /// <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. Voeg de methode FinaliseLabel() toe. Het is verantwoordelijk voor:

    • Stel de labeltekst in met de tag van de voorspelling met het hoogste vertrouwen.
    • Het aanroepen van de berekening van het begrenzingsvak op het quad-object, eerder geplaatst en het label in de scène plaatsen.
    • Het aanpassen van de labeldiepte met behulp van een Raycast richting de Begrenzingsbox, die moet botsen tegen het object in de echte wereld.
    • Het opnameproces opnieuw instellen zodat de gebruiker een andere installatiekopieën kan vastleggen.
        /// <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. Voeg de methode CalculateBoundingBoxPosition() toe, die als host fungeert voor een aantal berekeningen die nodig zijn om de begrenzingsvakcoördinaten te vertalen die zijn opgehaald uit de Service en deze proportioneel opnieuw te maken op de quad.

        /// <summary>
        /// This method hosts a series of calculations to determine the position 
        /// of the Bounding Box on the quad created in the real world
        /// by using the Bounding Box received back alongside the Best Prediction
        /// </summary>
        public Vector3 CalculateBoundingBoxPosition(Bounds b, BoundingBox boundingBox)
        {
            Debug.Log($"BB: left {boundingBox.left}, top {boundingBox.top}, width {boundingBox.width}, height {boundingBox.height}");
    
            double centerFromLeft = boundingBox.left + (boundingBox.width / 2);
            double centerFromTop = boundingBox.top + (boundingBox.height / 2);
            Debug.Log($"BB CenterFromLeft {centerFromLeft}, CenterFromTop {centerFromTop}");
    
            double quadWidth = b.size.normalized.x;
            double quadHeight = b.size.normalized.y;
            Debug.Log($"Quad Width {b.size.normalized.x}, Quad Height {b.size.normalized.y}");
    
            double normalisedPos_X = (quadWidth * centerFromLeft) - (quadWidth/2);
            double normalisedPos_Y = (quadHeight * centerFromTop) - (quadHeight/2);
    
            return new Vector3((float)normalisedPos_X, (float)normalisedPos_Y, 0);
        }
    
  10. Sla uw wijzigingen op in Visual Studio voordat u terugkeert naar Unity.

    Belangrijk

    Voordat u doorgaat, opent u de klasse CustomVisionAnalyser en in de methode AnalyseLastImageCaptured() moet u de volgende regels verwijderen:

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

Notitie

Maak u geen zorgen over het bericht 'Kan niet gevonden' van de klasse ImageCapture . U maakt het in het volgende hoofdstuk.

Hoofdstuk 10 - De klasse ImageCapture maken

De volgende klasse die u gaat maken, is de klasse ImageCapture .

Deze klasse is verantwoordelijk voor:

  • Een afbeelding vastleggen met behulp van de HoloLens-camera en deze opslaan in de app-map .
  • Tikbewegingen van de gebruiker verwerken.

Ga als volgt te werk om deze klasse te maken:

  1. Ga naar de map Scripts die u eerder hebt gemaakt.

  2. Klik met de rechtermuisknop in de map en klik vervolgens op C#-script maken>. Noem het script ImageCapture.

  3. Dubbelklik op het nieuwe ImageCapture-script om het te openen met Visual Studio.

  4. Vervang de naamruimten boven aan het bestand door het volgende:

    using System;
    using System.IO;
    using System.Linq;
    using UnityEngine;
    using UnityEngine.XR.WSA.Input;
    using UnityEngine.XR.WSA.WebCam;
    
  5. Voeg vervolgens de volgende variabelen toe in de klasse ImageCapture, boven de methode 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. De methoden Code for Awake() en Start() moeten nu worden toegevoegd:

        /// <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. Implementeer een handler die wordt aangeroepen wanneer een tikbeweging plaatsvindt:

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

    Belangrijk

    Wanneer de cursor groen is, betekent dit dat de camera beschikbaar is om de afbeelding te maken. Als de cursor rood is, betekent dit dat de camera bezet is.

  8. Voeg de methode toe die de toepassing gebruikt om het opnameproces voor afbeeldingen te starten en de installatiekopieën op te slaan:

        /// <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. Voeg de handlers toe die worden aangeroepen wanneer de foto is vastgelegd en voor wanneer deze gereed is om te worden geanalyseerd. Het resultaat wordt vervolgens doorgegeven aan de CustomVisionAnalyser voor analyse.

        /// <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. Sla uw wijzigingen op in Visual Studio voordat u terugkeert naar Unity.

Hoofdstuk 11 - De scripts in de scène instellen

Nu u alle benodigde code voor dit project hebt geschreven, is het tijd om de scripts in de scène en op de prefabs in te stellen, zodat ze zich correct gedragen.

  1. Selecteer in de Unity-editor in het deelvenster Hiërarchie de hoofdcamera.

  2. Klik in het Inspector-deelvenster, met de hoofdcamera geselecteerd, op Onderdeel toevoegen, zoek vervolgens naar ScèneOrganiser-script en dubbelklik erop om het toe te voegen.

    Schermopname van het ScèneOrganizer-script.

  3. Open in het projectvenster de map Prefabs, sleep de labelprefab naar het doelinvoergebied label, in het script SceneOrganiser dat u zojuist hebt toegevoegd aan de hoofdcamera, zoals wordt weergegeven in de onderstaande afbeelding:

    Schermopname van het script dat u hebt toegevoegd aan de hoofdcamera.

  4. Selecteer in het deelvenster Hiërarchie het onderliggend element GazeCursor van de hoofdcamera.

  5. Klik in het Inspector-deelvenster, met de GazeCursor geselecteerd, op Component toevoegen, zoek naar GazeCursor-script en dubbelklik erop om het toe te voegen.

    Schermopname van waar u het GazeCursor-script toevoegt.

  6. Selecteer opnieuw in het deelvenster Hiërarchie het spatialMapping-onderliggend element van de hoofdcamera.

  7. Klik in het Inspector-deelvenster, waarbij SpatialMapping is geselecteerd, op Component toevoegen, zoek vervolgens naar SpatialMapping-script en dubbelklik erop om het toe te voegen.

    Schermopname van waar u het SpatialMapping-script toevoegt.

De resterende scripts die u niet hebt ingesteld, worden tijdens runtime toegevoegd door de code in het ScèneOrganiser-script .

Hoofdstuk 12 - Voor het gebouw

Als u een grondige test van uw toepassing wilt uitvoeren, moet u deze sideloaden op uw Microsoft HoloLens.

Voordat u dit doet, moet u ervoor zorgen dat:

  • Alle instellingen die in hoofdstuk 3 worden genoemd, zijn correct ingesteld.

  • Het script SceneOrganiser is gekoppeld aan het object Main Camera .

  • Het script GazeCursor is gekoppeld aan het GazeCursor-object .

  • Het script SpatialMapping is gekoppeld aan het SpatialMapping-object .

  • In hoofdstuk 5, stap 6:

    • Zorg ervoor dat u de voorspellingssleutel van de service invoegt in de variabele predictionKey.
    • U hebt uw voorspellingseindpunt ingevoegd in de klasse predictionEndpoint .

Hoofdstuk 13: de UWP-oplossing bouwen en uw toepassing sideloaden

U bent nu klaar om uw toepassing te bouwen als een UWP-oplossing die u kunt implementeren op de Microsoft HoloLens. Ga als volgt te werk om het buildproces te starten:

  1. Ga naar Instellingen voor het > maken van bestanden.

  2. Tick Unity C#- projecten.

  3. Klik op Open Scènes toevoegen. Hiermee wordt de momenteel geopende scène toegevoegd aan de build.

    Schermopname waarin de knop Open scènes toevoegen is gemarkeerd.

  4. Klik op Opbouwen. Unity start een Bestandenverkenner-venster waarin u een map moet maken en vervolgens een map selecteert waarin u de app wilt inbouwen. Maak die map nu en noem deze app. Klik vervolgens met de map App geselecteerd op Map selecteren.

  5. Unity begint met het bouwen van uw project in de app-map .

  6. Zodra Unity klaar is met bouwen (het kan enige tijd duren), wordt er een Bestandenverkenner venster geopend op de locatie van uw build (controleer de taakbalk, omdat deze mogelijk niet altijd boven uw vensters wordt weergegeven, maar u ontvangt een melding over de toevoeging van een nieuw venster).

  7. Als u wilt implementeren op Microsoft HoloLens, hebt u het IP-adres van dat apparaat nodig (voor Extern implementeren) en om ervoor te zorgen dat er ook de ontwikkelaarsmodus is ingesteld. Dit doet u als volgt:

    1. Open de instellingen terwijl u uw HoloLens draagt.

    2. Ga naar Netwerk en Internet>Wi-Fi>Geavanceerde opties

    3. Noteer het IPv4-adres .

    4. Ga vervolgens terug naar Instellingen en ga vervolgens naar Update & Security>for Developers

    5. Stel de ontwikkelaarsmodus in.

  8. Navigeer naar uw nieuwe Unity-build (de app-map ) en open het oplossingsbestand met Visual Studio.

  9. Selecteer foutopsporing in de oplossingsconfiguratie.

  10. Selecteer x86, Remote Machine in het Solution Platform. U wordt gevraagd het IP-adres van een extern apparaat in te voegen (in dit geval de Microsoft HoloLens, die u hebt genoteerd).

    Schermopname van waar het IP-adres moet worden ingevoegd.

  11. Ga naar het menu Build en klik op Oplossing implementeren om de toepassing te sideloaden naar uw HoloLens.

  12. Uw app moet nu worden weergegeven in de lijst met geïnstalleerde apps op uw Microsoft HoloLens, klaar om te worden gestart.

De toepassing gebruiken:

  • Bekijk een object dat u hebt getraind met uw Azure Custom Vision Service, objectdetectie en gebruik de tikbeweging.
  • Als het object is gedetecteerd, wordt een labeltekst in de wereld weergegeven met de tagnaam.

Belangrijk

Telkens wanneer u een foto vastlegt en deze naar de Service verzendt, kunt u teruggaan naar de servicepagina en de service opnieuw trainen met de zojuist vastgelegde afbeeldingen. Aan het begin moet u waarschijnlijk ook de begrenzingsvakken corrigeren om nauwkeuriger te zijn en de Service opnieuw te trainen.

Notitie

De geplaatste labeltekst wordt mogelijk niet in de buurt van het object weergegeven wanneer de Microsoft HoloLens-sensoren en/of spatialTrackingComponent in Unity de juiste colliders niet kunnen plaatsen ten opzichte van de werkelijke objecten. Probeer de toepassing op een ander oppervlak te gebruiken als dat het geval is.

Uw Custom Vision-toepassing voor objectdetectie

Gefeliciteerd, u hebt een mixed reality-app gebouwd die gebruikmaakt van de Azure Custom Vision- objectdetectie-API, die een object van een afbeelding kan herkennen en vervolgens een geschatte positie voor dat object in 3D-ruimte kan bieden.

Schermopname van een mixed reality-app die gebruikmaakt van de Azure Custom Vision- objectdetectie-API.

Bonusoefeningen

Oefening 1

Als u het tekstlabel toevoegt, gebruikt u een semitransparante kubus om het echte object in een 3D-begrenzingsvak te verpakken.

Oefening 2

Train uw Custom Vision-service om meer objecten te herkennen.

Oefening 3

Een geluid afspelen wanneer een object wordt herkend.

Oefening 4

Gebruik de API om uw service opnieuw te trainen met dezelfde afbeeldingen die uw app analyseert, dus om de service nauwkeuriger te maken (doe zowel voorspellingen als trainen tegelijk).