Sdílet prostřednictvím


HoloLens (1. generace) a Azure 310: Detekce objektů

Poznámka

Kurzy Mixed Reality Academy byly navrženy s ohledem na HoloLens (1. generace) a Mixed Reality Asistivní náhlavní soupravy. Proto se domníváme, že je důležité ponechat tyto kurzy pro vývojáře, kteří stále hledají pokyny při vývoji pro tato zařízení. Tyto kurzy nebudou aktualizovány nejnovějšími sadami nástrojů nebo interakcemi používanými pro HoloLens 2. Budou zachovány, aby mohly pokračovat v práci na podporovaných zařízeních. V budoucnu bude k dispozici nová série kurzů, které předvedou vývoj pro HoloLens 2. Toto oznámení bude aktualizováno odkazem na tyto kurzy, jakmile budou zveřejněny.


V tomto kurzu se naučíte rozpoznávat vlastní vizuální obsah a jeho prostorovou pozici v rámci zadaného obrázku pomocí funkcí Azure Custom Vision rozpoznávání objektů v aplikaci hybridní reality.

Tato služba vám umožní vytrénovat model strojového učení pomocí obrázků objektů. Natrénovaný model pak použijete k rozpoznávání podobných objektů a přibližné jejich poloze v reálném světě, jak je to zajištěno zachycením kamery Microsoft HoloLens nebo kamerou připojenou k počítači pro imerzivní náhlavní soupravy (VR).

výsledek kurzu

Azure Custom Vision rozpoznávání objektů je služba Microsoftu, která vývojářům umožňuje vytvářet vlastní klasifikátory obrázků. Tyto klasifikátory lze pak použít s novými obrázky k detekci objektů v rámci tohoto nového obrázku tím, že v samotném obrázku poskytnou hranice rámečku . Služba poskytuje jednoduchý a snadno použitelný online portál pro zjednodušení tohoto procesu. Další informace najdete na následujících odkazech:

Po dokončení tohoto kurzu budete mít aplikaci pro hybridní realitu, která bude umět následující:

  1. Uživatel bude moct upřet pohledem na objekt, který vytrénoval pomocí služby Azure Custom Vision, rozpoznávání objektů.
  2. Uživatel použije gesto klepnutí k zachycení obrázku, na který se dívá.
  3. Aplikace odešle image do služby Azure Custom Vision Service.
  4. Zobrazí se odpověď ze služby, která zobrazí výsledek rozpoznávání jako text s mezerou ve světě. Toho dosáhnete tak, že použijete Microsoft HoloLens Spatial Tracking jako způsob, jak porozumět pozici rozpoznaného objektu na světě, a pak pomocí značky přidružené k tomu, co se zjistí na obrázku, k poskytnutí textu popisku.

Kurz se také zabývá ručním nahráváním obrázků, vytvářením značek a trénováním služby k rozpoznávání různých objektů (v uvedeném příkladu šálek) nastavením hraničního rámečku v obrázku, který odešlete.

Důležité

Po vytvoření a použití aplikace by se vývojář měl vrátit ke službě Azure Custom Vision Service, identifikovat predikce vytvořené službou a určit, jestli byly správné nebo ne (pomocí označení všeho, co služba vynechala, a úpravou ohraničující rámečky). Službu je pak možné přetrénovat, což zvýší pravděpodobnost, že rozpozná objekty reálného světa.

V tomto kurzu se naučíte, jak získat výsledky ze služby Azure Custom Vision ( Rozpoznávání objektů) do ukázkové aplikace založené na Unity. Bude na vás, abyste tyto koncepty použili na vlastní aplikaci, kterou možná vytváříte.

Podpora zařízení

Kurz HoloLens Imerzivní náhlavní soupravy
MR a Azure 310: Detekce objektů ✔️

Požadavky

Poznámka

Tento kurz je určený pro vývojáře, kteří mají základní zkušenosti s Unity a C#. Mějte také na paměti, že požadavky a písemné pokyny v tomto dokumentu představují to, co bylo testováno a ověřeno v době psaní tohoto dokumentu (červenec 2018). Můžete používat nejnovější software, jak je uvedeno v článku instalace nástrojů , i když by se nemělo předpokládat, že informace v tomto kurzu budou dokonale odpovídat tomu, co najdete v novějším softwaru, než je uvedeno níže.

Pro tento kurz doporučujeme následující hardware a software:

Než začnete

  1. Aby nedocházelo k problémům při sestavování tohoto projektu, důrazně doporučujeme vytvořit projekt uvedený v tomto kurzu v kořenové nebo téměř kořenové složce (dlouhé cesty ke složkám můžou způsobit problémy při sestavování).
  2. Nastavte a otestujte HoloLens. Pokud k tomu potřebujete podporu, navštivte článek o nastavení HoloLensu.
  3. Při zahájení vývoje nové aplikace HoloLens je vhodné provést kalibraci a ladění senzorů (někdy může pomoct provádět tyto úlohy pro každého uživatele).

Nápovědu k kalibraci potřebujete pomocí tohoto odkazu na článek Kalibrace HoloLensu.

Pokud potřebujete pomoc s laděním senzorů, použijte tento odkaz na článek o ladění senzorů HoloLens.

Kapitola 1 – Portál Custom Vision

Pokud chcete používat službu Azure Custom Vision Service, budete muset nakonfigurovat instanci této služby tak, aby byla dostupná pro vaši aplikaci.

  1. Přejděte na hlavní stránku Custom Vision Service.

  2. Klikněte na Začínáme.

    Snímek obrazovky se zvýrazněnou Začínáme tlačítkem

  3. Přihlaste se k portálu Custom Vision Portal.

    Snímek obrazovky znázorňující tlačítko Přihlásit se

  4. Pokud ještě nemáte účet Azure, budete si ho muset vytvořit. Pokud tento kurz sledujete v situaci ve třídě nebo testovacím prostředí, požádejte o pomoc s nastavením nového účtu svého instruktora nebo některého z specialistů.

  5. Jakmile se poprvé přihlásíte, zobrazí se výzva k zobrazení panelu Podmínky služby . Kliknutím na zaškrtávací políčko odsouhlaste podmínky. Pak klikněte na Souhlasím.

    Snímek obrazovky s panelem Podmínek služby

  6. Po vyjádření souhlasu s podmínkami se teď nacházíte v části Moje projekty . Klikněte na Nový projekt.

    Snímek obrazovky, který ukazuje, kde vybrat Nový projekt

  7. Na pravé straně se zobrazí karta s výzvou k zadání některých polí pro projekt.

    1. Vložení názvu projektu

    2. Vložení popisu projektu (volitelné)

    3. Zvolte skupinu prostředků nebo vytvořte novou. Skupina prostředků poskytuje způsob, jak monitorovat, řídit přístup, zřizovat a spravovat fakturaci kolekce prostředků Azure. Doporučujeme udržovat všechny služby Azure přidružené k jednomu projektu (např. tyto kurzy) ve společné skupině prostředků.

      Snímek obrazovky, který ukazuje, kam přidat podrobnosti o novém projektu

    4. Nastavte typy projektů jako Rozpoznávání objektů (Preview).

  8. Jakmile budete hotovi, klikněte na Vytvořit projekt a budete přesměrováni na stránku projektu Custom Vision Service.

