Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9374

HoloLens2 × Azure Cognitive Services (Face API 編)

$
0
0

はじめに

HoloLensアドベントカレンダー1日目の記事です!
APIよくわからないと弟子から相談があったので、Cognitive Services系をまとめていきたいと思いまーす。
今日は、Cognitive ServicesのFace APIをHoloLens2でやってみました。
実機なしでもできるのでやってみてください。

開発環境

  • Unity 2019.4.1f1
  • MRTK 2.5.1
  • OpenCV for Unity
  • Windows PC

導入

1.AzureポータルからFace APIを作成し、エンドポイントとサブスクリプションキーをメモしておいてください。
image.png
image.png

2.Unityでプロジェクトを作成、MRTK2.5.1をインポートします。なんかウィンドウでたらApplyします。

3.メニューのMixed Reality Toolkit->Add to Scene and Configureしてください。
image.png

4.Build Settingsから、Universal Windows PlatformにSwitch Platformして、以下のように設定してください。あとAdd Open ScenesでScenes/SampleSceneにチェックが入っていることを確認します。

image.png

5.MixedRealityToolkitのDefaultHoloLens2ConfigureProfileをcloneし、Diagnostics->Enable Diagnostics Systemのチェックを外します。これでCPU使用率とかのデバッグ情報を非表示にできます。

image.png

6.Project SettingsのXR Settings、Publishing Settings->Capabilitiesを以下のように設定してください。
image.png

image.png

7.空のGameObjectを作成し、名前を「TapToCapture」にします。
image.png

8.Add Componentから「TapToCapture.cs」スクリプトを作成します。エアタップしたら、画像をキャプチャし、Face APIに投げるスクリプトになります。

