HoloLens (1. Generation) und Azure 302b: Custom Vision


Hinweis

Die Tutorials der Mixed Reality Academy wurden im Hinblick auf HoloLens (1. Gen.) und immersive Mixed Reality-Headsets entworfen. Daher halten wir es für wichtig, diese Tutorials für Entwickler verfügbar zu halten, die noch nach Anleitung beim Entwickeln für diese Geräte suchen. Diese Tutorials werden nicht mit den neuesten Toolsets oder Interaktionen aktualisiert, die für HoloLens 2 verwendet werden. Sie werden gewartet, um weiterhin auf den unterstützten Geräten zu funktionieren. Es wird eine neue Reihe von Tutorials geben, die in Zukunft veröffentlicht werden, die zeigen, wie für HoloLens 2 entwickelt werden kann. Dieser Hinweis wird mit einem Link zu diesen Tutorials aktualisiert, wenn sie veröffentlicht werden.


In diesem Kurs erfahren Sie, wie Sie benutzerdefinierte visuelle Inhalte in einem bereitgestellten Bild mithilfe von Azure Custom Vision-Funktionen in einer Mixed Reality-Anwendung erkennen.

Mit diesem Dienst können Sie ein Machine Learning-Modell mithilfe von Objektbildern trainieren. Anschließend verwenden Sie das trainierte Modell, um ähnliche Objekte zu erkennen, wie durch die Kameraaufnahme von Microsoft HoloLens oder einer Kamera, die mit Ihrem PC für immersive Headsets (VR) verbunden ist.

Kursergebnis

Azure Custom Vision ist ein Microsoft Cognitive Service, mit dem Entwickler benutzerdefinierte Bildklassifizierungen erstellen können. Diese Klassifizierer können dann mit neuen Bildern verwendet werden, um Objekte in diesem neuen Bild zu erkennen oder zu klassifizieren. Der Dienst bietet ein einfaches, benutzerfreundliches Onlineportal, um den Prozess zu optimieren. Weitere Informationen finden Sie auf der Seite Azure Custom Vision Service.

Nach Abschluss dieses Kurses haben Sie eine Mixed Reality-Anwendung, die in zwei Modi arbeiten kann:

  • Analysemodus: Manuelles Einrichten des Custom Vision-Diensts durch Hochladen von Bildern, Erstellen von Tags und Trainieren des Diensts zum Erkennen verschiedener Objekte (in diesem Fall Maus und Tastatur). Anschließend erstellen Sie eine HoloLens-App, die Bilder mit der Kamera erfasst und versucht, diese Objekte in der realen Welt zu erkennen.

  • Trainingsmodus: Sie implementieren Code, der einen "Trainingsmodus" in Ihrer App aktiviert. Im Trainingsmodus können Sie Bilder mit der HoloLens-Kamera aufnehmen, die erfassten Bilder in den Dienst hochladen und das benutzerdefinierte Vision-Modell trainieren.

In diesem Kurs erfahren Sie, wie Sie die Ergebnisse aus dem Custom Vision Service in eine Unity-basierte Beispielanwendung abrufen. Sie müssen diese Konzepte auf eine benutzerdefinierte Anwendung anwenden, die Sie möglicherweise erstellen.

Geräteunterstützung

Kurs HoloLens Immersive Headsets
MR und Azure 302b: Custom Vision ✔️ ✔️

Hinweis

Während sich dieser Kurs hauptsächlich auf HoloLens konzentriert, können Sie das, was Sie in diesem Kurs lernen, auch auf Windows Mixed Reality immersiven (VR)-Headsets anwenden. Da immersive Headsets (VR) keine zugänglichen Kameras haben, benötigen Sie eine externe Kamera, die an Ihren PC angeschlossen ist. Während Sie den Kurs befolgen, werden Ihnen Notizen zu allen Änderungen angezeigt, die Sie möglicherweise anwenden müssen, um immersive Headsets (VR) zu unterstützen.

Voraussetzungen

Hinweis

Dieses Tutorial richtet sich an Entwickler, die über grundlegende Erfahrungen mit Unity und C# verfügen. Bitte beachten Sie auch, dass die Voraussetzungen und schriftlichen Anweisungen in diesem Dokument das darstellen, was zum Zeitpunkt des Schreibens (Juli 2018) getestet und überprüft wurde. Sie können die neueste Software verwenden, wie im Artikel Installieren der Tools aufgeführt, obwohl nicht davon ausgegangen werden sollte, dass die Informationen in diesem Kurs perfekt dem entsprechen, was Sie in neuerer Software finden, als die unten aufgeführten.

Wir empfehlen die folgende Hard- und Software für diesen Kurs:

Vorbereitung

  1. Um Probleme beim Erstellen dieses Projekts zu vermeiden, wird dringend empfohlen, das in diesem Tutorial erwähnte Projekt in einem Stamm- oder Fast-Root-Ordner zu erstellen (lange Ordnerpfade können zur Buildzeit Zu Problemen führen).
  2. Richten Sie Ihre HoloLens ein und testen Sie sie. Wenn Sie Unterstützung beim Einrichten Ihrer HoloLens benötigen, lesen Sie den Artikel HoloLens-Setup.
  3. Es ist eine gute Idee, Kalibrierung und Sensoroptimierung durchzuführen, wenn Sie mit der Entwicklung einer neuen HoloLens-App beginnen (manchmal kann dies helfen, diese Aufgaben für jeden Benutzer auszuführen).

Hilfe zur Kalibrierung finden Sie unter diesem Link zum Artikel HoloLens-Kalibrierung.

Hilfe zur Sensoroptimierung finden Sie unter diesem Link zum Artikel HoloLens Sensoroptimierung.

Kapitel 1: Das Custom Vision-Dienstportal

Um den Custom Vision-Dienst in Azure verwenden zu können, müssen Sie eine instance des Diensts konfigurieren, um für Ihre Anwendung verfügbar zu machen.

  1. Navigieren Sie zunächst zur Seite Custom Vision Service Standard.

  2. Klicken Sie auf die Schaltfläche Erste Schritte .

    Erste Schritte mit Custom Vision Service

  3. Melden Sie sich beim Custom Vision-Dienstportal an.

    Anmelden beim Portal

    Hinweis

    Wenn Sie noch nicht über ein Azure-Konto verfügen, müssen Sie ein Azure-Konto erstellen. Wenn Sie dieses Tutorial in einer Unterrichts- oder Labsituation befolgen, bitten Sie Ihren Kursleiter oder einen der Verantwortlichen um Hilfe beim Einrichten Ihres neuen Kontos.

  4. Sobald Sie zum ersten Mal angemeldet sind, werden Sie zum Bereich "Nutzungsbedingungen " aufgefordert. Klicken Sie auf das Kontrollkästchen, um den Bedingungen zuzustimmen. Klicken Sie dann auf Ich stimme zu.

    Nutzungsbedingungen

  5. Nachdem Sie den Bedingungen zugestimmt haben, werden Sie zum Abschnitt Projekte des Portals navigiert. Klicken Sie auf Neues Projekt.

    Erstellen eines neuen Projekts

  6. Auf der rechten Seite wird eine Registerkarte angezeigt, auf der Sie aufgefordert werden, einige Felder für das Projekt anzugeben.

    1. Fügen Sie einen Namen für Ihr Projekt ein.

    2. Fügen Sie eine Beschreibung für Ihr Projekt ein (optional).

    3. Wählen Sie eine Ressourcengruppe aus, oder erstellen Sie eine neue. Eine Ressourcengruppe bietet eine Möglichkeit zum Überwachen, Steuern des Zugriffs, Bereitstellen und Verwalten der Abrechnung für eine Sammlung von Azure-Ressourcen. Es wird empfohlen, alle Azure-Dienste, die einem einzelnen Projekt (z. B. diesen Kursen) zugeordnet sind, unter einer gemeinsamen Ressourcengruppe zu halten.

    4. Festlegen der Projekttypen auf Klassifizierung

    5. Legen Sie die Domänen auf Allgemein fest.

      Festlegen der Domänen

      Wenn Sie mehr über Azure-Ressourcengruppen erfahren möchten, lesen Sie den Artikel Ressourcengruppe.

  7. Wenn Sie fertig sind, klicken Sie auf Projekt erstellen, und Sie werden zur Custom Vision Dienst, Projektseite weitergeleitet.

Kapitel 2: Trainieren Ihres Custom Vision Projekts

Sobald Sie sich im Custom Vision-Portal befinden, besteht Ihr primäres Ziel darin, Ihr Projekt so zu trainieren, dass bestimmte Objekte in Bildern erkannt werden. Sie benötigen mindestens fünf (5) Bilder, obwohl zehn (10) für jedes Objekt, das Ihre Anwendung erkennen soll, bevorzugt werden. Sie können die in diesem Kurs bereitgestellten Bilder (Eine Computermaus und eine Tastatur) verwenden.

