HoloLens (1. Generation) und Azure 310: Objekterkennung
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 Lernprogrammen geben, die in Zukunft veröffentlicht werden, die zeigen, wie sie für HoloLens 2 entwickelt werden. Dieser Hinweis wird mit einem Link zu diesen Lernprogrammen aktualisiert, wenn sie veröffentlicht werden.
In diesem Kurs erfahren Sie, wie Sie benutzerdefinierte visuelle Inhalte und ihre räumliche Position innerhalb eines bereitgestellten Bilds erkennen, indem Sie die Funktionen von Azure Custom Vision "Object Detection" in einer Mixed Reality-Anwendung verwenden.
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 und ihre Position in der realen Welt anzunähern, wie die Kameraaufnahme von Microsoft HoloLens oder eine Kamera, die mit einem PC für immersive Headsets (VR) verbunden ist.
Azure Custom Vision, Object Detection ist ein Microsoft-Dienst, mit dem Entwickler benutzerdefinierte Bildklassifizierer erstellen können. Diese Klassifizierer können dann mit neuen Bildern verwendet werden, um Objekte innerhalb dieses neuen Bilds zu erkennen, indem Box-Grenzen innerhalb des Bilds selbst bereitgestellt werden. Der Dienst bietet ein einfaches, einfach zu bedienendes Onlineportal, um diesen Prozess zu optimieren. Weitere Informationen finden Sie unter den folgenden Links:
Nach Abschluss dieses Kurses haben Sie eine Mixed Reality-Anwendung, die folgende Aktionen ausführen kann:
- Der Benutzer kann ein Objekt betrachten , das er mit dem Azure Custom Vision Service, Objekterkennung, trainiert hat.
- Der Benutzer verwendet die Tippgeste , um ein Bild davon zu erfassen, was er betrachtet.
- Die App sendet das Bild an den Azure Custom Vision Service.
- Es wird eine Antwort vom Dienst angezeigt, die das Ergebnis der Erkennung als Weltraumtext anzeigt. Dazu wird die räumliche Nachverfolgung von Microsoft HoloLens verwendet, um die Weltposition des erkannten Objekts zu verstehen und dann das Tag zu verwenden, das dem erkannten Bild zugeordnet ist, um den Beschriftungstext bereitzustellen.
Der Kurs behandelt außerdem das manuelle Hochladen von Bildern, das Erstellen von Tags und das Training des Dienstes, um verschiedene Objekte (im bereitgestellten Beispiel eine Tasse) zu erkennen, indem sie das Begrenzungsfeld innerhalb des von Ihnen übermittelten Bilds festlegen.
Wichtig
Nach der Erstellung und Verwendung der App sollte der Entwickler zurück zum Azure Custom Vision Service navigieren und die vom Dienst vorgenommenen Vorhersagen identifizieren und bestimmen, ob sie korrekt waren oder nicht (durch Kategorisieren eines verpassten Diensts und Anpassen der umgebenden Felder). Der Dienst kann dann neu trainiert werden, wodurch die Wahrscheinlichkeit erhöht wird, dass objekte der realen Welt erkannt werden.
In diesem Kurs erfahren Sie, wie Sie die Ergebnisse aus dem Azure Custom Vision Service, Object Detection, in einer Unity-basierten Beispielanwendung abrufen. Sie müssen diese Konzepte auf eine benutzerdefinierte Anwendung anwenden, die Sie möglicherweise erstellen.
Unterstützung für Geräte
Kurs | HoloLens | Immersive Headsets |
---|---|---|
MR und Azure 310: Objekterkennung | ✔️ |
Voraussetzungen
Hinweis
Dieses Lernprogramm wurde für Entwickler entwickelt, die grundlegende Erfahrung mit Unity und C# haben. Bitte beachten Sie auch, dass die Voraussetzungen und schriftlichen Anweisungen in diesem Dokument darstellen, was zum Zeitpunkt der Schriftlichkeit (Juli 2018) getestet und überprüft wurde. Sie können die neueste Software verwenden, wie im Artikel "Tools installieren" aufgeführt, aber es sollte nicht davon ausgegangen werden, dass die Informationen in diesem Kurs perfekt mit dem übereinstimmen, was Sie in neuerer Software finden werden als die unten aufgeführten.
Wir empfehlen die folgende Hardware und Software für diesen Kurs:
- Ein Entwicklungs-PC
- Windows 10 Fall Creators Update (oder höher) mit aktivierter Entwicklermodus
- Das neueste Windows 10 SDK
- Unity 2017.4 LTS
- Visual Studio 2017
- Microsoft HoloLens mit aktiviertem Entwicklermodus
- Internetzugriff für Azure-Setup und Abruf des benutzerdefinierten Vision-Diensts
- Eine Reihe von mindestens fünfzehn (15) Bildern ist für jedes Objekt erforderlich, das von der benutzerdefinierten Vision erkannt werden soll. Wenn Sie möchten, können Sie die Bilder verwenden, die bereits mit diesem Kurs bereitgestellt werden, eine Reihe von Tassen).
Vor der Installation
- Um Probleme beim Erstellen dieses Projekts zu vermeiden, wird dringend empfohlen, das in diesem Lernprogramm erwähnte Projekt in einem Stamm- oder Near-Root-Ordner zu erstellen (lange Ordnerpfade können zu Buildzeit zu Problemen führen).
- Richten Sie Ihre HoloLens ein, und testen Sie sie. Wenn Sie Hierfür Unterstützung benötigen, besuchen Sie den HoloLens-Setupartikel.
- Es empfiehlt sich, beim Entwickeln einer neuen HoloLens-App Kalibrierung und Sensoroptimierung durchzuführen (manchmal kann es hilfreich sein, diese Aufgaben für jeden Benutzer auszuführen).
Hilfe zur Kalibrierung finden Sie unter diesem Link zum HoloLens-Kalibrierungsartikel.
Hilfe zur Sensoroptimierung finden Sie in diesem Link zum Artikel "HoloLens Sensor Tuning".
Kapitel 1 – Das benutzerdefinierte Vision-Portal
Um den Azure Custom Vision Service zu verwenden, müssen Sie eine Instanz davon konfigurieren, damit sie Ihrer Anwendung zur Verfügung gestellt wird.
Navigieren Sie zur Hauptseite des benutzerdefinierten Vision-Diensts.
Klicken Sie auf " Erste Schritte".
Melden Sie sich beim Benutzerdefinierten Vision-Portal an.
Wenn Sie noch nicht über ein Azure-Konto verfügen, müssen Sie ein Konto erstellen. Wenn Sie diesem Lernprogramm in einer Unterrichts- oder Laborsituation folgen, bitten Sie Ihren Kursleiter oder einen der Betreuer, Hilfe beim Einrichten Ihres neuen Kontos zu erhalten.
Sobald Sie zum ersten Mal angemeldet sind, werden Sie mit dem Bereich "Nutzungsbedingungen " aufgefordert. Klicken Sie auf das Kontrollkästchen, um den Bedingungen zuzustimmen. Klicken Sie dann auf "Ich stimme zu".
Nachdem Sie den Bedingungen zugestimmt haben, befinden Sie sich jetzt im Abschnitt "Meine Projekte ". Klicken Sie auf Neues Projekt.
Auf der rechten Seite wird eine Registerkarte angezeigt, die Sie auffordert, einige Felder für das Projekt anzugeben.
Einfügen eines Namens für Ihr Projekt
Einfügen einer Beschreibung für Ihr Projekt (Optional)
Wählen Sie eine Ressourcengruppe aus, oder erstellen Sie eine neue. Eine Ressourcengruppe bietet eine Möglichkeit, die Abrechnung für eine Sammlung von Azure-Ressourcen zu überwachen, zu steuern, den Zugriff zu steuern, bereitzustellen und zu verwalten. Es wird empfohlen, alle Azure-Dienste, die einem einzelnen Projekt (z. B. diesen Kursen) zugeordnet sind, unter einer gemeinsamen Ressourcengruppe zu halten.
Hinweis
Wenn Sie weitere Informationen zu Azure-Ressourcengruppen lesen möchten , navigieren Sie zu den zugehörigen Dokumenten.
Legen Sie die Projekttypen als Objekterkennung (Vorschau) fest.
Nachdem Sie fertig sind, klicken Sie auf " Projekt erstellen", und Sie werden zur Projektseite "Custom Vision Service" umgeleitet.
Kapitel 2 – Schulung Ihres benutzerdefinierten Vision-Projekts
Sobald Sie sich im Custom Vision Portal befindet, besteht Ihr Hauptziel darin, Ihr Projekt zu schulen, um bestimmte Objekte in Bildern zu erkennen.
Sie benötigen mindestens 15 (15) Bilder für jedes Objekt, das Von der Anwendung erkannt werden soll. Sie können die mit diesem Kurs bereitgestellten Bilder (eine Reihe von Tassen) verwenden.
So trainieren Sie Ihr Custom Vision-Projekt:
Klicken Sie auf die + Schaltfläche neben "Tags".
Fügen Sie einen Namen für das Tag hinzu, das verwendet wird, um Ihre Bilder zuzuordnen. In diesem Beispiel verwenden wir Bilder von Tassen für die Erkennung, haben also das Tag für diesen Cup benannt. Klicken Sie nach Abschluss auf " Speichern ".
Sie werden feststellen, dass Ihr Tag hinzugefügt wurde (Möglicherweise müssen Sie Ihre Seite neu laden, damit sie angezeigt wird).
Klicken Sie in der Mitte der Seite auf " Bilder hinzufügen".
Klicken Sie auf " Lokale Dateien durchsuchen", und navigieren Sie zu den Bildern, die Sie für ein Objekt hochladen möchten, wobei mindestens 15 (15) beträgt.
Tipp
Sie können mehrere Bilder gleichzeitig auswählen, um sie hochzuladen.
Drücken Sie "Dateien hochladen", nachdem Sie alle Bilder ausgewählt haben, mit denen Sie das Projekt trainieren möchten. Die Dateien werden mit dem Hochladen beginnen. Nachdem Sie die Bestätigung des Uploads erhalten haben, klicken Sie auf "Fertig".
An diesem Punkt werden Ihre Bilder hochgeladen, aber nicht markiert.
Verwenden Sie die Maus, um Ihre Bilder zu markieren. Wenn Sie mit dem Mauszeiger auf das Bild zeigen, unterstützt Sie eine Auswahlheraufhebung, indem Sie automatisch eine Auswahl um Ihr Objekt zeichnen. Wenn es nicht korrekt ist, können Sie Eigene zeichnen. Dazu klicken Sie mit der linken Maustaste, und ziehen Sie den Auswahlbereich, um das Objekt einzuschließen.
Nach der Auswahl des Objekts innerhalb des Bilds werden Sie mit einer kleinen Eingabeaufforderung aufgefordert, "Region-Tag hinzufügen" hinzuzufügen. Wählen Sie ihr zuvor erstelltes Tag ('Cup', im obigen Beispiel) aus, oder wenn Sie weitere Tags hinzufügen, geben Sie dies ein, und klicken Sie auf die Schaltfläche "+" (Plus).
Um das nächste Bild zu markieren, können Sie auf den Pfeil rechts neben dem Blatt klicken oder das Tagblatt schließen (indem Sie in der oberen rechten Ecke des Blatts auf das X klicken) und dann auf das nächste Bild klicken. Sobald Sie das nächste Bild fertig haben, wiederholen Sie dasselbe Verfahren. Führen Sie dies für alle Bilder aus, die Sie hochgeladen haben, bis sie alle markiert sind.
Hinweis
Sie können mehrere Objekte in demselben Bild auswählen, z. B. das folgende Bild:
Nachdem Sie alle markiert haben, klicken Sie auf die markierte Schaltfläche links auf dem Bildschirm, um die markierten Bilder anzuzeigen.
Sie sind jetzt bereit, Ihren Service zu trainieren. Klicken Sie auf die Schaltfläche "Trainieren ", und die erste Schulungsiteration beginnt.
Nachdem sie erstellt wurde, können Sie zwei Schaltflächen mit dem Namen "Standard - und Vorhersage-URL erstellen" anzeigen. Klicken Sie zuerst auf "Standard festlegen", und klicken Sie dann auf " Vorhersage-URL".
Hinweis
Der endpunkt, der von diesem Bereitgestellt wird, wird auf die iterationsmarkiert, die als Standard gekennzeichnet wurde. Wenn Sie später eine neue Iteration vornehmen und als Standard aktualisieren, müssen Sie den Code nicht ändern.
Nachdem Sie auf die Vorhersage-URL geklickt haben, öffnen Sie Editor, kopieren und einfügen Sie die URL (auch als " Vorhersageendpunkt" bezeichnet) und den Dienstvorhersageschlüssel, damit Sie sie später im Code abrufen können.
Kapitel 3 – Einrichten des Unity-Projekts
Im Folgenden sehen Sie eine typische Einrichtung für die Entwicklung mit Mixed Reality und ist daher eine gute Vorlage für andere Projekte.
Öffnen Sie Unity , und klicken Sie auf "Neu".
Jetzt müssen Sie einen Unity-Projektnamen angeben. Insert CustomVisionObjDetection. Stellen Sie sicher, dass der Projekttyp auf 3D festgelegt ist, und legen Sie den Speicherort an einer für Sie geeigneten Stelle fest (denken Sie daran, dass die Stammverzeichnisse besser sind). Klicken Sie dann auf "Projekt erstellen".
Wenn Unity geöffnet ist, lohnt es sich, den Standardmäßigen Skript-Editor auf Visual Studio festzulegen. Wechseln Sie zu "Einstellungen bearbeiten>", und navigieren Sie dann im neuen Fenster zu "Externe Tools". Ändern Sie den externen Skript-Editor in Visual Studio. Schließen Sie das Fenster Einstellungen.
Wechseln Sie als Nächstes zu "Dateibuildeinstellungen", und wechseln Sie zur Plattform zu Universelle Windows-Plattform, und klicken Sie dann auf die Schaltfläche "Plattform wechseln". >
Stellen Sie im gleichen Fenster mit den Buildeinstellungen sicher, dass Folgendes festgelegt ist:
Zielgerät ist auf HoloLens festgelegt
Buildtyp ist auf D3D festgelegt
SDK ist auf "Neueste Installation" festgelegt.
Visual Studio-Version ist auf "Neueste Installation" festgelegt.
Build und Ausführung ist auf den lokalen Computer festgelegt.
Die übrigen Einstellungen in den Buildeinstellungen sollten jetzt als Standard beibehalten werden.
Klicken Sie im selben Fenster "Buildeinstellungen " auf die Schaltfläche "Playereinstellungen ". Dadurch wird der zugehörige Bereich im Bereich geöffnet, in dem sich der Inspektor befindet.
In diesem Bereich müssen einige Einstellungen überprüft werden:
Auf der Registerkarte "Andere Einstellungen" folgendes:
Skripting Runtime Version sollte Experimental (.NET 4.6 Equivalent) sein, wodurch ein Neustart des Editors ausgelöst wird.
Scripting Back-End sollte .NET sein.
API-Kompatibilitätsstufe sollte .NET 4.6 sein.
Aktivieren Sie auf der Registerkarte "Veröffentlichungseinstellungen " unter "Funktionen" Folgendes:
InternetClient
Webcam
SpatialPerception
Klicken Sie weiter unten im Bereich unter "XR-Einstellungen" (unter "Veröffentlichungseinstellungen" zu finden) auf "Virtual Reality Unterstützt", und stellen Sie dann sicher, dass das Windows Mixed Reality SDK hinzugefügt wird.
Zurück in den Buildeinstellungen ist Unity C#-Projekte nicht mehr abgeblentet: Aktivieren Sie das Kontrollkästchen neben diesem.
Schließen Sie das Fenster Buildeinstellungen.
Klicken Sie im Editor auf ">Projekteinstellungen>bearbeiten".
Im Inspektorbereich werden die Grafikeinstellungen geöffnet. Scrollen Sie nach unten, bis ein Array namens Always Include Shader angezeigt wird. Fügen Sie einen Steckplatz hinzu, indem Sie die Größenvariable um eins erhöhen (in diesem Beispiel war es 8, sodass wir es 9 gemacht haben). Ein neuer Steckplatz wird in der letzten Position des Arrays angezeigt, wie unten dargestellt:
Klicken Sie im Slot auf den kleinen Zielkreis neben dem Slot, um eine Liste von Shadern zu öffnen. Suchen Sie nach den Legacy-Shadern/Transparent/Diffuse-Shader , und doppelklicken Sie darauf.
Kapitel 4 – Importieren des CustomVisionObjDetection Unity-Pakets
Für diesen Kurs erhalten Sie ein Unity Asset Package namens Azure-MR-310.unitypackage.
[TIPP] Alle von Unity unterstützten Objekte, einschließlich ganzer Szenen, können in eine Unitypackage-Datei verpackt und in anderen Projekten exportiert/importiert werden. Es ist die sicherste und effizienteste Möglichkeit, Ressourcen zwischen verschiedenen Unity-Projekten zu verschieben.
Sie finden das Azure-MR-310-Paket, das Sie hier herunterladen müssen.
Klicken Sie mit dem Unity-Dashboard vor Ihnen im Menü oben auf dem Bildschirm auf "Assets", und klicken Sie dann auf "Paket benutzerdefiniertes Paket > importieren".
Verwenden Sie die Dateiauswahl, um das Azure-MR-310.unitypackage-Paket auszuwählen, und klicken Sie auf " Öffnen". Ihnen wird eine Liste der Komponenten für dieses Objekt angezeigt. Bestätigen Sie den Import, indem Sie auf die Schaltfläche "Importieren " klicken.
Nachdem der Import abgeschlossen ist, werden Sie feststellen, dass Ordner aus dem Paket jetzt ihrem Ordner "Assets " hinzugefügt wurden. Diese Art von Ordnerstruktur ist typisch für ein Unity-Projekt.
Der Ordner "Materialien " enthält das Material, das vom Blickcursor verwendet wird.
Der Ordner Plugins enthält die Newtonsoft-DLL, die vom Code zum Deserialisieren der Dienstwebantwort verwendet wird. Die beiden (2) verschiedenen Versionen im Ordner und Unterordner sind erforderlich, damit die Bibliothek sowohl vom Unity-Editor als auch vom UWP-Build verwendet und erstellt werden kann.
Der Ordner "Prefabs" enthält die prefabs , die in der Szene enthalten sind. Dazu zählen:
- Der GazeCursor, der in der Anwendung verwendete Cursor. Arbeitet mit dem SpatialMapping-Prefab zusammen, um in der Szene auf physischen Objekten platziert werden zu können.
- The Label, which is the UI object used to display the object tag in the scene when required.
- Das SpatialMapping-Objekt, das es der Anwendung ermöglicht, mithilfe der räumlichen Nachverfolgung von Microsoft HoloLens eine virtuelle Karte zu erstellen.
Der Ordner "Szenen" , der derzeit die vordefinierte Szene für diesen Kurs enthält.
Öffnen Sie den Ordner "Szenen" im Projektbereich, und doppelklicken Sie auf das ObjDetectionScene, um die Szene zu laden, die Sie für diesen Kurs verwenden werden.
Hinweis
Es ist kein Code enthalten, sie schreiben den Code, indem Sie diesem Kurs folgen.
Kapitel 5 – Erstellen der CustomVisionAnalyser-Klasse.
An diesem Punkt sind Sie bereit, Code zu schreiben. Sie beginnen mit der CustomVisionAnalyser-Klasse .
Hinweis
Die Aufrufe des custom Vision Service, der im unten gezeigten Code ausgeführt wird, werden mithilfe der REST-API für benutzerdefinierte Vision ausgeführt. Mithilfe dieser Vorgehensweise erfahren Sie, wie Sie diese API implementieren und nutzen können (nützlich, um zu verstehen, wie Sie etwas ähnliches selbst implementieren). Beachten Sie, dass Microsoft ein custom Vision SDK anbietet, das auch zum Tätigen von Aufrufen an den Dienst verwendet werden kann. Weitere Informationen finden Sie im Artikel zum Custom Vision SDK.
Diese Klasse ist für Folgendes verantwortlich:
Laden des aktuellen Bilds, das als Bytearray erfasst wurde.
Senden des Bytearrays zur Analyse an Ihre Azure Custom Vision Service-Instanz .
Empfangen der Antwort als JSON-Zeichenfolge.
Deserialisieren der Antwort und Übergeben der resultierenden Vorhersage an die SceneOrganiser-Klasse , die die Anzeige der Antwort übernimmt.
So erstellen Sie diese Klasse:
Klicken Sie mit der rechten Maustaste in den Objektordner, der sich im Projektbereich befindet, und klicken Sie dann auf "Ordner erstellen>". Rufen Sie den Ordner Skripts auf.
Doppelklicken Sie auf den neu erstellten Ordner, um ihn zu öffnen.
Klicken Sie mit der rechten Maustaste in den Ordner, und klicken Sie dann auf "C#-Skript erstellen>". Nennen Sie das Skript CustomVisionAnalyser.
Doppelklicken Sie auf das neue CustomVisionAnalyser-Skript , um es mit Visual Studio zu öffnen.
Stellen Sie sicher, dass oben in der Datei auf die folgenden Namespaces verwiesen wird:
using Newtonsoft.Json; using System.Collections; using System.IO; using UnityEngine; using UnityEngine.Networking;
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> /// Bite array of the image to submit for analysis /// </summary> [HideInInspector] public byte[] imageBytes;
Hinweis
Stellen Sie sicher, dass Sie Ihren Dienstvorhersageschlüssel in die Variable "predictionKey " und Ihren "Prediction-Endpoint " in die Variable "predictionEndpoint " einfügen. Sie haben diese weiter oben in Editor kopiert, in Kapitel 2, Schritt 14.
Code für Awake() muss jetzt hinzugefügt werden, um die Instanzvariable zu initialisieren:
/// <summary> /// Initializes this class /// </summary> private void Awake() { // Allows this instance to behave like a singleton Instance = this; }
Fügen Sie die Coroutine (mit der statischen GetImageAsByteArray()-Methode darunter hinzu , die die Ergebnisse der Analyse des Bilds abruft, die von der ImageCapture-Klasse erfasst wird.
Hinweis
In der AnalyseImageCapture coroutine gibt es einen Aufruf der SceneOrganiser-Klasse , die Sie noch erstellen möchten. Lassen Sie diese Zeilen daher vorerst auskommentiert.
/// <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); }
Löschen Sie die Methoden Start() und Update(), da sie nicht verwendet werden.
Achten Sie darauf, ihre Änderungen in Visual Studio zu speichern, bevor Sie zu Unity zurückkehren.
Wichtig
Wie bereits erwähnt, machen Sie sich keine Sorgen über Code, der möglicherweise einen Fehler aufweist, da Sie in Kürze weitere Klassen bereitstellen werden, die diese beheben werden.
Kapitel 6 – 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 an den Custom Vision Service verwendet werden.
So erstellen Sie diese Klasse:
Klicken Sie mit der rechten Maustaste in den Ordner Skripts, und klicken Sie dann auf "C#-Skript erstellen>". Rufen Sie das Skript CustomVisionObjects auf.
Doppelklicken Sie auf das neue CustomVisionObjects-Skript , um es mit Visual Studio zu öffnen.
Stellen Sie sicher, dass oben in der Datei auf die folgenden Namespaces verwiesen wird:
using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Networking;
Löschen Sie die Methoden Start() und Update() innerhalb der CustomVisionObjects-Klasse , diese Klasse sollte jetzt leer sein.
Warnung
Es ist wichtig, dass Sie die nächste Anweisung sorgfältig befolgen. Wenn Sie die neuen Klassendeklarationen in die CustomVisionObjects-Klasse einfügen, erhalten Sie Kompilierungsfehler in Kapitel 10, die angeben, dass AnalysisRootObject und BoundingBox nicht gefunden werden.
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 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 /// 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; } }
Achten Sie darauf, ihre Änderungen in Visual Studio zu speichern, bevor Sie zu Unity zurückkehren.
Kapitel 7 – Erstellen der SpatialMapping-Klasse
Diese Klasse legt den Spatial Mapping Collider in der Szene fest, damit Kollisionen zwischen virtuellen Objekten und realen Objekten erkannt werden können.
So erstellen Sie diese Klasse:
Klicken Sie mit der rechten Maustaste in den Ordner Skripts, und klicken Sie dann auf "C#-Skript erstellen>". Rufen Sie das Skript SpatialMapping auf.
Doppelklicken Sie auf das neue SpatialMapping-Skript , um es mit Visual Studio zu öffnen.
Stellen Sie sicher, dass über der SpatialMapping-Klasse auf die folgenden Namespaces verwiesen wird:
using UnityEngine; using UnityEngine.XR.WSA;
Fügen Sie dann die folgenden Variablen in der SpatialMapping-Klasse oberhalb der Start() -Methode hinzu:
/// <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;
Add the Awake() and Start():
/// <summary> /// Initializes this class /// </summary> private void Awake() { // Allows this instance to behave like a singleton Instance = this; } /// <summary> /// Runs at initialization right after Awake method /// </summary> void Start() { // Initialize and configure the collider spatialMappingCollider = gameObject.GetComponent<SpatialMappingCollider>(); spatialMappingCollider.surfaceParent = this.gameObject; spatialMappingCollider.freezeUpdates = false; spatialMappingCollider.layer = physicsLayer; // define the mask PhysicsRaycastMask = 1 << physicsLayer; // set the object as active one gameObject.SetActive(true); }
Löschen Sie die Update()- Methode.
Achten Sie darauf, ihre Änderungen in Visual Studio zu speichern, bevor Sie zu Unity zurückkehren.
Kapitel 8 – Erstellen der GazeCursor-Klasse
Diese Klasse ist für das Einrichten des Cursors an der richtigen Position im realen Raum verantwortlich, indem sie den SpatialMappingCollider verwenden, der im vorherigen Kapitel erstellt wurde.
So erstellen Sie diese Klasse:
Klicken Sie mit der rechten Maustaste in den Ordner Skripts, und klicken Sie dann auf "C#-Skript erstellen>". Aufrufen des Skripts GazeCursor
Doppelklicken Sie auf das neue GazeCursor-Skript , um es mit Visual Studio zu öffnen.
Stellen Sie sicher, dass über der GazeCursor-Klasse auf den folgenden Namespace verwiesen wird:
using UnityEngine;
Fügen Sie dann die folgende Variable in der GazeCursor-Klasse oberhalb der Start() -Methode hinzu.
/// <summary> /// The cursor (this object) mesh renderer /// </summary> private MeshRenderer meshRenderer;
Aktualisieren Sie die Start() -Methode mit dem folgenden 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); }
Aktualisieren Sie die Update()- Methode mit dem folgenden 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; } }
Hinweis
Machen Sie sich keine Sorgen über den Fehler für die SceneOrganiser-Klasse, die nicht gefunden wird, erstellen Sie ihn im nächsten Kapitel.
Achten Sie darauf, ihre Änderungen in Visual Studio zu speichern, bevor Sie zu Unity zurückkehren.
Kapitel 9 – Erstellen der SceneOrganiser-Klasse
Diese Klasse sieht wie folgt aus:
Richten Sie die Hauptkamera ein, indem Sie die entsprechenden Komponenten an sie anfügen.
Wenn ein Objekt erkannt wird, ist es dafür verantwortlich, seine Position in der realen Welt zu berechnen und eine Tagbeschriftung in der Nähe des Objekts mit dem entsprechenden Tagnamen zu platzieren.
So erstellen Sie diese Klasse:
Klicken Sie mit der rechten Maustaste in den Ordner Skripts, und klicken Sie dann auf "C#-Skript erstellen>". Nennen Sie das Skript SceneOrganiser.
Doppelklicken Sie auf das neue SceneOrganiser-Skript , um es mit Visual Studio zu öffnen.
Stellen Sie sicher, dass über der SceneOrganiser-Klasse auf die folgenden Namespaces verwiesen wird:
using System.Collections.Generic; using System.Linq; using UnityEngine;
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 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;
Löschen Sie die Methoden Start() und Update().
Fügen Sie unter den Variablen die Awake() -Methode hinzu, die die Klasse initialisiert und die Szene einrichtet.
/// <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>(); }
Fügen Sie die PlaceAnalysisLabel() -Methode hinzu, die die Beschriftung in der Szene instanziiert (die an diesem Punkt für den Benutzer unsichtbar ist). Es platziert auch das Quad (auch unsichtbar), wo das Bild platziert wird, und überlappt mit der realen Welt. Dies ist wichtig, da die aus dem Dienst abgerufenen Boxkoordinaten nach der Analyse zurück in dieses Quad zurückverfolgt werden, um die ungefähre Position des Objekts in der realen Welt zu bestimmen.
/// <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; }
Fügen Sie die FinaliseLabel() -Methode hinzu. IISConfigurator ist für Folgendes zuständig:
- Festlegen des Beschriftungstexts mit dem Tag der Vorhersage mit der höchsten Vertrauenswürdigkeit.
- Aufrufen der Berechnung des Umgebenden Felds auf dem Quad-Objekt, zuvor positioniert und die Beschriftung in der Szene platziert.
- Anpassen der Beschriftungstiefe mithilfe eines Raycasts in Richtung der Begrenzungsbox, die gegen das Objekt in der realen Welt kollidieren sollte.
- Zurücksetzen des Aufnahmevorgangs, damit der Benutzer ein anderes Bild erfassen kann.
/// <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(); }
Fügen Sie die CalculateBoundingBoxPosition() -Methode hinzu, die eine Reihe von Berechnungen hostet, die zum Übersetzen der aus dem Dienst abgerufenen Begrenzungsfeldkoordinaten erforderlich sind, und erstellen Sie sie proportional auf dem Quad neu.
/// <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); }
Achten Sie darauf, ihre Änderungen in Visual Studio zu speichern, bevor Sie zu Unity zurückkehren.
Wichtig
Öffnen Sie vor dem Fortfahren die CustomVisionAnalyser-Klasse , und heben Sie in der AnalyseLastImageCaptured() -Methode die Kommentare aus den folgenden Zeilen auf:
// 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);
Hinweis
Machen Sie sich keine Sorgen über die ImageCapture-Klasse "Konnte nicht gefunden" Nachricht, Sie erstellen sie im nächsten Kapitel.
Kapitel 10 – Erstellen der ImageCapture-Klasse
Die nächste Klasse, die Sie erstellen werden, ist die ImageCapture-Klasse .
Diese Klasse ist für Folgendes verantwortlich:
- Aufnehmen eines Bilds mithilfe der HoloLens-Kamera und Speichern im App-Ordner .
- Behandeln von Tippgesten vom Benutzer.
So erstellen Sie diese Klasse:
Wechseln Sie zum Ordner "Skripts", den Sie zuvor erstellt haben.
Klicken Sie mit der rechten Maustaste in den Ordner, und klicken Sie dann auf "C#-Skript erstellen>". Nennen Sie das Skript ImageCapture.
Doppelklicken Sie auf das neue ImageCapture-Skript , um es mit Visual Studio zu öffnen.
Ersetzen Sie die Namespaces oben in der Datei durch Folgendes:
using System; using System.IO; using System.Linq; using UnityEngine; using UnityEngine.XR.WSA.Input; using UnityEngine.XR.WSA.WebCam;
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> /// Flagging if the capture loop is running /// </summary> internal bool captureIsActive; /// <summary> /// File path of current analysed photo /// </summary> internal string filePath = string.Empty;
Code for Awake() and Start() methods now to be added:
/// <summary> /// Called on initialization /// </summary> private void Awake() { Instance = this; } /// <summary> /// Runs at initialization right after Awake method /// </summary> void Start() { // Clean up the LocalState folder of this application from all photos stored DirectoryInfo info = new DirectoryInfo(Application.persistentDataPath); var fileInfo = info.GetFiles(); foreach (var file in fileInfo) { try { file.Delete(); } catch (Exception) { Debug.LogFormat("Cannot delete file: ", file.Name); } } // Subscribing to the Microsoft HoloLens API gesture recognizer to track user gestures recognizer = new GestureRecognizer(); recognizer.SetRecognizableGestures(GestureSettings.Tap); recognizer.Tapped += TapHandler; recognizer.StartCapturingGestures(); }
Implementieren Sie einen Handler, der aufgerufen wird, wenn eine Tippbewegung auftritt:
/// <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); } }
Wichtig
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.
Fügen Sie die Methode hinzu, die von der Anwendung zum Starten des Bildaufnahmeprozesses verwendet wird, und speichern Sie das Bild:
/// <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); }); }); }
Fügen Sie die Handler hinzu, die aufgerufen werden, wenn das Foto aufgenommen wurde und wann es analysiert werden kann. Das Ergebnis wird dann zur Analyse an den CustomVisionAnalyser übergeben.
/// <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(); }
Achten Sie darauf, ihre Änderungen in Visual Studio zu speichern, bevor Sie zu Unity zurückkehren.
Kapitel 11 – Einrichten der Skripts in der Szene
Nachdem Sie nun den gesamten code geschrieben haben, der für dieses Projekt erforderlich ist, ist zeit, die Skripts in der Szene und auf den Prefabs einzurichten, damit sie sich ordnungsgemäß verhalten.
Wählen Sie im Unity-Editor im Hierarchiebereich die Hauptkamera aus.
Klicken Sie im Inspektorbereich mit ausgewählter Hauptkamera auf " Komponente hinzufügen", suchen Sie dann nach "SceneOrganiser "-Skript, und doppelklicken Sie, um es hinzuzufügen.
Öffnen Sie im Projektbereich den Ordner "Prefabs", ziehen Sie das Label Prefab in den Eingabebereich "Label empty reference target input" in the SceneOrganiser script that you have just added to the Main Camera,as shown in the image below:
Wählen Sie im Hierarchiebereich das untergeordnete Element "GazeCursor " der Hauptkamera aus.
Klicken Sie im Inspektorbereich mit ausgewähltem GazeCursor auf " Komponente hinzufügen", suchen Sie dann nach GazeCursor-Skript , und doppelklicken Sie, um es hinzuzufügen.
Wählen Sie erneut im Hierarchiebereich das untergeordnete Element "SpatialMapping " der Hauptkamera aus.
Klicken Sie im Inspektorbereich mit ausgewählter Option "SpatialMapping " auf " Komponente hinzufügen", suchen Sie dann nach "SpatialMapping "-Skript, und doppelklicken Sie, um es hinzuzufügen.
Die verbleibenden Skripts, die Sie nicht festgelegt haben, werden während der Laufzeit vom Code im SceneOrganiser-Skript hinzugefügt.
Kapitel 12 - Vor dem Gebäude
Um einen gründlichen Test Ihrer Anwendung durchzuführen, müssen Sie sie auf Ihre Microsoft HoloLens querladen.
Bevor Sie vorgehen, stellen Sie folgendes sicher:
Alle im Kapitel 3 genannten Einstellungen sind korrekt festgelegt.
Das Skript SceneOrganiser ist an das Main Camera-Objekt angefügt.
Das Skript GazeCursor ist an das GazeCursor-Objekt angefügt.
Das Skript SpatialMapping ist mit dem SpatialMapping-Objekt verknüpft.
In Kapitel 5, Schritt 6:
- Stellen Sie sicher, dass Sie Ihren Dienstvorhersageschlüssel in die Variable "predictionKey " einfügen.
- Sie haben Ihren Vorhersageendpunkt in die predictionEndpoint-Klasse eingefügt.
Kapitel 13 – Erstellen der UWP-Lösung und Querladen Ihrer Anwendung
Sie sind jetzt bereit, Ihre Anwendung als UWP-Lösung zu erstellen, die Sie auf microsoft HoloLens bereitstellen können. So beginnen Sie den Buildprozess:
Wechseln Sie zu " Dateibuildeinstellungen > ".
Tick Unity C#-Projekte.
Klicken Sie auf " Offene Szenen hinzufügen". Dadurch wird der Build die aktuell geöffnete Szene hinzugefügt.
Klicken Sie auf Erstellen. Unity startet ein Explorer-Fenster, in dem Sie einen Ordner erstellen und dann einen Ordner auswählen müssen, in dem die App erstellt werden soll. Erstellen Sie diesen Ordner jetzt, und nennen Sie ihn " App". Klicken Sie dann mit ausgewähltem App-Ordner auf "Ordner auswählen".
Unity beginnt mit dem Erstellen Ihres Projekts im App-Ordner .
Nachdem Unity das Erstellen abgeschlossen hat (es kann einige Zeit dauern), öffnet es ein Explorer Fenster an der Position Ihres Builds (überprüfen Sie Ihre Taskleiste, da sie möglicherweise nicht immer über Ihren Fenstern angezeigt wird, sondern Sie über dem Hinzufügen eines neuen Fensters informiert).
Um die Bereitstellung auf Microsoft HoloLens auszuführen, benötigen Sie die IP-Adresse dieses Geräts (für Remotebereitstellung), und um sicherzustellen, dass auch der Entwicklermodus festgelegt ist. Gehen Sie hierzu folgendermaßen vor:
Öffnen Sie beim Tragen Ihrer HoloLens die Einstellungen.
Wechseln sie zu den erweiterten Optionen für Netzwerk- und Internet-WLAN>>
Notieren Sie sich die IPv4-Adresse .
Navigieren Sie als Nächstes zurück zu "Einstellungen" und dann zu "Update & Sicherheit>für Entwickler".
Legen Sie den Entwicklermodus aktiviert fest.
Navigieren Sie zu Ihrem neuen Unity-Build (dem App-Ordner ), und öffnen Sie die Projektmappendatei mit Visual Studio.
Wählen Sie in der Lösungskonfiguration "Debuggen" aus.
Wählen Sie in der Lösungsplattform x86, Remotecomputer aus. Sie werden aufgefordert, die IP-Adresse eines Remotegeräts (die Microsoft HoloLens, in diesem Fall, die Sie angegeben haben) einzufügen.
Wechseln Sie zum Menü "Erstellen ", und klicken Sie auf " Lösung bereitstellen", um die Anwendung in Ihre HoloLens querzuladen.
Ihre App sollte nun in der Liste der installierten Apps auf Ihrer Microsoft HoloLens angezeigt werden, damit sie gestartet werden können!
So verwenden Sie die Anwendung:
- Sehen Sie sich ein Objekt an, das Sie mit Ihrem benutzerdefinierten Azure Vision-Dienst, der Objekterkennung trainiert haben, und verwenden Sie die Tippbewegung.
- Wenn das Objekt erfolgreich erkannt wurde, wird ein Weltzeichenbeschriftungstext mit dem Tagnamen angezeigt.
Wichtig
Jedes Mal, wenn Sie ein Foto aufnehmen und an den Dienst senden, können Sie zur Seite "Dienst" zurückkehren und den Dienst mit den neu aufgenommenen Bildern neu trainieren. Am Anfang müssen Sie wahrscheinlich auch die Begrenzungsboxen korrigieren, um genauer zu sein und den Dienst neu zu trainieren.
Hinweis
Der platzierte Beschriftungstext wird möglicherweise nicht in der Nähe des Objekts angezeigt, wenn die Microsoft HoloLens-Sensoren und/oder "SpatialTrackingComponent" in Unity die entsprechenden Kollidierungen im Verhältnis zu den realen Objekten nicht platzieren. Versuchen Sie, die Anwendung auf einer anderen Oberfläche zu verwenden, wenn dies der Fall ist.
Ihre benutzerdefinierte Vision- und Objekterkennungsanwendung
Herzlichen Glückwunsch, Sie haben eine Mixed Reality-App erstellt, die die Azure Custom Vision, Objekterkennungs-API nutzt, die ein Objekt aus einem Bild erkennen kann, und dann eine ungefähre Position für dieses Objekt im 3D-Raum bereitstellen.
Zusatzübungen
Übung 1
Wenn Sie der Textbeschriftung hinzufügen, verwenden Sie einen halbtransparenten Würfel, um das reale Objekt in ein 3D-Begrenzungsfeld umzuschließen.
Übung 2
Schulen Sie Ihren custom Vision Service, um weitere Objekte zu erkennen.
Übung 3
Wiedergeben eines Sounds, wenn ein Objekt erkannt wird.
Übung 4
Verwenden Sie die API, um Ihren Dienst mit denselben Bildern zu trainieren, die Ihre App analysiert, sodass der Dienst genauer ist (sowohl Vorhersage als auch Schulung gleichzeitig durchführen).