共用方式為


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 月)。 您可以自由使用最新的軟體,如安裝工具文章中所列,不過不應該假設本課程中的資訊會完全符合您在較新的軟體中找到的內容,而不是下面所列的內容。

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

在您開始使用 Intune 之前

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

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

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

第 1 章 - Azure 入口網站

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

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

    注意

    如果您還沒有 Azure 帳戶,則必須建立一個帳戶。 如果您在教室或實驗室情況中遵循本教學課程,請洽詢您的講師或其中一名監看員,以協助設定您的新帳戶。

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

    搜尋臉部 API

    注意

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

  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. 在本教學課程中,您的應用程式必須呼叫您的服務,這是透過使用您服務的訂用帳戶「金鑰」來完成。 從臉部 API 服務的 [快速啟動] 頁面,第一個點是數位 1,以擷取密鑰。

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

    注意

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

第 2 章 - 使用「人員製作者」UWP 應用程式

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

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

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

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

注意

這些只是所需的步驟,如何執行這些動作的指示會進一步向下檔。 Person Maker 應用程式可讓您:

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

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

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

  • 訓練 您的 Azure 臉部 API 服務

請注意,若要訓練此應用程式來辨識人員,您將需要每個想要新增至人員群組的 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. 按兩下 [ 本機計算機 ],應用程式將會啟動。 請注意,在較小的螢幕上,所有內容都可能無法顯示,不過您可以進一步向下卷動來檢視它。

    人員製作者用戶介面

  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 設定] 中,勾選 [支援虛擬現實] 中的 [XR 設定],確定已新增 Windows Mixed Reality SDK

      更新 X R 設定。

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

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

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

第 4 章 - 主要相機設定

重要

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

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

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

    1. Camera 對象必須命名為主相機(請注意拼字!)

    2. 主相機 標記 必須設定為 MainCamera (請注意拼字!

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

    4. 將清除旗標設定純色

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

      設定相機元件

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

重要

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

若要協助您還原串行化和串行化接收並傳送至 Bot 服務的物件,您需要下載 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. 以滑鼠右鍵按下您先前建立的 Scripts 資料夾,然後按兩下 [ 建立], C# 腳稿。 呼叫 Script 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. 新增在使用者執行 Tap 手勢時所呼叫的 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,這可能更有趣。