Kapitola 2 : Trénování Custom Vision projektu

Na portálu Custom Vision je vaším hlavním cílem vytrénovat projekt tak, aby rozpoznával konkrétní objekty v obrázcích.

Pro každý objekt, který má vaše aplikace rozpoznat, potřebujete alespoň patnáct (15) obrázků. Můžete použít obrázky, které jsou součástí tohoto kurzu (řada šálků).

Trénujte projekt Custom Vision:

  1. Klikněte na + tlačítko vedle položky Značky.

    Snímek obrazovky znázorňující tlačítko + vedle položky Značky

  2. Přidejte název značky, která se použije k přidružení obrázků. V tomto příkladu používáme k rozpoznávání obrázky kelímků, proto jsme značku pojmenovali Cup ( Cup). Po dokončení klikněte na Uložit .

    Snímek obrazovky, který ukazuje, kam přidat název značky

  3. Všimněte si, že vaše značka byla přidána (možná budete muset stránku znovu načíst, aby se zobrazila).

    Snímek obrazovky, který ukazuje, kam se vaše značka přidala

  4. Klikněte na Přidat obrázky uprostřed stránky.

    Snímek obrazovky, který ukazuje, kam přidat obrázky

  5. Klikněte na Procházet místní soubory a přejděte na obrázky, které chcete nahrát pro jeden objekt, minimálně patnáct (15).

    Tip

    K nahrání můžete vybrat několik obrázků najednou.

    Snímek obrazovky znázorňující obrázky, které můžete nahrát

  6. Jakmile vyberete všechny obrázky, se kterými chcete projekt trénovat, stiskněte Nahrát soubory . Soubory se začnou nahrávat. Jakmile budete mít potvrzení o nahrání, klikněte na Hotovo.

    Snímek obrazovky znázorňující průběh nahraných obrázků

  7. V tuto chvíli se obrázky nahrají, ale nebudou označené.

    Snímek obrazovky znázorňující obrázek bez oznamů

  8. Pokud chcete obrázky označit, použijte myš. Když na obrázek najedete myší, zvýraznění výběru vám pomůže automaticky nakreslit výběr kolem objektu. Pokud není přesný, můžete nakreslit vlastní. Toho dosáhnete tak, že podržíte levým tlačítkem myši a přetáhnete oblast výběru tak, aby zahrnovala váš objekt.

    Snímek obrazovky, který ukazuje, jak označit obrázek

  9. Po výběru objektu v obrázku se zobrazí malá výzva k přidání značky oblasti. Vyberte dříve vytvořenou značku (ve výše uvedeném příkladu Cup) nebo pokud přidáváte další značky, zadejte ji a klikněte na tlačítko + (plus).

    Snímek obrazovky znázorňující značku, kterou jste přidali k obrázku

  10. Pokud chcete označit další obrázek, můžete kliknout na šipku napravo od okna nebo zavřít okno značky (kliknutím na X v pravém horním rohu okna) a potom kliknout na další obrázek. Jakmile budete mít připravený další obrázek, opakujte stejný postup. Udělejte to pro všechny obrázky, které jste nahráli, dokud nebudou všechny označené.

    Poznámka

    Na stejném obrázku můžete vybrat několik objektů, například na následujícím obrázku:

    Snímek obrazovky znázorňující více objektů na obrázku

  11. Jakmile je všechny označíte, kliknutím na označené tlačítko na levé straně obrazovky zobrazte označené obrázky.

    Snímek obrazovky, který zvýrazňuje tlačítko Tagged

  12. Teď jste připraveni službu trénovat. Klikněte na tlačítko Trénovat a spustí se první iterace trénování.

    Snímek obrazovky, který zvýrazňuje tlačítko Trénovat

    Snímek obrazovky znázorňující první iteraci trénování

  13. Po sestavení uvidíte dvě tlačítka s názvem Nastavit jako výchozí a Prediktivní adresa URL. Nejprve klikněte na Nastavit jako výchozí a pak klikněte na Prediktivní adresa URL.

    Snímek obrazovky, který zvýrazňuje tlačítko Nastavit jako výchozí

    Poznámka

    Koncový bod, který je z něj poskytnutý, je nastavený na to, která iterace byla označena jako výchozí. Proto pokud později vytvoříte novou iteraci a aktualizujete ji jako výchozí, nebudete muset kód měnit.

  14. Po kliknutí na adresu URL predikce otevřete Poznámkový blok a zkopírujte a vložte adresu URL (označovanou také jako prediktivní koncový bod) a klíč služby Prediction-Key, abyste je mohli později v kódu načíst, až ji budete potřebovat.

    Snímek obrazovky znázorňující koncový bod predikce a klíč predition

Kapitola 3 – Nastavení projektu Unity