So trainieren Sie Ihr Custom Vision Service-Projekt:

  1. Klicken Sie auf die + Schaltfläche neben Tags.

    Hinzufügen eines neuen Tags

  2. Fügen Sie den Namen des Objekts hinzu, das Sie erkennen möchten. Klicken Sie auf Speichern.

    Hinzufügen des Objektnamens und Speichern

  3. Sie werden feststellen, dass Ihr Tag hinzugefügt wurde (Möglicherweise müssen Sie Ihre Seite neu laden, damit es angezeigt wird). Klicken Sie auf das Kontrollkästchen neben Dem neuen Tag, falls es noch nicht aktiviert ist.

    Neues Tag aktivieren

  4. Klicken Sie in der Mitte der Seite auf Bilder hinzufügen .

    Hinzufügen von Bildern

  5. Klicken Sie auf Lokale Dateien durchsuchen, und wählen Sie dann die Bilder aus, die Sie hochladen möchten, wobei mindestens fünf (5) sind. Denken Sie daran, dass alle diese Bilder das Objekt enthalten sollten, das Sie trainieren.

    Hinweis

    Sie können mehrere Bilder gleichzeitig auswählen, um sie hochzuladen.

  6. Sobald Sie die Bilder auf der Registerkarte sehen können, wählen Sie das entsprechende Tag im Feld Meine Tags aus .

    Tags auswählen

  7. Klicken Sie auf Dateien hochladen. Die Dateien beginnen mit dem Hochladen. Nachdem Sie die Bestätigung des Uploads erhalten haben, klicken Sie auf Fertig.

    Hochladen von Dateien

  8. Wiederholen Sie denselben Vorgang, um ein neues Tag mit dem Namen Tastatur zu erstellen und die entsprechenden Fotos dafür hochzuladen. Deaktivieren Sie die OptionMaus , nachdem Sie die neuen Tags erstellt haben, um das Fenster Bilder hinzufügen anzuzeigen.

  9. Nachdem Sie beide Tags eingerichtet haben, klicken Sie auf Trainieren, und die erste Trainingsiteration beginnt mit dem Erstellen.

    Aktivieren der Trainingsiteration

  10. Nachdem sie erstellt wurde, können Sie die beiden Schaltflächen Standard erstellen und Vorhersage-URL anzeigen. Klicken Sie zuerst auf Standard festlegen , und klicken Sie dann auf Vorhersage-URL.

    Standard- und Vorhersage-URL festlegen

    Hinweis

    Die endpunkt-URL, die von diesem angegeben wird, ist auf die als Standard markierte Iteration festgelegt. Wenn Sie also später eine neue Iteration vornehmen und diese als Standard aktualisieren, müssen Sie Ihren Code nicht ändern.

  11. Nachdem Sie auf Vorhersage-URL geklickt haben, öffnen Sie Editor, kopieren und fügen Sie die URL und den Prediction-Key ein, sodass Sie ihn bei Bedarf später im Code abrufen können.

    Kopieren und Einfügen von URL und Prediction-Key

  12. Klicken Sie oben rechts auf den Zahnrad .

    Klicken Sie auf das Zahnradsymbol, um die Einstellungen zu öffnen.

  13. Kopieren Sie den Trainingsschlüssel , und fügen Sie ihn zur späteren Verwendung in einen Editor ein.

    Kopieren des Trainingsschlüssels

  14. Kopieren Sie außerdem Ihre Projekt-ID, und fügen Sie sie zur späteren Verwendung in Ihre Editor-Datei ein.

    Projekt-ID kopieren

Kapitel 3: Einrichten des Unity-Projekts

Das Folgende ist eine typische Einrichtung für die Entwicklung mit Mixed Reality und ist daher eine gute Vorlage für andere Projekte.

  1. Öffnen Sie Unity, und klicken Sie auf Neu.

    Erstellen eines neuen Unity-Projekts

  2. Sie müssen nun einen Unity-Projektnamen angeben. Fügen Sie AzureCustomVision ein. Stellen Sie sicher, dass die Projektvorlage auf 3D festgelegt ist. Legen Sie den Speicherort auf einen für Sie geeigneten Ort fest (denken Sie daran, dass näher an Stammverzeichnissen besser ist). Klicken Sie dann auf Projekt erstellen.

    Konfigurieren von Projekteinstellungen

  3. Wenn Unity geöffnet ist, lohnt es sich, zu überprüfen, ob der Standardskript-Editor auf Visual Studio festgelegt ist. Navigieren Sie zu Einstellungen bearbeiten>, und navigieren Sie dann im neuen Fenster zu Externe Tools. Ändern Sie den externen Skript-Editor in Visual Studio 2017. Schließen Sie das Fenster Einstellungen.

    Konfigurieren externer Tools

  4. Wechseln Sie als Nächstes zu Dateierstellungseinstellungen>, wählen Sie Universelle Windows-Plattform aus, und klicken Sie dann auf die Schaltfläche Plattform wechseln, um Ihre Auswahl anzuwenden.

    Konfigurieren von Buildeinstellungen

  5. Wenn Sie sich noch in den Dateibuildeinstellungen > befinden , stellen Sie sicher, dass:

    1. Zielgerät ist auf HoloLens festgelegt

      Legen Sie für die immersiven Headsets Zielgerät auf Beliebiges Gerät fest.

    2. Buildtyp ist auf D3D festgelegt

    3. SDK ist auf Zuletzt installiert festgelegt.

    4. Visual Studio-Version ist auf Zuletzt installiert festgelegt.

    5. Build and Run ist auf Lokaler Computer festgelegt.

    6. Speichern Sie die Szene, und fügen Sie sie dem Build hinzu.

      1. Wählen Sie dazu Offene Szenen hinzufügen aus. Es wird ein Fenster zum Speichern angezeigt.

        Hinzufügen einer offenen Szene zur Buildliste

      2. Erstellen Sie einen neuen Ordner für diese und jede zukünftige Szene, und wählen Sie dann die Schaltfläche Neuer Ordner aus, um einen neuen Ordner zu erstellen, und nennen Sie ihn Szenen.

        Erstellen eines neuen Szenenordners

      3. Öffnen Sie Den neu erstellten Ordner Scenes , und geben Sie dann im Textfeld Dateiname:CustomVisionScene ein, und klicken Sie dann auf Speichern.

        Neue Szenendatei benennen

        Beachten Sie, dass Sie Ihre Unity-Szenen im Ordner Assets speichern müssen, da sie dem Unity-Projekt zugeordnet sein müssen. Das Erstellen des Szenenordners (und anderer ähnlicher Ordner) ist eine typische Methode zum Strukturieren eines Unity-Projekts.

    7. Die restlichen Einstellungen in Buildeinstellungen sollten vorerst als Standard beibehalten werden.

      Standardbuildeinstellungen

  6. Klicken Sie im Fenster Buildeinstellungen auf die Schaltfläche Playereinstellungen . Dadurch wird der zugehörige Bereich in dem Bereich geöffnet, in dem sich der Inspektor befindet.

  7. In diesem Bereich müssen einige Einstellungen überprüft werden:

    1. Auf der Registerkarte Andere Einstellungen :

      1. Die Skriptversion der Runtime sollte experimentell (.NET 4.6 Equivalent) sein, was einen Neustart des Editors auslöst.

      2. Skript-Back-End sollte .NET sein

      3. API-Kompatibilitätsgrad sollte .NET 4.6 sein

      Festlegen der API-Kompantiblität

    2. Überprüfen Sie auf der Registerkarte Veröffentlichungseinstellungen unter Funktionen Folgendes:

      1. InternetClient

      2. Webcam

      3. Mikrofon

      Konfigurieren von Veröffentlichungseinstellungen

    3. Wählen Sie weiter unten im Bereich unter XR-Einstellungen (unter Veröffentlichungseinstellungen) die Option Virtual Reality Supported (Virtual Reality Supported) aus, stellen Sie sicher, dass das Windows Mixed Reality SDK hinzugefügt wurde.

    Konfigurieren von XR-Einstellungen

  8. Zurück in BuildeinstellungenUnity C#-Projekte ist nicht mehr abgeblendet. aktivieren Sie das Kontrollkästchen neben diesem.

  9. Schließen Sie das Fenster „Build Settings“ (Buildeinstellungen).

  10. Speichern Sie Ihre Szene und Ihr Projekt (FILE > SAVE SCENE / FILE > SAVE PROJECT).

Kapitel 4: Importieren der Newtonsoft-DLL in Unity

Wichtig

Wenn Sie die Unity Setup-Komponente dieses Kurses überspringen und direkt mit dem Code fortfahren möchten, können Sie dieses Azure-MR-302b.unitypackage herunterladen, es als benutzerdefiniertes Paket in Ihr Projekt importieren und dann mit Kapitel 6 fortfahren.

Dieser Kurs erfordert die Verwendung der Newtonsoft-Bibliothek , die Sie als DLL zu Ihren Ressourcen hinzufügen können. Das Paket, das diese Bibliothek enthält, kann über diesen Link heruntergeladen werden. Um die Newtonsoft-Bibliothek in Ihr Projekt zu importieren, verwenden Sie das Unity-Paket, das in diesem Kurs enthalten ist.

  1. Fügen Sie unitypackage zu Unity hinzu, indem Sie die Menüoption AssetsImportPackage> Custom Package (BenutzerdefiniertesPaket importieren) > verwenden.

  2. Vergewissern Sie sich im Eingeblendeten Feld Unity-Paket importieren , dass alles unter (und einschließlich) Plug-Ins ausgewählt ist.

    Importieren aller Paketelemente

  3. Klicken Sie auf die Schaltfläche Importieren , um die Elemente zu Ihrem Projekt hinzuzufügen.

  4. Wechseln Sie in der Projektansicht zum Ordner Newtonsoft unter Plugins , und wählen Sie das Plug-In Newtonsoft.Json aus.

    Auswählen des Newtonsoft-Plug-Ins

  5. Wenn Sie das Newtonsoft.Json-Plug-In ausgewählt haben, stellen Sie sicher, dass Beliebige Plattformdeaktiviert ist. Stellen Sie dann sicher, dass WSAPlayer ebenfalls deaktiviert ist, und klicken Sie dann auf Anwenden. Dies ist nur, um zu bestätigen, dass die Dateien ordnungsgemäß konfiguriert sind.

    Konfigurieren des Newtonsoft-Plug-Ins

    Hinweis

    Wenn Sie diese Plug-Ins markieren, werden sie nur im Unity-Editor verwendet. Es gibt einen anderen Satz davon im WSA-Ordner, der verwendet wird, nachdem das Projekt aus Unity exportiert wurde.

  6. Als Nächstes müssen Sie den WSA-Ordner im Ordner Newtonsoft öffnen. Es wird eine Kopie der soeben konfigurierten Datei angezeigt. Wählen Sie die Datei aus, und stellen Sie dann im Inspektor sicher, dass

    • Jede Plattform ist deaktiviert.
    • NurWSAPlayer ist aktiviert.
    • Nicht aktivierter Prozess

    Konfigurieren der Einstellungen der Newtonsoft-Plug-In-Plattform

