本日はチュートリアルの実施枠です。
Academyの「MR and Azure 304: Face recognition」の実施内容をまとめます。
docs.microsoft.com
前回記事の続きです。
bluebirdofoz.hatenablog.com
今回は「Chapter 6」です。
Chapter 6:Create the FaceAnalysis class
最初に作成するスクリプトは FaceAnalysis クラスです。
FaceAnalysisクラスの目的は、Azure Face Recognition Service との通信に必要なメソッドをホストすることです。
・キャプチャ画像を送信した後、それを分析し、その中の顔を識別し、既知の人物に属するかどうかを判断する。
・既知の人物が見つかった場合、このクラスはその名前をシーン内のUIテキストとして表示します。
1.Script フォルダを作成します。
Asset フォルダで右クリックし、Create > Folder を選択します。
2-3.作成した Script フォルダを開き、フォルダ内で右クリックして、Creapte -> C# Script を選択します。
Script の名称は FaceAnalysis に設定します。
4.新しいスクリプトをダブルクリックしてVisual Studioで開きます。
5-13.以下の通り、スクリプトを編集します。
・FaceAnalysis.cs
// 名前空間の追加 using Newtonsoft.Json; using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; using UnityEngine; using UnityEngine.Networking; public class FaceAnalysis : MonoBehaviour { // デシリアライズに使用されるオブジェクトクラスの追加 /// <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 /// 特定された人の名前とID /// </summary> public class IdentifiedPerson_RootObject { public string personId { get; set; } public string name { get; set; } } // メンバ変数の追加 /// <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 /// 作成された人物グループのID(名前) /// </summary> private const string personGroupId = "- Insert your group Id here -"; /// <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 // ゲームオブジェクトに ImageCapture クラスを追加する gameObject.AddComponent<ImageCapture>(); // Create the text label in the scene // シーンにテキストラベルを作成する CreateLabel(); } /// <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 = "."; } /// <summary> /// Detect faces from a submitted image /// 送信された画像から顔を検出する /// </summary> internal IEnumerator DetectFacesFromImage() { WWWForm webForm = new WWWForm(); string detectFacesEndpoint = string.Format("{0}detect", baseEndpoint); // 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 // 画像で検出された顔の顔IDを含むリストを作成する foreach (Face_RootObject faceRO in face_RootObject) { facesIdList.Add(faceRO.faceId); Debug.Log(string.Format("Detected face - Id: {0}", 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); } /// <summary> /// Identify the faces found in the image within the person group /// 人物グループの中から画像に含まれる顔を特定する /// 要求は識別された人の ID を返すが、名前は返さない /// </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; // Serialise to Json format // Json形式にシリアライズ string facesToIdentifyJson = JsonConvert.SerializeObject(facesToIdentify); // Change the object into a bytes array // オブジェクトをバイト配列に変更する byte[] facesData = Encoding.UTF8.GetBytes(facesToIdentifyJson); WWWForm webForm = new WWWForm(); string detectFacesEndpoint = string.Format("{0}identify", baseEndpoint); 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(string.Format("Get Person - jsonResponse: {0}", 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 // 次の「GetPerson」コールを遅らせることで、すべての顔候補が正しく表示される yield return new WaitForSeconds(3); } } } /// <summary> /// Provided a personId, retrieve the person name associated with it /// ID を指定すると、それに関連付けられた人名(name)を取得する /// </summary> internal IEnumerator GetPerson(string personId) { string getGroupEndpoint = string.Format("{0}persongroups/{1}/persons/{2}", baseEndpoint, personGroupId, 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(string.Format("Get Person - jsonResponse: {0}", jsonResponse)); IdentifiedPerson_RootObject identifiedPerson_RootObject = JsonConvert.DeserializeObject<IdentifiedPerson_RootObject>(jsonResponse); // Display the name of the person in the UI // ユーザーの名前をUIに表示する labelText.text = identifiedPerson_RootObject.name; } } }
※ この時点でエラーが表示されます(「The name ‘ImageCapture’ does not exist...」)。
これは、コードが ImageCapture クラスを参照するためです。ImageCapture クラスは、次の章で作成します
key と personGroupId を、第1章と第2章で作成したグループのサービスキーと ID で置き換えます。
14.Visual Studio で変更を保存して Unity に戻ります。
15.Hierarchy パネルの MixedRealityCameraParent を開きます。直下に MixedRealityCamera オブジェクトがあります。
FaceAnalysis スクリプトをこの MixedRealityCameraオブジェクトにドラッグして追加します。
Chapter 5 はここまでです。
次回は Chapter 6 を実施します。
bluebirdofoz.hatenablog.com