Následující příklad je typickým nastavením pro vývoj s hybridní realitou a jako takový je dobrou šablonou pro další projekty.

  1. Otevřete Unity a klikněte na Nový.

    Snímek obrazovky, který zvýrazňuje tlačítko Nový

  2. Teď budete muset zadat název projektu Unity. Vložte CustomVisionObjDetection. Ujistěte se, že je typ projektu nastavený na 3D, a nastavte umístění na místo, které je pro vás vhodné (nezapomeňte, že lepší je blíž ke kořenovým adresářům). Pak klikněte na Vytvořit projekt.

    Snímek obrazovky s podrobnostmi o projektu a místem, kde vybrat Vytvořit projekt

  3. Když je Unity otevřená, stojí za to zkontrolovat, jestli je výchozí Editor skriptů nastavený na Visual Studio. Přejděte na Upravit>předvolby a pak v novém okně přejděte na Externí nástroje. Změňte Editor externích skriptů na Visual Studio. Zavřete okno Předvolby .

    Snímek obrazovky, který ukazuje, kde změnit editor externích skriptů na Visual Studio

  4. Pak přejděte na Nastavení sestavení souboru>, přepněte platformu na Univerzální platforma Windows a potom klikněte na tlačítko Přepnout platformu.

    Snímek obrazovky, který zvýrazňuje tlačítko Přepnout platformu

  5. Ve stejném okně Nastavení sestavení se ujistěte, že jsou nastavené následující možnosti:

    1. Cílové zařízení je nastavené na HoloLens.

    2. Typ sestavení je nastavený na D3D.

    3. Sada SDK je nastavená na nejnovější nainstalovanou verzi.

    4. Verze sady Visual Studio je nastavená na nejnovější nainstalovanou verzi.

    5. Sestavení a spuštění je nastavené na místní počítač.

    6. Zbývající nastavení v nastavení sestavení by prozatím měla zůstat ve výchozím nastavení.

      Snímek obrazovky s možnostmi konfigurace nastavení sestavení

  6. Ve stejném okně Nastavení sestavení klikněte na tlačítko Nastavení přehrávače . Tím se otevře související panel v prostoru, kde se nachází inspektor .

  7. Na tomto panelu je potřeba ověřit několik nastavení:

    1. Na kartě Další nastavení :

      1. Skriptovací verze modulu runtime by měla být experimentální (ekvivalent .NET 4.6), která aktivuje potřebu restartovat editor.

      2. Skriptovací back-end by měl být .NET.

      3. Úroveň kompatibility rozhraní API by měla být .NET 4.6.

        Snímek obrazovky znázorňující možnost Úroveň kompatibility rozhraní API nastavenou na .NET 4.6

    2. Na kartě Nastavení publikování v části Možnosti zaškrtněte:

      1. InternetClient

      2. Webcam

      3. SpatialPerception

        Snímek obrazovky znázorňující horní polovinu možností konfigurace funkcíSnímek obrazovky znázorňující dolní polovinu možností konfigurace schopností

    3. Dále na panelu v nastavení XR (najdete pod nastavením publikování) zaškrtněte možnost Podpora virtuální reality a pak zkontrolujte, že je přidaná sada WINDOWS MIXED REALITY SDK.

      Snímek obrazovky, který ukazuje přidání sady WINDOWS MIXED REALITY SDK

  8. Když se vrátíte do nastavení sestavení, projekty Unity C# už nejsou šedě: zaškrtněte políčko vedle tohoto.

  9. Zavřete okno Nastavení sestavení .

  10. V editoru klikněte na Upravit>grafiku nastavení> projektu.

    Snímek obrazovky znázorňující vybranou možnost nabídky Grafika

  11. Na panelu inspektoru bude otevřeno Nastavení grafiky . Posuňte se dolů, dokud se nezobrazí pole s názvem Vždy zahrnout shadery. Přidejte slot zvětšením proměnné Size o jednu (v tomto příkladu to bylo 8, takže jsme udělali 9). Na poslední pozici pole se zobrazí nový slot, jak je znázorněno níže:

    Snímek obrazovky, který zvýrazňuje pole Vždy zahrnuté shadery

  12. Kliknutím na malý cílový kruh vedle slotu ve slotu otevřete seznam shaderů. Vyhledejte starší shader/transparentní/difuzní shader a poklikejte na něj.

    Snímek obrazovky, který zvýrazňuje starší shader/ transparentní/difuzní shader

Kapitola 4 – Import balíčku CustomVisionObjDetection Unity

Pro účely tohoto kurzu máte k dispozici balíček prostředků Unity s názvem Azure-MR-310.unitypackage.

[TIP] Všechny objekty podporované Unitym, včetně celých scén, lze zabalit do souboru .unitypackage a exportovat nebo importovat v jiných projektech. Je to nejbezpečnější a nejúčinnější způsob, jak přesouvat prostředky mezi různými projekty Unity.

Balíček Azure-MR-310, který potřebujete stáhnout, najdete tady.

  1. S řídicím panelem Unity před vámi klikněte v nabídce v horní části obrazovky na Assets (Prostředky) a pak klikněte na Import package Custom Package (Importovat vlastní balíček).>

    Snímek obrazovky se zvýrazněnou možností nabídky Vlastní balíček

  2. Pomocí nástroje pro výběr souborů vyberte balíček Azure-MR-310.unitypackage a klikněte na Otevřít. Zobrazí se vám seznam součástí tohoto prostředku. Potvrďte import kliknutím na tlačítko Importovat .

    Snímek obrazovky se seznamem komponent prostředků, které chcete importovat

  3. Po dokončení importu si všimnete, že složky z balíčku byly přidány do složky Assets . Tento druh struktury složek je typický pro projekt Unity.

    Snímek obrazovky s obsahem složky Assets

    1. Složka Materiály obsahuje materiál používaný kurzorem pohledu.

    2. Složka Plugins obsahuje knihovnu DLL Newtonsoft používanou kódem k deserializaci webové odpovědi služby. Dvě (2) různé verze obsažené ve složce a podsložce jsou nezbytné k tomu, aby bylo možné knihovnu používat a vytvářet editorem Unity i sestavením UPW.

    3. Složka Prefabs obsahuje prefaby obsažené ve scéně. Jedná se o:

      1. GazeCursor, kurzor použitý v aplikaci. Bude spolupracovat s předfabem SpatialMapping, aby bylo možné umístit do scény nad fyzickými objekty.
      2. Popisek, což je objekt uživatelského rozhraní, který se v případě potřeby používá k zobrazení značky objektu ve scéně.
      3. SpatialMapping, což je objekt, který aplikaci umožňuje použít vytvoření virtuální mapy pomocí prostorového sledování Microsoft HoloLens.
    4. Složka Scény , která aktuálně obsahuje předdefinované scény pro tento kurz.

  4. Otevřete složku Scene (Scény ) na panelu projektu a poklikáním na objDetectionScene načtěte scénu, kterou použijete pro tento kurz.

    Snímek obrazovky, který zobrazuje ObjDetectionScene ve složce Scenes

    Poznámka

    Není zahrnut žádný kód, napíšete ho podle tohoto kurzu.

Kapitola 5 – Vytvořte třídu CustomVisionAnalyser.

V tuto chvíli jste připraveni napsat nějaký kód. Začnete třídou CustomVisionAnalyser .

Poznámka

Volání služby Custom Vision, provedená v níže uvedeném kódu, se provádějí pomocí rozhraní REST API Custom Vision. Pomocí tohoto rozhraní zjistíte, jak implementovat a používat toto rozhraní API (užitečné pro pochopení toho, jak implementovat něco podobného na vlastní pěst). Mějte na paměti, že Microsoft nabízí sadu SDK Custom Vision, která se dá použít také k volání služby. Další informace najdete v článku Custom Vision SDK.

Tato třída zodpovídá za:

  • Načítá se nejnovější image zachycená jako pole bajtů.

  • Odeslání pole bajtů do instance služby Azure Custom Vision Service k analýze

  • Příjem odpovědi jako řetězce JSON

  • Deserializace odpovědi a předání výsledné předpovědi do Třídy SceneOrganiser , která se postará o to, jak by měla být odpověď zobrazena.