Kapitel 5: Kameraeinrichtung

  1. Wählen Sie im Hierarchiebereich die Hauptkamera aus.

  2. Nach der Auswahl können Sie alle Komponenten der Hauptkamera im Inspektorbereich sehen.

    1. Das Kameraobjekt muss den Namen Hauptkamera haben (beachten Sie die Schreibweise!)

    2. Das Hauptkameratag muss auf MainCamera festgelegt werden (beachten Sie die Schreibweise!)

    3. Stellen Sie sicher, dass die Transformationsposition auf 0, 0, 0 festgelegt ist.

    4. Legen Sie Clear Flags auf Einfarbig fest (ignorieren Sie dies für immersive Headsets).

    5. Legen Sie die Hintergrundfarbe der Kamerakomponente auf Schwarz, Alpha 0 (Hexcode: #00000000) fest (ignorieren Sie dies für immersive Headsets).

    Konfigurieren von Kamerakomponenteneigenschaften

Kapitel 6: Erstellen der CustomVisionAnalyser-Klasse.

An diesem Punkt können Sie Code schreiben.

Sie beginnen mit der CustomVisionAnalyser-Klasse .

Hinweis

Die Aufrufe des Custom Vision-Diensts, die im folgenden Code ausgeführt werden, werden mithilfe der Custom Vision REST-API ausgeführt. Dadurch sehen Sie, wie Sie diese API implementieren und nutzen (nützlich, um zu verstehen, wie Sie etwas Ähnliches selbst implementieren können). Beachten Sie, dass Microsoft ein Custom Vision Service SDK anbietet, das auch zum Tätigen von Aufrufen des Diensts verwendet werden kann. Weitere Informationen finden Sie im Artikel Custom Vision Service SDK.

Diese Klasse ist verantwortlich für:

  • Laden des neuesten Bilds, das als Array von Bytes erfasst wurde.

  • Senden des Bytearrays an Ihre Azure Custom Vision Service-instance zur Analyse.

  • Empfangen der Antwort als JSON-Zeichenfolge.

  • Deserialisieren der Antwort und Übergeben der resultierenden Prediction an die SceneOrganiser-Klasse , die sich darum kümmert, wie die Antwort angezeigt werden soll.

So erstellen Sie diese Klasse:

  1. Klicken Sie mit der rechten Maustaste auf den Ressourcenordner im Projektbereich, und klicken Sie dann auf Ordner erstellen>. Rufen Sie den Ordner Skripts auf.

    Ordner

  2. Doppelklicken Sie auf den gerade erstellten Ordner, um ihn zu öffnen.

  3. Klicken Sie mit der rechten Maustaste in den Ordner, und klicken Sie dann aufC#-Skripterstellen>. Nennen Sie das Skript CustomVisionAnalyser.

  4. Doppelklicken Sie auf das neue CustomVisionAnalyser-Skript , um es mit Visual Studio zu öffnen.

  5. Aktualisieren Sie die Namespaces am Anfang der Datei so, dass sie mit folgendem übereinstimmen:

    using System.Collections;
    using System.IO;
    using UnityEngine;
    using UnityEngine.Networking;
    using Newtonsoft.Json;
    
  6. Fügen Sie in der CustomVisionAnalyser-Klasse die folgenden Variablen hinzu:

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

    Hinweis

    Stellen Sie sicher, dass Sie Ihren Prediction Key in die variable predictionKey und Ihren Prediction Endpoint in die variable predictionEndpoint einfügen. Sie haben diese zuvor im Kurs in Editor kopiert.

  7. Code für Awake() muss jetzt hinzugefügt werden, um die Instanzvariable zu initialisieren:

        /// <summary>
        /// Initialises this class
        /// </summary>
        private void Awake()
        {
            // Allows this instance to behave like a singleton
            Instance = this;
        }
    
  8. Löschen Sie die Methoden Start() und Update().

  9. Fügen Sie als Nächstes die Coroutine hinzu (mit der statischen GetImageAsByteArray()- Methode darunter), die die Ergebnisse der Analyse des von der ImageCapture-Klasse erfassten Bilds erhält.

    Hinweis

    In der AnalyseImageCapture-Coroutine gibt es einen Aufruf der SceneOrganiser-Klasse , die Sie noch erstellen müssen. Lassen Sie daher diese Zeilen vorerst kommentiert.

        /// <summary>
        /// Call the Computer Vision Service to submit the image.
        /// </summary>
        public IEnumerator AnalyseLastImageCaptured(string imagePath)
        {
            WWWForm webForm = new WWWForm();
            using (UnityWebRequest unityWebRequest = UnityWebRequest.Post(predictionEndpoint, webForm))
            {
                // Gets a byte array out of the saved image
                imageBytes = GetImageAsByteArray(imagePath);
    
                unityWebRequest.SetRequestHeader("Content-Type", "application/octet-stream");
                unityWebRequest.SetRequestHeader("Prediction-Key", predictionKey);
    
                // The upload handler will help uploading the byte array with the request
                unityWebRequest.uploadHandler = new UploadHandlerRaw(imageBytes);
                unityWebRequest.uploadHandler.contentType = "application/octet-stream";
    
                // The download handler will help receiving the analysis from Azure
                unityWebRequest.downloadHandler = new DownloadHandlerBuffer();
    
                // Send the request
                yield return unityWebRequest.SendWebRequest();
    
                string jsonResponse = unityWebRequest.downloadHandler.text;
    
                // The response will be in JSON format, therefore it needs to be deserialized    
    
                // The following lines refers to a class that you will build in later Chapters
                // Wait until then to uncomment these lines
    
                //AnalysisObject analysisObject = new AnalysisObject();
                //analysisObject = JsonConvert.DeserializeObject<AnalysisObject>(jsonResponse);
                //SceneOrganiser.Instance.SetTagsToLastLabel(analysisObject);
            }
        }
    
        /// <summary>
        /// Returns the contents of the specified image file as a byte array.
        /// </summary>
        static byte[] GetImageAsByteArray(string imageFilePath)
        {
            FileStream fileStream = new FileStream(imageFilePath, FileMode.Open, FileAccess.Read);
    
            BinaryReader binaryReader = new BinaryReader(fileStream);
    
            return binaryReader.ReadBytes((int)fileStream.Length);
        }
    
  10. Speichern Sie ihre Änderungen in Visual Studio , bevor Sie zu Unity zurückkehren.

Kapitel 7: Erstellen der CustomVisionObjects-Klasse

Die Klasse, die Sie jetzt erstellen, ist die CustomVisionObjects-Klasse .

Dieses Skript enthält eine Reihe von Objekten, die von anderen Klassen zum Serialisieren und Deserialisieren der Aufrufe des Custom Vision Diensts verwendet werden.

Warnung

Es ist wichtig, dass Sie sich den Endpunkt notieren, den Der Custom Vision Service bereitstellt, da die folgende JSON-Struktur für Custom Vision Prediction v2.0 eingerichtet wurde. Wenn Sie über eine andere Version verfügen, müssen Sie möglicherweise die folgende Struktur aktualisieren.

So erstellen Sie diese Klasse:

  1. Klicken Sie mit der rechten Maustaste in den Ordner Skripts, und klicken Sie dann aufC#-Skripterstellen>. Rufen Sie das Skript CustomVisionObjects auf.

  2. Doppelklicken Sie auf das neue CustomVisionObjects-Skript , um es mit Visual Studio zu öffnen.

  3. Fügen Sie die folgenden Namespaces am Anfang der Datei hinzu:

    using System;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.Networking;
    
  4. Löschen Sie die Start()- und Update()- Methoden in der CustomVisionObjects-Klasse . diese Klasse sollte jetzt leer sein.

  5. Fügen Sie die folgenden Klassen außerhalb der CustomVisionObjects-Klasse hinzu. Diese Objekte werden von der Newtonsoft-Bibliothek verwendet, um die Antwortdaten zu serialisieren und zu deserialisieren:

    // The objects contained in this script represent the deserialized version
    // of the objects used by this application 
    
    /// <summary>
    /// Web request object for image data
    /// </summary>
    class MultipartObject : IMultipartFormSection
    {
        public string sectionName { get; set; }
    
        public byte[] sectionData { get; set; }
    
        public string fileName { get; set; }
    
        public string contentType { get; set; }
    }
    
    /// <summary>
    /// JSON of all Tags existing within the project
    /// contains the list of Tags
    /// </summary> 
    public class Tags_RootObject
    {
        public List<TagOfProject> Tags { get; set; }
        public int TotalTaggedImages { get; set; }
        public int TotalUntaggedImages { get; set; }
    }
    
    public class TagOfProject
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public int ImageCount { get; set; }
    }
    
    /// <summary>
    /// JSON of Tag to associate to an image
    /// Contains a list of hosting the tags,
    /// since multiple tags can be associated with one image
    /// </summary> 
    public class Tag_RootObject
    {
        public List<Tag> Tags { get; set; }
    }
    
    public class Tag
    {
        public string ImageId { get; set; }
        public string TagId { get; set; }
    }
    
    /// <summary>
    /// JSON of Images submitted
    /// Contains objects that host detailed information about one or more images
    /// </summary> 
    public class ImageRootObject
    {
        public bool IsBatchSuccessful { get; set; }
        public List<SubmittedImage> Images { get; set; }
    }
    
    public class SubmittedImage
    {
        public string SourceUrl { get; set; }
        public string Status { get; set; }
        public ImageObject Image { get; set; }
    }
    
    public class ImageObject
    {
        public string Id { get; set; }
        public DateTime Created { get; set; }
        public int Width { get; set; }
        public int Height { get; set; }
        public string ImageUri { get; set; }
        public string ThumbnailUri { get; set; }
    }
    
    /// <summary>
    /// JSON of Service Iteration
    /// </summary> 
    public class Iteration
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public bool IsDefault { get; set; }
        public string Status { get; set; }
        public string Created { get; set; }
        public string LastModified { get; set; }
        public string TrainedAt { get; set; }
        public string ProjectId { get; set; }
        public bool Exportable { get; set; }
        public string DomainId { get; set; }
    }
    
    /// <summary>
    /// Predictions received by the Service after submitting an image for analysis
    /// </summary> 
    [Serializable]
    public class AnalysisObject
    {
        public List<Prediction> Predictions { get; set; }
    }
    
    [Serializable]
    public class Prediction
    {
        public string TagName { get; set; }
        public double Probability { get; set; }
    }
    

