HoloLens (1. generációs) és az Azure 302b: Custom Vision


Megjegyzés

A Mixed Reality Academy oktatóanyagai HoloLens (1. generációs) és Mixed Reality Modern headsetekkel készültek. Ezért fontos, hogy ezeket az oktatóanyagokat a helyére helyezzük azoknak a fejlesztőknek, akik továbbra is útmutatást keresnek az eszközök fejlesztéséhez. Ezek az oktatóanyagok nem frissülnek a 2. HoloLens eszközökkel. A rendszer fenntartja őket, hogy továbbra is a támogatott eszközökön dolgoznak. A jövőben egy új oktatóanyag-sorozat lesz közzétéve, amely bemutatja, hogyan fejleszthet a 2. HoloLens való fejlesztéshez. Ez az értesítés frissülni fog az oktatóanyagokra mutató hivatkozással, amikor közzé lesznek téve.


Ebben a kurzusban megtanulja, hogyan ismerhető fel egyéni vizualizációs tartalom egy megadott képen belül az Azure Custom Vision vegyes valóságú alkalmazásokban való használatával.

Ez a szolgáltatás lehetővé teszi, hogy objektumképekkel betanítsa a gépi tanulási modellt. Ezután a betanított modell használatával felismeri a hasonló objektumokat, amelyek a kamerafelvétel alapján Microsoft HoloLens vagy a számítógéphez csatlakoztatott kamera segítségével modern (VR-) headsetek készíthetőek.

kurzus eredménye

Az Azure Custom Vision egy Microsoft Cognitive Services-szolgáltatás, amellyel a fejlesztők egyéni képosztályozókat hozhatnak létre. Ezek az osztályozók ezután új képekkel használhatók az új képeken belüli objektumok felismerésére vagy besorolására. A Szolgáltatás egy egyszerű, könnyen használható online portált biztosít a folyamat egyszerűsítése érdekében. További információért látogasson el az Azure Custom Vision szolgáltatás oldalára.

A kurzus elvégzése után egy vegyes valóságon áteső alkalmazással fog dolgozni, amely két módban fog működni:

  • Elemzési mód: a Custom Vision szolgáltatás manuális beállítása képek feltöltésével, címkék létrehozásával és a szolgáltatás különböző objektumok felismerésére való betanításával (ebben az esetben az egérrel és billentyűzettel). Ezután létre fog hozni egy HoloLens, amely képeket rögzít a kamerával, és megpróbálja felismerni ezeket az objektumokat a való világban.

  • Betanítás mód: olyan kódot implementálja, amely engedélyezi a "Betanítás módot" az alkalmazásban. A betanítás mód lehetővé teszi, hogy a HoloLens kamerájával rögzítsen képeket, feltöltse a rögzített képeket a Szolgáltatásba, és betanítsa az egyéni látásmodellt.

Ez a kurzus bemutatja, hogyan lehet az eredményeket a Custom Vision Service-ból egy Unity-alapú mintaalkalmazásba behozni. Ezeket a fogalmakat Egy ön által létrehozott egyéni alkalmazásra kell alkalmaznia.

Eszköztámogatás

Tanfolyam HoloLens Modern headsetek
MR és Azure 302b: Custom Vision ✔️ ✔️

Megjegyzés

Bár ez a kurzus elsősorban a HoloLens foglalkozik, az ebben a kurzusban tanultak használhatja a Windows Mixed Reality (VR-) headsetek kezeléséhez. Mivel a modern (VR-) headsetek nem érhetők el kamerákkal, a számítógéphez csatlakoztatott külső kamerára lesz szükség. Ahogy követi a tanfolyamot, jegyzeteket fog látni a modern (VR-) headsetek támogatásához esetlegesen szükséges módosításokról.

Előfeltételek

Megjegyzés

Ez az oktatóanyag olyan fejlesztők számára készült, akik alapszintű tapasztalattal rendelkeznek a Unity és a C# használatában. Vegye figyelembe azt is, hogy a dokumentum előfeltételei és írásos utasításai a cikk írásakor (2018. július) tesztelt és ellenőrzött adatokat jelölik. Használhatja a legújabb szoftvereket az eszközök telepítésével kapcsolatos cikkben felsoroltak szerint, de nem szabad feltételezni, hogy a kurzusban szereplő információk tökéletesen megegyeznek az újabb szoftverekben található információkkal, mint az alább felsoroltak.

Ehhez a kurzushoz a következő hardvereket és szoftvereket javasoljuk:

Előkészületek

  1. A projekt létrehozásakor felmerülő problémák elkerülése érdekében határozottan javasoljuk, hogy az oktatóanyagban említett projektet egy gyökér- vagy gyökérmappában hozza létre (a hosszú mappák elérési útjai problémákat okozhatnak a létrehozáskor).
  2. Állítsa be és tesztelje a HoloLens. Ha támogatásra van szüksége a HoloLens beállításához, látogasson el a HoloLens cikkre.
  3. Egy új alkalmazás fejlesztésekor jó ötlet lehet a bevetés és az érzékelő finomhangolása (néha segíthet az egyes felhasználóknál elvégezni ezeket HoloLens a feladatokat).

Ha segítségre van szüksége a hibaelrehozásról, kövesse ezt a hivatkozást, amely a HoloLens cikkre hivatkozik.

Ha segítségre van szüksége az érzékelő hangolásával, kövesse ezt a hivatkozást, amely a HoloLens Sensor Tuning (Érzékelő hangolása) cikkben található.

1. fejezet – A Custom Vision Service Portal

A Custom Vision Szolgáltatás Azure-ban való használatához konfigurálnia kell a szolgáltatás egy példányát, hogy elérhető legyen az alkalmazás számára.

  1. Először lépjen a Custom Vision szolgáltatás főoldalára.

  2. Kattintson a Első lépések gombra.

    A Custom Vision első lépések

  3. Jelentkezzen be a Custom Vision Service Portalra.

    Bejelentkezés a portálra

    Megjegyzés

    Ha még nem rendelkezik Azure-fiókkal, létre kell hoznia egyet. Ha osztályterem- vagy laborhelyzetben követi ezt az oktatóanyagot, kérjen segítséget az oktatótól vagy az egyik vezetőtől az új fiók beállításában.

  4. Miután először bejelentkezett, a rendszer kérni fogja a Szolgáltatási feltételek panelt. Kattintson a jelölőnégyzetre a feltételeknek való megegyezéshez. Ezután kattintson az Elfogadom gombra.

    Szolgáltatási feltételek

  5. Miután elfogadta a feltételeket, a portál Projektek szakasza lesz elérhető. Kattintson az Új Project elemre.

    Új projekt létrehozása

  6. A jobb oldalon megjelenik egy lap, amely kérni fogja, hogy adjon meg néhány mezőt a projekthez.

    1. Szúrjon be egy Nevet a projekthez.

    2. Szúrja be a projekt leírását (nem kötelező).

    3. Válasszon ki egy erőforráscsoportot, vagy hozzon létre egy újat. Az erőforráscsoportokkal monitorzhatja, szabályozhatja a hozzáférést, kiépítheti és kezelheti az Azure-beli eszközök gyűjteményének számlázását. Javasoljuk, hogy az összes Azure-szolgáltatást egyetlen projekthez (például ezekhez a tanfolyamokhoz) egy közös erőforráscsoportban tartsa.

    4. A típus Project besorolásra

    5. A Tartományok beállításnál adja meg a Következőt: Általános.

      A tartományok beállítása

      Ha többet szeretne tudni az Azure-erőforráscsoportokról, tekintse meg az erőforráscsoporttal kapcsolatos cikket.

  7. Ha elkészült, kattintson a Create project(Projekt létrehozása) elemre, és a rendszer átirányítja a Custom Vision Service, project (Szolgáltatás, projekt) lapra.

2. fejezet – A Custom Vision betanítás

Miután a Custom Vision, az elsődleges cél a projekt betanítása adott objektumok felismerésére a képeken. Legalább öt (5) képre van szükség, bár előnyben részesítik a tíz (10) előnyben részesített objektumot minden egyes objektumhoz, amit az alkalmazás felismer. Használhatja az ebben a kurzusban (számítógépes egérrel és billentyűzettel) biztosított képeket.