Vytvoření této třídy:

  1. Klikněte pravým tlačítkem na složku Asset umístěnou na panelu projektu a pak klikněte na Vytvořit>složku. Zavolejte složku Skripty.

    Snímek obrazovky, který ukazuje, jak vytvořit složku Scripts

  2. Poklikáním na nově vytvořenou složku otevřete.

  3. Klikněte pravým tlačítkem myši do složky a pak klikněte na Vytvořit>skript jazyka C#. Pojmenujte skript CustomVisionAnalyser.

  4. Poklikáním na nový skript CustomVisionAnalyser ho otevřete v sadě Visual Studio.

  5. Ujistěte se, že v horní části souboru odkazujete na následující obory názvů:

    using Newtonsoft.Json;
    using System.Collections;
    using System.IO;
    using UnityEngine;
    using UnityEngine.Networking;
    
  6. Do třídy CustomVisionAnalyser přidejte následující proměnné:

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

    Poznámka

    Nezapomeňte vložit klíč služby Prediction-Key do proměnné predictionKey a prediktivní koncový bod do proměnné predictionEndpoint . Zkopírovali jste je do Poznámkového bloku dříve v kapitole 2, krok 14.

  7. Pro inicializaci proměnné Instance je teď potřeba přidat kód pro Awake( ):

        /// <summary>
        /// Initializes this class
        /// </summary>
        private void Awake()
        {
            // Allows this instance to behave like a singleton
            Instance = this;
        }
    
  8. Přidejte korutin (se statickou metodou GetImageAsByteArray(), která získá výsledky analýzy obrázku zachycené třídou ImageCapture .

    Poznámka

    V korutinu AnalyzeImageCapture je volání třídy SceneOrganiser , kterou teprve chcete vytvořit. Proto prozatím nechte tyto řádky okomentované.

        /// <summary>
        /// Call the Computer Vision Service to submit the image.
        /// </summary>
        public IEnumerator AnalyseLastImageCaptured(string imagePath)
        {
            Debug.Log("Analyzing...");
    
            WWWForm webForm = new WWWForm();
    
            using (UnityWebRequest unityWebRequest = UnityWebRequest.Post(predictionEndpoint, webForm))
            {
                // Gets a byte array out of the saved image
                imageBytes = GetImageAsByteArray(imagePath);
    
                unityWebRequest.SetRequestHeader("Content-Type", "application/octet-stream");
                unityWebRequest.SetRequestHeader("Prediction-Key", predictionKey);
    
                // The upload handler will help uploading the byte array with the request
                unityWebRequest.uploadHandler = new UploadHandlerRaw(imageBytes);
                unityWebRequest.uploadHandler.contentType = "application/octet-stream";
    
                // The download handler will help receiving the analysis from Azure
                unityWebRequest.downloadHandler = new DownloadHandlerBuffer();
    
                // Send the request
                yield return unityWebRequest.SendWebRequest();
    
                string jsonResponse = unityWebRequest.downloadHandler.text;
    
                Debug.Log("response: " + jsonResponse);
    
                // Create a texture. Texture size does not matter, since
                // LoadImage will replace with the incoming image size.
                //Texture2D tex = new Texture2D(1, 1);
                //tex.LoadImage(imageBytes);
                //SceneOrganiser.Instance.quadRenderer.material.SetTexture("_MainTex", tex);
    
                // The response will be in JSON format, therefore it needs to be deserialized
                //AnalysisRootObject analysisRootObject = new AnalysisRootObject();
                //analysisRootObject = JsonConvert.DeserializeObject<AnalysisRootObject>(jsonResponse);
    
                //SceneOrganiser.Instance.FinaliseLabel(analysisRootObject);
            }
        }
    
        /// <summary>
        /// Returns the contents of the specified image file as a byte array.
        /// </summary>
        static byte[] GetImageAsByteArray(string imageFilePath)
        {
            FileStream fileStream = new FileStream(imageFilePath, FileMode.Open, FileAccess.Read);
    
            BinaryReader binaryReader = new BinaryReader(fileStream);
    
            return binaryReader.ReadBytes((int)fileStream.Length);
        }
    
  9. Odstraňte metody Start() a Update(), protože se nebudou používat.

  10. Před návratem do Unity nezapomeňte uložit změny v sadě Visual Studio.

Důležité

Jak už bylo zmíněno dříve, nedělejte si starosti s kódem, který může vypadat jako chyba, protože brzy poskytnete další třídy, které je opraví.

Kapitola 6 – Vytvoření třídy CustomVisionObjects

Třída, kterou teď vytvoříte, je Třída CustomVisionObjects .

Tento skript obsahuje řadu objektů používaných jinými třídami k serializaci a deserializaci volání Custom Vision Service.

Vytvoření této třídy:

  1. Klikněte pravým tlačítkem do složky Scripts (Skripty ) a pak klikněte na Create C# Script (Vytvořit>skript jazyka C#). Zavolejte skript CustomVisionObjects.

  2. Poklikáním na nový skript CustomVisionObjects ho otevřete v sadě Visual Studio.

  3. Ujistěte se, že v horní části souboru odkazujete na následující obory názvů:

    using System;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.Networking;
    
  4. Odstraňte metody Start() a Update() ve třídě CustomVisionObjects , tato třída by teď měla být prázdná.

    Upozornění

    Je důležité, abyste pečlivě dodržovali další instrukce. Pokud vložíte nové deklarace třídy do třídy CustomVisionObjects , zobrazí se chyby kompilace v kapitole 10, které uvádějí, že AnalysisRootObject a BoundingBox nebyly nalezeny.

  5. Přidejte následující třídy mimotřídu CustomVisionObjects . Tyto objekty používá knihovna Newtonsoft k serializaci a deserializaci dat odpovědi:

    // The objects contained in this script represent the deserialized version
    // of the objects used by this application 
    
    /// <summary>
    /// Web request object for image data
    /// </summary>
    class MultipartObject : IMultipartFormSection
    {
        public string sectionName { get; set; }
    
        public byte[] sectionData { get; set; }
    
        public string fileName { get; set; }
    
        public string contentType { get; set; }
    }
    
    /// <summary>
    /// JSON of all Tags existing within the project
    /// contains the list of Tags
    /// </summary> 
    public class Tags_RootObject
    {
        public List<TagOfProject> Tags { get; set; }
        public int TotalTaggedImages { get; set; }
        public int TotalUntaggedImages { get; set; }
    }
    
    public class TagOfProject
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public int ImageCount { get; set; }
    }
    
    /// <summary>
    /// JSON of Tag to associate to an image
    /// Contains a list of hosting the tags,
    /// since multiple tags can be associated with one image
    /// </summary> 
    public class Tag_RootObject
    {
        public List<Tag> Tags { get; set; }
    }
    
    public class Tag
    {
        public string ImageId { get; set; }
        public string TagId { get; set; }
    }
    
    /// <summary>
    /// JSON of images submitted
    /// Contains objects that host detailed information about one or more images
    /// </summary> 
    public class ImageRootObject
    {
        public bool IsBatchSuccessful { get; set; }
        public List<SubmittedImage> Images { get; set; }
    }
    
    public class SubmittedImage
    {
        public string SourceUrl { get; set; }
        public string Status { get; set; }
        public ImageObject Image { get; set; }
    }
    
    public class ImageObject
    {
        public string Id { get; set; }
        public DateTime Created { get; set; }
        public int Width { get; set; }
        public int Height { get; set; }
        public string ImageUri { get; set; }
        public string ThumbnailUri { get; set; }
    }
    
    /// <summary>
    /// JSON of Service Iteration
    /// </summary> 
    public class Iteration
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public bool IsDefault { get; set; }
        public string Status { get; set; }
        public string Created { get; set; }
        public string LastModified { get; set; }
        public string TrainedAt { get; set; }
        public string ProjectId { get; set; }
        public bool Exportable { get; set; }
        public string DomainId { get; set; }
    }
    
    /// <summary>
    /// Predictions received by the Service
    /// after submitting an image for analysis
    /// Includes Bounding Box
    /// </summary>
    public class AnalysisRootObject
    {
        public string id { get; set; }
        public string project { get; set; }
        public string iteration { get; set; }
        public DateTime created { get; set; }
        public List<Prediction> predictions { get; set; }
    }
    
    public class BoundingBox
    {
        public double left { get; set; }
        public double top { get; set; }
        public double width { get; set; }
        public double height { get; set; }
    }
    
    public class Prediction
    {
        public double probability { get; set; }
        public string tagId { get; set; }
        public string tagName { get; set; }
        public BoundingBox boundingBox { get; set; }
    }
    
  6. Před návratem do Unity nezapomeňte uložit změny v sadě Visual Studio.