Kapitel 8: Erstellen der VoiceRecognizer-Klasse

Diese Klasse erkennt die Spracheingabe des Benutzers.

So erstellen Sie diese Klasse:

  1. Klicken Sie mit der rechten Maustaste in den Ordner Skripts, und klicken Sie dann aufC#-Skripterstellen>. Rufen Sie das Skript VoiceRecognizer auf.

  2. Doppelklicken Sie auf das neue VoiceRecognizer-Skript , um es mit Visual Studio zu öffnen.

  3. Fügen Sie die folgenden Namespaces oberhalb der VoiceRecognizer-Klasse hinzu:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using UnityEngine;
    using UnityEngine.Windows.Speech;
    
  4. Fügen Sie dann die folgenden Variablen in der VoiceRecognizer-Klasse oberhalb der Start()- Methode hinzu:

        /// <summary>
        /// Allows this class to behave like a singleton
        /// </summary>
        public static VoiceRecognizer Instance;
    
        /// <summary>
        /// Recognizer class for voice recognition
        /// </summary>
        internal KeywordRecognizer keywordRecognizer;
    
        /// <summary>
        /// List of Keywords registered
        /// </summary>
        private Dictionary<string, Action> _keywords = new Dictionary<string, Action>();
    
  5. Fügen Sie die Methoden Awake() und Start() hinzu, mit denen die Benutzerschlüsselwörter eingerichtet werden, die beim Zuordnen eines Tags zu einem Bild erkannt werden:

        /// <summary>
        /// Called on initialization
        /// </summary>
        private void Awake()
        {
            Instance = this;
        }
    
        /// <summary>
        /// Runs at initialization right after Awake method
        /// </summary>
        void Start ()
        {
    
            Array tagsArray = Enum.GetValues(typeof(CustomVisionTrainer.Tags));
    
            foreach (object tagWord in tagsArray)
            {
                _keywords.Add(tagWord.ToString(), () =>
                {
                    // When a word is recognized, the following line will be called
                    CustomVisionTrainer.Instance.VerifyTag(tagWord.ToString());
                });
            }
    
            _keywords.Add("Discard", () =>
            {
                // When a word is recognized, the following line will be called
                // The user does not want to submit the image
                // therefore ignore and discard the process
                ImageCapture.Instance.ResetImageCapture();
                keywordRecognizer.Stop();
            });
    
            //Create the keyword recognizer 
            keywordRecognizer = new KeywordRecognizer(_keywords.Keys.ToArray());
    
            // Register for the OnPhraseRecognized event 
            keywordRecognizer.OnPhraseRecognized += KeywordRecognizer_OnPhraseRecognized;
        }
    
  6. Löschen Sie die Update() -Methode.

  7. Fügen Sie den folgenden Handler hinzu, der immer dann aufgerufen wird, wenn Spracheingaben erkannt werden:

        /// <summary>
        /// Handler called when a word is recognized
        /// </summary>
        private void KeywordRecognizer_OnPhraseRecognized(PhraseRecognizedEventArgs args)
        {
            Action keywordAction;
            // if the keyword recognized is in our dictionary, call that Action.
            if (_keywords.TryGetValue(args.text, out keywordAction))
            {
                keywordAction.Invoke();
            }
        }
    
  8. Speichern Sie ihre Änderungen in Visual Studio , bevor Sie zu Unity zurückkehren.

Hinweis

Machen Sie sich keine Sorgen um Code, bei dem möglicherweise ein Fehler auftritt, da Sie in Kürze weitere Klassen bereitstellen, die diese beheben werden.

Kapitel 9: Erstellen der CustomVisionTrainer-Klasse

Diese Klasse verkettet eine Reihe von Webaufrufen, um den Custom Vision-Dienst zu trainieren. Jeder Aufruf wird direkt über dem Code ausführlich erläutert.

