したかみ ぶろぐ

Unity成分多め

Vive Pro Eyeを触ってみる

f:id:vxd-naoshi-19961205-maro:20210725222057j:plain

始めに

Vive Pro Eyeを購入したので、Unityでアイトラッキングを試してみようと思います。

セットアップ

基本的なセットアップはフェイシャルトラッカーと同じなのでこちらを参照してください。

shitakami.hatenablog.com

アイトラッキングを試す

始めにVive Pro Eyeを起動し、次に「SRanipalRuntime」を起動します。

無事起動できた場合はタスクバーにあるSRanipalRuntimeのアイコンの目が付きます。

f:id:vxd-naoshi-19961205-maro:20210718224140p:plain


次にVive Pro Eyeをつけてメニューの左から3番目を選択して、アイトラッキングキャリブレーションを行います。

f:id:vxd-naoshi-19961205-maro:20210718224117p:plain

無事アイトラッキングが完了したら、SDKファイル内にある「FaceGym」で動作するか確認して問題無ければアイトラッキングできます。


もし、キャリブレーションの時点でエラーが出た場合はデバイスのアップデートが必要になると思うのでタスクバーのSRanipalRuntimeからアップデートをしてください。



アイトラッキングで使用されるデータについて

EyeData_v2

始めにEyeData_v2についてまとめます。

frame_sequencetimestampについてですが、調べてみたところサンプルで使われていないのでそこまで重要ではないかもしれません。

変数名 解説
no_user bool HMDを被っているか(trueの場合被っている)
frame_sequence int 再生してからのフレーム数? 公式サンプルでは未使用。
timestamp int サンプリングしたときの時間(ms)。 公式サンプルでは未使用。
verbose_data VerboseData 後述
expression_data EyeExpression 後述

f:id:vxd-naoshi-19961205-maro:20210731154743g:plain



VerboseData

VerboseDataには左右の目それぞれの情報と両方を合わせた情報、TrackingImprovementsがあります。 TrackingImprovementsについてですが、こちらもまたサンプルでは使われていないので解説は省きます。

変数名 解説
left SingleEyeData 左目の情報
right SingleEyeData 右目の情報
combined CombinedEyeData 両目合わせた情報
tracking_improvements TrackingImprovements 謎。公式サンプルでも未使用



SingleEyeData

SingleEyeDataでは目の詳細な情報が入っています。

変数名 解説
eye_data_validate_bit_mask System.UInt64 SingleEyeData.GetValidityで使用される。直接触ることはないと思う。
gaze_origin_mm Vector3 ローカル空間での目の位置(右手座標系)
gaze_direction_normalized Vector3 ローカル空間での視線ベクトル(右手座標系)
pupil_diameter_mm float 瞳孔径(瞳の大きさ)
eye_openness float まぶたが開いているか(0 ~ 1)
pupil_position_in_sensor_area Vector2 瞳の位置を0~1に正規化したもの 右->左でxが増加、上->下でyが増加

f:id:vxd-naoshi-19961205-maro:20210731160005g:plain


また、SingleEyeData.GetValidityでこれらの値が正しいかどうか判断できます。目を閉じている状態では、gaze_direction_normalizedpupil_position_in_sensor_areaが正しく取得できないので使用する際は確認した方がよいかもしれません。

public enum SingleEyeDataValidity : int
{
    /** The validity of the origin of gaze of the eye data */
    SINGLE_EYE_DATA_GAZE_ORIGIN_VALIDITY,
    /** The validity of the direction of gaze of the eye data */
    SINGLE_EYE_DATA_GAZE_DIRECTION_VALIDITY,
    /** The validity of the diameter of gaze of the eye data */
    SINGLE_EYE_DATA_PUPIL_DIAMETER_VALIDITY,
    /** The validity of the openness of the eye data */
    SINGLE_EYE_DATA_EYE_OPENNESS_VALIDITY,
    /** The validity of normalized position of pupil */
    SINGLE_EYE_DATA_PUPIL_POSITION_IN_SENSOR_AREA_VALIDITY
};

public struct SingleEyeData
{
    . . . . .
    . . . . .
    public bool GetValidity(SingleEyeDataValidity validity)
    {
        return (eye_data_validata_bit_mask & (ulong)(1 << (int)validity)) > 0;
    }
}



CombinedEyeData

こちらは両目合わせたデータです。

eye_dataですが、gaze_origin_mmgaze_direction_normalizedしか値を取得できませんでした。 (SRanipal_Eye_v2.GetGazeRay向けと思われる)

また、convergence_distance_validityですがずっとfalseの状態でconvergence_distance_mmが無効でした。サンプル内でも使われている個所はありませんでした。

変数名 解説
eye_data SigleEyeData 両方合わせたデータ
convergence_distance_validity bool 収束距離。ずっとfalse
convergence_distance_mm float 収束距離。値を取得できず

