HoloLens (第 1 代) 和 Azure 304:臉部辨識


注意

混合實境學院教學課程的設計是以 HoloLens (第 1 代) 和混合實境沉浸式頭戴裝置為準。 因此,對於仍在尋找這些裝置開發指引的開發人員而言,我們覺得這些教學課程很重要。 這些教學課程不會使用用於 HoloLens 2 的最新工具組或互動進行更新。 系統會保留這些資訊,以繼續在支援的裝置上運作。 未來將會張貼一系列新的教學課程,示範如何針對HoloLens 2進行開發。 此通知會在張貼時更新這些教學課程的連結。


outcome of completing this course

在此課程中,您將瞭解如何使用 Azure 認知服務搭配 Microsoft 臉部 API,將臉部辨識功能新增至混合實境應用程式。

Azure 臉部 API 是一項 Microsoft 服務,可為開發人員提供雲端中最進階的臉部演算法。 臉部 API有兩個主要功能:具有屬性的臉部偵測和臉部辨識。 這可讓開發人員只為臉部設定一組群組,然後稍後將查詢影像傳送至服務,以判斷臉部所屬的人員。 如需詳細資訊,請流覽 Azure 臉部辨識頁面

完成本課程之後,您將會有混合實境HoloLens應用程式,其可以執行下列動作:

  1. 使用點選手勢,使用內建HoloLens相機起始影像的擷取。
  2. 將擷取的映射傳送至 Azure 臉部 API 服務。
  3. 接收 臉部 API 演算法的結果。
  4. 使用簡單的使用者介面來顯示相符人員的名稱。

這會教導您如何將臉部 API 服務的結果取得到 Unity 型混合實境應用程式。

在您的應用程式中,您必須先瞭解如何將結果與您的設計整合。 本課程旨在教導您如何整合 Azure 服務與您的 Unity Project。 您的工作是使用此課程取得的知識來增強混合實境應用程式。

裝置支援

課程 HoloLens 沉浸式頭戴裝置
MR 和 Azure 304:臉部辨識 ✔️ ✔️

注意

雖然本課程主要著重于HoloLens,但您也可以將此課程中學到的內容套用至Windows Mixed Reality沉浸式 (VR) 頭戴裝置。 由於沉浸式 (VR) 頭戴式裝置沒有可存取的相機,因此您需要連線到電腦的外部相機。 隨著課程一起進行時,您會看到可能需要採用的任何變更附注,以支援沉浸式 (VR) 頭戴裝置。

必要條件

注意

本教學課程專為具備 Unity 和 C# 基本體驗的開發人員所設計。 另請注意,本檔中的必要條件和書面指示代表在撰寫 (2018 年 5 月) 時已測試及驗證的內容。 您可以隨意使用最新的軟體,如 安裝工具 一文中所列,但不應該假設本課程中的資訊會完全符合您在較新軟體中找到的內容,而不是下面所列的內容。

針對本課程,我們建議使用下列硬體和軟體:

在您開始使用 Intune 之前

  1. 若要避免建置此專案時發生問題,強烈建議您在根資料夾或近根資料夾中建立本教學課程中提及的專案, (長資料夾路徑可能會導致建置時間) 的問題。
  2. 設定及測試您的HoloLens。 如果您需要設定HoloLens的支援,請務必流覽HoloLens設定一文
  3. 在開始開發新的HoloLens應用程式時,最好執行校正和感應器微調 (有時有助於針對每個使用者執行這些工作) 。

如需校正的說明,請遵循此連結至HoloLens校正文章

如需感應器微調的說明,請遵循此連結,以取得感應器微調HoloLens文章

第 1 章 - Azure 入口網站