So erstellen Sie diese Klasse:

  1. Klicken Sie mit der rechten Maustaste in den Ordner Skripts, und klicken Sie dann aufC#-Skripterstellen>. Rufen Sie das Skript CustomVisionTrainer auf.

  2. Doppelklicken Sie auf das neue CustomVisionTrainer-Skript , um es mit Visual Studio zu öffnen.

  3. Fügen Sie die folgenden Namespaces über der CustomVisionTrainer-Klasse hinzu:

    using Newtonsoft.Json;
    using System.Collections;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;
    using UnityEngine;
    using UnityEngine.Networking;
    
  4. Fügen Sie dann die folgenden Variablen in der CustomVisionTrainer-Klasse oberhalb der Start()- Methode hinzu.

    Hinweis

    Die hier verwendete Trainings-URL wird in der dokumentation Custom Vision Training 1.2 bereitgestellt und weist folgende Struktur auf:https://southcentralus.api.cognitive.microsoft.com/customvision/v1.2/Training/projects/{projectId}/
    Weitere Informationen finden Sie in der Referenz-API Custom Vision Training v1.2.

    Warnung

    Es ist wichtig, dass Sie sich den Endpunkt notieren, den der Custom Vision Service für den Trainingsmodus bereitstellt, da die verwendete JSON-Struktur (innerhalb der CustomVisionObjects-Klasse) für Custom Vision Training v1.2 eingerichtet wurde. Wenn Sie über eine andere Version verfügen, müssen Sie möglicherweise die Objektstruktur aktualisieren.

        /// <summary>
        /// Allows this class to behave like a singleton
        /// </summary>
        public static CustomVisionTrainer Instance;
    
        /// <summary>
        /// Custom Vision Service URL root
        /// </summary>
        private string url = "https://southcentralus.api.cognitive.microsoft.com/customvision/v1.2/Training/projects/";
    
        /// <summary>
        /// Insert your prediction key here
        /// </summary>
        private string trainingKey = "- Insert your key here -";
    
        /// <summary>
        /// Insert your Project Id here
        /// </summary>
        private string projectId = "- Insert your Project Id here -";
    
        /// <summary>
        /// Byte array of the image to submit for analysis
        /// </summary>
        internal byte[] imageBytes;
    
        /// <summary>
        /// The Tags accepted
        /// </summary>
        internal enum Tags {Mouse, Keyboard}
    
        /// <summary>
        /// The UI displaying the training Chapters
        /// </summary>
        private TextMesh trainingUI_TextMesh;
    

    Wichtig

    Stellen Sie sicher, dass Sie ihren Dienstschlüssel (Trainingsschlüssel) und den Wert der Projekt-ID hinzufügen, die Sie zuvor notiert haben. Dies sind die Werte, die Sie zuvor im Portal (Kapitel 2, Schritt 10) erfasst haben.

  5. Fügen Sie die folgenden Start() - und Awake()- Methoden hinzu. Diese Methoden werden bei der Initialisierung aufgerufen und enthalten den Aufruf zum Einrichten der Benutzeroberfläche:

        /// <summary>
        /// Called on initialization
        /// </summary>
        private void Awake()
        {
            Instance = this;
        }
    
        /// <summary>
        /// Runs at initialization right after Awake method
        /// </summary>
        private void Start()
        { 
            trainingUI_TextMesh = SceneOrganiser.Instance.CreateTrainingUI("TrainingUI", 0.04f, 0, 4, false);
        }
    
  6. Löschen Sie die Update() -Methode. Diese Klasse benötigt sie nicht.

  7. Fügen Sie die RequestTagSelection()- Methode hinzu. Diese Methode wird zum ersten Mal aufgerufen, wenn ein Bild erfasst und auf dem Gerät gespeichert wurde. Sie kann nun an den Custom Vision-Dienst übermittelt werden, um es zu trainieren. Diese Methode zeigt auf der Trainingsoberfläche eine Reihe von Schlüsselwörtern an, die der Benutzer zum Markieren des erfassten Bilds verwenden kann. Außerdem wird die VoiceRecognizer-Klasse benachrichtigt, damit sie dem Benutzer die Spracheingabe abhört.

        internal void RequestTagSelection()
        {
            trainingUI_TextMesh.gameObject.SetActive(true);
            trainingUI_TextMesh.text = $" \nUse voice command \nto choose between the following tags: \nMouse\nKeyboard \nor say Discard";
    
            VoiceRecognizer.Instance.keywordRecognizer.Start();
        }
    
  8. Fügen Sie die VerifyTag()- Methode hinzu. Diese Methode empfängt die von der VoiceRecognizer-Klasse erkannte Spracheingabe, überprüft ihre Gültigkeit und beginnt dann mit dem Trainingsprozess.

        /// <summary>
        /// Verify voice input against stored tags.
        /// If positive, it will begin the Service training process.
        /// </summary>
        internal void VerifyTag(string spokenTag)
        {
            if (spokenTag == Tags.Mouse.ToString() || spokenTag == Tags.Keyboard.ToString())
            {
                trainingUI_TextMesh.text = $"Tag chosen: {spokenTag}";
                VoiceRecognizer.Instance.keywordRecognizer.Stop();
                StartCoroutine(SubmitImageForTraining(ImageCapture.Instance.filePath, spokenTag));
            }
        }
    
  9. Fügen Sie die SubmitImageForTraining()- Methode hinzu. Mit dieser Methode wird der trainingsprozess Custom Vision Service gestartet. Der erste Schritt besteht darin, die Tag-ID aus dem Dienst abzurufen, die der überprüften Spracheingabe des Benutzers zugeordnet ist. Die Tag-ID wird dann zusammen mit dem Bild hochgeladen.

        /// <summary>
        /// Call the Custom Vision Service to submit the image.
        /// </summary>
        public IEnumerator SubmitImageForTraining(string imagePath, string tag)
        {
            yield return new WaitForSeconds(2);
            trainingUI_TextMesh.text = $"Submitting Image \nwith tag: {tag} \nto Custom Vision Service";
            string imageId = string.Empty;
            string tagId = string.Empty;
    
            // Retrieving the Tag Id relative to the voice input
            string getTagIdEndpoint = string.Format("{0}{1}/tags", url, projectId);
            using (UnityWebRequest www = UnityWebRequest.Get(getTagIdEndpoint))
            {
                www.SetRequestHeader("Training-Key", trainingKey);
                www.downloadHandler = new DownloadHandlerBuffer();
                yield return www.SendWebRequest();
                string jsonResponse = www.downloadHandler.text;
    
                Tags_RootObject tagRootObject = JsonConvert.DeserializeObject<Tags_RootObject>(jsonResponse);
    
                foreach (TagOfProject tOP in tagRootObject.Tags)
                {
                    if (tOP.Name == tag)
                    {
                        tagId = tOP.Id;
                    }             
                }
            }
    
            // Creating the image object to send for training
            List<IMultipartFormSection> multipartList = new List<IMultipartFormSection>();
            MultipartObject multipartObject = new MultipartObject();
            multipartObject.contentType = "application/octet-stream";
            multipartObject.fileName = "";
            multipartObject.sectionData = GetImageAsByteArray(imagePath);
            multipartList.Add(multipartObject);
    
            string createImageFromDataEndpoint = string.Format("{0}{1}/images?tagIds={2}", url, projectId, tagId);
    
            using (UnityWebRequest www = UnityWebRequest.Post(createImageFromDataEndpoint, multipartList))
            {
                // Gets a byte array out of the saved image
                imageBytes = GetImageAsByteArray(imagePath);           
    
                //unityWebRequest.SetRequestHeader("Content-Type", "application/octet-stream");
                www.SetRequestHeader("Training-Key", trainingKey);
    
                // The upload handler will help uploading the byte array with the request
                www.uploadHandler = new UploadHandlerRaw(imageBytes);
    
                // The download handler will help receiving the analysis from Azure
                www.downloadHandler = new DownloadHandlerBuffer();
    
                // Send the request
                yield return www.SendWebRequest();
    
                string jsonResponse = www.downloadHandler.text;
    
                ImageRootObject m = JsonConvert.DeserializeObject<ImageRootObject>(jsonResponse);
                imageId = m.Images[0].Image.Id;
            }
            trainingUI_TextMesh.text = "Image uploaded";
            StartCoroutine(TrainCustomVisionProject());
        }
    
  10. Fügen Sie die TrainCustomVisionProject()- Methode hinzu. Sobald das Bild übermittelt und markiert wurde, wird diese Methode aufgerufen. Es wird eine neue Iteration erstellt, die mit allen vorherigen Bildern trainiert wird, die an den Dienst übermittelt wurden, plus dem soeben hochgeladenen Bild. Nachdem das Training abgeschlossen wurde, ruft diese Methode eine Methode auf, um die neu erstellte Iteration als Standard festzulegen, sodass der Endpunkt, den Sie für die Analyse verwenden, die neueste trainierte Iteration ist.

        /// <summary>
        /// Call the Custom Vision Service to train the Service.
        /// It will generate a new Iteration in the Service
        /// </summary>
        public IEnumerator TrainCustomVisionProject()
        {
            yield return new WaitForSeconds(2);
    
            trainingUI_TextMesh.text = "Training Custom Vision Service";
    
            WWWForm webForm = new WWWForm();
    
            string trainProjectEndpoint = string.Format("{0}{1}/train", url, projectId);
    
            using (UnityWebRequest www = UnityWebRequest.Post(trainProjectEndpoint, webForm))
            {
                www.SetRequestHeader("Training-Key", trainingKey);
                www.downloadHandler = new DownloadHandlerBuffer();
                yield return www.SendWebRequest();
                string jsonResponse = www.downloadHandler.text;
                Debug.Log($"Training - JSON Response: {jsonResponse}");
    
                // A new iteration that has just been created and trained
                Iteration iteration = new Iteration();
                iteration = JsonConvert.DeserializeObject<Iteration>(jsonResponse);
    
                if (www.isDone)
                {
                    trainingUI_TextMesh.text = "Custom Vision Trained";
    
                    // Since the Service has a limited number of iterations available,
                    // we need to set the last trained iteration as default
                    // and delete all the iterations you dont need anymore
                    StartCoroutine(SetDefaultIteration(iteration)); 
                }
            }
        }
    
  11. Fügen Sie die SetDefaultIteration()- Methode hinzu. Diese Methode legt die zuvor erstellte und trainierte Iteration als Standard fest. Nach Abschluss des Vorgangs muss diese Methode die vorherige Iteration löschen, die im Dienst vorhanden ist. Zum Zeitpunkt der Erstellung dieses Kurses gilt eine Beschränkung von maximal zehn (10) Iterationen, die gleichzeitig im Dienst vorhanden sein dürfen.

        /// <summary>
        /// Set the newly created iteration as Default
        /// </summary>
        private IEnumerator SetDefaultIteration(Iteration iteration)
        {
            yield return new WaitForSeconds(5);
            trainingUI_TextMesh.text = "Setting default iteration";
    
            // Set the last trained iteration to default
            iteration.IsDefault = true;
    
            // Convert the iteration object as JSON
            string iterationAsJson = JsonConvert.SerializeObject(iteration);
            byte[] bytes = Encoding.UTF8.GetBytes(iterationAsJson);
    
            string setDefaultIterationEndpoint = string.Format("{0}{1}/iterations/{2}", 
                                                            url, projectId, iteration.Id);
    
            using (UnityWebRequest www = UnityWebRequest.Put(setDefaultIterationEndpoint, bytes))
            {
                www.method = "PATCH";
                www.SetRequestHeader("Training-Key", trainingKey);
                www.SetRequestHeader("Content-Type", "application/json");
                www.downloadHandler = new DownloadHandlerBuffer();
    
                yield return www.SendWebRequest();
    
                string jsonResponse = www.downloadHandler.text;
    
                if (www.isDone)
                {
                    trainingUI_TextMesh.text = "Default iteration is set \nDeleting Unused Iteration";
                    StartCoroutine(DeletePreviousIteration(iteration));
                }
            }
        }
    
  12. Fügen Sie die DeletePreviousIteration()- Methode hinzu. Diese Methode sucht und löscht die vorherige nicht standardmäßige Iteration:

        /// <summary>
        /// Delete the previous non-default iteration.
        /// </summary>
        public IEnumerator DeletePreviousIteration(Iteration iteration)
        {
            yield return new WaitForSeconds(5);
    
            trainingUI_TextMesh.text = "Deleting Unused \nIteration";
    
            string iterationToDeleteId = string.Empty;
    
            string findAllIterationsEndpoint = string.Format("{0}{1}/iterations", url, projectId);
    
            using (UnityWebRequest www = UnityWebRequest.Get(findAllIterationsEndpoint))
            {
                www.SetRequestHeader("Training-Key", trainingKey);
                www.downloadHandler = new DownloadHandlerBuffer();
                yield return www.SendWebRequest();
    
                string jsonResponse = www.downloadHandler.text;
    
                // The iteration that has just been trained
                List<Iteration> iterationsList = new List<Iteration>();
                iterationsList = JsonConvert.DeserializeObject<List<Iteration>>(jsonResponse);
    
                foreach (Iteration i in iterationsList)
                {
                    if (i.IsDefault != true)
                    {
                        Debug.Log($"Cleaning - Deleting iteration: {i.Name}, {i.Id}");
                        iterationToDeleteId = i.Id;
                        break;
                    }
                }
            }
    
            string deleteEndpoint = string.Format("{0}{1}/iterations/{2}", url, projectId, iterationToDeleteId);
    
            using (UnityWebRequest www2 = UnityWebRequest.Delete(deleteEndpoint))
            {
                www2.SetRequestHeader("Training-Key", trainingKey);
                www2.downloadHandler = new DownloadHandlerBuffer();
                yield return www2.SendWebRequest();
                string jsonResponse = www2.downloadHandler.text;
    
                trainingUI_TextMesh.text = "Iteration Deleted";
                yield return new WaitForSeconds(2);
                trainingUI_TextMesh.text = "Ready for next \ncapture";
    
                yield return new WaitForSeconds(2);
                trainingUI_TextMesh.text = "";
                ImageCapture.Instance.ResetImageCapture();
            }
        }
    
  13. Die letzte Methode, die dieser Klasse hinzugefügt werden soll, ist die GetImageAsByteArray() -Methode, die in Webaufrufen verwendet wird, um das erfasste Bild in ein Bytearray zu konvertieren.

        /// <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);
        }
    
  14. Speichern Sie ihre Änderungen in Visual Studio , bevor Sie zu Unity zurückkehren.