Kapitola 7 – Vytvoření třídy SpatialMapping

Tato třída nastaví uchycení prostorového mapování ve scéně tak, aby bylo možné detekovat kolize mezi virtuálními objekty a skutečnými objekty.

Vytvoření této třídy:

  1. Klikněte pravým tlačítkem do složky Scripts (Skripty ) a pak klikněte na Create C# Script (Vytvořit>skript jazyka C#). Zavolejte skript SpatialMapping.

  2. Poklikáním na nový skript SpatialMapping ho otevřete v sadě Visual Studio.

  3. Ujistěte se, že nad třídou SpatialMapping odkazujete na následující obory názvů:

    using UnityEngine;
    using UnityEngine.XR.WSA;
    
  4. Potom přidejte následující proměnné do třídy SpatialMapping nad metodu Start():

        /// <summary>
        /// Allows this class to behave like a singleton
        /// </summary>
        public static SpatialMapping Instance;
    
        /// <summary>
        /// Used by the GazeCursor as a property with the Raycast call
        /// </summary>
        internal static int PhysicsRaycastMask;
    
        /// <summary>
        /// The layer to use for spatial mapping collisions
        /// </summary>
        internal int physicsLayer = 31;
    
        /// <summary>
        /// Creates environment colliders to work with physics
        /// </summary>
        private SpatialMappingCollider spatialMappingCollider;
    
  5. Přidejte hodnoty Awake() a Start():

        /// <summary>
        /// Initializes this class
        /// </summary>
        private void Awake()
        {
            // Allows this instance to behave like a singleton
            Instance = this;
        }
    
        /// <summary>
        /// Runs at initialization right after Awake method
        /// </summary>
        void Start()
        {
            // Initialize and configure the collider
            spatialMappingCollider = gameObject.GetComponent<SpatialMappingCollider>();
            spatialMappingCollider.surfaceParent = this.gameObject;
            spatialMappingCollider.freezeUpdates = false;
            spatialMappingCollider.layer = physicsLayer;
    
            // define the mask
            PhysicsRaycastMask = 1 << physicsLayer;
    
            // set the object as active one
            gameObject.SetActive(true);
        }
    
  6. Odstraňte metodu Update().

  7. Před návratem do Unity nezapomeňte uložit změny v sadě Visual Studio.

Kapitola 8 – Vytvoření třídy GazeCursor

Tato třída je zodpovědná za nastavení kurzoru na správné místo v reálném prostoru pomocí SpatialMappingCollider vytvořené v předchozí kapitole.

Vytvoření této třídy:

  1. Klikněte pravým tlačítkem do složky Scripts (Skripty ) a pak klikněte na Create C# Script (Vytvořit>skript jazyka C#). Volání skriptu GazeCursor

  2. Poklikáním na nový skript GazeCursor ho otevřete v sadě Visual Studio.

  3. Ujistěte se, že nad třídou GazeCursor odkazuje následující obor názvů:

    using UnityEngine;
    
  4. Pak přidejte následující proměnnou do třídy GazeCursor nad metodu Start().

        /// <summary>
        /// The cursor (this object) mesh renderer
        /// </summary>
        private MeshRenderer meshRenderer;
    
  5. Aktualizujte metodu Start() následujícím kódem:

        /// <summary>
        /// Runs at initialization right after the Awake method
        /// </summary>
        void Start()
        {
            // Grab the mesh renderer that is on the same object as this script.
            meshRenderer = gameObject.GetComponent<MeshRenderer>();
    
            // Set the cursor reference
            SceneOrganiser.Instance.cursor = gameObject;
            gameObject.GetComponent<Renderer>().material.color = Color.green;
    
            // If you wish to change the size of the cursor you can do so here
            gameObject.transform.localScale = new Vector3(0.01f, 0.01f, 0.01f);
        }
    
  6. Aktualizujte metodu Update() následujícím kódem:

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

    Poznámka

    Nedělejte si starosti s chybou pro třídu SceneOrganiser nebyla nalezena, vytvoříte ji v další kapitole.

  7. Před návratem do Unity nezapomeňte uložit změny v sadě Visual Studio.

Kapitola 9 – Vytvoření třídy SceneOrganiser

Tato třída:

  • Nastavte hlavní kameru tak, že k ní připojíte příslušné komponenty.

  • Když zjistíte objekt, bude zodpovědný za výpočet jeho pozice v reálném světě a umístí popisek značky poblíž s odpovídajícím názvem značky.