f:id:vxd-naoshi-19961205-maro:20210731160906g:plain



EyeExpression, SingleEyeExpression

これらはv2から追加されたデータです。

始めにEyeExpressionについてです。こちらは右目と左目それぞれのSingleEyeExpressionを保持しています。

変数名 解説
left SingleEyeExpression 右目
right SingleEyeExpression 左目


次にSingleEyeExpressionです。

変数名 解説
eye_wide float 目の開き具合
eye_squeeze float 目をギュッと閉じた具合
eye_frown float しかめっ面具合(値取得できず)

f:id:vxd-naoshi-19961205-maro:20210731162621g:plain



EyeShape_v2

こちらはenum型で目を開いているか、閉じているか、右を見ているかなどを表しています。こちらは後述するSRanipal_Eye_v2.GetEyeWeightingsで使われます。

名前 意味
Eye_Left(Right)_Blink 目を閉じているか
Eye_Left(Right)_Wide 目を開けているか
Eye_Left(Right)_Right 目が右を見ているか
Eye_Left(Right)_Left 目が左を見ているか
Eye_Left(Right)_Up 目が上を見ているか
Eye_Left(Right)_Down 目が下を見ているか
Eye_Frown しかめっ面しているか
Eye_Left(Right)_Squeeze 目をギュッと閉じているか(v2のみ)

f:id:vxd-naoshi-19961205-maro:20210720234749g:plain



ラッキング情報を取得する

SRanipal_Eye_API.GetEyeData_v2

こちらはAPIを呼び出し、EyeData_v2の情報を取得することができます。 返り値のErrorからはアイトラッキングが動作しているか、アイトラッキングが出来るHMDであるかなどが得られます。

/// <summary>
/// Gets data from anipal's Eye module.
/// </summary>
/// <param name="data">ViveSR.anipal.Eye.EyeData</param>
/// <returns>Indicates the resulting ViveSR.Error status of this method.</returns>
[DllImport("SRanipal")]
public static extern Error GetEyeData_v2(ref EyeData_v2 data);



SRanipal_Eye_v2.WrapperRegisterEyeDataCallback

こちらはAPI側にトラッキング情報を取得した際に呼び出すメソッドのポインタを渡します。あとはAPI側がトラッキング情報を取得するたびに自動的にメソッドが呼ばれ、そこでトラッキング情報を受け取ることができます。

注意として呼び出すメソッドはstaticでなければいけません。(staticでない場合はUnityが落ちる)

void Start()
{
    SRanipal_Eye_v2.WrapperRegisterEyeDataCallback(
        Marshal.GetFunctionPointerForDelegate<SRanipal_Eye_v2.CallbackBasic>(EyeCallback));
}

private static void EyeCallback(ref EyeData_v2 eye_data)
{
    _eyeData = eye_data;
}


逆に、自動的にメソッドが呼ばれるのをやめるにはSRanipal_Eye_v2.WrapperUnRegisterEyeDataCallbackを使います。

SRanipal_Eye_v2.WrapperUnRegisterEyeDataCallback(
    Marshal.GetFunctionPointerForDelegate<SRanipal_Eye_v2.CallbackBasic>(EyeCallback));

追記

SRanipal_Eye_API.GetEyeData_v2SRanipal_Eye_v2.WrapperRegisterEyeDataCallback の差についてまとめました。

shitakami.hatenablog.com



SRanipal_Eye_v2.GetEyeWeightings

こちらはDictionary<EyeShape_v2, float>を取得できます。EyeShape_v2の各要素を0~1の範囲で表します。

関数は2つありまして、1つはEyeData_v2から計算してそれぞれの値を求める関数です。もう一方はAPIから値を直接受け取る関数です。

/// Gets weighting values from anipal's Eye module when enable eye callback function.
public static bool GetEyeWeightings(out Dictionary<EyeShape_v2, float> shapes, EyeData_v2 eye_data)

/// Gets weighting values from anipal's Eye module.
public static bool GetEyeWeightings(out Dictionary<EyeShape_v2, float> shapes)



最後に

取得できるデータについてまとめると長くなってしまいました。

触ってみての感想ですが、目を動かす・目を開ける閉じるなどは問題なく出来ましたが、ギュッと閉じる・大きく開くなどをするとHMDがずれてしまうのでどこまでトラッキングするか少し考える必要があるのかなと思いました。

少し意外だったことが眼鏡をつけたままでも問題なくトラッキング出来たことです。眼鏡をつけない場合と比べて精度が落ちるかはわかりませんが、特に違和感はありませんでした。

ラッキング情報を取得する以外にもRayを飛ばす機能などもあるので、のちのち触ってみようかなと思います。


追記

SDKに用意されているメソッドについてまとめました。

shitakami.hatenablog.com



参考

qiita.com

umehashi.hatenablog.com