HoloLens (1. generacji) i Azure 310: wykrywanie obiektów
Uwaga
Samouczki akademii rzeczywistości mieszanej zostały zaprojektowane z myślą o urządzeniach HoloLens (1. generacji) i zestawach słuchawkowych immersyjnych rzeczywistości mieszanej. W związku z tym uważamy, że ważne jest pozostawienie tych samouczków na miejscu dla deweloperów, którzy nadal szukają wskazówek dotyczących opracowywania tych urządzeń. Te samouczki nie zostaną zaktualizowane przy użyciu najnowszych zestawów narzędzi ani interakcji używanych dla urządzenia HoloLens 2. Będą one utrzymywane w celu kontynuowania pracy na obsługiwanych urządzeniach. W przyszłości zostanie opublikowana nowa seria samouczków, które pokażą, jak opracowywać urządzenia HoloLens 2. To powiadomienie zostanie zaktualizowane za pomocą linku do tych samouczków po ich opublikowaniu.
W tym kursie dowiesz się, jak rozpoznawać zawartość wizualizacji niestandardowej i jej położenie przestrzenne w udostępnionym obrazie przy użyciu funkcji wykrywania obiektów usługi Azure Custom Vision w aplikacji rzeczywistości mieszanej.
Ta usługa umożliwia trenowanie modelu uczenia maszynowego przy użyciu obrazów obiektów. Następnie użyjesz wytrenowanego modelu, aby rozpoznać podobne obiekty i przybliżyć ich lokalizację w świecie rzeczywistym, jak zapewnia przechwytywanie aparatu Microsoft HoloLens lub aparat łączący się z komputerem na potrzeby immersywnych zestawów słuchawkowych (VR).
Azure Custom Vision, wykrywanie obiektów to usługa firmy Microsoft, która umożliwia deweloperom tworzenie niestandardowych klasyfikatorów obrazów. Klasyfikatory te mogą być następnie używane z nowymi obrazami do wykrywania obiektów na tym nowym obrazie, zapewniając granice pola w samym obrazie. Usługa zapewnia prosty, łatwy w użyciu portal online, aby usprawnić ten proces. Aby uzyskać więcej informacji, odwiedź następujące linki:
Po ukończeniu tego kursu będziesz mieć aplikację rzeczywistości mieszanej, która będzie mogła wykonać następujące czynności:
- Użytkownik będzie mógł patrzeć na obiekt, który trenował przy użyciu usługi Azure Custom Vision Service, wykrywania obiektów.
- Użytkownik użyje gestu Tap, aby przechwycić obraz tego, co patrzy.
- Aplikacja wyśle obraz do usługi Azure Custom Vision Service.
- Zostanie wyświetlona odpowiedź z usługi, która wyświetli wynik rozpoznawania jako tekst w przestrzeni światowej. Zostanie to wykonane za pomocą śledzenia przestrzennego urządzenia Microsoft HoloLens jako sposobu zrozumienia pozycji światowej rozpoznanego obiektu, a następnie użycia tagu skojarzonego z wykrytymi elementami na obrazie, aby podać tekst etykiety.
Kurs obejmuje również ręczne przekazywanie obrazów, tworzenie tagów i trenowanie usługi w celu rozpoznawania różnych obiektów (w podanym przykładzie kubek), ustawiając pole granic w przesłanym obrazie.
Ważne
Po utworzeniu i użyciu aplikacji deweloper powinien wrócić do usługi Azure Custom Vision Service i zidentyfikować przewidywania dokonane przez usługę oraz określić, czy są poprawne, czy nie (przez tagowanie niczego, czego usługa nieodebrała, i dostosowywanie pól ograniczenia). Następnie usługa może zostać ponownie wytrenowana, co zwiększy prawdopodobieństwo rozpoznawania obiektów w świecie rzeczywistym.
W tym kursie nauczysz się, jak uzyskać wyniki z usługi Azure Custom Vision Service, wykrywania obiektów w przykładowej aplikacji opartej na środowisku Unity. Zastosowanie tych pojęć do niestandardowej aplikacji, którą można utworzyć, będzie możliwe.
Obsługa urządzeń
Kurs | HoloLens | Immersyjne zestawy nagłowne |
---|---|---|
MR i Azure 310: wykrywanie obiektów | ✔️ |
Wymagania wstępne
Uwaga
Ten samouczek jest przeznaczony dla deweloperów, którzy mają podstawowe doświadczenie w językach Unity i C#. Należy również pamiętać, że wymagania wstępne i pisemne instrukcje zawarte w tym dokumencie reprezentują to, co zostało przetestowane i zweryfikowane w momencie pisania dokumentu (lipiec 2018 r.). Możesz bezpłatnie korzystać z najnowszego oprogramowania, jak wymieniono w artykule dotyczącym instalacji narzędzi , choć nie należy zakładać, że informacje zawarte w tym kursie doskonale pasują do tego, co znajdziesz w nowszym oprogramowaniu niż to, co zostało wymienione poniżej.
Na potrzeby tego kursu zalecamy następujące oprogramowanie i sprzęt:
- Komputer deweloperzy
- Windows 10 Fall Creators Update (lub nowszy) z włączonym trybem dewelopera
- Najnowszy zestaw SDK systemu Windows 10
- Unity 2017.4 LTS
- Visual Studio 2017
- Urządzenie Microsoft HoloLens z włączonym trybem dewelopera
- Dostęp do Internetu dla konfiguracji platformy Azure i pobierania usługi Custom Vision Service
- Wymagana jest seria co najmniej 15 (15) obrazów dla każdego obiektu, który ma być rozpoznawany przez usługę Custom Vision. Jeśli chcesz, możesz użyć obrazów już dostarczonych z tym kursem, serii kubków).
Przed rozpoczęciem
- Aby uniknąć problemów podczas tworzenia tego projektu, zdecydowanie zaleca się utworzenie projektu wymienionego w tym samouczku w folderze głównym lub niemal głównym (długie ścieżki folderów mogą powodować problemy w czasie kompilacji).
- Skonfiguruj i przetestuj urządzenie HoloLens. Jeśli potrzebujesz pomocy technicznej, zapoznaj się z artykułem dotyczącym konfigurowania urządzenia HoloLens.
- Dobrym pomysłem jest przeprowadzenie kalibracji i dostrajania czujników podczas tworzenia nowej aplikacji HoloLens (czasami może to pomóc w wykonywaniu tych zadań dla każdego użytkownika).
Aby uzyskać pomoc dotyczącą kalibracji, skorzystaj z tego linku do artykułu Kalibracja urządzenia HoloLens.
Aby uzyskać pomoc dotyczącą dostrajania czujników, skorzystaj z tego linku do artykułu Dostrajanie czujników HoloLens.
Rozdział 1 — Portal custom vision
Aby korzystać z usługi Azure Custom Vision Service, należy skonfigurować wystąpienie, które ma zostać udostępnione aplikacji.
Przejdź do strony głównej usługi Custom Vision Service.
Kliknij pozycję Wprowadzenie.
Zaloguj się do portalu Custom Vision.
Jeśli nie masz jeszcze konta platformy Azure, musisz je utworzyć. Jeśli obserwujesz ten samouczek w sytuacji w klasie lub laboratorium, poproś instruktora lub jednego z opiekunów o pomoc przy konfigurowaniu nowego konta.
Po pierwszym zalogowaniu się zostanie wyświetlony monit z panelem Warunki świadczenia usługi . Kliknij pole wyboru, aby zaakceptować warunki. Następnie kliknij przycisk Zgadzam się.
Po uzgodnieniu warunków jesteś teraz w sekcji Moje projekty . Kliknij pozycję Nowy projekt.
Po prawej stronie zostanie wyświetlona karta z monitem o podanie niektórych pól dla projektu.
Wstaw nazwę projektu
Wstaw opis projektu (opcjonalnie)
Wybierz grupę zasobów lub utwórz nową. Grupa zasobów umożliwia monitorowanie, kontrolowanie dostępu, aprowizowania i zarządzania rozliczeniami dla kolekcji zasobów platformy Azure. Zaleca się zachowanie wszystkich usług platformy Azure skojarzonych z jednym projektem (np. takimi jak te kursy) w ramach wspólnej grupy zasobów.
Uwaga
Jeśli chcesz dowiedzieć się więcej o grupach zasobów platformy Azure, przejdź do skojarzonej witryny Docs
Ustaw typy projektów jako Wykrywanie obiektów (wersja zapoznawcza).
Po zakończeniu kliknij pozycję Utwórz projekt i nastąpi przekierowanie do strony projektu usługi Custom Vision Service.
Rozdział 2 . Szkolenie projektu usługi Custom Vision
Po przejściu do portalu Custom Vision głównym celem jest trenowanie projektu w celu rozpoznawania określonych obiektów na obrazach.
Potrzebujesz co najmniej piętnaście (15) obrazów dla każdego obiektu, który ma być rozpoznawany przez aplikację. Możesz użyć obrazów dostarczonych z tym kursem (seria kubków).
Aby wytrenować projekt usługi Custom Vision:
+ Kliknij przycisk obok pozycji Tagi.
Dodaj nazwę tagu, który będzie używany do kojarzenia obrazów. W tym przykładzie używamy obrazów kubków do rozpoznawania, dlatego nazwaliśmy tag dla tego elementu Cup. Kliknij pozycję Zapisz po zakończeniu.
Zauważysz , że tag został dodany (może być konieczne ponowne załadowanie strony w celu wyświetlenia go).
Kliknij pozycję Dodaj obrazy w środku strony.
Kliknij pozycję Przeglądaj pliki lokalne i przejdź do obrazów, które chcesz przekazać dla jednego obiektu, przy czym minimalna wartość to piętnaście (15).
Napiwek
Możesz jednocześnie wybrać kilka obrazów, aby je przekazać.
Po wybraniu wszystkich obrazów, za pomocą których chcesz wytrenować projekt, naciśnij pozycję Przekaż pliki . Pliki rozpoczną przekazywanie. Po potwierdzeniu przekazania kliknij przycisk Gotowe.
W tym momencie obrazy są przekazywane, ale nie otagowane.
Aby oznaczyć obrazy, użyj myszy. Po umieszczeniu wskaźnika myszy na obrazie wyróżnienie zaznaczenia ułatwi automatyczne rysowanie zaznaczenia wokół obiektu. Jeśli nie jest to dokładne, możesz narysować własne. Można to zrobić, trzymając lewym przyciskiem myszy i przeciągając region zaznaczenia, aby objąć obiekt.
Po wybraniu obiektu na obrazie zostanie wyświetlony mały monit z prośbą o dodanie tagu regionu. Wybierz wcześniej utworzony tag ('Cup', w powyższym przykładzie) lub jeśli dodasz więcej tagów, wpisz go i kliknij przycisk + (plus).
Aby oznaczyć następny obraz, możesz kliknąć strzałkę po prawej stronie bloku lub zamknąć blok tagu (klikając znak X w prawym górnym rogu bloku), a następnie kliknij następny obraz. Gdy będzie gotowy następny obraz, powtórz tę samą procedurę. Zrób to dla wszystkich przekazanych obrazów, dopóki nie zostaną oznaczone.
Uwaga
Możesz wybrać kilka obiektów na tym samym obrazie, jak na poniższym obrazie:
Po oznaczeniu ich wszystkich kliknij przycisk oznakowany po lewej stronie ekranu, aby wyświetlić oznakowane obrazy.
Teraz możesz wytrenować usługę. Kliknij przycisk Train (Trenowanie), a pierwsza iteracja trenowania rozpocznie się.
Po utworzeniu będzie można zobaczyć dwa przyciski o nazwie Ustaw jako domyślny i Adres URL przewidywania. Kliknij pozycję Ustaw jako domyślne, a następnie kliknij pozycję Adres URL przewidywania.
Uwaga
Punkt końcowy, który jest dostarczany z tego elementu, jest ustawiony na niezależnie od tego, która iteracja została oznaczona jako domyślna. W związku z tym, jeśli później wprowadzisz nową iterację i zaktualizujesz ją jako domyślną, nie musisz zmieniać kodu.
Po kliknięciu pozycji Adres URL przewidywania otwórz Notatnik i skopiuj i wklej adres URL (nazywany również punktem końcowym przewidywania) i klucz przewidywania usługi, aby można było go pobrać, gdy będzie potrzebny później w kodzie.
Rozdział 3 . Konfigurowanie projektu aparatu Unity
Poniżej przedstawiono typową konfigurację do opracowywania za pomocą rzeczywistości mieszanej, a w związku z tym jest to dobry szablon dla innych projektów.
Otwórz aparat Unity i kliknij pozycję Nowy.
Teraz musisz podać nazwę projektu aparatu Unity. Wstaw element CustomVisionObjDetection. Upewnij się, że typ projektu jest ustawiony na 3D i ustaw lokalizację na odpowiednią dla Ciebie (pamiętaj, że bliżej katalogów głównych jest lepiej). Następnie kliknij pozycję Utwórz projekt.
Po otwarciu aparatu Unity warto sprawdzić, czy domyślny edytor skryptów jest ustawiony na program Visual Studio. Przejdź do pozycji Edytuj>preferencje, a następnie w nowym oknie przejdź do pozycji Narzędzia zewnętrzne. Zmień edytor skryptów zewnętrznych na Visual Studio. Zamknij okno Preferencje.
Następnie przejdź do pozycji Ustawienia kompilacji pliku > i przełącz platformę na platforma uniwersalna systemu Windows, a następnie kliknij przycisk Przełącz platformę.
W tym samym oknie Ustawienia kompilacji upewnij się, że ustawiono następujące ustawienia:
Urządzenie docelowe jest ustawione na urządzenie HoloLens
Typ kompilacji jest ustawiony na D3D
Zestaw SDK jest ustawiony na najnowszą zainstalowaną
Dla wersji programu Visual Studio jest ustawiona wartość Najnowsza zainstalowana
Kompilowanie i uruchamianie jest ustawione na komputer lokalny
Pozostałe ustawienia w obszarze Ustawienia kompilacji powinny być pozostawione jako domyślne na razie.
W tym samym oknie Ustawienia kompilacji kliknij przycisk Ustawienia odtwarzacza, spowoduje to otwarcie powiązanego panelu w przestrzeni, w której znajduje się inspektor .
W tym panelu należy zweryfikować kilka ustawień:
Na karcie Inne ustawienia:
Wersja środowiska uruchomieniowego skryptów powinna być eksperymentalna (odpowiednik platformy.NET 4.6), co spowoduje konieczność ponownego uruchomienia edytora.
Zaplecze skryptów powinno mieć wartość .NET.
Poziom zgodności interfejsu API powinien mieć wartość .NET 4.6.
Na karcie Ustawienia publikowania w obszarze Możliwości sprawdź:
InternetClient
Kamera internetowa
SpatialPerception
W dalszej części panelu w obszarze Ustawienia XR (znaleziono poniżej ustawień publikowania), zaznacz pole Virtual Reality Supported (Obsługiwane w rzeczywistości wirtualnej), a następnie upewnij się, że dodano zestaw WINDOWS Mixed Reality SDK .
Po powrocie do ustawień kompilacji projekty języka C# aparatu Unity nie są już wyszarzone: zaznacz pole wyboru obok tego.
Zamknij okno Build Settings (Ustawienia kompilacji).
W edytorze kliknij pozycję Edytuj>grafikę ustawień>projektu.
W Panelu inspektora zostaną otwarte ustawienia grafiki. Przewiń w dół, aż zobaczysz tablicę o nazwie Zawsze uwzględnij cieniowania. Dodaj gniazdo, zwiększając zmienną Size o jedną (w tym przykładzie było to 8, więc zrobiliśmy to 9). Zostanie wyświetlone nowe miejsce w ostatniej pozycji tablicy, jak pokazano poniżej:
W miejscu kliknij mały okrąg docelowy obok miejsca, aby otworzyć listę cieniowania. Poszukaj starszego cieniowania/przezroczystego/rozproszonego cieniowania i kliknij go dwukrotnie.
Rozdział 4 . Importowanie pakietu Aparatu Unity CustomVisionObjDetection
Na potrzeby tego kursu otrzymasz pakiet zasobów aparatu Unity o nazwie Azure-MR-310.unitypackage.
[PORADA] Wszystkie obiekty obsługiwane przez aparat Unity, w tym całe sceny, można spakować do pliku unitypackage i wyeksportować /zaimportować w innych projektach. Jest to najbezpieczniejszy i najbardziej wydajny sposób przenoszenia zasobów między różnymi projektami aparatu Unity.
Pakiet Azure-MR-310, który należy pobrać tutaj.
Po wybraniu pulpitu nawigacyjnego aparatu Unity kliknij pozycję Zasoby w menu w górnej części ekranu, a następnie kliknij pozycję Importuj pakiet niestandardowy pakietu>.
Użyj selektora plików, aby wybrać pakiet Azure-MR-310.unitypackage , a następnie kliknij przycisk Otwórz. Zostanie wyświetlona lista składników tego zasobu. Potwierdź importowanie, klikając przycisk Importuj.
Po zakończeniu importowania zauważysz, że foldery z pakietu zostały dodane do folderu Assets . Taka struktura folderów jest typowa dla projektu aparatu Unity.
Folder Materials zawiera materiał używany przez kursor spojrzenia.
Folder Plugins zawiera bibliotekę DLL Newtonsoft używaną przez kod do deserializacji odpowiedzi internetowej usługi. Dwie (2) różne wersje zawarte w folderze i podfolderie są niezbędne, aby umożliwić używanie i kompilowanie biblioteki zarówno przez Edytor aparatu Unity, jak i kompilację platformy UWP.
Folder Prefabs zawiera prefabryki zawarte w scenie. Są to:
- GazeCursor , kursor używany w aplikacji. Będzie współpracować z prefabem SpatialMapping, aby można było umieścić w scenie na podstawie obiektów fizycznych.
- Etykieta, czyli obiekt interfejsu użytkownika używany do wyświetlania tagu obiektu w scenie, jeśli jest to wymagane.
- SpatialMapping, czyli obiekt, który umożliwia aplikacji tworzenie mapy wirtualnej przy użyciu śledzenia przestrzennego urządzenia Microsoft HoloLens.
Folder Sceny , który obecnie zawiera wstępnie utworzoną scenę dla tego kursu.
Otwórz folder Sceny w panelu projektu i kliknij dwukrotnie ikonę ObjDetectionScene, aby załadować scenę używaną na potrzeby tego kursu.
Uwaga
Nie dołączono żadnego kodu. Napiszesz kod, postępując zgodnie z tym kursem.
Rozdział 5 . Tworzenie klasy CustomVisionAnalyser.
W tym momencie możesz napisać kod. Rozpoczniesz od klasy CustomVisionAnalyser .
Uwaga
Wywołania usługi Custom Vision Service wykonane w poniższym kodzie są wykonywane przy użyciu interfejsu API REST usługi Custom Vision. Korzystając z tego, zobaczysz, jak zaimplementować i wykorzystać ten interfejs API (przydatne do zrozumienia, jak zaimplementować coś podobnego samodzielnie). Należy pamiętać, że firma Microsoft oferuje zestaw Custom Vision SDK , który może również służyć do nawiązywania wywołań do usługi. Aby uzyskać więcej informacji, zobacz artykuł Dotyczący zestawu Custom Vision SDK.
Ta klasa jest odpowiedzialna za:
Ładowanie najnowszego obrazu przechwyconego jako tablica bajtów.
Wysyłanie tablicy bajtów do wystąpienia usługi Azure Custom Vision Service na potrzeby analizy.
Odbieranie odpowiedzi jako ciągu JSON.
Deserializowanie odpowiedzi i przekazanie wynikowej prognozy do klasy SceneOrganiser , która zajmie się sposobem wyświetlania odpowiedzi.
Aby utworzyć tę klasę:
Kliknij prawym przyciskiem myszy folder elementów zawartości znajdujący się w panelu projektu, a następnie kliknij polecenie Utwórz>folder. Wywołaj folder Scripts.
Kliknij dwukrotnie nowo utworzony folder, aby go otworzyć.
Kliknij prawym przyciskiem myszy wewnątrz folderu, a następnie kliknij polecenie Utwórz>skrypt języka C#. Nadaj skryptowi nazwę CustomVisionAnalyser.
Kliknij dwukrotnie nowy skrypt CustomVisionAnalyser , aby otworzyć go za pomocą programu Visual Studio.
Upewnij się, że w górnej części pliku istnieją następujące przestrzenie nazw:
using Newtonsoft.Json; using System.Collections; using System.IO; using UnityEngine; using UnityEngine.Networking;
W klasie CustomVisionAnalyser dodaj następujące zmienne:
/// <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;
Uwaga
Upewnij się, że wstawisz klucz przewidywania usługi do zmiennej predictionKey i punktu końcowego przewidywania do zmiennej predictionEndpoint. Skopiowano je do Notatnika wcześniej, w rozdziale 2, krok 14.
Teraz należy dodać kod dla aplikacji Awake(), aby zainicjować zmienną wystąpienia:
/// <summary> /// Initializes this class /// </summary> private void Awake() { // Allows this instance to behave like a singleton Instance = this; }
Dodaj metodę coroutine (ze statyczną metodą GetImageAsByteArray(), która uzyska wyniki analizy obrazu przechwyconą przez klasę ImageCapture .
Uwaga
W coroutine AnalyseImageCapture istnieje wywołanie klasy SceneOrganiser, którą jeszcze utworzysz. W związku z tym pozostaw te wiersze komentarza na razie.
/// <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); }
Usuń metody Start() i Update(), ponieważ nie będą używane.
Przed powrotem do aparatu Unity pamiętaj o zapisaniu zmian w programie Visual Studio.
Ważne
Jak wspomniano wcześniej, nie martw się o kod, który może wydawać się mieć błąd, ponieważ wkrótce udostępnisz kolejne klasy, co rozwiąże te problemy.
Rozdział 6 . Tworzenie klasy CustomVisionObjects
Klasa, którą utworzysz teraz, to klasa CustomVisionObjects .
Ten skrypt zawiera wiele obiektów używanych przez inne klasy do serializacji i deserializacji wywołań wykonanych w usłudze Custom Vision Service.
Aby utworzyć tę klasę:
Kliknij prawym przyciskiem myszy wewnątrz folderu Skrypty, a następnie kliknij pozycję Utwórz>skrypt języka C#. Wywołaj skrypt CustomVisionObjects.
Kliknij dwukrotnie nowy skrypt CustomVisionObjects , aby otworzyć go za pomocą programu Visual Studio.
Upewnij się, że w górnej części pliku istnieją następujące przestrzenie nazw:
using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Networking;
Usuń metody Start() i Update() wewnątrz klasy CustomVisionObjects. Ta klasa powinna być teraz pusta.
Ostrzeżenie
Ważne jest, aby uważnie postępować zgodnie z następną instrukcją. Jeśli umieścisz nowe deklaracje klas w klasie CustomVisionObjects, w rozdziale 10 wystąpią błędy kompilacji z informacją, że nie można odnaleźć klasy AnalysisRootObject i BoundingBox.
Dodaj następujące klasy poza klasą CustomVisionObjects. Te obiekty są używane przez bibliotekę Newtonsoft do serializacji i deserializacji danych odpowiedzi:
// 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; } }
Przed powrotem do aparatu Unity pamiętaj o zapisaniu zmian w programie Visual Studio.
Rozdział 7 . Tworzenie klasy SpatialMapping
Ta klasa ustawi zderzacz mapowania przestrzennego w scenie, aby móc wykrywać kolizje między obiektami wirtualnymi i obiektami rzeczywistymi.
Aby utworzyć tę klasę:
Kliknij prawym przyciskiem myszy wewnątrz folderu Skrypty, a następnie kliknij pozycję Utwórz>skrypt języka C#. Wywołaj skrypt SpatialMapping.
Kliknij dwukrotnie nowy skrypt SpatialMapping , aby otworzyć go za pomocą programu Visual Studio.
Upewnij się, że nad klasą SpatialMapping istnieją następujące przestrzenie nazw:
using UnityEngine; using UnityEngine.XR.WSA;
Następnie dodaj następujące zmienne wewnątrz klasy SpatialMapping powyżej metody Start():
/// <summary> /// Allows this class to behave like a singleton /// </summary> public static SpatialMapping Instance; /// <summary> /// Used by the GazeCursor as a property with the Raycast call /// </summary> internal static int PhysicsRaycastMask; /// <summary> /// The layer to use for spatial mapping collisions /// </summary> internal int physicsLayer = 31; /// <summary> /// Creates environment colliders to work with physics /// </summary> private SpatialMappingCollider spatialMappingCollider;
Dodaj aplikację Awake() i 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); }
Usuń metodę Update().
Przed powrotem do aparatu Unity pamiętaj o zapisaniu zmian w programie Visual Studio.
Rozdział 8 . Tworzenie klasy GazeCursor
Ta klasa jest odpowiedzialna za skonfigurowanie kursora w prawidłowej lokalizacji w przestrzeni rzeczywistej przez użycie elementu SpatialMappingCollider utworzonego w poprzednim rozdziale.
Aby utworzyć tę klasę:
Kliknij prawym przyciskiem myszy wewnątrz folderu Skrypty, a następnie kliknij pozycję Utwórz>skrypt języka C#. Wywoływanie skryptu GazeCursor
Kliknij dwukrotnie nowy skrypt GazeCursor , aby otworzyć go za pomocą programu Visual Studio.
Upewnij się, że masz następującą przestrzeń nazw, do której odwołuje się klasa GazeCursor :
using UnityEngine;
Następnie dodaj następującą zmienną wewnątrz klasy GazeCursor powyżej metody Start().
/// <summary> /// The cursor (this object) mesh renderer /// </summary> private MeshRenderer meshRenderer;
Zaktualizuj metodę Start() przy użyciu następującego kodu:
/// <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); }
Zaktualizuj metodę Update() przy użyciu następującego kodu:
/// <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; } }
Uwaga
Nie martw się o błąd nie znaleziono klasy SceneOrganiser , utworzysz go w następnym rozdziale.
Przed powrotem do aparatu Unity pamiętaj o zapisaniu zmian w programie Visual Studio.
Rozdział 9 . Tworzenie klasy SceneOrganiser
Ta klasa:
Skonfiguruj aparat główny, dołączając do niego odpowiednie składniki.
Po wykryciu obiektu będzie on odpowiedzialny za obliczanie swojej pozycji w świecie rzeczywistym i umieszczenie etykiety tagu w pobliżu z odpowiednią nazwą tagu.
Aby utworzyć tę klasę:
Kliknij prawym przyciskiem myszy wewnątrz folderu Skrypty, a następnie kliknij pozycję Utwórz>skrypt języka C#. Nadaj skryptowi nazwę SceneOrganiser.
Kliknij dwukrotnie nowy skrypt SceneOrganiser , aby otworzyć go za pomocą programu Visual Studio.
Upewnij się, że nad klasą SceneOrganiser istnieją następujące przestrzenie nazw:
using System.Collections.Generic; using System.Linq; using UnityEngine;
Następnie dodaj następujące zmienne wewnątrz klasy SceneOrganiser powyżej metody Start():
/// <summary> /// Allows this class to behave like a singleton /// </summary> public static SceneOrganiser Instance; /// <summary> /// The cursor object attached to the Main Camera /// </summary> internal GameObject cursor; /// <summary> /// The label used to display the analysis on the objects in the real world /// </summary> public GameObject label; /// <summary> /// Reference to the last Label positioned /// </summary> internal Transform lastLabelPlaced; /// <summary> /// Reference to the last Label positioned /// </summary> internal TextMesh lastLabelPlacedText; /// <summary> /// Current threshold accepted for displaying the label /// Reduce this value to display the recognition more often /// </summary> internal float probabilityThreshold = 0.8f; /// <summary> /// The quad object hosting the imposed image captured /// </summary> private GameObject quad; /// <summary> /// Renderer of the quad object /// </summary> internal Renderer quadRenderer;
Usuń metody Start() i Update().
Poniżej zmiennych dodaj metodę Awake(), która zainicjuje klasę i skonfiguruje scenę.
/// <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>(); }
Dodaj metodę PlaceAnalysisLabel(), która utworzy wystąpienie etykiety w scenie (co w tym momencie jest niewidoczne dla użytkownika). Umieszcza również czworokąt (również niewidoczny), w którym znajduje się obraz, i nakłada się na świat rzeczywisty. Jest to ważne, ponieważ współrzędne pola pobrane z usługi po analizie są śledzone z powrotem do tego czworokąta, aby określić przybliżoną lokalizację obiektu w świecie rzeczywistym.
/// <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; }
Dodaj metodę FinaliseLabel(). Odpowiada za:
- Ustawianie tekstu Etykiety za pomocą tagu przewidywania z największą ufnością.
- Wywołanie obliczeń pola ograniczenia na obiekcie czworokąt, umieszczonym wcześniej i umieszczeniu etykiety w scenie.
- Dostosowanie głębokości etykiety przy użyciu Raycast w kierunku pola ograniczenia, które powinno zderzyć się z obiektem w świecie rzeczywistym.
- Zresetowanie procesu przechwytywania w celu umożliwienia użytkownikowi przechwytywania innego obrazu.
/// <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(); }
Dodaj metodę CalculateBoundingBoxPosition(), która hostuje wiele obliczeń niezbędnych do przetłumaczenia współrzędnych pola ograniczenia pobranych z usługi i utwórz je proporcjonalnie na czworokącie.
/// <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); }
Przed powrotem do aparatu Unity pamiętaj o zapisaniu zmian w programie Visual Studio.
Ważne
Przed kontynuowanym otwórz klasę CustomVisionAnalyser i w metodzie AnalysisLastImageCaptured() usuń komentarz z następujących wierszy:
// 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);
Uwaga
Nie martw się o komunikat klasy ImageCapture "nie można odnaleźć", utworzysz go w następnym rozdziale.
Rozdział 10 — Tworzenie klasy ImageCapture
Następną klasą , którą utworzysz, jest klasa ImageCapture .
Ta klasa jest odpowiedzialna za:
- Przechwytywanie obrazu przy użyciu aparatu HoloLens i przechowywanie go w folderze App .
- Obsługa gestów naciśnięcia od użytkownika.
Aby utworzyć tę klasę:
Przejdź do utworzonego wcześniej folderu Skrypty .
Kliknij prawym przyciskiem myszy wewnątrz folderu, a następnie kliknij polecenie Utwórz>skrypt języka C#. Nadaj skryptowi nazwę ImageCapture.
Kliknij dwukrotnie nowy skrypt ImageCapture , aby otworzyć go za pomocą programu Visual Studio.
Zastąp przestrzenie nazw w górnej części pliku następującymi elementami:
using System; using System.IO; using System.Linq; using UnityEngine; using UnityEngine.XR.WSA.Input; using UnityEngine.XR.WSA.WebCam;
Następnie dodaj następujące zmienne wewnątrz klasy ImageCapture powyżej metody Start():
/// <summary> /// Allows this class to behave like a singleton /// </summary> public static ImageCapture Instance; /// <summary> /// Keep counts of the taps for image renaming /// </summary> private int captureCount = 0; /// <summary> /// Photo Capture object /// </summary> private PhotoCapture photoCaptureObject = null; /// <summary> /// Allows gestures recognition in HoloLens /// </summary> private GestureRecognizer recognizer; /// <summary> /// Flagging if the capture loop is running /// </summary> internal bool captureIsActive; /// <summary> /// File path of current analysed photo /// </summary> internal string filePath = string.Empty;
Teraz należy dodać kod dla metod Awake() i Start():
/// <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(); }
Zaimplementuj procedurę obsługi, która zostanie wywołana, gdy wystąpi gest Tap:
/// <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); } }
Ważne
Gdy kursor jest zielony, oznacza to, że aparat jest dostępny do wykonania obrazu. Gdy kursor jest czerwony, oznacza to, że aparat jest zajęty.
Dodaj metodę używaną przez aplikację do uruchomienia procesu przechwytywania obrazu i zapisania obrazu:
/// <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); }); }); }
Dodaj programy obsługi, które będą wywoływane po uchwyceniu zdjęcia i gdy będzie gotowe do przeanalizowania. Wynik jest następnie przekazywany do klasy CustomVisionAnalyser na potrzeby analizy.
/// <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(); }
Przed powrotem do aparatu Unity pamiętaj o zapisaniu zmian w programie Visual Studio.
Rozdział 11 — Konfigurowanie skryptów na scenie
Teraz, po napisaniu całego kodu niezbędnego dla tego projektu, nadszedł czas, aby skonfigurować skrypty w scenie i w prefab, aby działały prawidłowo.
W edytorze aparatu Unity w panelu hierarchii wybierz aparat główny.
W Panelu inspektora z wybraną pozycją Aparat główny kliknij pozycję Dodaj składnik, a następnie wyszukaj skrypt SceneOrganiser i kliknij dwukrotnie, aby go dodać.
W panelu projektu otwórz folder Prefabs, przeciągnij prefab etykietę do pustego obszaru wejściowego odwołania Etykieta, w skryscie SceneOrganiser, który właśnie został dodany do aparatu głównego, jak pokazano na poniższej ilustracji:
W panelu hierarchii wybierz element podrzędny GazeCursor głównego aparatu fotograficznego.
W Panelu inspektora z wybraną pozycją GazeCursor kliknij pozycję Dodaj składnik, a następnie wyszukaj skrypt GazeCursor i kliknij dwukrotnie, aby go dodać.
Ponownie w panelu hierarchii wybierz element podrzędny SpatialMapping głównego aparatu.
W panelu Inspector (Inspektor Panel) z wybraną pozycją SpatialMapping kliknij pozycję Dodaj składnik, a następnie wyszukaj skrypt SpatialMapping i kliknij dwukrotnie, aby go dodać.
Pozostałe skrypty, które nie zostały ustawione, zostaną dodane przez kod w skryscie SceneOrganiser w czasie wykonywania.
Rozdział 12 - Przed budynkiem
Aby przeprowadzić dokładny test aplikacji, należy załadować ją bezpośrednio do urządzenia Microsoft HoloLens.
Przed wykonaniem upewnij się, że:
Wszystkie ustawienia wymienione w rozdziale 3 są ustawione poprawnie.
Skrypt SceneOrganiser jest dołączony do obiektu Main Camera .
Skrypt GazeCursor jest dołączony do obiektu GazeCursor .
Skrypt SpatialMapping jest dołączony do obiektu SpatialMapping .
W rozdziale 5, krok 6:
- Upewnij się, że wstawisz klucz przewidywania usługi do zmiennej predictionKey .
- Punkt końcowy przewidywania został wstawiony do klasy predictionEndpoint.
Rozdział 13 — Tworzenie rozwiązania platformy UWP i ładowanie bezpośrednie aplikacji
Teraz możesz przystąpić do tworzenia aplikacji jako rozwiązania platformy UWP, które będzie można wdrożyć na urządzeniu Microsoft HoloLens. Aby rozpocząć proces kompilacji:
Przejdź do pozycji Ustawienia kompilacji pliku>.
Zaznacz projekty języka C# aparatu Unity.
Kliknij pozycję Dodaj otwarte sceny. Spowoduje to dodanie aktualnie otwartej sceny do kompilacji.
Kliknij pozycję Kompiluj. Aparat Unity uruchomi okno Eksplorator plików, w którym należy utworzyć, a następnie wybierz folder do skompilowania aplikacji. Utwórz teraz ten folder i nadaj mu nazwę Aplikacja. Następnie po wybraniu folderu Aplikacja kliknij pozycję Wybierz folder.
Aparat Unity rozpocznie kompilowanie projektu w folderze App .
Po zakończeniu kompilowania środowiska Unity (może to zająć trochę czasu), zostanie otwarte okno Eksplorator plików w lokalizacji kompilacji (sprawdź pasek zadań, ponieważ może nie zawsze pojawiać się nad oknami, ale powiadomi o dodaniu nowego okna).
Aby przeprowadzić wdrożenie na urządzeniu Microsoft HoloLens, musisz mieć adres IP tego urządzenia (w przypadku wdrożenia zdalnego) i upewnić się, że ma on również ustawiony tryb dewelopera. Czynność:
Podczas noszenia urządzenia HoloLens otwórz ustawienia.
Przejdź do pozycji Sieć i Internetowe>opcje zaawansowane sieci Wi-Fi>
Zanotuj adres IPv4 .
Następnie przejdź z powrotem do pozycji Ustawienia, a następnie przejdź do pozycji Aktualizuj i zabezpieczenia>dla deweloperów
Ustaw tryb dewelopera wł.
Przejdź do nowej kompilacji aparatu Unity ( folderu App ) i otwórz plik rozwiązania za pomocą programu Visual Studio.
W obszarze Konfiguracja rozwiązania wybierz pozycję Debuguj.
W polu Platforma rozwiązania wybierz pozycję x86, Maszyna zdalna. Zostanie wyświetlony monit o wstawienie adresu IP urządzenia zdalnego (w tym przypadku zanotowano urządzenie Microsoft HoloLens).
Przejdź do menu Kompilacja i kliknij pozycję Wdróż rozwiązanie, aby załadować aplikację bezpośrednio do urządzenia HoloLens.
Aplikacja powinna być teraz wyświetlana na liście zainstalowanych aplikacji na urządzeniu Microsoft HoloLens, które będą gotowe do uruchomienia.
Aby użyć aplikacji:
- Przyjrzyj się obiektowi, który został wytrenowany za pomocą usługi Azure Custom Vision Service, wykrywania obiektów i użyj gestu Naciśnij.
- Jeśli obiekt zostanie pomyślnie wykryty, zostanie wyświetlony tekst etykiety na świecie z nazwą tagu.
Ważne
Za każdym razem, gdy przechwycisz zdjęcie i wyślesz je do usługi, możesz wrócić do strony Usługi i ponownie wytrenować usługę przy użyciu nowo przechwyconych obrazów. Na początku prawdopodobnie trzeba będzie również poprawić pola ograniczenia, aby były dokładniejsze i ponownie wytrenować usługę.
Uwaga
Umieszczony tekst etykiety może nie pojawić się w pobliżu obiektu, gdy czujniki urządzenia Microsoft HoloLens i/lub element SpatialTrackingComponent w a unity nie mogą umieścić odpowiednich zderzaków względem obiektów świata rzeczywistego. Spróbuj użyć aplikacji na innej powierzchni, jeśli tak jest.
Aplikacja custom vision, wykrywania obiektów
Gratulacje, utworzono aplikację rzeczywistości mieszanej, która korzysta z interfejsu API wykrywania obiektów usługi Azure Custom Vision, który może rozpoznać obiekt z obrazu, a następnie udostępnić przybliżoną pozycję dla tego obiektu w przestrzeni 3D.
Ćwiczenia dodatkowe
Ćwiczenie 1
Dodanie do etykiety tekstowej umożliwia użycie półprzezroczystego modułu w celu opakowania rzeczywistego obiektu w polu ograniczenia 3D.
Ćwiczenie 2
Trenowanie usługi Custom Vision Service w celu rozpoznawania większej liczby obiektów.
Ćwiczenie 3
Odtwarzaj dźwięk po rozpoznaniu obiektu.
Ćwiczenie 4
Użyj interfejsu API, aby ponownie wytrenować usługę przy użyciu tych samych obrazów, które analizuje aplikacja, aby usługa mogła być bardziej dokładna (wykonaj jednocześnie przewidywanie i trenowanie).