夜風のMixedReality

xRと出会って変わった人生と出会った技術を書き残すためのGeekなHoloRangerの居場所

HoloLens 2 で指のジェスチャー判定を行う

本日はHoloLens 2の表現調査の記事です。

HoloLens 2ではHandTrackingによってユーザーの手を取得できます。

これはMixedRealityPoseとして各関節ごとに取得してアプリ内でデータを扱うことができます。

今回はオリジナルでジェスチャーを作成する一環として、指が伸びているのか、曲げられているのかを検知できる機能を作成しました。

今回は紹介記事です。明日の記事で解説を行います。

〇実機

今回は人差し指、中指を検知し、指が伸ばされていると各色のボールが出現するようにしました。

youtu.be

〇コード

次のようなスクリプトを書きました。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Microsoft.MixedReality.Toolkit;
using Microsoft.MixedReality.Toolkit.Input;
using Microsoft.MixedReality.Toolkit.Utilities;
using UnityEngine.Events;


public class FingerDitected : MonoBehaviour
{
    [SerializeField,Header("Target")]
    Handedness handType;
    
    [SerializeField,Range(0,90)]
    private float indexThreshold = 5;

    [SerializeField,Range(0,90)]
    private float middleThreshold = 5;

    [SerializeField] 
    UnityEvent IndexFingerDetectEvent;

    [SerializeField] 
    UnityEvent IndexFingerLostEvent;

    [SerializeField] 
    UnityEvent MiddleFingerDetectEvent;

    [SerializeField] 
    UnityEvent MiddleFingerLostEvent;

    private bool? handdetected;


    void Update()
    {
        handdetected = HandJointUtils.FindHand(handType)?.TryGetJoint(TrackedHandJoint.Palm, out MixedRealityPose PalmPose);
        if (handdetected != null&& handdetected==true)
        {
            if (indexfingerDetected())
            {
                IndexFingerDetectEvent.Invoke();
            }
            else
            {
                IndexFingerLostEvent.Invoke();
            }

            if (middlefingerDetected())
            {
                MiddleFingerDetectEvent.Invoke();
            }
            else
            {
                MiddleFingerLostEvent.Invoke();
            }   
        }
    }
    
    private bool indexfingerDetected()
    {
        var jointedHand = HandJointUtils.FindHand(handType);
        if (jointedHand.TryGetJoint(TrackedHandJoint.Palm,out MixedRealityPose PalmPose))
        {
            //各関節のpose
            MixedRealityPose indexTipPose,indexDistalPose,IndexKnucklePose,indexMiddlePose; 
            if(jointedHand.TryGetJoint(TrackedHandJoint.IndexTip,out indexTipPose)&& jointedHand.TryGetJoint(TrackedHandJoint.IndexDistalJoint,out indexDistalPose)&&jointedHand.TryGetJoint(TrackedHandJoint.IndexMiddleJoint,out indexMiddlePose)&& jointedHand.TryGetJoint(TrackedHandJoint.IndexKnuckle,out IndexKnucklePose))
            {
                Vector3 f1 = IndexKnucklePose.Position - PalmPose.Position;
                Vector3 f2 = indexMiddlePose.Position - IndexKnucklePose.Position;
                Vector3 f3 = indexDistalPose.Position - indexMiddlePose.Position;
                Vector3 f4 = indexTipPose.Position - indexDistalPose.Position;
                
            float c = Vector3.Angle(PalmPose.Position, f1);
            float d = Vector3.Angle(f1, f2);
            float e = Vector3.Angle(f2, f3);
            float f = Vector3.Angle(f3, f4);

            float aba = (Mathf.Abs(d) + Mathf.Abs(e) + Mathf.Abs(f)) / 3;

                if (aba < indexThreshold)
                {
                    return true;
                }
            }
        }
        return false;
    }

    private bool middlefingerDetected()
    {
        var jointedHand = HandJointUtils.FindHand(handType);
        if (jointedHand.TryGetJoint(TrackedHandJoint.Palm, out MixedRealityPose PalmPose))
        {
            MixedRealityPose middleTipsPose,middleDistalPose, middleKnucklePose,middleMiddlePose;
            if (jointedHand.TryGetJoint(TrackedHandJoint.MiddleTip, out middleTipsPose) &&
                jointedHand.TryGetJoint(TrackedHandJoint.MiddleDistalJoint, out middleDistalPose) &&
                jointedHand.TryGetJoint(TrackedHandJoint.MiddleKnuckle, out middleKnucklePose)&&jointedHand.TryGetJoint(TrackedHandJoint.MiddleMiddleJoint,out middleMiddlePose))
            {
                Vector3 f1= middleKnucklePose.Position - PalmPose.Position;
                Vector3 f2 = middleMiddlePose.Position - middleKnucklePose.Position;
                Vector3 f3 = middleDistalPose.Position - middleMiddlePose.Position;
                Vector3 f4 = middleTipsPose.Position - middleDistalPose.Position;
                
                float c = Vector3.Angle(PalmPose.Position, f1);
                float d = Vector3.Angle(f1, f2);
                float e = Vector3.Angle(f2, f3);
                float f = Vector3.Angle(f3, f4);

                float aba = (Mathf.Abs(d) + Mathf.Abs(e) + Mathf.Abs(f)) / 3;

                if (aba < middleThreshold)
                {
                    return true;
                }
                
            }
        }

        return false;
    }
}

〇使い方

MRTKが導入されている適当なゲームオブジェクトを作成し、[FingerDetector]をアタッチします。

f:id:Holomoto-Sumire:20210227214518j:plain

●HandType

右手、左手、どちらをtrackingするかを指定します。

f:id:Holomoto-Sumire:20210227214644j:plain

●indexThreshold 、middleThreshold

人差し指、中指の検知閾値です。

値が小さいほどしっかりと人差し指を伸ばす必要があります。 逆に値が大きいほど完全に指を伸ばしていなくても検知されます。

f:id:Holomoto-Sumire:20210227214934j:plain

● IndexFingerDetectEvent,MiddleFingerDetectEvent

人差し指、中指が伸ばされたときに発火するイベントになります。

f:id:Holomoto-Sumire:20210227215319j:plain

● IndexFingerLostEvent。MiddleFingerLostEvent

人差し指、中指が曲げられたときに発火するイベントです。

f:id:Holomoto-Sumire:20210227215337j:plain

このindexFingerDetectEvent,MiddleFingerDetectEventIndexFingerLostEvent。MiddleFingerLostEventに任意のアクションを設定します。

次回細かい解説を行います。