若要在 Azure 中使用 臉部 API 服務,您必須將服務的實例設定為可供您的應用程式使用。

  1. 首先,登入 Azure 入口網站

    注意

    如果您還沒有 Azure 帳戶,則必須建立一個帳戶。 如果您在教室或實驗室案例中遵循本教學課程,請詢問講師或其中一個專業人員,以協助設定新的帳戶。

  2. 登入之後,按一下左上角的 [ 新增 ],然後搜尋 臉部 API,然後按 Enter鍵。

    search for face api

    注意

    New一詞可能已取代為在較新的入口網站中建立資源

  3. 新頁面會提供 臉部 API 服務的描述。 在此提示的左下方,選取 [ 建立 ] 按鈕,以建立與此服務的關聯。

    face api information

  4. 按一下 [ 建立] 之後:

    1. 插入此服務實例所需的名稱。

    2. 選取一個訂用帳戶。

    3. 選取適合您的定價層,如果這是第一次建立 臉部 API 服務,您應該可以使用名為 F0) 的免費層 (。

    4. 選擇 資源群組 或建立新的群組。 資源群組提供一種方式來監視、控制存取、布建和管理 Azure 資產集合的計費。 建議您保留與單一專案相關聯的所有 Azure 服務 (例如,這些實驗室) 在通用資源群組底下) 。

      如果您想要深入瞭解 Azure 資源群組,請 流覽資源群組一文

    5. 您稍後使用的 UWP 應用程式 Person Maker需要針對位置使用「美國西部」。

    6. 您也必須確認您已瞭解套用至此服務的條款及條件。

    7. 選取[建立]。*

      create face api service

  5. 按一下 [ 建立],* 您必須等候服務建立,這可能需要一分鐘的時間。

  6. 建立服務實例之後,入口網站中會出現通知。

    service creation notification

  7. 按一下通知以探索新的服務實例。

    go to resource notification

  8. 當您準備好時,請按一下通知中的 [ 移至資源 ] 按鈕,以探索新的服務實例。

    access face api keys

  9. 在本教學課程中,您的應用程式必須呼叫您的服務,這是透過使用您服務的訂用帳戶 'key' 來完成的。 從臉部 API服務的[快速入門] 頁面,第一個點是數位 1,用來抓取金鑰。

  10. 在 [服務] 頁面上,如果 [快速啟動] 頁面 () ,或 [服務] 導覽功能表中的 [金鑰] 連結 (左側選取藍色的 [金鑰] 超連結) ,以顯示您的金鑰。

    注意

    記下其中一個金鑰並加以保護,因為稍後您將需要它。

第 2 章 - 使用'Person Maker' UWP 應用程式

請務必下載名為 Person Maker的預先建置 UWP 應用程式。 此應用程式不是本課程的最終產品,只是協助您建立 Azure 專案的工具,稍後項目將依賴此專案。

Person Maker 可讓您建立與人員及人員群組相關聯的 Azure 專案。 應用程式會以格式放置所有必要的資訊,稍後可供 FaceAPI 使用,以便辨識您所新增人員的人臉。

[重要] 人員製作者 會使用一些基本節流,協助確保您不會超過 免費訂用帳戶層每分鐘的服務呼叫數目。 頂端的綠色文字會在節流發生時變更為紅色並更新為「作用中」;如果是這種情況,只要等候應用程式 (它將會等到它可以繼續存取臉部服務,並在您可以再次使用時更新為 'IN-ACTIVE',) 。

此應用程式使用 Microsoft.ProjectOxford.Face 程式庫,可讓您充分利用臉部 API。 此程式庫是免費的NuGet套件。 如需此專案的詳細資訊,以及類似的 API, 請務必造訪 API 參考文章

注意

這些只是必要的步驟,有關如何執行這些動作的指示會進一步瞭解檔。 Person Maker應用程式可讓您:

  • 建立 人員群組,這是由您想要與其建立關聯的數個人員組成的群組。 透過您的 Azure 帳戶,您可以裝載多個人員群組。

  • 建立 人員,這是人員群組的成員。 每位人員都有一些與其相關聯的 臉部 影像。

  • 臉部影像指派給人員,以允許 Azure 臉部 API 服務透過對應的臉部辨識人員

  • 訓練您的 Azure 臉部 API 服務

請注意,若要訓練此應用程式以辨識人員,您將需要 10 (10) 要新增至人員群組之每個人的特寫相片。 Windows 10 Cam 應用程式可協助您進行這些作業。 您必須確保每張相片都清楚 (避免從主旨) 模糊、遮蔽或太遠、具有 jpg 或 png 檔案格式的相片、影像檔案大小不超過 4 MB,且不超過 1 KB

注意

如果您遵循本教學課程,請勿使用自己的臉部進行訓練,因為當您將HoloLens放開時,無法自行查看。 使用同事或同事的臉部。

