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


注意

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


完成本課程的結果

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

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

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

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

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

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

裝置支援

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

注意

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

必要條件

注意

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

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

開始之前

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

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

如需感測器微調的說明,請遵循此 連結至 HoloLens 感測器微調文章

第 1 章 - Azure 入口網站

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

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

    注意

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

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

    搜尋臉部 API

    注意

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

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

    臉部 API 資訊

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

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

    2. 選取一個訂用帳戶。

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

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

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

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

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

    7. 選取 [建立]。*

      建立臉部 API 服務

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

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

    服務建立通知

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

    移至資源通知

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

    存取臉部 API 金鑰

  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 套件]) 。

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

    人員製作者用戶介面

  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 ,然後按兩下 [ 新增]。

    啟動新的 Unity 專案。

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

    提供新 Unity 專案的詳細數據。

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

    更新文本編輯器喜好設定。

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

    [建置設定] 視窗,將平臺切換至 UWP。

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

    1. 目標裝置 設定為 HoloLens

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

    2. 組建類型 設定為 D3D

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

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

    5. [建置並執行 ] 設定為 [本機計算機]

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

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

        按兩下 [新增開啟的場景] 按鈕

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

        建立新的腳本資料夾

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

        為新的場景指定名稱。

    7. 其餘設定在 [建置設定] 中,現在應該保留為預設值。

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

    開啟播放機設定。

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

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

      1. 腳本運行時間版本 應該是 實驗 性 (.NET 4.6 對等) 。 變更這會觸發需要重新啟動編輯器。

      2. 腳本後端 應該是 .NET

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

        更新其他設定。

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

      • InternetClient

      • 網路攝影機

        更新發佈設定。

    3. 進一步在面板下方的 [XR 設定] (中找到 [發佈設定]) ,勾選 [支援的虛擬實境],確定已新增 Windows Mixed Reality SDK

      更新 X R 設定。

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

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

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

第 4 章 - 主要相機設定

重要

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

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

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

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

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

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

    4. 清除旗標 設定為 純色

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

      設定相機元件

第 5 章 – 匯入 Newtonsoft.Json 連結庫

重要

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

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

若要匯入連結庫:

  1. 下載 Unity 套件。

  2. 按兩下 [資產]、[ 匯入套件]、 [自定義套件]。

    匯入 Newtonsoft.Json

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

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

    匯入 Newtonsoft.Json 資產

第 6 章 - 建立 FaceAnalysis 類別

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

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

若要建立 FaceAnalysis 類別:

  1. 在 [ 資產資料夾 ] 的 [項目面板] 中按下滑鼠右鍵,然後按兩下 [ 建立>資料夾]。 呼叫 [腳稿] 資料夾。

    建立 FaceAnalysis 類別。

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

  3. 在資料夾內按下滑鼠右鍵,然後按兩下 [ 建立>C# 腳本]。 呼叫 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 腳本從 [專案] 面板中的 [腳本] 資料夾拖曳至 [ 階層] 面板中的主要相機物件。 新的腳本元件將會新增至主要相機。

將 FaceAnalysis 放在主要相機上

第 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# 專案。

    部署 Visual Studio 解決方案

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

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

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

    從 Visual Studio 部署解決方案

  7. 開啟您的應用程式資料夾,然後開啟新的專案方案 (,如上所示,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] [ 遠端計算機]。

    變更解決方案組態

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

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

注意

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

第 10 章 - 使用應用程式

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

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

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

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

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

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

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

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

完成本課程的結果

額外練習

練習 1

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

練習 2

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