Kapitel 10: Erstellen der SceneOrganiser-Klasse

Diese Klasse führt Folgendes aus:

  • Erstellen Sie ein Cursor-Objekt , das an die Hauptkamera angefügt werden soll.

  • Erstellen Sie ein Label-Objekt , das angezeigt wird, wenn der Dienst die realen Objekte erkennt.

  • Richten Sie die Hauptkamera ein, indem Sie die entsprechenden Komponenten an sie anfügen.

  • Wenn Sie sich im Analysemodus befinden, erstellen Sie die Bezeichnungen zur Laufzeit im entsprechenden Weltbereich relativ zur Position der Hauptkamera, und zeigen Sie die vom Custom Vision-Dienst empfangenen Daten an.

  • Wenn Sie sich im Trainingsmodus befinden, erstellen Sie die Benutzeroberfläche, die die verschiedenen Phasen des Trainingsprozesses anzeigt.

So erstellen Sie diese Klasse:

  1. Klicken Sie mit der rechten Maustaste in den Ordner Skripts, und klicken Sie dann aufC#-Skript erstellen>. Nennen Sie das Skript SceneOrganiser.

  2. Doppelklicken Sie auf das neue SceneOrganiser-Skript , um es mit Visual Studio zu öffnen.

  3. Sie benötigen nur einen Namespace, und entfernen Sie die anderen aus der SceneOrganiser-Klasse :

    using UnityEngine;
    
  4. Fügen Sie dann die folgenden Variablen in der SceneOrganiser-Klasse oberhalb der Start() -Methode hinzu:

        /// <summary>
        /// Allows this class to behave like a singleton
        /// </summary>
        public static SceneOrganiser Instance;
    
        /// <summary>
        /// The cursor object attached to the camera
        /// </summary>
        internal GameObject cursor;
    
        /// <summary>
        /// The label used to display the analysis on the objects in the real world
        /// </summary>
        internal GameObject label;
    
        /// <summary>
        /// Object providing the current status of the camera.
        /// </summary>
        internal TextMesh cameraStatusIndicator;
    
        /// <summary>
        /// Reference to the last label positioned
        /// </summary>
        internal Transform lastLabelPlaced;
    
        /// <summary>
        /// Reference to the last label positioned
        /// </summary>
        internal TextMesh lastLabelPlacedText;
    
        /// <summary>
        /// Current threshold accepted for displaying the label
        /// Reduce this value to display the recognition more often
        /// </summary>
        internal float probabilityThreshold = 0.5f;
    
  5. Löschen Sie die Start()- und Update()- Methoden.

  6. Fügen Sie direkt unter den Variablen die Awake() -Methode hinzu, mit der die Klasse initialisiert und die Szene eingerichtet wird.

        /// <summary>
        /// Called on initialization
        /// </summary>
        private void Awake()
        {
            // Use this class instance as singleton
            Instance = this;
    
            // Add the ImageCapture class to this GameObject
            gameObject.AddComponent<ImageCapture>();
    
            // Add the CustomVisionAnalyser class to this GameObject
            gameObject.AddComponent<CustomVisionAnalyser>();
    
            // Add the CustomVisionTrainer class to this GameObject
            gameObject.AddComponent<CustomVisionTrainer>();
    
            // Add the VoiceRecogniser class to this GameObject
            gameObject.AddComponent<VoiceRecognizer>();
    
            // Add the CustomVisionObjects class to this GameObject
            gameObject.AddComponent<CustomVisionObjects>();
    
            // Create the camera Cursor
            cursor = CreateCameraCursor();
    
            // Load the label prefab as reference
            label = CreateLabel();
    
            // Create the camera status indicator label, and place it above where predictions
            // and training UI will appear.
            cameraStatusIndicator = CreateTrainingUI("Status Indicator", 0.02f, 0.2f, 3, true);
    
            // Set camera status indicator to loading.
            SetCameraStatus("Loading");
        }
    
  7. Fügen Sie nun die CreateCameraCursor()- Methode hinzu, die den Hauptkameracursor erstellt und positioniert, und die CreateLabel() -Methode, die das Analysis Label-Objekt erstellt.

        /// <summary>
        /// Spawns cursor for the Main Camera
        /// </summary>
        private GameObject CreateCameraCursor()
        {
            // Create a sphere as new cursor
            GameObject newCursor = GameObject.CreatePrimitive(PrimitiveType.Sphere);
    
            // Attach it to the camera
            newCursor.transform.parent = gameObject.transform;
    
            // Resize the new cursor
            newCursor.transform.localScale = new Vector3(0.02f, 0.02f, 0.02f);
    
            // Move it to the correct position
            newCursor.transform.localPosition = new Vector3(0, 0, 4);
    
            // Set the cursor color to red
            newCursor.GetComponent<Renderer>().material = new Material(Shader.Find("Diffuse"));
            newCursor.GetComponent<Renderer>().material.color = Color.green;
    
            return newCursor;
        }
    
        /// <summary>
        /// Create the analysis label object
        /// </summary>
        private GameObject CreateLabel()
        {
            // Create a sphere as new cursor
            GameObject newLabel = new GameObject();
    
            // Resize the new cursor
            newLabel.transform.localScale = new Vector3(0.01f, 0.01f, 0.01f);
    
            // Creating the text of the label
            TextMesh t = newLabel.AddComponent<TextMesh>();
            t.anchor = TextAnchor.MiddleCenter;
            t.alignment = TextAlignment.Center;
            t.fontSize = 50;
            t.text = "";
    
            return newLabel;
        }
    
  8. Fügen Sie die SetCameraStatus()-Methode hinzu, die Nachrichten verarbeitet, die für das Textgitter vorgesehen sind und die status der Kamera bereitstellen.

        /// <summary>
        /// Set the camera status to a provided string. Will be coloured if it matches a keyword.
        /// </summary>
        /// <param name="statusText">Input string</param>
        public void SetCameraStatus(string statusText)
        {
            if (string.IsNullOrEmpty(statusText) == false)
            {
                string message = "white";
    
                switch (statusText.ToLower())
                {
                    case "loading":
                        message = "yellow";
                        break;
    
                    case "ready":
                        message = "green";
                        break;
    
                    case "uploading image":
                        message = "red";
                        break;
    
                    case "looping capture":
                        message = "yellow";
                        break;
    
                    case "analysis":
                        message = "red";
                        break;
                }
    
                cameraStatusIndicator.GetComponent<TextMesh>().text = $"Camera Status:\n<color={message}>{statusText}..</color>";
            }
        }
    
  9. Fügen Sie die Methoden PlaceAnalysisLabel() und SetTagsToLastLabel() hinzu, wodurch die Daten aus dem Custom Vision Service in der Szene erzeugt und angezeigt werden.

        /// <summary>
        /// Instantiate a label in the appropriate location relative to the Main Camera.
        /// </summary>
        public void PlaceAnalysisLabel()
        {
            lastLabelPlaced = Instantiate(label.transform, cursor.transform.position, transform.rotation);
            lastLabelPlacedText = lastLabelPlaced.GetComponent<TextMesh>();
        }
    
        /// <summary>
        /// Set the Tags as Text of the last label created. 
        /// </summary>
        public void SetTagsToLastLabel(AnalysisObject analysisObject)
        {
            lastLabelPlacedText = lastLabelPlaced.GetComponent<TextMesh>();
    
            if (analysisObject.Predictions != null)
            {
                foreach (Prediction p in analysisObject.Predictions)
                {
                    if (p.Probability > 0.02)
                    {
                        lastLabelPlacedText.text += $"Detected: {p.TagName} {p.Probability.ToString("0.00 \n")}";
                        Debug.Log($"Detected: {p.TagName} {p.Probability.ToString("0.00 \n")}");
                    }
                }
            }
        }
    
  10. Fügen Sie schließlich die CreateTrainingUI()- Methode hinzu, die die Benutzeroberfläche erzeugt, die die mehreren Phasen des Trainingsprozesses anzeigt, wenn sich die Anwendung im Trainingsmodus befindet. Diese Methode wird auch verwendet, um die Kamera status Objekt zu erstellen.

        /// <summary>
        /// Create a 3D Text Mesh in scene, with various parameters.
        /// </summary>
        /// <param name="name">name of object</param>
        /// <param name="scale">scale of object (i.e. 0.04f)</param>
        /// <param name="yPos">height above the cursor (i.e. 0.3f</param>
        /// <param name="zPos">distance from the camera</param>
        /// <param name="setActive">whether the text mesh should be visible when it has been created</param>
        /// <returns>Returns a 3D text mesh within the scene</returns>
        internal TextMesh CreateTrainingUI(string name, float scale, float yPos, float zPos, bool setActive)
        {
            GameObject display = new GameObject(name, typeof(TextMesh));
            display.transform.parent = Camera.main.transform;
            display.transform.localPosition = new Vector3(0, yPos, zPos);
            display.SetActive(setActive);
            display.transform.localScale = new Vector3(scale, scale, scale);
            display.transform.rotation = new Quaternion();
            TextMesh textMesh = display.GetComponent<TextMesh>();
            textMesh.anchor = TextAnchor.MiddleCenter;
            textMesh.alignment = TextAlignment.Center;
            return textMesh;
        }
    
  11. Speichern Sie ihre Änderungen in Visual Studio , bevor Sie zu Unity zurückkehren.