執行 人員製作者

  1. 開啟PersonMaker資料夾,然後按兩下PersonMaker 解決方案,以Visual Studio開啟它。

  2. 開啟 PersonMaker 解決方案之後,請確定:

    1. [ 解決方案組態 ] 設定為 [ 偵錯]。

    2. 解決方案平臺設定為x86

    3. 目標平臺本機電腦

    4. 您可能也需要NuGet套件 (以滑鼠右鍵按一下[解決方案],然後選取 [還原NuGet套件) ]。

  3. 按一下 [本機電腦 ],應用程式將會啟動。 請注意,在較小的螢幕上,所有內容都可能不會顯示,但您可以向下捲動以檢視它。

    person maker user interface

  4. 從 Azure 內的臉部 API服務插入您的Azure 驗證金鑰。您應該擁有此金鑰。

  5. 插入:

    1. 您想要指派人員群組的識別碼。 識別碼必須是小寫,不含空格。 記下此識別碼,因為稍後會在 Unity 專案中需要此識別碼。
    2. 您想要指派給人員群組 (的名稱可以有空格) 。
  6. [建立人員群組 ] 按鈕。 確認訊息應該會出現在按鈕下方。

注意

如果您有「拒絕存取」錯誤,請檢查您為 Azure 服務設定的位置。 如上所述,此應用程式是針對「美國西部」所設計。

重要

您也會注意到,您也可以按一下 [ 擷取已知群組 ] 按鈕:如果您已經建立人員群組,而且想要使用該群組,而不是建立新的群組, 請注意,如果您按一下 [建立 具有已知群組的人員群組],這也會擷取群組。

  1. 插入您要建立的人員名稱

    1. 按一下 [ 建立人員] 按鈕。

    2. 確認訊息應該會出現在按鈕下方。

    3. 如果您想要刪除先前建立的人員,您可以將名稱寫入文字方塊中,然後按 [刪除人員]

  2. 請確定您知道要新增至群組之人員的十張 (10 張) 相片的位置。

  3. [建立] 和 [開啟資料夾] 以開啟Windows檔案總管與人員相關聯的資料夾。 在 資料夾中新增十個 (10 個) 影像。 這些必須是 JPGPNG 檔案格式。

  4. 按一下 [ 提交至 Azure]。 計數器會顯示提交的狀態,後面接著完成的訊息。

  5. 一旦計數器完成,並顯示確認訊息,請按一下 [ 訓練 ] 來訓練您的服務。

程式完成後,您就可以開始移至 Unity。

第 3 章 - 設定 Unity 專案

以下是使用混合實境進行開發的一般設定,因此是其他專案的良好範本。

  1. 開啟 Unity ,然後按一下 [ 新增]。

    Start new Unity project.

  2. 您現在必須提供 Unity Project名稱。 插入 MR_FaceRecognition。 請確定專案類型已設定為 3D。 將 [位置] 設定為適合您 (記住的位置,更接近根目錄) 。 然後按一下 [建立專案]。

    Provide details for new Unity project.

  3. 開啟 Unity 時,值得檢查預設的腳本編輯器已設定為Visual Studio。 移至 [編輯 > 喜好設定 ],然後從新視窗中流覽至 [外部工具]。 將外部腳本編輯器變更為Visual Studio 2017。 關閉 [ 喜好設定 ] 視窗。

    Update script editor preference.

  4. 接下來,移至 [檔案 > 建置] 設定,然後按一下 [切換平臺] 按鈕,將平臺切換為通用 Windows 平臺

    Build Settings window, switch platform to UWP.

  5. 移至[檔案 > 建置] 設定,並確定:

    1. 目標裝置設定為HoloLens

      針對沉浸式頭戴式裝置,將 [目標裝置 ] 設定為 [任何裝置]。

    2. 組建類型 設定為 D3D

    3. SDK 設定為 [最新安裝]

    4. Visual Studio版本設定為[最新安裝]

    5. [建置並執行 ] 設定為 [本機電腦]

    6. 儲存場景,並將它新增至組建。

      1. 選取 [ 新增開啟場景]來執行此動作。 隨即會出現儲存視窗。

        Click add open scenes button

      2. 選取 [ 新增資料夾 ] 按鈕,以建立新的資料夾,將它命名為 Scenes

        Create new scripts folder

      3. 開啟新建立 的 Scenes 資料夾,然後在 [ 檔案名:文字] 欄位中輸入 FaceRecScene,然後按 [ 儲存]。

        Give new scene a name.

    7. 組建設定中的其餘設定現在應保留為預設值。

  6. 在 [建置設定] 視窗中,按一下 [播放程式設定] 按鈕,這會在Inspector所在的空間中開啟相關的面板。

    Open player settings.

  7. 在此面板中,需要驗證一些設定:

    1. 在 [其他設定] 索引標籤中:

      1. ScriptingRuntime版本應該是實驗性 (.NET 4.6 對等) 。 變更這會觸發需要重新開機編輯器。

      2. 腳本後端 應該是 .NET

      3. API 相容性層級 應該是 .NET 4.6

        Update other settings.

    2. 在 [發佈設定] 索引標籤的 [功能] 底下,檢查:

      • InternetClient

      • 網路攝影機

        Updating publishing settings.

    3. 此外,在XR 設定 (中,于 [發佈設定) ]、[支援虛擬實境] 底下找到,確定已新增Windows Mixed Reality SDK

      Update the X R Settings.

  8. 回到[建置設定],Unity C# 專案不再呈現灰色;請勾選此旁的核取方塊。

  9. 關閉 [建置設定] 視窗。

  10. 儲存場景並Project (檔案 > 儲存場景/檔案 > 儲存專案) 。