Vytvoření této třídy:

  1. Klikněte pravým tlačítkem do složky Scripts (Skripty ) a pak klikněte na Create C# Script (Vytvořit>skript jazyka C#). Pojmenujte skript SceneOrganiser.

  2. Poklikáním na nový skript SceneOrganiser ho otevřete v sadě Visual Studio.

  3. Ujistěte se, že nad třídou SceneOrganiser odkazujete na následující obory názvů:

    using System.Collections.Generic;
    using System.Linq;
    using UnityEngine;
    
  4. Pak přidejte následující proměnné do třídy SceneOrganiser nad metodu Start():

        /// <summary>
        /// Allows this class to behave like a singleton
        /// </summary>
        public static SceneOrganiser Instance;
    
        /// <summary>
        /// The cursor object attached to the Main Camera
        /// </summary>
        internal GameObject cursor;
    
        /// <summary>
        /// The label used to display the analysis on the objects in the real world
        /// </summary>
        public GameObject label;
    
        /// <summary>
        /// Reference to the last Label positioned
        /// </summary>
        internal Transform lastLabelPlaced;
    
        /// <summary>
        /// Reference to the last Label positioned
        /// </summary>
        internal TextMesh lastLabelPlacedText;
    
        /// <summary>
        /// Current threshold accepted for displaying the label
        /// Reduce this value to display the recognition more often
        /// </summary>
        internal float probabilityThreshold = 0.8f;
    
        /// <summary>
        /// The quad object hosting the imposed image captured
        /// </summary>
        private GameObject quad;
    
        /// <summary>
        /// Renderer of the quad object
        /// </summary>
        internal Renderer quadRenderer;
    
  5. Odstraňte metody Start() a Update().

  6. Pod proměnné přidejte metodu Awake(), která inicializuje třídu a nastaví scénu.

        /// <summary>
        /// Called on initialization
        /// </summary>
        private void Awake()
        {
            // Use this class instance as singleton
            Instance = this;
    
            // Add the ImageCapture class to this Gameobject
            gameObject.AddComponent<ImageCapture>();
    
            // Add the CustomVisionAnalyser class to this Gameobject
            gameObject.AddComponent<CustomVisionAnalyser>();
    
            // Add the CustomVisionObjects class to this Gameobject
            gameObject.AddComponent<CustomVisionObjects>();
        }
    
  7. Přidejte metodu PlaceAnalysisLabel(), která vytvoří instanci popisku ve scéně (která je v tomto okamžiku pro uživatele neviditelná). Také umístí čtyřúhelník (také neviditelný) na místo, kde je obrázek umístěn, a překrývá se s reálným světem. To je důležité, protože souřadnice pole načtené ze služby po analýze jsou trasovány zpět do tohoto čtyřúhelníku, aby se určilo přibližné umístění objektu v reálném světě.

        /// <summary>
        /// Instantiate a Label in the appropriate location relative to the Main Camera.
        /// </summary>
        public void PlaceAnalysisLabel()
        {
            lastLabelPlaced = Instantiate(label.transform, cursor.transform.position, transform.rotation);
            lastLabelPlacedText = lastLabelPlaced.GetComponent<TextMesh>();
            lastLabelPlacedText.text = "";
            lastLabelPlaced.transform.localScale = new Vector3(0.005f,0.005f,0.005f);
    
            // Create a GameObject to which the texture can be applied
            quad = GameObject.CreatePrimitive(PrimitiveType.Quad);
            quadRenderer = quad.GetComponent<Renderer>() as Renderer;
            Material m = new Material(Shader.Find("Legacy Shaders/Transparent/Diffuse"));
            quadRenderer.material = m;
    
            // Here you can set the transparency of the quad. Useful for debugging
            float transparency = 0f;
            quadRenderer.material.color = new Color(1, 1, 1, transparency);
    
            // Set the position and scale of the quad depending on user position
            quad.transform.parent = transform;
            quad.transform.rotation = transform.rotation;
    
            // The quad is positioned slightly forward in font of the user
            quad.transform.localPosition = new Vector3(0.0f, 0.0f, 3.0f);
    
            // The quad scale as been set with the following value following experimentation,  
            // to allow the image on the quad to be as precisely imposed to the real world as possible
            quad.transform.localScale = new Vector3(3f, 1.65f, 1f);
            quad.transform.parent = null;
        }
    
  8. Přidejte metodu FinaleLabel(). Zodpovídá za:

    • Nastavení textu popisku pomocí značky Predikce s nejvyšší spolehlivostí
    • Volání výpočtu ohraničujícího rámečku na čtyřúhelníku objektu, umístěného dříve, a umístění popisku do scény.
    • Úprava hloubky popisku pomocí raycastu směrem k ohraničující rámeček, který by měl kolidovat s objektem v reálném světě.
    • Resetování procesu zachycení, aby uživatel mohl zachytit jinou image.
        /// <summary>
        /// Set the Tags as Text of the last label created. 
        /// </summary>
        public void FinaliseLabel(AnalysisRootObject analysisObject)
        {
            if (analysisObject.predictions != null)
            {
                lastLabelPlacedText = lastLabelPlaced.GetComponent<TextMesh>();
                // Sort the predictions to locate the highest one
                List<Prediction> sortedPredictions = new List<Prediction>();
                sortedPredictions = analysisObject.predictions.OrderBy(p => p.probability).ToList();
                Prediction bestPrediction = new Prediction();
                bestPrediction = sortedPredictions[sortedPredictions.Count - 1];
    
                if (bestPrediction.probability > probabilityThreshold)
                {
                    quadRenderer = quad.GetComponent<Renderer>() as Renderer;
                    Bounds quadBounds = quadRenderer.bounds;
    
                    // Position the label as close as possible to the Bounding Box of the prediction 
                    // At this point it will not consider depth
                    lastLabelPlaced.transform.parent = quad.transform;
                    lastLabelPlaced.transform.localPosition = CalculateBoundingBoxPosition(quadBounds, bestPrediction.boundingBox);
    
                    // Set the tag text
                    lastLabelPlacedText.text = bestPrediction.tagName;
    
                    // Cast a ray from the user's head to the currently placed label, it should hit the object detected by the Service.
                    // At that point it will reposition the label where the ray HL sensor collides with the object,
                    // (using the HL spatial tracking)
                    Debug.Log("Repositioning Label");
                    Vector3 headPosition = Camera.main.transform.position;
                    RaycastHit objHitInfo;
                    Vector3 objDirection = lastLabelPlaced.position;
                    if (Physics.Raycast(headPosition, objDirection, out objHitInfo, 30.0f,   SpatialMapping.PhysicsRaycastMask))
                    {
                        lastLabelPlaced.position = objHitInfo.point;
                    }
                }
            }
            // Reset the color of the cursor
            cursor.GetComponent<Renderer>().material.color = Color.green;
    
            // Stop the analysis process
            ImageCapture.Instance.ResetImageCapture();        
        }
    
  9. Přidejte metodu CalculateBoundingBoxPosition(), která hostuje řadu výpočtů potřebných k překladu souřadnic ohraničujícího rámečku načtených ze služby a jejich proporcionálnímu vytvoření na čtyřúhelníku.

        /// <summary>
        /// This method hosts a series of calculations to determine the position 
        /// of the Bounding Box on the quad created in the real world
        /// by using the Bounding Box received back alongside the Best Prediction
        /// </summary>
        public Vector3 CalculateBoundingBoxPosition(Bounds b, BoundingBox boundingBox)
        {
            Debug.Log($"BB: left {boundingBox.left}, top {boundingBox.top}, width {boundingBox.width}, height {boundingBox.height}");
    
            double centerFromLeft = boundingBox.left + (boundingBox.width / 2);
            double centerFromTop = boundingBox.top + (boundingBox.height / 2);
            Debug.Log($"BB CenterFromLeft {centerFromLeft}, CenterFromTop {centerFromTop}");
    
            double quadWidth = b.size.normalized.x;
            double quadHeight = b.size.normalized.y;
            Debug.Log($"Quad Width {b.size.normalized.x}, Quad Height {b.size.normalized.y}");
    
            double normalisedPos_X = (quadWidth * centerFromLeft) - (quadWidth/2);
            double normalisedPos_Y = (quadHeight * centerFromTop) - (quadHeight/2);
    
            return new Vector3((float)normalisedPos_X, (float)normalisedPos_Y, 0);
        }
    
  10. Před návratem do Unity nezapomeňte uložit změny v sadě Visual Studio.

    Důležité

    Než budete pokračovat, otevřete třídu CustomVisionAnalyser a v metodě AnalyzeLastImageCaptured()zrušte komentář následujících řádků:

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

