HoloLens 2 × MRTK v2.3 で アイトラッキング機能を追加する方法

# Overview : 概要

HoloLens 2 から ``Eye Tracking`` のデータを取得できるようになりました :clap_tone1:！ユーザーから取得した視線データの活用方法 ( 操作のトリガーやデータの可視化・分析など ) は数多く考えられ、今後 HoloLens 2 アプリケーション開発で重要なポイントとなると思われるため、基礎情報を整理しておきたいと思います。まず今回は Unity プロジェクトに対して ``Eye Tracking`` 機能を追加する方法を公式ドキュメント __「 [Getting started with eye tracking in MRTK](https://microsoft.github.io/MixedRealityToolkit-Unity/Documentation/EyeTracking/EyeTracking_BasicSetup.html) 」__ に従って、解説 & 紹介します。

# TOC ( Table of Contents ) : 目次

- __Expected : 本記事を読んで実現できること__
- __Development Environment : 開発環境__
- __Detail of HoloLens 2's Hardware ( Sensor ) : ハードウェアについて__
- __Required : 必要事項__
- __Procedure : 導入手順__
- __Appendix : 追加情報__

# Expected : 本記事を読んで実現できること

- MRTK v2.3 を活用してアイトラッキングの挙動を検証すること

# Development Environment : 開発環境
- Windows PC ( Windows 10 / ``SDK 10.0.18362`` )
 - Unity ( ``2018.4.3f1`` )
 - Visual Studio ( ``Community 2017`` )
 - Mixed Reality Toolkit ( ``MRTK v2.3`` )
 
- HoloLens 2 ( ``OSビルド番号 : 10.0.18362.1053``  )

# Detail of HoloLens 2's Hardware ( Sensor ) : ハードウェアについて
実装を行う前に Microsoft HoloLens 2 に搭載されているセンサー系を簡単にご紹介したいと思います。
Microsoft HoloLens 2 には、以下のセンサー類 ( 下表 ) が搭載されています。

| HoloLens 2 Sensor | 概要 |
| :--- | :--- |
| Head Tracking | 4台の可視光カメラ |
| <font color="black">__Eye Tracking__</font> | <font color="black">__2台の赤外線カメラ__</font> |
| Depth | 1-MP ToF 深度センサー ( ※ Time of Flight ) |
| IMU ( ``Inertial Measurement Unit`` ) | 加速度計、ジャイロスコープ、磁力計 |
| Camera | 静止画 8MP/ 動画 1080p30 |  

``Eye Tracking`` には、HoloLens 2 ディスプレイ部分に搭載されている __2つの赤外線センサー ( 参考 : 下図 )__ が使用されます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180432/f302e9cd-fdba-7f75-3027-a2839a0f436b.jpeg" width="300">


# Required : 必要事項
``Eye Tracking`` 機能を正しく動作させるためには、以下の要件に対応する必要があります。

- ✅ 入力システムに 「 ``Eye Gaze Data Provider`` 」 を追加する必要があります。  
　 ( これによりプラットフォーム側からアイトラッキングデータを取得することができるようになります。 )
- ✅ MixedRealityPlaySpace > Main Camera にアタッチされた
   　「 Gaze Provider ( Component ) 」 の [ ``Use Eye Tracking`` ] 項目にチェックを入れる必要がります。
- ✅ Visual Stuido ビルドでビルドする際に ``Package.appxmanifest`` の 「 ``Capability : GazeInput`` 」 を有効にする必要があります。
- ✅ HoloLens を装着しているユーザーにて ``Eye Tracking`` をキャリブレーションする必要があります。

<font color="black">__【 重要 】 これらの要件が満たされていない場合は、「 Eye Tracking 」 ではなく、<font color="red"> 「 Head Gaze Tracking 」 </font>に自動的に切り替わります。__</font>

# Procedure : 導入手順
それでは実際にアイトラッキングを動作させるサンプルアプリを作成してみたいと思います。

__【 前提条件 】 以下、MRTK パッケージをインポート済みであること。__

- Microsoft.MixedReality.Toolkit.Unity.Foundation.2.3.0.unitypackage

#### 01. Unity プロジェクト 「New Scene」 を開きます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180432/491d6ed4-70f5-23f4-30de-c26ecf768536.png" width="700">

### 02. グローバルメニュー [ Mixed Reality Toolkit ] > 「 Add to Scene and Configure ... ( 赤枠部分 ) 」 を実行します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180432/1eb2303d-b9ed-80f7-9def-ec439f0b5c67.png" width="500">

上記ボタンを押下すると、以下ゲームオブジェクトがヒエラルキーに追加されます。

- __MixedRealityToolkit__
- __MixedRealityPlayspace__

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180432/8a464803-9d2f-13bd-3984-6731506a8850.png" width="600">

### 03. MRTK : Input System に ``Eye Gaze data Provider`` を追加します。
既存の標準プロファイルでは、独自に設定の追加・変更できないので、新規プロファイルを作成します。
``Hierarchy`` より ``MixedRealityToolkit`` を選択し、``Inspector`` を確認します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180432/640529ba-bc1d-4f47-5181-47ff3ebea8e2.png" width="600">

MixedRealityToolkit ( Componet ) の最上部、プロファイルの ``Copy & Customize`` ボタンを押下します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180432/7ee3133c-8fc8-8bf5-01c8-06ecc4424775.png" width="400">

上記ウィザードが表示されるので、「 __Cloning Profile :__ ``DefaultHoloLens2ConfigurationProfile`` 」、「 __Profile Name :__ ``EyeTrackingSampleMixedRealityToolkitConfigurationProfile`` (任意) 」 の入力を確認し、``Clone``ボタンを押下します。
( ※ カスタムプロファイルは、``MixedRealityToolkit.Generated`` > ``CustomProfiles`` 配下に格納されます。 )


続いて Input System のプロファイルを ``Clone`` します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180432/2325f6d2-c109-4916-c344-1cfe87626a1d.png" width="600">

さきほどと同様のウィザードが表示されるので、同じように ``Clone`` します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180432/33dc14b4-492b-3433-a36e-9d53310b47bf.png" width="400">

プロファイルの ``Clone`` が完了すると、設定の変更が可能になります。
変更が可能になった ``Input System settings`` 内に Eye Tracking 用のデータプロバイダーを追加していきます。
まず、``+ Add Data Provider`` ボタンを押下します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180432/e9f2551f-1da9-aade-bef7-d1b6d9aab6d7.png" width="600">

最下部に ``New Data Provider 10`` という名前のデータプロバイダーが追加されます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180432/22618404-5d91-2759-d6da-baee27630a5e.png" width="600">

上記で追加したデータプロバイダーに、``Eye Tracking`` 設定を追加します。
Type 項目の下矢印ボタンを押下し、[ ``Microsoft.MixedReality.Toolkit.WindowsMixedReality.Input`` ] > [ ``WindowsMixedRealityEyeGazeDataProvider`` ] を選択します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180432/bb1d91a6-71ac-1ebe-c060-b79e923afddd.png" width="600">

これで Eye Gaze Provider の登録が完了します。

### 04. Gaze Provider にて Eye Tracking を有効化します。
``MixedRealityPlayspace`` > ``Main Camera`` にアタッチされている ``Gaze Provider`` の __[ Use Eye Tracking ]__ にチェックをいれます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180432/6a050905-d9b2-bca5-a312-3049ee39594a.png" width="600">


### 05. EyeGazeProvider で取得できる主なデータ
 
| EyeGazeProvider メンバ変数 | 型 | 概要 | 備考 |
| :--- | :--- | :--- | :--- |
| UseEyeTracking | bool | 使用ハードウェアがアイトラッキング可能かつ、アプリの権限 [視線入力] が有効になっている場合 true 、それ以外の場合は false を返す | |
| IsEyeCalibrationValid | bool? ( null許容値 ) | 装着者のアイトラッキング機能のキャリブレーション有無 ( ``true`` / ``false`` ) を示す。アイトラッキングシステムからデータを受け取っていない場合は ``null`` を返す。 | 視線入力のパーミッションが有効ではない場合 ``null`` が返ってくる。 |
| IsEyeGazeValid | bool | 現在のアイトラッキングデータが有効かどうか ( ``true`` / ``false`` ) を示す。 タイムアウト時間を超えた場合、ハードウェアのトラッキングが欠如している場合、パーミッションの問題などで無効になる可能性があります。 | 極度にディスプレイの外に視線を向けるとアイトラッキングが失われ、``False`` が返ってくる。 |
| GazeOrigin | Vector3 | 視線の原点。``IsEyeGazeValid`` が ``false`` の場合、Head-Gaze の原点を返すので注意。 | |
| GazeDirection | Vector3 | 視線の向き。``IsEyeGazeValid`` が ``false`` の場合、Head-Gaze の向きを返すので注意。 | |
| HitInfo, HitPosition, HitNormal | ー | 現在見ているターゲット ( GameObject ) の情報を返します。 | |


### 06. Eye Gaze Provider から値を取得する
Data Provider は Mixed Reality Toolkit Service からデータを取得するコンポーネントです。
今回の場合、Input System ( Service ) から Eye Gaze Provider 経由でトラッキングデータの取得を行います。

アプリケーション内のソースコードから Data Provider へアクセスする場合、``IMixedRealityDataProviderAccess``インターフェースを経由します。さらに 簡単に Data Provider へアクセスしたい場合は、 ``CoreService`` という Helper Class を使用すると良いみたいです。

例えば、Eye Tracking のキャリブレーション有無を確認したい場合はこのような記述で値を取得することができます。

```cs
bool? CalibrationStatus = CoreServices.InputSystem?.EyeGazeProvider?.IsEyeCalibrationValid;
```

### 07. Package.appxmanifest の 機能 : ``視線入力`` を有効にします。
Visual Studio でビルドを実行する前に Package.appxmanifest の Capability 設定を下記の通り変更します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180432/ff44db1c-9437-8954-a2f4-cdaf43755516.png" width="1000">

※ この設定をしないと Eye Tracking データが取得できないので、必ずチェックを入れるようにしてください。

以上の手順を行うことで、Eye Tracking を既存プロジェクトに組み込むことができます。

# Appendix
## Calibration チェック用の Prefab
MRTK Example 内にキャリブレーションのチェックを行う ``EyeCalibrationChecker`` という Prefab が公開されています。
( Path : ``Assets/MixedRealityToolkit.Examples/Demos/EyeTracking/General/Prefabs/EyeCalibrationChecker.prefab`` )
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180432/06d1a9fa-f41c-4d6e-0f91-34ec808210c7.png" width="600">

こちらの Prefab にアタッチされている ``EyeCalibrationChecker.cs`` スクリプト内でキャリブレーション有無のチェックを実行しているようです。 

```EyeCalibrationCheker.cs
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using Microsoft.MixedReality.Toolkit.Input;
using UnityEngine;
using UnityEngine.Events;

namespace Microsoft.MixedReality.Toolkit.Examples.Demos.EyeTracking
{
    /// <summary>
    /// Checks whether the user is calibrated and prompts a notification to encourage the user to calibrate.
    /// </summary>
    [AddComponentMenu("Scripts/MRTK/Examples/EyeCalibrationChecker")]
    public class EyeCalibrationChecker : MonoBehaviour
    {
        [Tooltip("For testing purposes, you can manually assign whether the user is eye calibrated or not.")]
        [SerializeField]
        private bool editorTestUserIsCalibrated = true;

        public UnityEvent OnEyeCalibrationDetected;
        public UnityEvent OnNoEyeCalibrationDetected;

        private bool? prevCalibrationStatus = null;

        private void Update()
        {
            bool? calibrationStatus;

            if (Application.isEditor)
            {
                calibrationStatus = editorTestUserIsCalibrated;
            }
            else
            {
                calibrationStatus = CoreServices.InputSystem?.EyeGazeProvider?.IsEyeCalibrationValid;
            }

            if (calibrationStatus != null)
            {
                if (prevCalibrationStatus != calibrationStatus)
                {
                    if (calibrationStatus == false)
                    {
                        OnNoEyeCalibrationDetected.Invoke();
                    }
                    else
                    {
                        OnEyeCalibrationDetected.Invoke();
                    }
                    prevCalibrationStatus = calibrationStatus;
                }
            }
        }
    }
}

```



# See Also

- [Getting started with eye tracking in MRTK](https://microsoft.github.io/MixedRealityToolkit-Unity/Documentation/EyeTracking/EyeTracking_BasicSetup.html)
- [Accessing eye tracking data in your Unity script](https://microsoft.github.io/MixedRealityToolkit-Unity/Documentation/EyeTracking/EyeTracking_EyeGazeProvider.html)
- [視線検出技術](https://www.fujitsu.com/jp/group/labs/resources/tech/techguide/list/eye-movements/p03.html) ( 富士通研究所 )
- [TOF ( Time of Flight )](https://www.kddi.com/yogo/%E3%83%9E%E3%83%AB%E3%83%81%E3%83%A1%E3%83%87%E3%82%A3%E3%82%A2/TOF.html)
- [IMU ( 慣性計測装置 )](https://ja.wikipedia.org/wiki/%E6%85%A3%E6%80%A7%E8%A8%88%E6%B8%AC%E8%A3%85%E7%BD%AE)