Wichtig

Öffnen Sie vor dem Fortfahren die CustomVisionAnalyser-Klasse , und heben Sie in der AnalyseLastImageCaptured()- Methode die Auskommentierung der folgenden Zeilen auf:

  AnalysisObject analysisObject = new AnalysisObject();
  analysisObject = JsonConvert.DeserializeObject<AnalysisObject>(jsonResponse);
  SceneOrganiser.Instance.SetTagsToLastLabel(analysisObject);

Kapitel 11: Erstellen der ImageCapture-Klasse

Die nächste Klasse, die Sie erstellen, ist die ImageCapture-Klasse .

Diese Klasse ist für Folgendes zuständig:

  • Erfassen eines Bilds mithilfe der HoloLens-Kamera und Speichern im App-Ordner .

  • Behandeln von Tippen-Gesten des Benutzers.

  • Beibehalten des Enumerationswerts , der bestimmt, ob die Anwendung im Analyse - oder Trainingsmodus ausgeführt wird.

So erstellen Sie diese Klasse:

  1. Wechseln Sie zum Ordner Skripts, den Sie zuvor erstellt haben.

  2. Klicken Sie mit der rechten Maustaste in den Ordner, und klicken Sie dann auf C#-Skript erstellen>. Nennen Sie das Skript ImageCapture.

  3. Doppelklicken Sie auf das neue ImageCapture-Skript , um es mit Visual Studio zu öffnen.

  4. Ersetzen Sie die Namespaces am Anfang der Datei durch Folgendes:

    using System;
    using System.IO;
    using System.Linq;
    using UnityEngine;
    using UnityEngine.XR.WSA.Input;
    using UnityEngine.XR.WSA.WebCam;
    
  5. Fügen Sie dann die folgenden Variablen in der ImageCapture-Klasse oberhalb der Start() -Methode hinzu:

        /// <summary>
        /// Allows this class to behave like a singleton
        /// </summary>
        public static ImageCapture Instance;
    
        /// <summary>
        /// Keep counts of the taps for image renaming
        /// </summary>
        private int captureCount = 0;
    
        /// <summary>
        /// Photo Capture object
        /// </summary>
        private PhotoCapture photoCaptureObject = null;
    
        /// <summary>
        /// Allows gestures recognition in HoloLens
        /// </summary>
        private GestureRecognizer recognizer;
    
        /// <summary>
        /// Loop timer
        /// </summary>
        private float secondsBetweenCaptures = 10f;
    
        /// <summary>
        /// Application main functionalities switch
        /// </summary>
        internal enum AppModes {Analysis, Training }
    
        /// <summary>
        /// Local variable for current AppMode
        /// </summary>
        internal AppModes AppMode { get; private set; }
    
        /// <summary>
        /// Flagging if the capture loop is running
        /// </summary>
        internal bool captureIsActive;
    
        /// <summary>
        /// File path of current analysed photo
        /// </summary>
        internal string filePath = string.Empty;
    
  6. Code für die Methoden Awake() und Start() muss jetzt hinzugefügt werden:

        /// <summary>
        /// Called on initialization
        /// </summary>
        private void Awake()
        {
            Instance = this;
    
            // Change this flag to switch between Analysis Mode and Training Mode 
            AppMode = AppModes.Training;
        }
    
        /// <summary>
        /// Runs at initialization right after Awake method
        /// </summary>
        void Start()
        {
            // Clean up the LocalState folder of this application from all photos stored
            DirectoryInfo info = new DirectoryInfo(Application.persistentDataPath);
            var fileInfo = info.GetFiles();
            foreach (var file in fileInfo)
            {
                try
                {
                    file.Delete();
                }
                catch (Exception)
                {
                    Debug.LogFormat("Cannot delete file: ", file.Name);
                }
            } 
    
            // Subscribing to the HoloLens API gesture recognizer to track user gestures
            recognizer = new GestureRecognizer();
            recognizer.SetRecognizableGestures(GestureSettings.Tap);
            recognizer.Tapped += TapHandler;
            recognizer.StartCapturingGestures();
    
            SceneOrganiser.Instance.SetCameraStatus("Ready");
        }
    
  7. Implementieren Sie einen Handler, der aufgerufen wird, wenn eine Tippen-Geste auftritt.

        /// <summary>
        /// Respond to Tap Input.
        /// </summary>
        private void TapHandler(TappedEventArgs obj)
        {
            switch (AppMode)
            {
                case AppModes.Analysis:
                    if (!captureIsActive)
                    {
                        captureIsActive = true;
    
                        // Set the cursor color to red
                        SceneOrganiser.Instance.cursor.GetComponent<Renderer>().material.color = Color.red;
    
                        // Update camera status to looping capture.
                        SceneOrganiser.Instance.SetCameraStatus("Looping Capture");
    
                        // Begin the capture loop
                        InvokeRepeating("ExecuteImageCaptureAndAnalysis", 0, secondsBetweenCaptures);
                    }
                    else
                    {
                        // The user tapped while the app was analyzing 
                        // therefore stop the analysis process
                        ResetImageCapture();
                    }
                    break;
    
                case AppModes.Training:
                    if (!captureIsActive)
                    {
                        captureIsActive = true;
    
                        // Call the image capture
                        ExecuteImageCaptureAndAnalysis();
    
                        // Set the cursor color to red
                        SceneOrganiser.Instance.cursor.GetComponent<Renderer>().material.color = Color.red;
    
                        // Update camera status to uploading image.
                        SceneOrganiser.Instance.SetCameraStatus("Uploading Image");
                    }              
                    break;
            }     
        }
    

    Hinweis

    Im Analysemodus fungiert die TapHandler-Methode als Schalter zum Starten oder Beenden der Fotoaufnahmeschleife.

    Im Trainingsmodus wird ein Bild von der Kamera aufgenommen.

    Wenn der Cursor grün ist, bedeutet dies, dass die Kamera verfügbar ist, um das Bild aufzunehmen.

    Wenn der Cursor rot ist, bedeutet dies, dass die Kamera ausgelastet ist.

  8. Fügen Sie die Methode hinzu, die von der Anwendung zum Starten des Bildaufnahmeprozesses und zum Speichern des Bilds verwendet wird.

        /// <summary>
        /// Begin process of Image Capturing and send To Azure Custom Vision Service.
        /// </summary>
        private void ExecuteImageCaptureAndAnalysis()
        {
            // Update camera status to analysis.
            SceneOrganiser.Instance.SetCameraStatus("Analysis");
    
            // Create a label in world space using the SceneOrganiser class 
            // Invisible at this point but correctly positioned where the image was taken
            SceneOrganiser.Instance.PlaceAnalysisLabel();
    
            // Set the camera resolution to be the highest possible
            Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First();
    
            Texture2D targetTexture = new Texture2D(cameraResolution.width, cameraResolution.height);
    
            // Begin capture process, set the image format
            PhotoCapture.CreateAsync(false, delegate (PhotoCapture captureObject)
            {
                photoCaptureObject = captureObject;
    
                CameraParameters camParameters = new CameraParameters
                {
                    hologramOpacity = 0.0f,
                    cameraResolutionWidth = targetTexture.width,
                    cameraResolutionHeight = targetTexture.height,
                    pixelFormat = CapturePixelFormat.BGRA32
                };
    
                // Capture the image from the camera and save it in the App internal folder
                captureObject.StartPhotoModeAsync(camParameters, delegate (PhotoCapture.PhotoCaptureResult result)
                {
                    string filename = string.Format(@"CapturedImage{0}.jpg", captureCount);
                    filePath = Path.Combine(Application.persistentDataPath, filename);          
                    captureCount++;              
                    photoCaptureObject.TakePhotoAsync(filePath, PhotoCaptureFileOutputFormat.JPG, OnCapturedPhotoToDisk);              
                });
            });   
        }
    
  9. Fügen Sie die Handler hinzu, die aufgerufen werden, wenn das Foto aufgenommen wurde und wann es analysiert werden kann. Das Ergebnis wird dann an customVisionAnalyser oder CustomVisionTrainer übergeben, je nachdem, in welchem Modus der Code festgelegt ist.

        /// <summary>
        /// Register the full execution of the Photo Capture. 
        /// </summary>
        void OnCapturedPhotoToDisk(PhotoCapture.PhotoCaptureResult result)
        {
                // Call StopPhotoMode once the image has successfully captured
                photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
        }
    
    
        /// <summary>
        /// The camera photo mode has stopped after the capture.
        /// Begin the Image Analysis process.
        /// </summary>
        void OnStoppedPhotoMode(PhotoCapture.PhotoCaptureResult result)
        {
            Debug.LogFormat("Stopped Photo Mode");
    
            // Dispose from the object in memory and request the image analysis 
            photoCaptureObject.Dispose();
            photoCaptureObject = null;
    
            switch (AppMode)
            {
                case AppModes.Analysis:
                    // Call the image analysis
                    StartCoroutine(CustomVisionAnalyser.Instance.AnalyseLastImageCaptured(filePath));
                    break;
    
                case AppModes.Training:
                    // Call training using captured image
                    CustomVisionTrainer.Instance.RequestTagSelection();
                    break;
            }
        }
    
        /// <summary>
        /// Stops all capture pending actions
        /// </summary>
        internal void ResetImageCapture()
        {
            captureIsActive = false;
    
            // Set the cursor color to green
            SceneOrganiser.Instance.cursor.GetComponent<Renderer>().material.color = Color.green;
    
            // Update camera status to ready.
            SceneOrganiser.Instance.SetCameraStatus("Ready");
    
            // Stop the capture loop if active
            CancelInvoke();
        }
    
  10. Speichern Sie ihre Änderungen in Visual Studio , bevor Sie zu Unity zurückkehren.

  11. Nachdem alle Skripts abgeschlossen wurden, kehren Sie zum Unity-Editor zurück, klicken Sie dann auf die SceneOrganiser-Klasse , und ziehen Sie sie aus dem Ordner Skripts in das Hauptobjekt Kamera im Hierarchiebereich.

