MetaQuestProのフェイストラッキングでデータを取得する

# はじめに
MetaQuestProのフェイストラッキングでデータを取得する方法です。

一からプロジェクトを作成するのは大変なので，Metaが公開しているフェイストラッキングやボディトラッキングのサンプルパッケージであるUnity-Movementを利用します。

https://developer.oculus.com/documentation/unity/move-samples/

https://github.com/oculus-samples/Unity-Movement

# 事前知識
MetaQuestProのフェイストラッキングはFACS（Facial Action Coding System）に基づいた表情設計となっています。FACSについては以下の記事がわかりやすいです。

https://qiita.com/Takuya-Shuto-engineer/items/2a8958b19305e1a228e2

[リファレンス](https://developer.oculus.com/reference/unity/v57/class_o_v_r_face_expressions/)を読んでみると，OVRFaceExpressionsとして定義された各キーの値を利用することがができそうです。各キーが何を表しているのかを以下にまとめます。これはあくまで私の予想なので，間違いがあるかもしれません。

| OVRFaceExpressions | 内容 |
|:-:|:-:|
| BrowLowererL  | 左眉全体を下げる  |
| BrowLowererR  | 左眉全体を上げる  |
| CheekPuffL  |  左頬を膨らませる |
| CheekPuffR | 右頬を膨らませる  |
| CheekRaiserL  | 左頬を引き上げる  |
| CheekRaiserR  |  右頬を引き上げる |
| CheekSuckL  | 左頬を吸い込む  |
| CheekSuckR  | 右頬を吸い込む  |
| ChinRaiserB  | 下顎をあげる？  |
| ChinRaiserT  |  上顎をあげる？ |
| DimplerL  | 左側のえくぼを作る  |
| DimplerR  | 左側のえくぼを作る  |
| EyesClosedL  | 左目を閉じる  |
| EyesClosedR  | 右目を閉じる  |
| EyesLookDownL  | 左目を下に動かす  |
| EyesLookDownR  | 右目を下に動かす  |
| EyesLookLeftL  |  左目を左に動かす |
| EyesLookLeftR  | 右目を左に動かす  |
| EyesLookRightL  | 左目を右に動かす  |
| EyesLookRightR  | 右目を右に動かす  |
|  EyesLookUpL | 左目を上に動かす  |
| EyesLookUpR  | 右目を上に動かす  |
| InnerBrowRaiserL  | 左眉の内側を引き上げる  |
| InnerBrowRaiserR  | 右眉の内側を引き上げる  |
| JawDrop  | 顎を下げる  |
| JawSidewaysLeft  | 顎を左に動かす  |
| JawSidewaysRight  | 顎を右に動かす  |
| JawThrust  | 顎を突き出す  |
| LidTightenerL  | 左目を細める  |
| LidTightenerR  | 右目を細める  |
| LipCornerDepressorL  | 左口角を下げる  |
| LipCornerDepressorR  | 右口角を下げる  |
| LipCornerPullerL | 左口角を上げる  |
| LipCornerPullerR  | 右口角を上げる  |
| LipFunnelerLB  | 下唇の左側をすぼめる  |
| LipFunnelerLT  | 上唇の左側をすぼめる  |
| LipFunnelerRB | 下唇の右側をすぼめる  |
| LipFunnelerRT | 上唇の右側をすぼめる  |
| LipPressorL  | 唇の左側を押し合わせる  |
| LipPressorR  | 唇の右側を押し合わせる |
| LipPuckerL  | 唇の左側を突き出す  |
| LipPuckerR  | 唇の右側を突き出す |
| LipStretcherL  | 左側の唇を横に広げる  |
| LipStretcherR | 右側の唇を横に広げる  |
| LipSuckLB  | 下唇の左側を巻き込む  |
| LipSuckLT  | 上唇の左側を巻き込む  |
| LipSuckRB |  下唇の右側を巻き込む |
| LipSuckRT  | 上唇の右側を巻き込む  |
| LipTightenerL  | 左の口角を引き締める  |
| LipTightenerR | 右の口角を引き締める  |
| LipsToward  |唇を突き出す   |
| LowerLipDepressorL  | 左の下唇を引き下げる  |
| LowerLipDepressorR  |  右の下唇を引き下げる |
| MouthLeft | 口を左に歪める  |
| MouthRight | 口を右に歪める  |
| NoseWrinklerL  | 鼻を左に引き上げる  |
| NoseWrinklerR  | 鼻を右に引き上げる  |
| OuterBrowRaiserL | 左眉の外側を上げる  |
| OuterBrowRaiserR  | 右眉の外側を上げる  |
| UpperLidRaiserL  | 左の上まぶたを引き上げる  |
| UpperLidRaiserR  | 右の上まぶたを引き上げる  |
| UpperLipRaiserL  | 左の上唇を引き上げる  |
| UpperLipRaiserR  | 右の上唇を引き上げる  |

それぞれの値は0.0～1.0のfloat型で管理されており，値が大きいほどその部分を大きく動かしているという意味になります。

### Unity-Movementの環境構築

Unity-Movementを動かすためにはUnityの初期設定が必要なので，以下の記事を参考に設定を行いました。

https://bluebirdofoz.hatenablog.com/entry/2022/11/20/025234

# ソースコードの修正

まずはデータを取得できることを確認します。修正するのは以下のソースコードです。
```
PATH_TO_UNITY_PROJECT/プロジェクト名/Assets/Oculus/VR/Scripts/OVRFaceExpression.cs
```

毎フレーム呼び出されるUpdateメソッドを修正します。GetWeightメソッドが用意されているので，FaceExpressionのキーを引数に渡してあげることで，値を取得することができます。
```c#:OVRFaceExpression.cs
private void Update() {
        ValidExpressions = OVRPlugin.GetFaceState(OVRPlugin.Step.Render, -1, ref _currentFaceState) && _currentFaceState.Status.IsValid;

        EyeFollowingBlendshapesValid = ValidExpressions && _currentFaceState.Status.IsEyeFollowingBlendshapesValid;

        if (ValidExpressions) {
            // これでBrowLowererLの値がfloat型でLog出力されるはず
            Debug.Log(GetWeight(FaceExpression.BrowLowererL));
        }
    }
```

この状態でビルド，実行してLogcatなどのLog出力ツールで見てみると，以下の様にちゃんとデータが出力できていることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359441/73eca688-474e-a6cc-9e70-0bc87256e8e3.png)



# 終わりに
次はこれらのデータをCSVに出力してみようと思います。