A Custom Vision Service-projekt betanítás:

  1. Kattintson a + Címkék melletti gombra.

    Új címke hozzáadása

  2. Adja meg annak az objektumnak a nevét, amely felismerhető. Kattintson a Mentés gombra.

    Objektumnév hozzáadása és mentése

  3. Figyelje meg, hogy a címke hozzá lett adva (előfordulhat, hogy újra be kell töltenie az oldalt, hogy megjelenjen). Ha még nincs bejelölve, kattintson az új címke mellett található jelölőnégyzetre.

    Új címke engedélyezése

  4. Kattintson az oldal közepén található Képek hozzáadása elemre.

    Képek hozzáadása

  5. Kattintson a Helyi fájlok tallózása elemre, keresse meg, majd válassza ki a feltölteni kívánt képeket úgy, hogy a minimális érték öt (5). Ne feledje, hogy az összes képnek tartalmaznia kell a betanított objektumot.

    Megjegyzés

    Egyszerre több képet is kiválaszthat a feltöltéshez.

  6. Ha látja a képeket a lapon, válassza ki a megfelelő címkét a Saját címkék mezőben.

    Címkék kiválasztása

  7. Kattintson a Fájlok feltöltése elemre. A fájlok feltöltése megkezdődik. Ha megerősítést kap a feltöltésről, kattintson a Kész gombra.

    Fájlok feltöltése

  8. Ismételje meg ugyanezt a folyamatot egy új, Billentyűzet nevű címke létrehozásához, és töltse fel hozzá a megfelelő fényképeket. Az új címkék létrehozása után törölje az Egér jelölőnégyzet jelölését, hogy meg tudja mutatni a Képek hozzáadása ablakot.

  9. Ha mindkét címkét beállította, kattintson a Betanítás elemre, és az első betanítási iteráció elkezdi a építést.

    Betanítási iteráció engedélyezése

  10. A felépítés után két gombot láthat: Make default (Alapértelmezettként) és Prediction URL (Előrejelzési URL-cím). Először kattintson az Alapértelmezett beállítás, majd az Előrejelzési URL elemre.

    Alapértelmezett és előrejelzési URL-cím beállítása

    Megjegyzés

    Az ebből a címből megadott végponti URL-cím az alapértelmezettként megjelölt iterációra van beállítva. Ezért ha később új iterációt hoz létre, és alapértelmezés szerint frissíti, nem kell módosítania a kódot.

  11. Miután a Prediction URL(Előrejelzés URL-címe) elemre kattintott, nyissa meg a Jegyzettömb, majd másolja és illessze be az URL-címet és a Prediction-Key (Előrejelzési kulcs) adatokat, hogy később szükség szerint lekérhető legyen a kódban.

    URL-cím és url-cím Prediction-Key

  12. Kattintson a fogara a képernyő jobb felső részén.

    Kattintson a fogaras ikonra a beállítások megnyitásához

  13. Másolja ki a betanítókulcsot, és illessze be egy Jegyzettömb, későbbi használatra.

    Betanítás kulcsának másolása

  14. Másolja ki a Project azonosítóját is, és illessze be a Jegyzettömb fájlba későbbi használatra.

    Projektazonosító másolása

3. fejezet – A Unity-projekt beállítása