Kapitel 12 – Vor dem Erstellen

Um einen gründlichen Test Ihrer Anwendung durchzuführen, müssen Sie sie auf Ihre HoloLens querladen.

Stellen Sie zunächst Folgendes sicher:

  • Alle in Kapitel 2 erwähnten Einstellungen sind ordnungsgemäß festgelegt.

  • Alle Felder in der Hauptkamera, Inspektorbereich, werden ordnungsgemäß zugewiesen.

  • Das Skript SceneOrganiser ist an das Hauptkameraobjekt angefügt.

  • Stellen Sie sicher, dass Sie Ihren Vorhersageschlüssel in die Variable predictionKey einfügen.

  • Sie haben Ihren Prediction Endpoint in die variable predictionEndpoint eingefügt.

  • Sie haben Ihren Trainingsschlüssel in die Variable trainingKey der CustomVisionTrainer-Klasse eingefügt.

  • Sie haben Ihre Projekt-ID in die variable projectId der CustomVisionTrainer-Klasse eingefügt.

Kapitel 13: Erstellen und Querladen Ihrer Anwendung

So starten Sie den Buildprozess :

  1. Wechseln Sie zu Dateibuildeinstellungen>.

  2. Ticken Sie Unity C#-Projekte.

  3. Klicken Sie auf Erstellen. Unity startet ein Explorer Fenster, in dem Sie einen Ordner erstellen und dann auswählen müssen, in dem die App erstellt werden soll. Erstellen Sie diesen Ordner jetzt, und nennen Sie ihn App. Klicken Sie dann bei ausgewählter App-Ordner auf Ordner auswählen.

  4. Unity beginnt mit dem Erstellen Ihres Projekts im Ordner App .

  5. Sobald Unity den Bau abgeschlossen hat (es kann einige Zeit dauern), öffnet es ein Explorer Fenster an der Position Ihres Builds (überprüfen Sie Ihre Taskleiste, da es möglicherweise nicht immer über Ihren Fenstern angezeigt wird, aber Sie über das Hinzufügen eines neuen Fensters benachrichtigt).

So stellen Sie auf HoloLens bereit:

  1. Sie benötigen die IP-Adresse Ihrer HoloLens (für Remote Deploy) und um sicherzustellen, dass sich Ihre HoloLens im Entwicklermodus befindet. Gehen Sie dazu folgendermaßen vor:

    1. Öffnen Sie die Einstellungen, während Sie Ihre HoloLens tragen.

    2. Wechseln Sie zu Netzwerk & Internet>Wi-Fi>Erweiterte Optionen

    3. Notieren Sie sich die IPv4-Adresse .

    4. Navigieren Sie als Nächstes zurück zu Einstellungen und dann zu Aktualisieren & Sicherheit>für Entwickler.

    5. Legen Sie den Entwicklermodus ein.

  2. Navigieren Sie zu Ihrem neuen Unity-Build (dem Ordner App ), und öffnen Sie die Projektmappendatei mit Visual Studio.

  3. Wählen Sie in der Projektmappenkonfigurationdebuggen aus.

  4. Wählen Sie auf der Lösungsplattformdie Option x86, Remotecomputer aus. Sie werden aufgefordert, die IP-Adresse eines Remotegeräts einzugeben (in diesem Fall die HoloLens, die Sie sich notiert haben).

    Festlegen der IP-Adresse

  5. Navigieren Sie zum Menü Erstellen , und klicken Sie auf Lösung bereitstellen , um die Anwendung in Ihre HoloLens querzuladen.

  6. Ihre App sollte jetzt in der Liste der installierten Apps auf Ihrer HoloLens angezeigt werden, bereit für den Start!

Hinweis

Legen Sie für die Bereitstellung auf einem immersiven Headset die Lösungsplattform auf Lokaler Computer fest, und legen Sie die Konfiguration auf Debuggen fest, wobei x86 als Plattform verwendet wird. Stellen Sie dann auf dem lokalen Computer bereit, indem Sie das Menüelement Erstellen verwenden, und wählen Sie Lösung bereitstellen aus.

So verwenden Sie die Anwendung:

Um die App-Funktionalität zwischen Trainingsmodus und Vorhersagemodus zu wechseln, müssen Sie die AppMode-Variable aktualisieren, die sich in der Awake() -Methode befindet, die sich in der ImageCapture-Klasse befindet.

        // Change this flag to switch between Analysis mode and Training mode 
        AppMode = AppModes.Training;

oder

        // Change this flag to switch between Analysis mode and Training mode 
        AppMode = AppModes.Analysis;

Im Trainingsmodus :

  • Sehen Sie sich Maus oder Tastatur an, und verwenden Sie die Tippen-Geste.

  • Als Nächstes wird Text angezeigt, in dem Sie aufgefordert werden, ein Tag anzugeben.

  • Sagen Sie entweder Maus oder Tastatur.

Im Vorhersagemodus :

  • Sehen Sie sich ein Objekt an, und verwenden Sie die Tippen-Geste.

  • Der Text wird angezeigt, indem das erkannte Objekt mit der höchsten Wahrscheinlichkeit angegeben wird (dies ist normalisiert).

Kapitel 14: Auswerten und Verbessern Ihres Custom Vision Modells

Um Ihren Dienst genauer zu machen, müssen Sie das für die Vorhersage verwendete Modell weiter trainieren. Dies wird erreicht, indem Sie Ihre neue Anwendung mit dem Trainings- und Vorhersagemodus verwenden, wobei Sie das Portal besuchen müssen, was in diesem Kapitel behandelt wird. Seien Sie darauf vorbereitet, Ihr Portal mehrmals zu besuchen, um Ihr Modell kontinuierlich zu verbessern.

  1. Navigieren Sie erneut zu Ihrem Azure Custom Vision-Portal, und wählen Sie in Ihrem Projekt die Registerkarte Vorhersagen (oben in der Mitte der Seite) aus:

    Registerkarte

  2. Sie sehen alle Images, die während der Ausführung Ihrer Anwendung an Ihren Dienst gesendet wurden. Wenn Sie mit dem Mauszeiger auf die Bilder zeigen, erhalten Sie die Vorhersagen, die für dieses Bild getroffen wurden:

    Liste der Vorhersagebilder

  3. Wählen Sie eines Ihrer Bilder aus, um es zu öffnen. Nach dem Öffnen sehen Sie die Vorhersagen, die für dieses Bild auf der rechten Seite getroffen wurden. Wenn Die Vorhersagen korrekt waren und Sie dieses Bild dem Trainingsmodell Ihres Diensts hinzufügen möchten, klicken Sie auf das Eingabefeld Meine Tags , und wählen Sie das Tag aus, das Sie zuordnen möchten. Wenn Sie fertig sind, klicken Sie unten rechts auf die Schaltfläche Speichern und schließen , und fahren Sie mit dem nächsten Bild fort.

    Auswählen des zu öffnenden Bilds

  4. Sobald Sie wieder im Raster der Bilder sind, werden Sie feststellen, dass die Bilder, denen Sie Tags hinzugefügt (und gespeichert) haben, entfernt werden. Wenn Sie Bilder finden, von denen Sie glauben, dass sie ihr markiertes Element nicht enthalten, können Sie sie löschen, indem Sie auf das Häkchen für dieses Bild klicken (kann dies für mehrere Bilder tun) und dann in der oberen rechten Ecke der Rasterseite auf Löschen klicken. Im folgenden Popup können Sie entweder auf Ja, löschen oder Nein klicken, um den Löschvorgang zu bestätigen bzw. abzubrechen.

    Löschen von Images

  5. Wenn Sie bereit sind, den Vorgang fortzusetzen, klicken Sie oben rechts auf die grüne Schaltfläche Trainieren . Ihr Dienstmodell wird mit allen Bildern trainiert, die Sie jetzt bereitgestellt haben (wodurch es genauer wird). Klicken Sie nach Abschluss des Trainings erneut auf die Schaltfläche Standard festlegen , damit Ihre Vorhersage-URL weiterhin die aktuellste Iteration Ihres Diensts verwendet.

    Starten des TrainingsdienstmodellsWählen Sie die Option .

Ihre fertige Custom Vision-API-Anwendung

Herzlichen Glückwunsch! Sie haben eine Mixed Reality-App erstellt, die die Azure Custom Vision-API nutzt, um reale Objekte zu erkennen, das Dienstmodell zu trainieren und das Vertrauen des Gesehenen anzuzeigen.

Beispiel für fertiges Projekt

Zusatzübungen

Übung 1

Trainieren Sie Ihren Custom Vision Service, um weitere Objekte zu erkennen.

Übung 2

Um das Gelernte zu erweitern, absolvieren Sie die folgenden Übungen:

Geben Sie einen Sound wieder, wenn ein Objekt erkannt wird.

Übung 3

Verwenden Sie die API, um Ihren Dienst mit denselben Bildern, die Ihre App analysiert, neu zu trainieren, um den Dienst genauer zu gestalten (gleichzeitige Vorhersage und Training).