Poznámka

Nedělejte si starosti se zprávou Třídy ImageCapture "nepodařilo se najít", vytvoříte ji v další kapitole.

Kapitola 10 – Vytvoření třídy ImageCapture

Další třídou, kterou vytvoříte, je třída ImageCapture .

Tato třída zodpovídá za:

  • Zachycení obrázku pomocí kamery HoloLens a jeho uložení do složky Aplikace
  • Zpracování gest klepnutí od uživatele.

Vytvoření této třídy:

  1. Přejděte do složky Scripts , kterou jste vytvořili dříve.

  2. Klikněte pravým tlačítkem myši do složky a pak klikněte na Vytvořit>skript jazyka C#. Pojmenujte skript ImageCapture.

  3. Poklikáním na nový skript ImageCapture ho otevřete v sadě Visual Studio.

  4. Obory názvů v horní části souboru nahraďte následujícím kódem:

    using System;
    using System.IO;
    using System.Linq;
    using UnityEngine;
    using UnityEngine.XR.WSA.Input;
    using UnityEngine.XR.WSA.WebCam;
    
  5. Pak přidejte následující proměnné do třídy ImageCapture nad metodu Start():

        /// <summary>
        /// Allows this class to behave like a singleton
        /// </summary>
        public static ImageCapture Instance;
    
        /// <summary>
        /// Keep counts of the taps for image renaming
        /// </summary>
        private int captureCount = 0;
    
        /// <summary>
        /// Photo Capture object
        /// </summary>
        private PhotoCapture photoCaptureObject = null;
    
        /// <summary>
        /// Allows gestures recognition in HoloLens
        /// </summary>
        private GestureRecognizer recognizer;
    
        /// <summary>
        /// Flagging if the capture loop is running
        /// </summary>
        internal bool captureIsActive;
    
        /// <summary>
        /// File path of current analysed photo
        /// </summary>
        internal string filePath = string.Empty;
    
  6. Teď je potřeba přidat kód pro metody Awake() a 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();
        }
    
  7. Implementujte obslužnou rutinu, která se bude volat, když dojde k gestu klepnutí:

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

    Důležité

    Když je kurzor zelený, znamená to, že kamera je k dispozici pro pořízení snímku. Když je kurzor červený, znamená to, že kamera je zaneprázdněná.

  8. Přidejte metodu, kterou aplikace použije ke spuštění procesu zachycení image a uložení image:

        /// <summary>
        /// Begin process of image capturing and send to Azure Custom Vision Service.
        /// </summary>
        private void ExecuteImageCaptureAndAnalysis()
        {
            // Create a label in world space using the ResultsLabel class 
            // Invisible at this point but correctly positioned where the image was taken
            SceneOrganiser.Instance.PlaceAnalysisLabel();
    
            // Set the camera resolution to be the highest possible
            Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending
                ((res) => res.width * res.height).First();
            Texture2D targetTexture = new Texture2D(cameraResolution.width, cameraResolution.height);
    
            // Begin capture process, set the image format
            PhotoCapture.CreateAsync(true, delegate (PhotoCapture captureObject)
            {
                photoCaptureObject = captureObject;
    
                CameraParameters camParameters = new CameraParameters
                {
                    hologramOpacity = 1.0f,
                    cameraResolutionWidth = targetTexture.width,
                    cameraResolutionHeight = targetTexture.height,
                    pixelFormat = CapturePixelFormat.BGRA32
                };
    
                // Capture the image from the camera and save it in the App internal folder
                captureObject.StartPhotoModeAsync(camParameters, delegate (PhotoCapture.PhotoCaptureResult result)
                {
                    string filename = string.Format(@"CapturedImage{0}.jpg", captureCount);
                    filePath = Path.Combine(Application.persistentDataPath, filename);          
                    captureCount++;              
                    photoCaptureObject.TakePhotoAsync(filePath, PhotoCaptureFileOutputFormat.JPG, OnCapturedPhotoToDisk);              
                });
            });
        }
    
  9. Přidejte obslužné rutiny, které se budou volat, jakmile bude fotka zachycena a kdy je připravená k analýze. Výsledek se pak předá do CustomVisionAnalyser k analýze.

        /// <summary>
        /// Register the full execution of the Photo Capture. 
        /// </summary>
        void OnCapturedPhotoToDisk(PhotoCapture.PhotoCaptureResult result)
        {
            try
            {
                // Call StopPhotoMode once the image has successfully captured
                photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
            }
            catch (Exception e)
            {
                Debug.LogFormat("Exception capturing photo to disk: {0}", e.Message);
            }
        }
    
        /// <summary>
        /// The camera photo mode has stopped after the capture.
        /// Begin the image analysis process.
        /// </summary>
        void OnStoppedPhotoMode(PhotoCapture.PhotoCaptureResult result)
        {
            Debug.LogFormat("Stopped Photo Mode");
    
            // Dispose from the object in memory and request the image analysis 
            photoCaptureObject.Dispose();
            photoCaptureObject = null;
    
            // Call the image analysis
            StartCoroutine(CustomVisionAnalyser.Instance.AnalyseLastImageCaptured(filePath)); 
        }
    
        /// <summary>
        /// Stops all capture pending actions
        /// </summary>
        internal void ResetImageCapture()
        {
            captureIsActive = false;
    
            // Set the cursor color to green
            SceneOrganiser.Instance.cursor.GetComponent<Renderer>().material.color = Color.green;
    
            // Stop the capture loop if active
            CancelInvoke();
        }
    
  10. Před návratem do Unity nezapomeňte uložit změny v sadě Visual Studio.

Kapitola 11 – Nastavení skriptů ve scéně