TapToCapture.cs
usingSystem.Collections;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem;usingUnityEngine;usingMicrosoft.MixedReality.Toolkit.Utilities;usingSystem.Threading.Tasks;usingOpenCVForUnity.CoreModule;usingOpenCVForUnity.UnityUtils;usingOpenCVForUnity.ImgprocModule;publicclassTapToCapture:MonoBehaviour{publicGameObjectquad;[System.Serializable]publicclassFace{publicstringfaceId;publicFaceRectanglefaceRectangle;publicFaceAttributefaceAttributes;}[System.Serializable]publicclassFaceRectangle{publicinttop;publicintleft;publicintwidth;publicintheight;}[System.Serializable]publicclassFaceAttribute{publicfloatage;publicstringgender;}UnityEngine.Windows.WebCam.PhotoCapturephotoCaptureObject=null;Texture2DtargetTexture=null;privatestringendpoint="https://<Your Endpoint>.cognitiveservices.azure.com/face/v1.0/detect";privatestringsubscription_key="<Insert Your API Key>";publicvoidAirTap(){ResolutioncameraResolution=UnityEngine.Windows.WebCam.PhotoCapture.SupportedResolutions.OrderByDescending((res)=>res.width*res.height).First();targetTexture=newTexture2D(cameraResolution.width,cameraResolution.height);// PhotoCapture オブジェクトを作成しますUnityEngine.Windows.WebCam.PhotoCapture.CreateAsync(false,delegate(UnityEngine.Windows.WebCam.PhotoCapturecaptureObject){photoCaptureObject=captureObject;UnityEngine.Windows.WebCam.CameraParameterscameraParameters=newUnityEngine.Windows.WebCam.CameraParameters();cameraParameters.hologramOpacity=0.0f;cameraParameters.cameraResolutionWidth=cameraResolution.width;cameraParameters.cameraResolutionHeight=cameraResolution.height;cameraParameters.pixelFormat=UnityEngine.Windows.WebCam.CapturePixelFormat.BGRA32;// カメラをアクティベートしますphotoCaptureObject.StartPhotoModeAsync(cameraParameters,delegate(UnityEngine.Windows.WebCam.PhotoCapture.PhotoCaptureResultresult){// 写真を撮りますphotoCaptureObject.TakePhotoAsync(OnCapturedPhotoToMemoryAsync);});});}asyncvoidOnCapturedPhotoToMemoryAsync(UnityEngine.Windows.WebCam.PhotoCapture.PhotoCaptureResultresult,UnityEngine.Windows.WebCam.PhotoCaptureFramephotoCaptureFrame){// ターゲットテクスチャに RAW 画像データをコピーしますphotoCaptureFrame.UploadImageDataToTexture(targetTexture);byte[]bodyData=targetTexture.EncodeToJPG();Responseresponse=newResponse();try{// string query = endpoint + "?detectionModel=detection_02&returnFaceId=true";// string query = endpoint + "?detectionModel=detection_01&returnFaceId=true&returnFaceLandmarks=false&returnFaceAttributes=age,gender,headPose,smile,facialHair,glasses,emotion,hair,makeup,occlusion,accessories,blur,exposure,noise";stringquery=endpoint+"?detectionModel=detection_01&returnFaceId=true&returnFaceAttributes=age,gender";varheaders=newDictionary<string,string>();headers.Add("Ocp-Apim-Subscription-Key",subscription_key);// headers.Add("Content-Type", "application/octet-stream");response=awaitRest.PostAsync(query,bodyData,headers,-1,true);}catch(Exceptione){photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);return;}if(!response.Successful){photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);return;}Debug.Log(response.ResponseCode);Debug.Log(response.ResponseBody);stringnewResponseBody="{ \"results\": "+response.ResponseBody+"}";Face[]faces=JsonHelper.FromJson<Face>(newResponseBody);MatimgMat=newMat(targetTexture.height,targetTexture.width,CvType.CV_8UC4);Utils.texture2DToMat(targetTexture,imgMat);// Debug.Log("imgMat.ToString() " + imgMat.ToString());foreach(varfaceinfaces){//Debug.Log(face.faceId);//Debug.Log(face.faceRectangle.left);//Debug.Log(face.faceRectangle.top);//Debug.Log(face.faceRectangle.width);//Debug.Log(face.faceRectangle.height);Imgproc.putText(imgMat,face.faceAttributes.age.ToString()+","+face.faceAttributes.gender,newPoint(face.faceRectangle.left,face.faceRectangle.top-10),Imgproc.FONT_HERSHEY_SIMPLEX,1.5,newScalar(0,0,255,255),2,Imgproc.LINE_AA,false);Imgproc.rectangle(imgMat,newPoint(face.faceRectangle.left,face.faceRectangle.top),newPoint(face.faceRectangle.left+face.faceRectangle.width,face.faceRectangle.top+face.faceRectangle.height),newScalar(0,0,255,255),2);}Texture2Dtexture=newTexture2D(imgMat.cols(),imgMat.rows(),TextureFormat.RGBA32,false);Utils.matToTexture2D(imgMat,texture);// テクスチャが適用されるゲームオブジェクトを作成// GameObject quad = GameObject.CreatePrimitive(PrimitiveType.Quad);   RendererquadRenderer=quad.GetComponent<Renderer>()asRenderer;// quadRenderer.material = new Material(Shader.Find("Unlit/UnlitTexture"));// quad.transform.parent = this.transform;// quad.transform.localPosition = new Vector3(0.0f, 0.0f, 3.0f);quadRenderer.material.SetTexture("_MainTex",texture);// カメラを非アクティブにしますphotoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);}voidOnStoppedPhotoMode(UnityEngine.Windows.WebCam.PhotoCapture.PhotoCaptureResultresult){// photo capture のリソースをシャットダウンしますphotoCaptureObject.Dispose();photoCaptureObject=null;}}

9.PhotoCaptureのサンプルはこちらです。エアタップしたら、画像キャプチャするようにInputActionHandlerをAdd Componentし、AirTap関数を作成します。エアタップ(On Input Action Started)したらAirTap関数が発火するように設定します。

10.撮影できたら、targetTextureに画像データが入っているので、JPGにエンコードして、Face APIに投げます。FaceAPIのサンプルはこちらC#Pythonです。

11.endpointとsubscription_keyにメモしておいたものを貼り付けてください。

12.クエリパラメータは、detection_01モデルを使用、FaceId、年齢と性別を返すように設定しています。

<your endpoint>?detectionModel=detection_01&returnFaceId=true&returnFaceAttributes=age,gender"

ちなみにfaceAttributesはsmile, headPose, gender, age, facialHair, glasses, emotion, blur, exposure, noise, makeup, accessories, occlusion, hairといった情報が取れます。

13.MRTKのRestを用いてHTTPリクエストします。
ヘッダーは、"Ocp-Apim-Subscription-Key": subscription_keyを指定、"Content-Type": "application/octet-stream"はRestの中でやってくれるのでコメントアウトします。

14.クエリと画像データ、ヘッダーをPOSTします。
response = await Rest.PostAsync(query, bodyData, headers, -1, true);

15.response.ResponseBodyが下記のように返ってくればOKです。

[{"faceId":"f1b97cf1-58d0-4dc9-9169-e19cb0655e48","faceRectangle":{"top":347,"left":451,"width":285,"height":285},"faceAttributes":{"gender":"male","age":23.0}}]

16.Face APIのResponseBodyがリストのjsonになっているので、パースできるようにJsonHelper.csスクリプトを作成します。

JsonHelper.cs
usingUnityEngine;usingSystem;publicclassJsonHelper{publicstaticT[]FromJson<T>(stringjson){Wrapper<T>wrapper=JsonUtility.FromJson<Wrapper<T>>(json);returnwrapper.results;}[Serializable]privateclassWrapper<T>{publicT[]results;}}

JsonHelperについて
- yuiyoichi/JsonHelper.cs
- How to load an array with JsonUtility?
- UnityのJsonUtilityでJSON配列を処理する

17.返ってきたResponseBodyを次のようにすることで、パースすることが可能になります。

{"results":[{...}]}

18.あとは仕様に合わせてFaceクラスとFaceRectangleクラス、FaceAttributeクラスを作成しました。

19.顔検出結果をOpenCVを使って画像に描画し、Quadのマテリアルに割り当てます。3D Object->Quadを作成しましょう。
image.png

OpenCV for Unity サンプルはこちら
- Texture2DからMatに変換
- 矩形を描画(Imgproc.rectangle)
- テキストを描画(Imgproc.putText)

20.OrbitalをAdd Componentし、Quadがカメラに追従するようにしています。
image.png

21.TapToCaptureにQuadをD&Dしてアタッチしたら完成です。

実行

HoloLens2にデプロイして、実行した結果がこちらになります。Editor上でもできるので試してみてください。
20201201_043500_HoloLens.jpg

お疲れ様でした。
明日は弟子(@Horomoto-Asahi)による「HoloLens 2のSpatialAwarenessの調査」です。


Viewing all articles
Browse latest Browse all 9374

Latest Images

Trending Articles