Az alábbiakban egy tipikus, vegyes valóságban való fejlesztésre vonatkozó beállítás van beállítva, és mint ilyen, jó sablon más projektekhez.

  1. Nyissa meg a Unityt, és kattintson a New (Új) elemre.

    Új Unity-projekt létrehozása

  2. Most meg kell adnia egy Unity-projekt nevét. Az AzureCustomVision beszúrása. Győződjön meg arról, hogy a projektsablon beállítása 3D. A Hely beállításnál adja meg az Önnek megfelelő helyet (ne feledje, hogy a gyökérkönyvtárakhoz közelebb is jobb). Ezután kattintson a Create project (Projekt létrehozása) elemre.

    Projektbeállítások konfigurálása

  3. Ha a Unity meg van nyitva, érdemes ellenőrizni, hogy az alapértelmezett Script Editor értéke Visual Studio. Lépjen a Beállítások > szerkesztése lapra, majd az új ablakban lépjen a Külső eszközök elemre. Módosítsa a Külső szkriptszerkesztőt a 2017 Visual Studio re. Zárja be a Beállítások ablakot.

    Külső eszközök konfigurálása

  4. Ezután a File > Build Gépház (Build Gépház) menüben válassza az Universal Windows Platform (Univerzális Windows platform) lehetőséget, majd kattintson a Switch Platform (Platformváltás) gombra a választás alkalmazáshoz.

    Buildbeállítások konfigurálása

  5. Amíg továbbra is a Fájl > Build Gépház, és győződjön meg a következőről:

    1. A Céleszköz beállítása HoloLens

      A modern headsetek számára a Target Device (Céleszköz) beállításnál adja meg a Any Device (Bármely eszköz) beállítását.

    2. A Build Type (Build típusa) beállítása D3D

    3. Az SDK a Legújabb telepítve beállításra van állítva

    4. Visual Studio Verziója a Legújabb telepítve beállításra van állítva

    5. A Build and Run (Összeállítás és futtatás) beállítása Local Machine (Helyi gép)

    6. Mentse a jelenet, és adja hozzá a buildhez.

      1. Ehhez válassza az Add Open Scenes (Nyitott jelenetek hozzáadása) gombra. Megjelenik egy Mentés ablak.

        Nyitott jelenet hozzáadása a buildlistához

      2. Hozzon létre egy új mappát ehhez és minden jövőbeli jelenethez, majd válassza az Új mappa gombot egy új mappa létrehozásához, és adja neki a Scenes nevet.

        Új jelenetmappa létrehozása

      3. Nyissa meg az újonnan létrehozott Scenes mappát, majd a Fájlnév: szövegmezőbe írja be a CustomVisionScene nevet, majd kattintson a Mentés gombra.

        Új jelenetfájl elnevezése

        Vegye figyelembe, hogy a Unity-jeleneteket az Assets mappába kell mentenie, mivel a Unity-projekthez kell őket társítanunk. A scenes mappa (és más hasonló mappák) létrehozása egy Unity-projekt strukturálható tipikus módja.

    7. A Build Gépház többi beállítását az alapértelmezett értéken kell hagynunk.

      Alapértelmezett buildbeállítások

  6. A Build Gépház ablakban kattintson a Player Gépház gombra, ezzel megnyitja a kapcsolódó panelt abban a térben, ahol az Inspector található.

  7. Ezen a panelen ellenőrizni kell néhány beállítást:

    1. Az Egyéb Gépház lapon:

      1. A parancsprogram-futtatás verziójának Kísérleti (.NET 4.6-beli megfelelő) verziónak kell lennie, ami kiváltja a szerkesztő újraindításának szükségét.

      2. A szkriptek háttérrendszerének .NET-nek kell lennie

      3. Az API kompatibilitási szintjének .NET 4.6-nak kell lennie

      API-kompatibilitás beállítása

    2. A Közzétételi lap Gépház a Képességek alatt ellenőrizze a következőt:

      1. InternetClient

      2. Webkamera

      3. Mikrofon

      Közzétételi beállítások konfigurálása

    3. A panelen lejjebb, az XR Gépház (a Publish Gépház alatt található) jelölje be a Virtual Reality supported (Virtuális valóság támogatása) jelölőnégyzetet, és győződjön meg arról, hogy az Windows Mixed Reality SDK hozzá van adva.

    XR-beállítások konfigurálása

  8. A Build Gépház Unity C # Projects már nem szürkül ki, jelölje be a mellette látható jelölőnégyzetet.

  9. Zárja be a Build Gépház ablakot.

  10. Mentse a Scene and project (FILE > SAVE SCENE /FILE > SAVE PROJECT) (A SAVE PROJECT (> PROJEKT MENTÉSE) fájlt.

4. fejezet – A Newtonsoft DLL importálása a Unityben

Fontos

Ha ki szeretné hagyni enneka kurzusnak a Unity beállítási összetevőjét, és rögtön a kódba szeretne ugrani, nyugodtan töltse le ezt az Azure-MR-302b.unitypackagecsomagot, importálja a projektbe Egyéni csomagként, majd folytassa a 6.fejezetből.

Ehhez a kurzushoz a Newtonsoft kódtárat kell használni, amelyet DLL-ként adhat hozzá az eszközökhez. A kódtárat tartalmazó csomag innen tölthető le:. A Newtonsoft kódtárnak a projektbe való importálásához használja a kurzushoz készült Unity-csomagot.

  1. Adja hozzá a .unitypackage-et a Unityhez az Assets > Import Package > Custom Package menüelem használatával.

  2. A felugró Import Unity Package (Import Unity-csomag) mezőben győződjön meg arról, hogy minden elem ki van jelölve a beépülő modulok alatt (és beleértve a beépülő modulokat is).

    Az összes csomagelemek importálása

  3. Kattintson az Importálás gombra az elemek projekthez való hozzáadásához.

  4. A projektnézet Beépülő modulok mappájában, a Newtonsoft mappában válassza ki aNewtonsoft.Jsbeépülő modult.

    Válassza a Newtonsoft beépülő modul lehetőséget

  5. Ha a Newtonsoft.Js a beépülő modulon jelölőnégyzet be van jelölve, győződjön meg arról, hogy a Bármely platform jelölőnégyzet nincs bejelölve, majd győződjön meg arról, hogy a WSAPlayer is be van jelölve, majd kattintson az Apply (Alkalmaz) gombra. Ez csak a fájlok megfelelő konfigurálásának megerősítésére való.

    A Newtonsoft beépülő modul konfigurálása

    Megjegyzés

    Ezeknek a beépülő moduloknak a megjelölése úgy konfigurálja őket, hogy csak a Unity-szerkesztőben használják őket. Ezek egy másik készlete található a WSA mappában, amelyet a rendszer a projekt Unityből való exportálása után fog használni.

  6. Ezután meg kell nyitnia a WSA mappát a Newtonsoft mappában. Láthatja ugyanannak a fájlnak a másolatát, amit az előbb konfigurált. Válassza ki a fájlt, majd a vizsgálóban győződjön meg arról, hogy

    • Bármely platform jelölőnégyzete nincs bejelölve
    • csak a WSAPlayer van bejelölve
    • A Ne folyamat be van jelölve

    A Newtonsoft beépülő modul platformbeállításának konfigurálása

5. fejezet – Kamera beállítása

  1. A Hierarchia panelen válassza a Main Camera (Fő kamera) lehetőséget.

  2. Miután kiválasztotta, a Main Camera összes összetevője jelölve lesz az Inspector panelen.

    1. A kameraobjektumnak Main Camera (Main Camera) nevet kell adnunk (jegyezze fel a helyesírást!)

    2. A Main Camera Tag (Main Camera Tag) címkét MainCamera (jegyezze fel a helyesírást!)

    3. Győződjön meg arról, hogy az Átalakítás pozíciója 0, 0, 0

    4. Állítsa a Clear Flags (Átlátszó jelzők) jelölőt Folytonos színre (ezt hagyja figyelmen kívül a modern headsetek használatakor).

    5. Állítsa a kamera-összetevő háttérszínét Fekete, Alfa 0 (Hexikus kód: #00000000) (ezt hagyja figyelmen kívül a modern headsetek használatakor).

    Kamera összetevő tulajdonságainak konfigurálása

6. fejezet – A CustomVisionAnalyser osztály létrehozása.

Most már készen áll arra, hogy kódot írjon.

A CustomVisionAnalyser osztálysal fog kezdeni.

Megjegyzés

Az alábbi kódban Custom Vision az Custom Vision REST API. Ennek használatával látni fogja, hogyan valósíthatja meg és használhatja ezt az API-t (hasznos lehet megérteni, hogyan valósíthat meg valami ehhez hasonlót saját maga). Vegye figyelembe, hogy a Microsoft egy Custom Vision Service SDK-t kínál, amely a szolgáltatás hívására is használható. További információt a Custom Vision Service SDK-val kapcsolatos cikkben talál.

Ez az osztály a következőért felelős:

  • A rögzített legújabb kép betöltése bájttömbként.

  • A bájttömb elküldése az Azure Custom Vision Service-példánynak elemzés céljából.

  • A válasz JSON-sztringként lesz fogadva.

  • A válasz deszerializálása és az eredményül kapott előrejelzés átadása a SceneOrganiser osztálynak, amely gondoskodik a válasz megjelenítéséről.

Az osztály létrehozása:

  1. Kattintson a jobb gombbal az Eszközmappa elemre a Project panelen, majd kattintson a Create > Folder (Mappa létrehozása) elemre. Hívja meg a scripts mappát.

    Parancsfájlok mappa létrehozása

  2. Kattintson duplán a most létrehozott mappára a megnyitásához.

  3. Kattintson a jobb gombbal a mappában, majd kattintson a Create C Script > (C-szkript # létrehozása) parancsra. A szkriptnek nevezze el a CustomVisionAnalyser nevet.

  4. Kattintson duplán az új CustomVisionAnalyser szkriptre, hogy az a következővel Visual Studio.

  5. Frissítse a fájl tetején található névtereket, hogy megegyeznek a következővel:

    using System.Collections;
    using System.IO;
    using UnityEngine;
    using UnityEngine.Networking;
    using Newtonsoft.Json;
    
  6. A CustomVisionAnalyser osztályban adja hozzá a következő változókat:

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

    Megjegyzés

    Győződjön meg arról, hogy beszúrta az előrejelzési kulcsot a predictionKey változóba és az előrejelzési végpontot a predictionEndpoint változóba. Ezeket a kurzus korábbi Jegyzettömb átmásolta a korábbiakba.

  7. Az Új() függvény kódját hozzá kell adni a Instance változó inicializálása érdekében:

        /// <summary>
        /// Initialises this class
        /// </summary>
        private void Awake()
        {
            // Allows this instance to behave like a singleton
            Instance = this;
        }
    
  8. Törölje a Start() és az Update() metódust.

  9. Ezután adja hozzá a coroutine-t (a statikus GetImageAsByteArray() metódussal alatta), amely az ImageCapture osztály által rögzített képelemzés eredményeit fogja kapni.

    Megjegyzés

    A Egy olyan Hívás a SceneOrganiser osztályhoz, amely még létre van hozva, a EgyEndimageCapture coroutine-ban van. Ezért most hagyja megjegyzésként ezeket a sorokat.

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

7. fejezet – A CustomVisionObjects osztály létrehozása

Most a CustomVisionObjects osztályt fogja létrehozni.

Ez a szkript számos objektumot tartalmaz, amelyet más osztályok a szolgáltatáshoz való hívás szerializálásához és deszerializálásához Custom Vision használnak.

Figyelmeztetés

Fontos, hogy jegyezze fel az Custom Vision Service által elérhető végpontot, mivel az alábbi JSON-struktúra úgy lett beállítva, hogy működjön Custom Vision Prediction v2.0-val. Ha más verzióval rendelkezik, előfordulhat, hogy frissítenie kell az alábbi struktúrát.

Az osztály létrehozása:

  1. Kattintson a jobb gombbal a Scripts (Szkriptek) mappára, majd kattintson a Create C Script > (C-szkript # létrehozása) parancsra. Hívja meg a CustomVisionObjects szkriptet.

  2. Kattintson duplán az új CustomVisionObjects szkriptre, hogy az a következővel Visual Studio.

  3. Adja hozzá a következő névtereket a fájl tetejéhez:

    using System;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.Networking;
    
  4. Törölje a Start() és az Update() metódust a CustomVisionObjects osztályban; Az osztálynak most üresnek kell lennie.

  5. Adja hozzá a következő osztályokat a CustomVisionObjects osztályon kívül. Ezeket az objektumokat a Newtonsoft kódtár használja a válaszadatok szerializálásához és de szerializálásához:

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

8. fejezet – A VoiceRecognizer osztály létrehozása

Ez az osztály felismeri a felhasználó hangbemenetét.

Az osztály létrehozása:

  1. Kattintson a jobb gombbal a Scripts (Szkriptek) mappára, majd kattintson a Create C Script > (C-szkript # létrehozása) parancsra. Hívja meg a VoiceRecognizer szkriptet.

  2. Kattintson duplán az új VoiceRecognizer szkriptre, hogy az a következővel Visual Studio.

  3. Adja hozzá a következő névtereket a VoiceRecognizer osztály felett:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using UnityEngine;
    using UnityEngine.Windows.Speech;
    
  4. Ezután adja hozzá a következő változókat a VoiceRecognizer osztályhoz a Start() metódus felett:

        /// <summary>
        /// Allows this class to behave like a singleton
        /// </summary>
        public static VoiceRecognizer Instance;
    
        /// <summary>
        /// Recognizer class for voice recognition
        /// </summary>
        internal KeywordRecognizer keywordRecognizer;
    
        /// <summary>
        /// List of Keywords registered
        /// </summary>
        private Dictionary<string, Action> _keywords = new Dictionary<string, Action>();
    
  5. Adja hozzá az Új() és Start() metódusokat, amelyek közül az utóbbi be fogja állítani a címke képhez való társításakor felismerhető felhasználói kulcsszavakat:

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

  7. Adja hozzá a következő kezelőt, amelyet a rendszer a hangbemenet felismerésekor hív meg:

        /// <summary>
        /// Handler called when a word is recognized
        /// </summary>
        private void KeywordRecognizer_OnPhraseRecognized(PhraseRecognizedEventArgs args)
        {
            Action keywordAction;
            // if the keyword recognized is in our dictionary, call that Action.
            if (_keywords.TryGetValue(args.text, out keywordAction))
            {
                keywordAction.Invoke();
            }
        }
    
  8. Mielőtt visszatér a Unitybe, mindenképpen mentse a módosításokat a Visual Studio, mielőtt visszatér a Unitybe.

Megjegyzés

Ne aggódjon olyan kód miatt, amely hibának tűnik, mivel hamarosan további osztályokat is meg fog adni, amelyek kijavítják ezeket.

9. fejezet – A CustomVisionTrainer osztály létrehozása

Ez az osztály egy sor webes hívást fog összefűzni a Custom Vision szolgáltatás betanítás érdekében. Az egyes hívásokat a kód fölött részletesen ismertetjük.

Az osztály létrehozása:

  1. Kattintson a jobb gombbal a Scripts (Szkriptek) mappára, majd kattintson a Create C Script > (C-szkript # létrehozása) parancsra. Hívja meg a CustomVisionTrainer szkriptet.

  2. Kattintson duplán az új CustomVisionTrainer szkriptre, hogy az a következővel Visual Studio.

  3. Adja hozzá a következő névtereket a CustomVisionTrainer osztály felett:

    using Newtonsoft.Json;
    using System.Collections;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;
    using UnityEngine;
    using UnityEngine.Networking;
    
  4. Ezután adja hozzá a következő változókat a CustomVisionTrainer osztályhoz a Start() metódus felett.

    Megjegyzés

    Az itt használt betanítás URL-címe a Custom Vision Training 1.2 dokumentációjában található, és a szerkezete a következő: https://southcentralus.api.cognitive.microsoft.com/customvision/v1.2/Training/projects/{projectId}/
    További információért látogasson el a Custom Vision Training 1.2-es verzió referencia API-jának webhelyre.

    Figyelmeztetés

    Fontos, hogy jegyezze fel az Custom Vision Service által a betanítás módhoz használt végpontot, mivel a használt JSON-struktúra (a CustomVisionObjects osztályon belül) az Custom Vision Training v1.2-velvaló munkára lett beállítva. Ha más verzióval rendelkezik, előfordulhat, hogy frissítenie kell az Objektumok struktúrát.

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

    Fontos

    Győződjön meg arról, hogy hozzáadta a szolgáltatáskulcs (betanításkulcs) és a Project azonosító értékét, amelyeket korábban feljegyzett; a kurzus korábbi részében (2. fejezet, 10.lépés és újabb) gyűjtött értékek.

  5. Adja hozzá a következő Start() és Az Ének () metódusokat. Ezeket a metódusokat inicializáláskor hívjuk meg, és tartalmazzák a felhasználói felület beállítására vonatkozó hívást:

        /// <summary>
        /// Called on initialization
        /// </summary>
        private void Awake()
        {
            Instance = this;
        }
    
        /// <summary>
        /// Runs at initialization right after Awake method
        /// </summary>
        private void Start()
        { 
            trainingUI_TextMesh = SceneOrganiser.Instance.CreateTrainingUI("TrainingUI", 0.04f, 0, 4, false);
        }
    
  6. Törölje az Update() metódust. Ennek az osztálynak nincs szüksége rá.

  7. Adja hozzá a RequestTagSelection() metódust. Ezt a metódust akkor hívjuk meg először, ha egy rendszerképet rögzítettek és az eszközön tároltak, és most már készen áll a Custom Vision szolgáltatásnak a betanítása céljából való elküldre. Ez a metódus a betanítás felhasználói felületén kulcsszavakat jelenít meg, amelyek használatával a felhasználó felcímkézheti a rögzített képet. A VoiceRecognizer osztályt is riasztja, hogy elkezdje figyelni a felhasználó hangbemenetét.

        internal void RequestTagSelection()
        {
            trainingUI_TextMesh.gameObject.SetActive(true);
            trainingUI_TextMesh.text = $" \nUse voice command \nto choose between the following tags: \nMouse\nKeyboard \nor say Discard";
    
            VoiceRecognizer.Instance.keywordRecognizer.Start();
        }
    
  8. Adja hozzá a VerifyTag() metódust. Ez a metódus fogadja a VoiceRecognizer osztály által felismert hangbemenetet, és ellenőrzi annak érvényességét, majd megkezdi a betanítási folyamatot.

        /// <summary>
        /// Verify voice input against stored tags.
        /// If positive, it will begin the Service training process.
        /// </summary>
        internal void VerifyTag(string spokenTag)
        {
            if (spokenTag == Tags.Mouse.ToString() || spokenTag == Tags.Keyboard.ToString())
            {
                trainingUI_TextMesh.text = $"Tag chosen: {spokenTag}";
                VoiceRecognizer.Instance.keywordRecognizer.Stop();
                StartCoroutine(SubmitImageForTraining(ImageCapture.Instance.filePath, spokenTag));
            }
        }
    
  9. Adja hozzá a SubmitImageForTraining() metódust. Ez a metódus megkezdi Custom Vision szolgáltatás betanítási folyamatát. Az első lépés a címkeazonosító lekérése a szolgáltatásból, amely a felhasználó ellenőrzött beszédbemenetéhez van társítva. A címkeazonosítót ezután feltölti a képpel együtt.

        /// <summary>
        /// Call the Custom Vision Service to submit the image.
        /// </summary>
        public IEnumerator SubmitImageForTraining(string imagePath, string tag)
        {
            yield return new WaitForSeconds(2);
            trainingUI_TextMesh.text = $"Submitting Image \nwith tag: {tag} \nto Custom Vision Service";
            string imageId = string.Empty;
            string tagId = string.Empty;
    
            // Retrieving the Tag Id relative to the voice input
            string getTagIdEndpoint = string.Format("{0}{1}/tags", url, projectId);
            using (UnityWebRequest www = UnityWebRequest.Get(getTagIdEndpoint))
            {
                www.SetRequestHeader("Training-Key", trainingKey);
                www.downloadHandler = new DownloadHandlerBuffer();
                yield return www.SendWebRequest();
                string jsonResponse = www.downloadHandler.text;
    
                Tags_RootObject tagRootObject = JsonConvert.DeserializeObject<Tags_RootObject>(jsonResponse);
    
                foreach (TagOfProject tOP in tagRootObject.Tags)
                {
                    if (tOP.Name == tag)
                    {
                        tagId = tOP.Id;
                    }             
                }
            }
    
            // Creating the image object to send for training
            List<IMultipartFormSection> multipartList = new List<IMultipartFormSection>();
            MultipartObject multipartObject = new MultipartObject();
            multipartObject.contentType = "application/octet-stream";
            multipartObject.fileName = "";
            multipartObject.sectionData = GetImageAsByteArray(imagePath);
            multipartList.Add(multipartObject);
    
            string createImageFromDataEndpoint = string.Format("{0}{1}/images?tagIds={2}", url, projectId, tagId);
    
            using (UnityWebRequest www = UnityWebRequest.Post(createImageFromDataEndpoint, multipartList))
            {
                // Gets a byte array out of the saved image
                imageBytes = GetImageAsByteArray(imagePath);           
    
                //unityWebRequest.SetRequestHeader("Content-Type", "application/octet-stream");
                www.SetRequestHeader("Training-Key", trainingKey);
    
                // The upload handler will help uploading the byte array with the request
                www.uploadHandler = new UploadHandlerRaw(imageBytes);
    
                // The download handler will help receiving the analysis from Azure
                www.downloadHandler = new DownloadHandlerBuffer();
    
                // Send the request
                yield return www.SendWebRequest();
    
                string jsonResponse = www.downloadHandler.text;
    
                ImageRootObject m = JsonConvert.DeserializeObject<ImageRootObject>(jsonResponse);
                imageId = m.Images[0].Image.Id;
            }
            trainingUI_TextMesh.text = "Image uploaded";
            StartCoroutine(TrainCustomVisionProject());
        }
    
  10. Adja hozzá a TrainCustomVisionProject() metódust. A rendszer a rendszerkép elküldt és címkézett elott ezt a metódust fogja meghívni. Létrehoz egy új iterációt, amely a Szolgáltatásba elküldött összes korábbi képpel, valamint az éppen feltöltött képpel lesz betanítva. A betanítás befejezése után ez a metódus egy metódust hív meg, amely az újonnan létrehozott iterációt alapértelmezettként adja meg, hogy az elemzéshez használt végpont a legújabb betanított iteráció.

        /// <summary>
        /// Call the Custom Vision Service to train the Service.
        /// It will generate a new Iteration in the Service
        /// </summary>
        public IEnumerator TrainCustomVisionProject()
        {
            yield return new WaitForSeconds(2);
    
            trainingUI_TextMesh.text = "Training Custom Vision Service";
    
            WWWForm webForm = new WWWForm();
    
            string trainProjectEndpoint = string.Format("{0}{1}/train", url, projectId);
    
            using (UnityWebRequest www = UnityWebRequest.Post(trainProjectEndpoint, webForm))
            {
                www.SetRequestHeader("Training-Key", trainingKey);
                www.downloadHandler = new DownloadHandlerBuffer();
                yield return www.SendWebRequest();
                string jsonResponse = www.downloadHandler.text;
                Debug.Log($"Training - JSON Response: {jsonResponse}");
    
                // A new iteration that has just been created and trained
                Iteration iteration = new Iteration();
                iteration = JsonConvert.DeserializeObject<Iteration>(jsonResponse);
    
                if (www.isDone)
                {
                    trainingUI_TextMesh.text = "Custom Vision Trained";
    
                    // Since the Service has a limited number of iterations available,
                    // we need to set the last trained iteration as default
                    // and delete all the iterations you dont need anymore
                    StartCoroutine(SetDefaultIteration(iteration)); 
                }
            }
        }
    
  11. Adja hozzá a SetDefaultIteration() metódust. Ez a metódus a korábban létrehozott és betanított iterációt alapértelmezettként fogja beállítani. Ha befejeződött, a metódusnak törölnie kell a szolgáltatásban meglévő előző iterációt. Ennek a kurzusnak a megírásakor legfeljebb tíz (10) iteráció létezhet egyszerre a szolgáltatásban.

        /// <summary>
        /// Set the newly created iteration as Default
        /// </summary>
        private IEnumerator SetDefaultIteration(Iteration iteration)
        {
            yield return new WaitForSeconds(5);
            trainingUI_TextMesh.text = "Setting default iteration";
    
            // Set the last trained iteration to default
            iteration.IsDefault = true;
    
            // Convert the iteration object as JSON
            string iterationAsJson = JsonConvert.SerializeObject(iteration);
            byte[] bytes = Encoding.UTF8.GetBytes(iterationAsJson);
    
            string setDefaultIterationEndpoint = string.Format("{0}{1}/iterations/{2}", 
                                                            url, projectId, iteration.Id);
    
            using (UnityWebRequest www = UnityWebRequest.Put(setDefaultIterationEndpoint, bytes))
            {
                www.method = "PATCH";
                www.SetRequestHeader("Training-Key", trainingKey);
                www.SetRequestHeader("Content-Type", "application/json");
                www.downloadHandler = new DownloadHandlerBuffer();
    
                yield return www.SendWebRequest();
    
                string jsonResponse = www.downloadHandler.text;
    
                if (www.isDone)
                {
                    trainingUI_TextMesh.text = "Default iteration is set \nDeleting Unused Iteration";
                    StartCoroutine(DeletePreviousIteration(iteration));
                }
            }
        }
    
  12. Adja hozzá a DeletePreviousIteration() metódust. Ez a metódus megkeresi és törli az előző nem alapértelmezett iterációt:

        /// <summary>
        /// Delete the previous non-default iteration.
        /// </summary>
        public IEnumerator DeletePreviousIteration(Iteration iteration)
        {
            yield return new WaitForSeconds(5);
    
            trainingUI_TextMesh.text = "Deleting Unused \nIteration";
    
            string iterationToDeleteId = string.Empty;
    
            string findAllIterationsEndpoint = string.Format("{0}{1}/iterations", url, projectId);
    
            using (UnityWebRequest www = UnityWebRequest.Get(findAllIterationsEndpoint))
            {
                www.SetRequestHeader("Training-Key", trainingKey);
                www.downloadHandler = new DownloadHandlerBuffer();
                yield return www.SendWebRequest();
    
                string jsonResponse = www.downloadHandler.text;
    
                // The iteration that has just been trained
                List<Iteration> iterationsList = new List<Iteration>();
                iterationsList = JsonConvert.DeserializeObject<List<Iteration>>(jsonResponse);
    
                foreach (Iteration i in iterationsList)
                {
                    if (i.IsDefault != true)
                    {
                        Debug.Log($"Cleaning - Deleting iteration: {i.Name}, {i.Id}");
                        iterationToDeleteId = i.Id;
                        break;
                    }
                }
            }
    
            string deleteEndpoint = string.Format("{0}{1}/iterations/{2}", url, projectId, iterationToDeleteId);
    
            using (UnityWebRequest www2 = UnityWebRequest.Delete(deleteEndpoint))
            {
                www2.SetRequestHeader("Training-Key", trainingKey);
                www2.downloadHandler = new DownloadHandlerBuffer();
                yield return www2.SendWebRequest();
                string jsonResponse = www2.downloadHandler.text;
    
                trainingUI_TextMesh.text = "Iteration Deleted";
                yield return new WaitForSeconds(2);
                trainingUI_TextMesh.text = "Ready for next \ncapture";
    
                yield return new WaitForSeconds(2);
                trainingUI_TextMesh.text = "";
                ImageCapture.Instance.ResetImageCapture();
            }
        }
    
  13. Az osztály utolsó hozzáadandó metódusa a GetImageAsByteArray() metódus, amelyet a webes hívások használnak a rögzített kép bájttömbökké konvertálására.

        /// <summary>
        /// Returns the contents of the specified image file as a byte array.
        /// </summary>
        static byte[] GetImageAsByteArray(string imageFilePath)
        {
            FileStream fileStream = new FileStream(imageFilePath, FileMode.Open, FileAccess.Read);
            BinaryReader binaryReader = new BinaryReader(fileStream);
            return binaryReader.ReadBytes((int)fileStream.Length);
        }
    
  14. Mielőtt visszatér a Unitybe, mentse a módosításokat a Visual Studio, mielőtt visszatér a Unitybe.

10. fejezet – A SceneOrganiser osztály létrehozása

Ez az osztály a következőt fogja:

  • Hozzon létre egy kurzorobjektumot a Main Camera-hoz való csatoláshoz.

  • Hozzon létre egy Label objektumot, amely akkor jelenik meg, ha a szolgáltatás felismeri a valós objektumokat.

  • A Main Camera beállításhoz csatolja hozzá a megfelelő összetevőket.

  • Elemzés módban a futtatáskor, a Main Camera pozíciójához viszonyítva a megfelelő világterületen jelenítse meg a Címkék adatokat, és jelenítse meg a Custom Vision Service-től kapott adatokat.

  • Betanítási módban a a betanítási folyamat különböző szakaszait megjelenítő felhasználói felületet jelenítse meg.

Az osztály létrehozása:

  1. Kattintson a jobb gombbal a Scripts (Szkriptek) mappára, majd kattintson a Create C Script > (C-szkript # létrehozása) parancsra. A szkriptnek nevezze el a SceneOrganiser nevet.

  2. Kattintson duplán az új SceneOrganiser szkriptre, és nyissa meg az Visual Studio.

  3. Csak egy névtérre lesz szüksége, a többit pedig távolítsa el a SceneOrganiser osztály felett:

    using UnityEngine;
    
  4. Ezután adja hozzá a következő változókat a SceneOrganiser osztályhoz a Start() metódus felett:

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

  6. Közvetlenül a változók alatt adja hozzá az Egyed() metódust, amely inicializálja az osztályt, és beállít egy jelenet.

        /// <summary>
        /// Called on initialization
        /// </summary>
        private void Awake()
        {
            // Use this class instance as singleton
            Instance = this;
    
            // Add the ImageCapture class to this GameObject
            gameObject.AddComponent<ImageCapture>();
    
            // Add the CustomVisionAnalyser class to this GameObject
            gameObject.AddComponent<CustomVisionAnalyser>();
    
            // Add the CustomVisionTrainer class to this GameObject
            gameObject.AddComponent<CustomVisionTrainer>();
    
            // Add the VoiceRecogniser class to this GameObject
            gameObject.AddComponent<VoiceRecognizer>();
    
            // Add the CustomVisionObjects class to this GameObject
            gameObject.AddComponent<CustomVisionObjects>();
    
            // Create the camera Cursor
            cursor = CreateCameraCursor();
    
            // Load the label prefab as reference
            label = CreateLabel();
    
            // Create the camera status indicator label, and place it above where predictions
            // and training UI will appear.
            cameraStatusIndicator = CreateTrainingUI("Status Indicator", 0.02f, 0.2f, 3, true);
    
            // Set camera status indicator to loading.
            SetCameraStatus("Loading");
        }
    
  7. Most adja hozzá a CreateCameraCursor() metódust, amely létrehozza és pozícióba hozza létre a Main Camera kurzort, valamint a CreateLabel() metódust, amely létrehozza az Analysis Label objektumot.

        /// <summary>
        /// Spawns cursor for the Main Camera
        /// </summary>
        private GameObject CreateCameraCursor()
        {
            // Create a sphere as new cursor
            GameObject newCursor = GameObject.CreatePrimitive(PrimitiveType.Sphere);
    
            // Attach it to the camera
            newCursor.transform.parent = gameObject.transform;
    
            // Resize the new cursor
            newCursor.transform.localScale = new Vector3(0.02f, 0.02f, 0.02f);
    
            // Move it to the correct position
            newCursor.transform.localPosition = new Vector3(0, 0, 4);
    
            // Set the cursor color to red
            newCursor.GetComponent<Renderer>().material = new Material(Shader.Find("Diffuse"));
            newCursor.GetComponent<Renderer>().material.color = Color.green;
    
            return newCursor;
        }
    
        /// <summary>
        /// Create the analysis label object
        /// </summary>
        private GameObject CreateLabel()
        {
            // Create a sphere as new cursor
            GameObject newLabel = new GameObject();
    
            // Resize the new cursor
            newLabel.transform.localScale = new Vector3(0.01f, 0.01f, 0.01f);
    
            // Creating the text of the label
            TextMesh t = newLabel.AddComponent<TextMesh>();
            t.anchor = TextAnchor.MiddleCenter;
            t.alignment = TextAlignment.Center;
            t.fontSize = 50;
            t.text = "";
    
            return newLabel;
        }
    
  8. Adja hozzá a SetCameraStatus() metódust, amely a kamera állapotát megadásával kezeli a szöveges hálóhoz szánt üzeneteket.

        /// <summary>
        /// Set the camera status to a provided string. Will be coloured if it matches a keyword.
        /// </summary>
        /// <param name="statusText">Input string</param>
        public void SetCameraStatus(string statusText)
        {
            if (string.IsNullOrEmpty(statusText) == false)
            {
                string message = "white";
    
                switch (statusText.ToLower())
                {
                    case "loading":
                        message = "yellow";
                        break;
    
                    case "ready":
                        message = "green";
                        break;
    
                    case "uploading image":
                        message = "red";
                        break;
    
                    case "looping capture":
                        message = "yellow";
                        break;
    
                    case "analysis":
                        message = "red";
                        break;
                }
    
                cameraStatusIndicator.GetComponent<TextMesh>().text = $"Camera Status:\n<color={message}>{statusText}..</color>";
            }
        }
    
  9. Adja hozzá a PlaceAnalysisLabel() és SetTagsToLastLabel() metódusokat, amelyek a Custom Vision Service-ről származó adatokat fogják kihozni és megjeleníteni a jelenetben.

        /// <summary>
        /// Instantiate a label in the appropriate location relative to the Main Camera.
        /// </summary>
        public void PlaceAnalysisLabel()
        {
            lastLabelPlaced = Instantiate(label.transform, cursor.transform.position, transform.rotation);
            lastLabelPlacedText = lastLabelPlaced.GetComponent<TextMesh>();
        }
    
        /// <summary>
        /// Set the Tags as Text of the last label created. 
        /// </summary>
        public void SetTagsToLastLabel(AnalysisObject analysisObject)
        {
            lastLabelPlacedText = lastLabelPlaced.GetComponent<TextMesh>();
    
            if (analysisObject.Predictions != null)
            {
                foreach (Prediction p in analysisObject.Predictions)
                {
                    if (p.Probability > 0.02)
                    {
                        lastLabelPlacedText.text += $"Detected: {p.TagName} {p.Probability.ToString("0.00 \n")}";
                        Debug.Log($"Detected: {p.TagName} {p.Probability.ToString("0.00 \n")}");
                    }
                }
            }
        }
    
  10. Végül adja hozzá a CreateTrainingUI() metódust, amely elindítja a betanítási folyamat több szakaszát megjelenítő felhasználói felületet, amikor az alkalmazás Betanítási módban van. Ezt a módszert a kamera állapotobjektumának létrehozására is használni fogjuk.

        /// <summary>
        /// Create a 3D Text Mesh in scene, with various parameters.
        /// </summary>
        /// <param name="name">name of object</param>
        /// <param name="scale">scale of object (i.e. 0.04f)</param>
        /// <param name="yPos">height above the cursor (i.e. 0.3f</param>
        /// <param name="zPos">distance from the camera</param>
        /// <param name="setActive">whether the text mesh should be visible when it has been created</param>
        /// <returns>Returns a 3D text mesh within the scene</returns>
        internal TextMesh CreateTrainingUI(string name, float scale, float yPos, float zPos, bool setActive)
        {
            GameObject display = new GameObject(name, typeof(TextMesh));
            display.transform.parent = Camera.main.transform;
            display.transform.localPosition = new Vector3(0, yPos, zPos);
            display.SetActive(setActive);
            display.transform.localScale = new Vector3(scale, scale, scale);
            display.transform.rotation = new Quaternion();
            TextMesh textMesh = display.GetComponent<TextMesh>();
            textMesh.anchor = TextAnchor.MiddleCenter;
            textMesh.alignment = TextAlignment.Center;
            return textMesh;
        }
    
  11. Mielőtt visszatér a Unitybe, mentse a módosításokat a Visual Studio, mielőtt visszatér a Unitybe.

Fontos

A folytatás előtt nyissa meg a CustomVisionAnalyser osztályt, és a KéntelmektImageCaptured() metódusban a következő sorokat ne használja fel a rendszer:

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

11. fejezet – Az ImageCapture osztály létrehozása

A következő osztály, amit létre fog hozni, az ImageCapture osztály.

Ez az osztály a következőért felelős:

  • Kép rögzítése a HoloLens és tárolása az alkalmazásmappában.

  • Koppintásos kézmozdulatok kezelése a felhasználótól.

  • Az Enum (Felsorolás) érték fenntartása, amely meghatározza, hogy az alkalmazás Elemzési vagy Betanítás módban fog-e futni.

Az osztály létrehozása:

  1. Ugrás a korábban létrehozott Scripts mappára.

  2. Kattintson a jobb gombbal a mappában, majd kattintson a Create > C Script (C-szkript # létrehozása) parancsra. A szkriptnek nevezze el az ImageCapture nevet.

  3. Kattintson duplán az új ImageCapture-szkriptre, hogy az a következővel Visual Studio.

  4. Cserélje le a fájl tetején található névtereket a következőre:

    using System;
    using System.IO;
    using System.Linq;
    using UnityEngine;
    using UnityEngine.XR.WSA.Input;
    using UnityEngine.XR.WSA.WebCam;
    
  5. Ezután adja hozzá a következő változókat az ImageCapture osztályhoz a Start() metódus felett:

        /// <summary>
        /// Allows this class to behave like a singleton
        /// </summary>
        public static ImageCapture Instance;
    
        /// <summary>
        /// Keep counts of the taps for image renaming
        /// </summary>
        private int captureCount = 0;
    
        /// <summary>
        /// Photo Capture object
        /// </summary>
        private PhotoCapture photoCaptureObject = null;
    
        /// <summary>
        /// Allows gestures recognition in HoloLens
        /// </summary>
        private GestureRecognizer recognizer;
    
        /// <summary>
        /// Loop timer
        /// </summary>
        private float secondsBetweenCaptures = 10f;
    
        /// <summary>
        /// Application main functionalities switch
        /// </summary>
        internal enum AppModes {Analysis, Training }
    
        /// <summary>
        /// Local variable for current AppMode
        /// </summary>
        internal AppModes AppMode { get; private set; }
    
        /// <summary>
        /// Flagging if the capture loop is running
        /// </summary>
        internal bool captureIsActive;
    
        /// <summary>
        /// File path of current analysed photo
        /// </summary>
        internal string filePath = string.Empty;
    
  6. Most hozzá kell adni a Kódot a Következő Metódus() és Start() metódushoz:

        /// <summary>
        /// Called on initialization
        /// </summary>
        private void Awake()
        {
            Instance = this;
    
            // Change this flag to switch between Analysis Mode and Training Mode 
            AppMode = AppModes.Training;
        }
    
        /// <summary>
        /// Runs at initialization right after Awake method
        /// </summary>
        void Start()
        {
            // Clean up the LocalState folder of this application from all photos stored
            DirectoryInfo info = new DirectoryInfo(Application.persistentDataPath);
            var fileInfo = info.GetFiles();
            foreach (var file in fileInfo)
            {
                try
                {
                    file.Delete();
                }
                catch (Exception)
                {
                    Debug.LogFormat("Cannot delete file: ", file.Name);
                }
            } 
    
            // Subscribing to the HoloLens API gesture recognizer to track user gestures
            recognizer = new GestureRecognizer();
            recognizer.SetRecognizableGestures(GestureSettings.Tap);
            recognizer.Tapped += TapHandler;
            recognizer.StartCapturingGestures();
    
            SceneOrganiser.Instance.SetCameraStatus("Ready");
        }
    
  7. Implementálja a kezelőt, amely egy koppintásos kézmozdulat esetén lesz meghívva.

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

    Megjegyzés

    Elemzési módban a TapHandler metódus a fényképrögzítési hurok indítási vagy leállítási kapcsolója.

    Betanítás módban egy képet rögzít a kameráról.

    Ha a kurzor zöld, az azt jelenti, hogy a kamera rendelkezésre áll a kép felvételére.

    Ha a kurzor piros, az azt jelenti, hogy a kamera foglalt.

  8. Adja hozzá azt a metódust, amely alapján az alkalmazás elindítja a képrögzítési folyamatot, és tárolja a lemezképet.

        /// <summary>
        /// Begin process of Image Capturing and send To Azure Custom Vision Service.
        /// </summary>
        private void ExecuteImageCaptureAndAnalysis()
        {
            // Update camera status to analysis.
            SceneOrganiser.Instance.SetCameraStatus("Analysis");
    
            // Create a label in world space using the SceneOrganiser class 
            // Invisible at this point but correctly positioned where the image was taken
            SceneOrganiser.Instance.PlaceAnalysisLabel();
    
            // Set the camera resolution to be the highest possible
            Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First();
    
            Texture2D targetTexture = new Texture2D(cameraResolution.width, cameraResolution.height);
    
            // Begin capture process, set the image format
            PhotoCapture.CreateAsync(false, delegate (PhotoCapture captureObject)
            {
                photoCaptureObject = captureObject;
    
                CameraParameters camParameters = new CameraParameters
                {
                    hologramOpacity = 0.0f,
                    cameraResolutionWidth = targetTexture.width,
                    cameraResolutionHeight = targetTexture.height,
                    pixelFormat = CapturePixelFormat.BGRA32
                };
    
                // Capture the image from the camera and save it in the App internal folder
                captureObject.StartPhotoModeAsync(camParameters, delegate (PhotoCapture.PhotoCaptureResult result)
                {
                    string filename = string.Format(@"CapturedImage{0}.jpg", captureCount);
                    filePath = Path.Combine(Application.persistentDataPath, filename);          
                    captureCount++;              
                    photoCaptureObject.TakePhotoAsync(filePath, PhotoCaptureFileOutputFormat.JPG, OnCapturedPhotoToDisk);              
                });
            });   
        }
    
  9. Adja hozzá a kezelőket, amelyek akkor lesznek meghívva, amikor a fénykép rögzített, és amikor az készen áll az elemzésre. Az eredmény ezután a CustomVisionAnalyser vagy a CustomVisionTrainer számára lesz átküldve attól függően, hogy a kód milyen módban van beállítva.

        /// <summary>
        /// Register the full execution of the Photo Capture. 
        /// </summary>
        void OnCapturedPhotoToDisk(PhotoCapture.PhotoCaptureResult result)
        {
                // Call StopPhotoMode once the image has successfully captured
                photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
        }
    
    
        /// <summary>
        /// The camera photo mode has stopped after the capture.
        /// Begin the Image Analysis process.
        /// </summary>
        void OnStoppedPhotoMode(PhotoCapture.PhotoCaptureResult result)
        {
            Debug.LogFormat("Stopped Photo Mode");
    
            // Dispose from the object in memory and request the image analysis 
            photoCaptureObject.Dispose();
            photoCaptureObject = null;
    
            switch (AppMode)
            {
                case AppModes.Analysis:
                    // Call the image analysis
                    StartCoroutine(CustomVisionAnalyser.Instance.AnalyseLastImageCaptured(filePath));
                    break;
    
                case AppModes.Training:
                    // Call training using captured image
                    CustomVisionTrainer.Instance.RequestTagSelection();
                    break;
            }
        }
    
        /// <summary>
        /// Stops all capture pending actions
        /// </summary>
        internal void ResetImageCapture()
        {
            captureIsActive = false;
    
            // Set the cursor color to green
            SceneOrganiser.Instance.cursor.GetComponent<Renderer>().material.color = Color.green;
    
            // Update camera status to ready.
            SceneOrganiser.Instance.SetCameraStatus("Ready");
    
            // Stop the capture loop if active
            CancelInvoke();
        }
    
  10. Mielőtt visszatér a Unitybe, mentse a módosításokat a Visual Studio, mielőtt visszatér a Unitybe.

  11. Most, hogy az összes szkript elkészült, vissza a Unity-szerkesztőbe, és húzza a SceneOrganiser osztályt a Scripts mappából a Main Camera objektumra a Hierarchy Panelen.

12. fejezet – A felépítés előtt

Az alkalmazás alapos tesztelésének elvégzéséhez közvetlenül kell azt a saját HoloLens.

Mielőtt ezt meghozta, győződjön meg a következőről:

  • A 2. fejezetben említett beállítások megfelelően vannak beállítva.

  • A Main Camera (Fő kamera) inspector (Vizsgáló) panelen minden mező megfelelően van kiosztva.

  • A SceneOrganiser szkript a Main Camera objektumhoz van csatolva.

  • Győződjön meg arról, hogy beszúrta az előrejelzési kulcsot a predictionKey változóba.

  • Beszúrta az előrejelzési végpontot a predictionEndpoint változóba.

  • Beszúrta a betanítási kulcsot a CustomVisionTrainer osztály trainingKey változóba.

  • Beszúrta a Project CustomVisionTrainer osztály projectId változóba.

13. fejezet – Az alkalmazás összeállítása és saját maga által való kiépítése

A buildfolyamat megkezdéséhez:

  1. A Fájl létrehozása > a Gépház.

  2. A Unity C Projects # órajele.

  3. Kattintson a Build (Build) gombra. A Unity elindít egy Fájlkezelő, amelyben létre kell hoznia, majd ki kell választania egy mappát, amelybe az alkalmazást felépíti. Hozza létre ezt a mappát, és nevezze el App néven. Ezután jelölje ki az Alkalmazás mappát, és kattintson a Mappa kiválasztása elemre.

  4. A Unity megkezdi a projekt az App mappába való építését.

  5. Miután a Unity befejezte az összeállítást (ez némi időt is vegyen el), megnyit egy Fájlkezelő-ablakot a build helyén (ellenőrizze a feladatsávot, mert előfordulhat, hogy nem mindig jelenik meg az ablakok fölött, de értesítést küld egy új ablakról).

Üzembe helyezés a következő HoloLens:

  1. A távoli üzembe helyezéshez szüksége lesz a HoloLens IP-címére, és annak biztosításához, hogy a HoloLens fejlesztői módban legyen. Ehhez tegye a következőket:

    1. Bár a saját HoloLens, nyissa meg a Gépház.

    2. Ugrás a Hálózati & Internet > Wi-Fi speciális > beállításaira

    3. Jegyezze fel az IPv4-címet.

    4. Ezután lépjen vissza a Gépház, majd az Update & Security for Developers (Biztonsági frissítés > fejlesztőknek) lapra.

    5. Állítsa be a Fejlesztői módot.

  2. Lépjen az új Unity-buildre (az App mappára), és nyissa meg a megoldásfájlt a Visual Studio.

  3. A Megoldáskonfigurációban válassza a Hibakeresés lehetőséget.

  4. A Megoldásplatformon válassza az x86, Távoli gép lehetőséget. A rendszer kérni fogja, hogy szúrja be egy távoli eszköz IP-címét (a HoloLens, ebben az esetben, amit feljegyzett).

    IP-cím beállítása

  5. A Build (Build) menüben kattintson a Deploy Solution (Megoldás üzembe helyezése) elemre az alkalmazás saját HoloLens.

  6. Az alkalmazásnak most már meg kell jelenni a telepített alkalmazások listájában a HoloLens, készen áll az indulásra!

Megjegyzés

A modern headsetben való üzembe helyezéshez állítsa a Megoldásplatformot helyi gépre, a konfigurációt pedig hibakeresésre, az x86 platformot pedig állítsa a platformra. Ezután telepítse az elemet a helyi gépre a Build (Build) menüelem használatával, majd válassza a Deploy Solution (Megoldás üzembe helyezése) lehetőséget.

Az alkalmazás használata:

Ha át kell váltania az alkalmazás funkcióit a Betanítás és az Előrejelzés mód között, frissítenie kell az AppMode változót, amely az ImageCapture osztályOny() metódusában található.

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

vagy

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

Betanítás módban:

  • Nézze meg az Egér vagy a Billentyűzet billentyűparancsot, és használja a Koppintás kézmozdulatot.

  • Ezután megjelenik egy szöveg, amely arra kéri, hogy adjon meg egy címkét.

  • Mondja ki az Egér vagy a Billentyűzet billentyűparancsot.

Előrejelzési módban:

  • Nézzen meg egy objektumot, és használja a Koppintás kézmozdulatot.

  • Megjelenik egy szöveg, amely az észlelt objektumot jelöli a legnagyobb valószínűséggel (ez normalizálva van).

14. fejezet – A Custom Vision értékelése és fejlesztése

A szolgáltatás pontosságának érdekében folytatnia kell az előrejelzéshez használt modell betanítását. Ez az új alkalmazás használatával történik a betanítás és az előrejelzési módokkal, az utóbbi pedig a portál felkeresását követeli meg, amit ebben a fejezetben is látunk. Készüljön fel arra, hogy sokszor visszatér a portálra, és folyamatosan fejleszti a modellt.

  1. Az Azure Custom Vision portálra, és ha már a projektben van, válassza az Előrejelzések lapot (az oldal tetején):

    Az Előrejelzések lap kiválasztása

  2. Látni fogja az alkalmazás futása közben a szolgáltatásnak küldött összes rendszerképet. Ha az egérmutatót a képek fölé húzza, a rendszer a képre vonatkozó előrejelzéseket fogja biztosítani:

    Előrejelzési képek listája

  3. Válassza ki az egyik képet a megnyitásához. Ha meg van nyitva, a jobb oldalon láthatja az erre a képre vonatkozó előrejelzéseket. Ha az előrejelzések helyesek voltak, és ezt a képet szeretné hozzáadni a szolgáltatás betanító modelljéhez, kattintson a Saját címkék beviteli mezőre, és válassza ki a társítani kívánt címkét. Ha elkészült, kattintson a jobb alsó sarokban található Mentés és bezárás gombra, és folytassa a következő képpel.

    A megnyitni kívánt kép kiválasztása

  4. Miután visszatért a képrácshoz, észreveheti, hogy a rendszer eltávolítja a képeket, amelyekhez címkéket adott hozzá (és mentett). Ha olyan képeket talál, amelyeken úgy gondolja, hogy a címkézett elem nem található bennük, törölheti őket. Ehhez kattintson a képen látható pipára (több kép esetén ezt is meg lehet tenni), majd kattintson a rácslap jobb felső sarkában található Törlés gombra. A következő előugró ablakban kattintson az Igen, törlés vagy Nem lehetőségre a törlés megerősítéséhez, illetve a törlés törléséhez.

    Rendszerképek törlése

  5. Ha készen áll a folytatásra, kattintson a zöld Betanítás gombra a jobb felső sarokban. A szolgáltatásmodell az összes megadott kép alapján lesz betanítva (így pontosabb lesz). A betanítás befejezése után kattintson még egyszer az Alapértelmezett beállítása gombra, hogy az előrejelzési URL-cím továbbra is a szolgáltatás legfrissebb iterációját használja.

    A betanítás szolgáltatásmodellének kezdete  Válassza az Alapértelmezett beállítás beállítása lehetőséget

A kész CUSTOM VISION API-alkalmazás

Gratulálunk, felépített egy vegyes valóságot használó alkalmazást, amely az Azure Custom Vision API-t használja valós objektumok felismeréséhez, a szolgáltatásmodell betanítása és az eddig látottak megbízhatóságának megjelenítéséhez.

Kész projekt – példa

Soron kívüli gyakorlatok

1. gyakorlat

A Custom Vision betanítása több objektum felismeréséhez.

2. gyakorlat

A tanultak bővítésének módjaként a következő gyakorlatokat kell végrehajtania:

Hang lejátszása objektum felismerésekor.

3. gyakorlat

Az API-val újra betanítja a szolgáltatást ugyanazokkal a képekkel, amelyeken az alkalmazás elemz, így pontosabb lesz a szolgáltatás (egyszerre kell előrejelzést és betanításokat is) használni.