Teď, když jste napsali veškerý kód potřebný pro tento projekt, je čas nastavit skripty ve scéně a na předfabách, aby se chovaly správně.

  1. V Editoru Unity na panelu hierarchie vyberte hlavní kameru.

  2. Na panelu inspektoru s vybranou hlavní kamerou klikněte na Přidat komponentu, vyhledejte skript SceneOrganiser a poklikáním ho přidejte.

    Snímek obrazovky se skriptem SceneOrganizer

  3. Na panelu projektu otevřete složku Prefabs a přetáhněte prefabLabel do vstupní oblasti Popisek prázdného cílového odkazu ve skriptu SceneOrganiser , který jste právě přidali do hlavní kamery, jak je znázorněno na následujícím obrázku:

    Snímek obrazovky znázorňující skript, který jste přidali do hlavní kamery

  4. Na panelu hierarchie vyberte podřízený objekt GazeCursorhlavní kamery.

  5. Na panelu inspektoru s vybranou možností GazeCursor klikněte na Přidat komponentu, vyhledejte skript GazeCursor a poklikáním ho přidejte.

    Snímek obrazovky, který ukazuje, kam přidáte skript GazeCursor

  6. Znovu na panelu hierarchie vyberte podřízenou položku SpatialMappinghlavní kamery.

  7. Na panelu inspektoru s vybranou možností SpatialMapping klikněte na Přidat komponentu, vyhledejte skript SpatialMapping a poklikáním ho přidejte.

    Snímek obrazovky, který ukazuje, kam přidáte skript SpatialMapping

Zbývající skripty, které jste nenastavili, budou přidány kódem ve skriptu SceneOrganiser za běhu.

Kapitola 12 - Před budovou

Pokud chcete provést důkladný test aplikace, budete ji muset bokem načíst do Microsoft HoloLens.

Než to uděláte, ujistěte se, že:

  • Všechna nastavení uvedená v kapitole 3 jsou nastavena správně.

  • Skript SceneOrganiser je připojen k objektu Main Camera .

  • Skript GazeCursor je připojen k objektu GazeCursor .

  • Skript SpatialMapping je připojen k objektu SpatialMapping .

  • V kapitole 5 krok 6:

    • Nezapomeňte vložit klíč predikce služby do proměnné predictionKey .
    • Do třídy predictionEndpoint jste vložili koncový bod předpovědi.

Kapitola 13 – Sestavení řešení PRO UPW a zkušební načtení aplikace

Teď jste připraveni sestavit aplikaci jako řešení PRO UPW, které budete moct nasadit do Microsoft HoloLens. Zahájení procesu sestavení:

  1. Přejděte na Nastavení sestavení souboru>.

  2. Zaškrtněte políčko Projekty Unity C#.

  3. Klikněte na Přidat otevřené scény. Tím se do sestavení přidá aktuálně otevřená scéna.

    Snímek obrazovky, který zvýrazňuje tlačítko Přidat otevřené scény

  4. Klikněte na Sestavit. Unity spustí Průzkumník souborů okno, ve kterém musíte vytvořit a pak vybrat složku, do které chcete aplikaci sestavit. Vytvořte teď složku a pojmenujte ji App. Potom vyberte složku Aplikace a klikněte na Vybrat složku.

  5. Unity začne vytvářet projekt do složky Aplikace .

  6. Jakmile Unity dokončí sestavování (může to nějakou dobu trvat), otevře se okno Průzkumník souborů v umístění vašeho buildu (zkontrolujte hlavní panel, protože se nemusí vždy zobrazovat nad vašimi okny, ale upozorní vás na přidání nového okna).

  7. Pokud chcete provést nasazení do Microsoft HoloLens, budete potřebovat IP adresu tohoto zařízení (pro vzdálené nasazení) a zajistit, že má také nastavený režim vývojáře. Použijte následující postup:

    1. Při nošení HoloLensu otevřete Nastavení.

    2. Přejděte do částiRozšířené možnostiwi-fi>sítě & internetu>.

    3. Poznamenejte si IPv4 adresu.

    4. Pak přejděte zpět na Nastavení a pak na Aktualizovat zabezpečení &>pro vývojáře.

    5. Nastavte vývojářský režimna zapnuto.

  8. Přejděte do nového sestavení Unity (složka Aplikace ) a otevřete soubor řešení v sadě Visual Studio.

  9. V části Konfigurace řešení vyberte Ladit.

  10. Na platformě řešení vyberte x86, Vzdálený počítač. Zobrazí se výzva k vložení IP adresy vzdáleného zařízení (v tomto případě Microsoft HoloLens, kterou jste si poznamenali).

    Snímek obrazovky, který ukazuje, kam vložit IP adresu

  11. Přejděte do nabídky Sestavení a klikněte na Nasadit řešení a načtěte aplikaci do HoloLensu bokem.

  12. Vaše aplikace by se teď měla zobrazit v seznamu nainstalovaných aplikací na vašem Microsoft HoloLens a měla by být připravená ke spuštění.

Použití aplikace:

  • Podívejte se na objekt, který jste vytrénovali pomocí služby Azure Custom Vision a rozpoznávání objektů, a použijte gesto klepnutí.
  • Pokud se objekt úspěšně rozpozná, zobrazí se světoprostorový text popisku s názvem značky.

Důležité

Pokaždé, když pořídíte fotku a odešlete ji službě, můžete se vrátit na stránku Služba a znovu natrénovat službu s nově zachycenými obrázky. Na začátku budete pravděpodobně také muset opravit ohraničující rámečky , aby byly přesnější a přetrénovat službu.

Poznámka

Umístěný text popisku se nemusí zobrazit v blízkosti objektu, pokud Microsoft HoloLens senzory a/nebo SpatialTrackingComponent v Unity nedokáže umístit příslušné kolidéry vzhledem k objektům reálného světa. Pokud tomu tak je, zkuste aplikaci použít na jiném povrchu.

Vaše aplikace Custom Vision, Rozpoznávání objektů

Blahopřejeme, vytvořili jste aplikaci hybridní reality, která využívá azure Custom Vision rozhraní API pro detekci objektů, které dokáže rozpoznat objekt z obrázku a pak poskytnout přibližnou pozici objektu v 3D prostoru.

Snímek obrazovky znázorňující aplikaci hybridní reality, která využívá azure Custom Vision rozhraní API pro detekci objektů

Bonusová cvičení

Cvičení 1

Přidáním do textového popisku pomocí poloprůhledné datové krychle zabalte skutečný objekt do 3D ohraničujícího rámečku.

Cvičení 2

Trénujte službu Custom Vision Service tak, aby rozpoznala více objektů.

Cvičení 3

Přehraje zvuk, když je objekt rozpoznán.

Cvičení 4

Pomocí rozhraní API znovu vytrénujte službu se stejnými obrázky, které vaše aplikace analyzuje, aby byla služba přesnější (proveďte predikci i trénování současně).