第 4 章 - 主要相機設定

重要

如果您想要略過本課程的 Unity 設定 元件,並直接進入程式碼,請隨意 下載此 .unitypackage,並將其匯入您的專案作為 自訂套件。 請注意,此套件也包含 Newtonsoft DLL的匯入,如 第 5 章所述。 透過此匯入,您可以從 第 6 章繼續進行。

  1. 在 [ 階層 面板] 中,選取 [主要相機]。

  2. 選取之後,您將能夠在偵測器面板中看到主要相機的所有元件。

    1. Camera 物件必須命名為Main Camera (記下拼字!)

    2. 主要相機 標籤 必須設定為 MainCamera (記下拼字!)

    3. 確定 [轉換位置 ] 設定為 0、0、0

    4. 清除旗標 設定為 純色

    5. 將相機元件 的背景 色彩設定為 黑色、Alpha 0 (十六進位代碼: #000000000)

      set up camera components

第 5 章 – 匯入 Newtonsoft.Json 程式庫

重要

如果您在 上一章中匯入 '.unitypackage',您可以略過本章。

若要協助您還原序列化和序列化收到的物件,並傳送至Bot Service您需要下載Newtonsoft.Json程式庫。 您會在此 Unity 套件檔案中找到已使用正確 Unity 資料夾結構的相容版本。

若要匯入程式庫:

  1. 下載 Unity 套件。

  2. 按一下 [資產]、[ 匯入套件]、 [自訂套件]。

    Import Newtonsoft.Json

  3. 尋找您已下載的 Unity 套件,然後按一下 [開啟]。

  4. 請確定封裝的所有元件都已刻度,然後按一下 [ 入]。

    Import the Newtonsoft.Json assets

第 6 章 - 建立 FaceAnalysis 類別

FaceAnalysis 類別的目的是裝載與 Azure 臉部辨識服務通訊所需的方法。

  • 傳送服務擷取影像之後,它會分析影像並識別內的臉部,並判斷是否有任何屬於已知人員。
  • 如果找到已知的人員,此類別會在場景中將其名稱顯示為 UI 文字。

若要建立 FaceAnalysis 類別:

  1. 以滑鼠右鍵按一下位於 [Project面板] 的[資產資料夾],然後按一下 [建立>][資料夾]。 呼叫 [腳本] 資料夾。

    Create the FaceAnalysis class.

  2. 按兩下剛才建立的資料夾,以開啟它。

  3. 在資料夾內按一下滑鼠右鍵,然後按一下[CreateC># 腳本]。 呼叫 FaceAnalysis腳本。

  4. 按兩下新的FaceAnalysis腳本,以Visual Studio 2017 開啟它。

  5. FaceAnalysis 類別上方輸入下列命名空間:

        using Newtonsoft.Json;
        using System.Collections;
        using System.Collections.Generic;
        using System.IO;
        using System.Text;
        using UnityEngine;
        using UnityEngine.Networking;
    
  6. 您現在必須新增用於還原序列化的所有物件。 這些物件必須在FaceAnalysis腳本外部新增 (下方大括弧) 。

        /// <summary>
        /// The Person Group object
        /// </summary>
        public class Group_RootObject
        {
            public string personGroupId { get; set; }
            public string name { get; set; }
            public object userData { get; set; }
        }
    
        /// <summary>
        /// The Person Face object
        /// </summary>
        public class Face_RootObject
        {
            public string faceId { get; set; }
        }
    
        /// <summary>
        /// Collection of faces that needs to be identified
        /// </summary>
        public class FacesToIdentify_RootObject
        {
            public string personGroupId { get; set; }
            public List<string> faceIds { get; set; }
            public int maxNumOfCandidatesReturned { get; set; }
            public double confidenceThreshold { get; set; }
        }
    
        /// <summary>
        /// Collection of Candidates for the face
        /// </summary>
        public class Candidate_RootObject
        {
            public string faceId { get; set; }
            public List<Candidate> candidates { get; set; }
        }
    
        public class Candidate
        {
            public string personId { get; set; }
            public double confidence { get; set; }
        }
    
        /// <summary>
        /// Name and Id of the identified Person
        /// </summary>
        public class IdentifiedPerson_RootObject
        {
            public string personId { get; set; }
            public string name { get; set; }
        }
    
  7. 不會使用 Start () Update () 方法,因此現在請加以刪除。

  8. FaceAnalysis 類別內,新增下列變數:

        /// <summary>
        /// Allows this class to behave like a singleton
        /// </summary>
        public static FaceAnalysis Instance;
    
        /// <summary>
        /// The analysis result text
        /// </summary>
        private TextMesh labelText;
    
        /// <summary>
        /// Bytes of the image captured with camera
        /// </summary>
        internal byte[] imageBytes;
    
        /// <summary>
        /// Path of the image captured with camera
        /// </summary>
        internal string imagePath;
    
        /// <summary>
        /// Base endpoint of Face Recognition Service
        /// </summary>
        const string baseEndpoint = "https://westus.api.cognitive.microsoft.com/face/v1.0/";
    
        /// <summary>
        /// Auth key of Face Recognition Service
        /// </summary>
        private const string key = "- Insert your key here -";
    
        /// <summary>
        /// Id (name) of the created person group 
        /// </summary>
        private const string personGroupId = "- Insert your group Id here -";
    

    注意

    金鑰personGroupId 取代為您先前建立之群組的服務金鑰和識別碼。

  9. 新增 Awake () 方法,以初始化 類別、將 ImageCapture 類別新增至 Main Camera,並呼叫 Label 建立方法:

        /// <summary>
        /// Initialises this class
        /// </summary>
        private void Awake()
        {
            // Allows this instance to behave like a singleton
            Instance = this;
    
            // Add the ImageCapture Class to this Game Object
            gameObject.AddComponent<ImageCapture>();
    
            // Create the text label in the scene
            CreateLabel();
        }
    
  10. 新增 CreateLabel () 方法,這個方法會建立 Label 物件以顯示分析結果:

        /// <summary>
        /// Spawns cursor for the Main Camera
        /// </summary>
        private void CreateLabel()
        {
            // Create a sphere as new cursor
            GameObject newLabel = new GameObject();
    
            // Attach the label to the Main Camera
            newLabel.transform.parent = gameObject.transform;
    
            // Resize and position the new cursor
            newLabel.transform.localScale = new Vector3(0.4f, 0.4f, 0.4f);
            newLabel.transform.position = new Vector3(0f, 3f, 60f);
    
            // Creating the text of the Label
            labelText = newLabel.AddComponent<TextMesh>();
            labelText.anchor = TextAnchor.MiddleCenter;
            labelText.alignment = TextAlignment.Center;
            labelText.tabSize = 4;
            labelText.fontSize = 50;
            labelText.text = ".";       
        }
    
  11. 新增 DetectFacesFromImage () GetImageAsByteArray () 方法。 前者會要求臉部辨識服務偵測提交影像中的任何可能臉部,而後者則需要將擷取的影像轉換成位元組陣列:

        /// <summary>
        /// Detect faces from a submitted image
        /// </summary>
        internal IEnumerator DetectFacesFromImage()
        {
            WWWForm webForm = new WWWForm();
            string detectFacesEndpoint = $"{baseEndpoint}detect";
    
            // Change the image into a bytes array
            imageBytes = GetImageAsByteArray(imagePath);
    
            using (UnityWebRequest www = 
                UnityWebRequest.Post(detectFacesEndpoint, webForm))
            {
                www.SetRequestHeader("Ocp-Apim-Subscription-Key", key);
                www.SetRequestHeader("Content-Type", "application/octet-stream");
                www.uploadHandler.contentType = "application/octet-stream";
                www.uploadHandler = new UploadHandlerRaw(imageBytes);
                www.downloadHandler = new DownloadHandlerBuffer();
    
                yield return www.SendWebRequest();
                string jsonResponse = www.downloadHandler.text;
                Face_RootObject[] face_RootObject = 
                    JsonConvert.DeserializeObject<Face_RootObject[]>(jsonResponse);
    
                List<string> facesIdList = new List<string>();
                // Create a list with the face Ids of faces detected in image
                foreach (Face_RootObject faceRO in face_RootObject)
                {
                    facesIdList.Add(faceRO.faceId);
                    Debug.Log($"Detected face - Id: {faceRO.faceId}");
                }
    
                StartCoroutine(IdentifyFaces(facesIdList));
            }
        }
    
        /// <summary>
        /// Returns the contents of the specified 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);
        }
    
  12. 新增 IdentifyFaces () 方法,這個方法會要求 臉部辨識服務 識別先前在提交的影像中偵測到的任何已知臉部。 要求會傳回已識別人員的識別碼,但不會傳回名稱:

        /// <summary>
        /// Identify the faces found in the image within the person group
        /// </summary>
        internal IEnumerator IdentifyFaces(List<string> listOfFacesIdToIdentify)
        {
            // Create the object hosting the faces to identify
            FacesToIdentify_RootObject facesToIdentify = new FacesToIdentify_RootObject();
            facesToIdentify.faceIds = new List<string>();
            facesToIdentify.personGroupId = personGroupId;
            foreach (string facesId in listOfFacesIdToIdentify)
            {
                facesToIdentify.faceIds.Add(facesId);
            }
            facesToIdentify.maxNumOfCandidatesReturned = 1;
            facesToIdentify.confidenceThreshold = 0.5;
    
            // Serialize to Json format
            string facesToIdentifyJson = JsonConvert.SerializeObject(facesToIdentify);
            // Change the object into a bytes array
            byte[] facesData = Encoding.UTF8.GetBytes(facesToIdentifyJson);
    
            WWWForm webForm = new WWWForm();
            string detectFacesEndpoint = $"{baseEndpoint}identify";
    
            using (UnityWebRequest www = UnityWebRequest.Post(detectFacesEndpoint, webForm))
            {
                www.SetRequestHeader("Ocp-Apim-Subscription-Key", key);
                www.SetRequestHeader("Content-Type", "application/json");
                www.uploadHandler.contentType = "application/json";
                www.uploadHandler = new UploadHandlerRaw(facesData);
                www.downloadHandler = new DownloadHandlerBuffer();
    
                yield return www.SendWebRequest();
                string jsonResponse = www.downloadHandler.text;
                Debug.Log($"Get Person - jsonResponse: {jsonResponse}");
                Candidate_RootObject [] candidate_RootObject = JsonConvert.DeserializeObject<Candidate_RootObject[]>(jsonResponse);
    
                // For each face to identify that ahs been submitted, display its candidate
                foreach (Candidate_RootObject candidateRO in candidate_RootObject)
                {
                    StartCoroutine(GetPerson(candidateRO.candidates[0].personId));
    
                    // Delay the next "GetPerson" call, so all faces candidate are displayed properly
                    yield return new WaitForSeconds(3);
                }           
            }
        }
    
  13. 新增 GetPerson () 方法。 藉由提供人員識別碼,此方法接著會要求 臉部辨識服務 傳回識別人員的名稱:

        /// <summary>
        /// Provided a personId, retrieve the person name associated with it
        /// </summary>
        internal IEnumerator GetPerson(string personId)
        {
            string getGroupEndpoint = $"{baseEndpoint}persongroups/{personGroupId}/persons/{personId}?";
            WWWForm webForm = new WWWForm();
    
            using (UnityWebRequest www = UnityWebRequest.Get(getGroupEndpoint))
            {
                www.SetRequestHeader("Ocp-Apim-Subscription-Key", key);
                www.downloadHandler = new DownloadHandlerBuffer();
                yield return www.SendWebRequest();
                string jsonResponse = www.downloadHandler.text;
    
                Debug.Log($"Get Person - jsonResponse: {jsonResponse}");
                IdentifiedPerson_RootObject identifiedPerson_RootObject = JsonConvert.DeserializeObject<IdentifiedPerson_RootObject>(jsonResponse);
    
                // Display the name of the person in the UI
                labelText.text = identifiedPerson_RootObject.name;
            }
        }
    
  14. 記得在返回 Unity 編輯器之前儲存變更。

  15. 在 Unity 編輯器中,將 FaceAnalysis 腳本從 [Project] 面板中的 [腳本] 資料夾拖曳至 [階層] 面板中的主要相機物件。 新的腳本元件將會新增至主要相機。

Place FaceAnalysis onto the Main Camera

第 7 章 - 建立 ImageCapture 類別

ImageCapture類別的目的是裝載與Azure 臉部辨識服務通訊所需的方法,以分析您將擷取的影像、識別臉部,以及判斷其是否屬於已知人員。 如果找到已知的人員,此類別會在場景中將其名稱顯示為 UI 文字。

若要建立 ImageCapture 類別:

  1. 在先前建立的 [腳本 ] 資料夾內按一下滑鼠右鍵,然後按一下 [ 建立]、 [C# 腳本]。 呼叫 ImageCapture腳本。

  2. 按兩下新的ImageCapture腳本,以使用 Visual Studio 2017 開啟它。

  3. 在 ImageCapture 類別上方輸入下列命名空間:

        using System.IO;
        using System.Linq;
        using UnityEngine;
        using UnityEngine.XR.WSA.Input;
        using UnityEngine.XR.WSA.WebCam;
    
  4. ImageCapture 類別內,新增下列變數:

        /// <summary>
        /// Allows this class to behave like a singleton
        /// </summary>
        public static ImageCapture instance;
    
        /// <summary>
        /// Keeps track of tapCounts to name the captured images 
        /// </summary>
        private int tapsCount;
    
        /// <summary>
        /// PhotoCapture object used to capture images on HoloLens 
        /// </summary>
        private PhotoCapture photoCaptureObject = null;
    
        /// <summary>
        /// HoloLens class to capture user gestures
        /// </summary>
        private GestureRecognizer recognizer;
    
  5. 新增Awake () Start () 初始化類別所需的方法,並允許HoloLens擷取使用者的手勢:

        /// <summary>
        /// Initialises this class
        /// </summary>
        private void Awake()
        {
            instance = this;
        }
    
        /// <summary>
        /// Called right after Awake
        /// </summary>
        void Start()
        {
            // Initialises user gestures capture 
            recognizer = new GestureRecognizer();
            recognizer.SetRecognizableGestures(GestureSettings.Tap);
            recognizer.Tapped += TapHandler;
            recognizer.StartCapturingGestures();
        }
    
  6. 新增 TapHandler () ,當使用者執行 選手勢時呼叫:

        /// <summary>
        /// Respond to Tap Input.
        /// </summary>
        private void TapHandler(TappedEventArgs obj)
        {
            tapsCount++;
            ExecuteImageCaptureAndAnalysis();
        }
    
  7. 新增 ExecuteImageCaptureAndAnalysis () 方法,這會開始進行影像擷取的程式:

        /// <summary>
        /// Begin process of Image Capturing and send To Azure Computer Vision service.
        /// </summary>
        private void ExecuteImageCaptureAndAnalysis()
        {
            Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending
                ((res) => res.width * res.height).First();
            Texture2D targetTexture = new Texture2D(cameraResolution.width, cameraResolution.height);
    
            PhotoCapture.CreateAsync(false, delegate (PhotoCapture captureObject)
            {
                photoCaptureObject = captureObject;
    
                CameraParameters c = new CameraParameters();
                c.hologramOpacity = 0.0f;
                c.cameraResolutionWidth = targetTexture.width;
                c.cameraResolutionHeight = targetTexture.height;
                c.pixelFormat = CapturePixelFormat.BGRA32;
    
                captureObject.StartPhotoModeAsync(c, delegate (PhotoCapture.PhotoCaptureResult result)
                {
                    string filename = string.Format(@"CapturedImage{0}.jpg", tapsCount);
                    string filePath = Path.Combine(Application.persistentDataPath, filename);
    
                    // Set the image path on the FaceAnalysis class
                    FaceAnalysis.Instance.imagePath = filePath;
    
                    photoCaptureObject.TakePhotoAsync
                    (filePath, PhotoCaptureFileOutputFormat.JPG, OnCapturedPhotoToDisk);
                });
            });
        }
    
  8. 新增完成相片擷取程式時所呼叫的處理常式:

        /// <summary>
        /// Called right after the photo capture process has concluded
        /// </summary>
        void OnCapturedPhotoToDisk(PhotoCapture.PhotoCaptureResult result)
        {
            photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
        }
    
        /// <summary>
        /// Register the full execution of the Photo Capture. If successful, it will begin the Image Analysis process.
        /// </summary>
        void OnStoppedPhotoMode(PhotoCapture.PhotoCaptureResult result)
        {
            photoCaptureObject.Dispose();
            photoCaptureObject = null;
    
            // Request image caputer analysis
            StartCoroutine(FaceAnalysis.Instance.DetectFacesFromImage());
        }
    
  9. 記得在返回 Unity 編輯器之前儲存變更。

第 8 章 - 建置解決方案

若要執行應用程式的完整測試,您必須將它側載至HoloLens。

在您執行之前,請確定:

  • 第 3 章中所述的所有設定都會正確設定。
  • 腳本 FaceAnalysis會附加至 Main Camera 物件。
  • 驗證 金鑰群組識別碼 都已在 FaceAnalysis 腳本中設定。

此時,您已準備好建置解決方案。 建置解決方案之後,您就可以開始部署應用程式。

若要開始建置程式:

  1. 按一下 [檔案]、[儲存],以儲存目前的場景。

  2. 移至 [檔案]、[建置設定],按一下 [新增開啟場景]。

  3. 請務必刻度 Unity C# 專案。

    Deploy the Visual Studio solution

  4. 按 [建置]。 這麼做之後,Unity 會啟動檔案總管視窗,您需要在其中建立,然後選取要建置應用程式的資料夾。 立即在 Unity 專案中建立該資料夾,並將其命名為 App。 然後選取 [應用程式] 資料夾後,按 [選取資料夾]。

  5. Unity 會開始建置您的專案,並輸出至 [應用程式] 資料夾。

  6. 一旦 Unity 完成建置 (可能需要一些時間) ,它會在組建的位置開啟檔案總管視窗。

    Deploy the solution from Visual Studio

  7. 開啟您的應用程式資料夾,然後開啟新的Project解決方案 (,如上所示,MR_FaceRecognition.sln) 。

第 9 章 - 部署您的應用程式

若要在HoloLens上部署:

  1. 您需要遠端部署) HoloLens (的 IP 位址,並確保HoloLens處於開發人員模式。 若要這樣做:

    1. 在HoloLens時,開啟設定
    2. 移至 [網路 & 網際網路 > ] > Wi-Fi [進階選項]
    3. 請注意 IPv4 位址。
    4. 接下來,流覽回設定,然後流覽& 回更新開發人員的安全性 >
    5. 設定 [開啟開發人員模式]。
  2. 流覽至新的 Unity 組建, ([應用程式] 資料夾) ,並使用Visual Studio開啟方案檔。

  3. 在 [方案組態] 中,選取 [ 偵錯]。

  4. 在 [解決方案平臺] 中,選取 [x86]、[ 遠端電腦]。

    Change the solution configuration

  5. 移至 [建置] 功能表,然後按一下 [部署方案],將應用程式側載至您的HoloLens。

  6. 您的應用程式現在應該會出現在您HoloLens上安裝的應用程式清單中,並準備好啟動!

注意

若要部署至沉浸式頭戴式裝置,請將 [解決方案平臺 ] 設定為 [ 本機電腦],並將 [組 ] 設定為 [ 錯],並將 x86 設定為 [平臺]。 然後使用 [建置] 功能表,選取 [ 部署解決方案],部署至本機電腦。

第 10 章 - 使用應用程式

  1. 戴著HoloLens,啟動應用程式。

  2. 查看您已向 臉部 API註冊的人員。 請確認:

    • 人臉不是太遠且清楚可見
    • 環境光源不是太深
  3. 使用點選手勢來擷取人員的圖片。

  4. 等候應用程式傳送分析要求並接收回應。

  5. 如果已成功辨識人員,該人員的名稱會顯示為 UI 文字。

  6. 您可以每隔幾秒鐘使用點選手勢重複擷取程式。

您已完成的 Azure 臉部 API 應用程式

恭喜,您已建置混合實境應用程式,利用 Azure 臉部辨識服務來偵測影像內的臉部,並識別任何已知的臉部。

outcome of completing this course

額外練習

練習 1

Azure 臉部 API功能強大,足以偵測單一影像中最多 64 個臉部。 擴充應用程式,使其可以辨識其他許多人的兩或三個臉部。

練習 2

Azure 臉部 API也可以提供所有類型的屬性資訊。 將此專案整合到應用程式中。 結合 表情 API時,這可能會更有趣。