2023.01.06

Ultraleap Hand Tracking SDK 取得可能な情報について

WEBマガジン非接触部品・センサ

  • TOP>
  • 特集>
  • Ultraleap Hand Tracking SDK 取得可能な情報について
コーンテクノロジー
この記事の監修者
コーンズテクノロジー編集部
コーンズテクノロジーでは先進的な製品・技術を日本産業界へ紹介する技術専門商社として、通信計測・自動車・防衛セキュリティ・電子機器装置・航空宇宙・産業機械といった技術分野のお役立ち情報を紹介しています。

1. はじめに
2. SDK サンプルプログラムの記述例
3. LEAP_HAND構造体の構成
4. Ultraleap ステレオカメラのxyz座標
5. handで取得可能な情報
6. armで取得可能な情報
7. palmで取得可能な情報
8. digitsで取得可能な情報
9. おわりに
 

1. はじめに

以前、当社のWebマガジンにて、「LMCを用いて手の関節の3次元座標を標準出力する」方法をまとめました。

実際は関節の3次元座標情報以外にも、SDKを用いて様々な手の情報が取得可能です。

しかしながらこれら情報の取得方法がSDK付属のサンプルプログラムに含まれておらず、Ultraleap社のAPI仕様書を読み調べる必要がありました。

今回、様々な手の情報の取得方法をWebマガジンとしてまとめましたので、ご参照下さい。

なお、2022年12月時点の最新バージョンであるVersion 5.7.2のSDK(以下SDK5.7.2と記載します)をWindowsPCにインストール済みとして説明しております。今後、SDKのバージョンが更新された場合、一部仕様変更が入る可能性がある旨、ご了承ください。

なお、SDK5.7.2のインストールについては別途Webマガジンの記事「Ultraleap社 Hand Tracking用Windows版SDK Version5.7.2について」でまとめていますのでご参考下さい。

また、特に記載がなければ、Ultraleapのステレオカメラ(Leap Motion Controller(以下LMCと記載します)、Stereo IR170(以下SIR170と記載します)、3Di等のこと)の設置・設定に関してはDeskTopモード、つまり下記写真のようにステレオカメラを卓上に置き、NIRレンズが上を向いた状態としています。

 

2. SDK サンプルプログラムの記述例

SDK5.7.2のサンプルプログラム「CallBackSample.c」の27行目~41行目に、OnFrame関数という手の認識結果を処理する関数が存在します。
この箇所をいろいろと修正することで、様々な手の情報が取得可能となります。

なお、SDK5.7.2のサンプルプログラムは、初期設定のままインストールした場合、下記フォルダに含まれています。
C:\Program Files\Ultraleap\LeapSDK\samples

OnFrame関数の内容をサンプルプログラム中にコメントを追記して説明します。
下記サンプルプログラム中の「//★」の箇所が追記したコメントです。

/** Callback for when a frame of tracking data is available. */
static void OnFrame(const LEAP_TRACKING_EVENT *frame){
    //★60フレーム毎に一回、フレームIDおよび検出した手の数を表示している
    //★不要であれば削除可能、
    if (frame->info.frame_id % 60 == 0)
        printf(“Frame %lli with %i hands.\n”, (long long int)frame->info.frame_id, frame->nHands);    //★以下、検出した手の数だけ繰り返し(frame->nHandsは最大で2(左手・右手の両手検出時))
    for(uint32_t h = 0; h < frame->nHands; h++){
        //★実体「hand」に一つの手の情報をセット
        LEAP_HAND* hand = &frame->pHands[h];
       //★各種手の情報を標準出力
       printf(” Hand id %i is a %s hand with position (%f, %f, %f).\n”,
       hand->id, //★手のID、検出毎に1インクリメントされる
       (hand->type == eLeapHandType_Left ? “left” : “right”), //★左 or 右
       hand->palm.position.x, //★手の平のx座標
       hand->palm.position.y, //★手の平のy座標
       hand->palm.position.z); //★手の平のz座標 ※xyz座標の定義は後述
   }
}

上記サンプルプログラムのhand変数(LEAP_HAND構造体)の中に、様々な手の情報が含まれています。

 

3. LEAP_HAND構造体の構成

LEAP_HAND構造体の中に様々な手の情報が含まれていますが、LEAP_HAND構造体の構成を理解されるとプログラムが書きやすいと考えております。

ここでは、LEAP_HAND構造体の構成について、説明します。

LEAP_HAND構造体は手の平から手首、そして肘までの範囲の情報を取り扱っています。

LEAP_HAND構造体はarmおよびpalmをメンバ変数として持っています。

armはLEAP_BONE構造体、palmはLEAP_PALM構造体を用いて宣言しています。

LEAP_PALM構造体は、gidits配列をメンバ変数として持っています。
gidits配列はLEAP_DIGIT構造体を用いて宣言しています。
またdigits配列は共有体として別の変数名を持っています。
gidits配列と添え字の関係および別変数名の関係は以下の通りです。
()内は別変数名を示しています。

digits[0] (thumb) :親指
digits[1] (index) :人差し指
digits[2] (middle):中指
digits[3] (ring) :薬指
digits[4] (pinky) :小指

本Webマガジンにてdigits[0-4]と記載した場合、digits[0]からdigits[4]のいずれかであることを示しています。

LEAP_DIGIT構造体は、bones配列をメンバ変数として持っています。
bones配列はLEAP_HAND構造体のarm変数と同じく、LEAP_BONE構造体を用いて宣言しています。
またbones配列は共有体として別の変数名を持っています。
bones配列と添え字の関係および別変数名の関係は以下の通りです。
()内は別変数名を示しています。

bones[0] (metacarpal):中手骨
bones[1] (proximal):基節骨
bones[2] (intermediate):中節骨
bones[3] (distal):末節骨

本Webマガジンにてboines[0-3]と記載した場合、bones[0]からbones[3]のいずれかであることを示しています。

なお、上記の図の通り、親指(digits[0], thumb)に関して、中手骨(bones[0], metacarpal)のデータは無効となっています。
医学上、親指には中手骨(bones[0], metacarpal)ではなく中節骨(bones[2], intermediate)がありませんが、データ処理の簡便さから、上記のような構成としています。

後述しますが、それぞれの構造体に含まれるその他のメンバ変数を参照することにより、各種手の情報が取得可能です。

 

4. Ultraleap ステレオカメラのxyz座標

LEAP_PALM構造体やLEAP_BONE構造体にはxyz座標のデータが含まれています。
このxyz座標を用いて各種手の関節がどこにあるかを知ることができます。

xyz座標の原点は、Ultraleap社のステレオカメラモジュールの左右カメラ間の中心です。

LMCおよびSIR170のxyz座標の原点および座標軸を以下の図に示します。
なお、三次元座標系は右手系(ワールド座標系)を使用しています。
y軸に関してはプラス値のみ、マイナス値はもたない仕様になっています。

xyz座標の単位はmmです。


 

 

5. handで取得可能な情報

以下、サンプルプログラム中のhand変数(LEAP_HAND構造体)で取得可能な情報に関してまとめます。

・左手・右手

「hand->type」で、左手または右手の情報を取得できます。
取得した値がeLaeapHandType_Leftであれば左手、eLaeapHandType_Rightであれば右手です。

hand->type == eLaeapHandType_Left時のUltraleapのHand Tracking SDK5.7.2のControl Panel(以下、Control Panelと記載します)の表示例を以下に示します。

hand->type == eLeapHandType_Right時のControl Panelの表示例を以下に示します。

なお、SDK5.7.2のControl Panelは、旧VersionのSDKではVisualizerという名前でした。操作方法やレイアウトも変わっていますのでご注意下さい。

プログラムの記述例を以下に示します。

for(uint32_t h = 0; h < frame->nHands; h++){
    LEAP_HAND* hand = &frame->pHands[h];
    printf(“Hand is a %s hand.\n”, (hand->type == eLeapHandType_Left ? “left” : “right”));
}

・指の平均角度

「hand->grab_angle」で、手の平に対する指の平均角度を取得できます。
値はラジアンで、0で水平、πでGrab(グー)の状態を示します。

hand->grab_angle == 0(0度)時ののControl Panelの表示例を以下に示します。

hand->grab_angle == π/2(90度)時ののControl Panelの表示例を以下に示します。

hand->grab_angle == π(180度)時ののControl Panelの表示例を以下に示します。

プログラムの記述例を以下に示します。

for(uint32_t h = 0; h < frame->nHands; h++){
    LEAP_HAND* hand = &frame->pHands[h];
    printf(“%s hand grab_angle:%8.3f\n”, (hand->type == eLeapHandType_Left ? “left” : “right”), hand->grab_angle);
}

・Grab状態

「hand->grab_strength」で、Grab(グー)の状態を正規化した推定値を取得できます。
この値は0.0~1.0の範囲であり、0.0で「Grabではない」、1.0で「Grabである」を意味します。

hand->grab_strength == 0.0時のControl Panelの表示例を以下に示します。

hand->grab_strength == 0.5時のControl Panelの表示例を以下に示します。

hand->grab_strength == 1.0時のControl Panelの表示例を以下に示します。

プログラムの記述例を以下に示します。

for(uint32_t h = 0; h < frame->nHands; h++){
    LEAP_HAND* hand = &frame->pHands[h];
    printf(“%s hand grab_strength:%8.3f\n”, (hand->type == eLeapHandType_Left ? “left” : “right”), hand->grab_strength);
}

・Pinch状態

「hand_->pinch_strength」で、Pinch(摘まむ)状態を正規化し推定理を取得できます。
この値は0.0~1.0の範囲であり、0.0で「Pinchではない」、1.0で「Pinchである」を意味します。

hand->pinch_strength == 0.0時のControl Panelの表示例を以下に示します。

hand->pinch_strength == 0.5時のControl Panelの表示例を以下に示します。

hand->pinch_strength == 1.0時のControl Panelの表示例を以下に示します。

プログラムの記述例を以下に示します。

for(uint32_t h = 0; h < frame->nHands; h++){
    LEAP_HAND* hand = &frame->pHands[h];
    printf(“%s hand pinch_strength:%8.3f\n”, (hand->type == eLeapHandType_Left ? “left” : “right”), hand->pinch_strength);
}

・親指<->人差し指間の距離

「hand->pinch_distance」で、親指の先と人差し指の先の距離を取得できます。
単位はmmです。

hand->pinch_distance == 0(接している)時のControl Panelの表示例を以下に示します。

hand->pinch_distance == 50(50mm離れている)時のControl Panelの表示例を以下に示します。

hand->pinch_distance == 90(90mm離れている)時のControl Panelの表示例を以下に示します。

プログラムの記述例を以下に示します。

for(uint32_t h = 0; h < frame->nHands; h++){
    LEAP_HAND* hand = &frame->pHands[h];
    printf(“%s hand pinch_distance:%8.3f\n”, (hand->type == eLeapHandType_Left ? “left” : “right”), hand->pinch_distance);
}

6. armで取得可能な情報

以下、サンプルプログラム中のarm変数(LEAP_BONE構造体)で取得可能な情報に関してまとめます。

・肘のxyz座標

「hand->arm.prev_join」で、肘の座標を取得できます。
単位はmmです。

x座標、y座標、z座標それぞれは、以下の記述で取得できます。
 肘のx座標:hand->arm.prev_join.x
 肘のy座標:hand->arm.prev_join.y
 肘のz座標:hand->arm.prev_join.z

hand->arm.prev_joinのxyz座標 == (103.412, 174.639, 117.134)時のControl Panelの表示例を以下に示します。

hand->arm.prev_joinのxyz座標 == (112.632, 211.418, 254.134)時のControl Panelの表示例を以下に示します。

なお、Control Panel上では肘に2つのxyz座標(黄色い丸)が表示されていますが、実際はxyz座標は1点のみ、後述する腕の幅と回転を用いて2点の表示に変換しています(視覚的に腕の向きや回転分かりやすくするため)。

プログラムの記述例を以下に示します。

for(uint32_t h = 0; h < frame->nHands; h++){
    LEAP_HAND* hand = &frame->pHands[h];
    printf(“%s hand arm elbow:x=%8.3f, y=%8.3f, z=%8.3f\n”, (hand->type == eLeapHandType_Left ? “left” : “right”), hand->arm.prev_joint.x, hand->arm.prev_joint.y, hand->arm.prev_joint.z);
}

・手首のxyz座標

「hand->arm.next_join」で、手首の座標を取得できます。
単位はmmです。

x座標、y座標、z座標それぞれは、以下の記述で取得できます。
 手首のx座標:hand->arm.next_join.x
 手首のy座標:hand->arm.next_join.y
 手首のz座標:hand->arm.next_join.z

hand->arm.next_joinのxyz座標 == (-6.995, 181.289, -4.487)時のControl Panelの表示例を以下に示します。

hand->arm.next_joinのxyz座標 == (-207.166, 201.969, -33.280時)時のControl Panelの表示例を以下に示します。

肘のxyz座標時と同じく、Control Panel上では手首に2つのxyz座標(黄色い丸)が表示されていますが、前記した理由と同じです。

プログラムの記述例を以下に示します。

for(uint32_t h = 0; h < frame->nHands; h++){
    LEAP_HAND* hand = &frame->pHands[h];
    printf(“%s hand arm wrist:x=%8.3f, y=%8.3f, z=%8.3f\n”, (hand->type == eLeapHandType_Left ? “left” : “right”), hand->arm.next_joint.x, hand->arm. next_joint.y, hand->next.prev_joint.z);
}

・腕の幅

「hand->arm.width」で、腕の平均幅を取得できます。
単位はmmです。

hand->arm.width == 57.459時のControl Panelの表示例を以下に示します。

hand->arm.width == 57.224時のControl Panelの表示例を以下に示します。

上記2つの図のように、腕がどのような位置・向きにあっても似た値になっています。

プログラムの記述例を以下に示します。

for(uint32_t h = 0; h < frame->nHands; h++){
    LEAP_HAND* hand = &frame->pHands[h];
    printf(“%s hand arm width:%8.3f\n”, (hand->type == eLeapHandType_Left ? “left” : “right”), hand->arm.width);
}

・腕の回転(クォータニオン)

「hand->arm.rotation」で、腕の回転(クォータニオン)を取得できます。
xyzwで表記します。
※クォータニオンについてはInternet上でいろいろと記載があるので、検索して調べて下さい。

x,y,z,wそれぞれは、以下の記述で取得できます。
 腕のx:hand->arm.rotation.x
 腕のy:hand->arm.rotation.y
 腕のz:hand->arm.rotation.z
 腕のw:hand->arm.rotation.w

hand->arm.rotationのxyzw == (0.203, 0.005, -0.039, 0.978)時のControl Panelの表示例を以下に示します。

hand->arm.rotationのxyzw == (-0.120, -0.283, 0.949, -0.075)時のControl Panelの表示例を以下に示します。

プログラムの記述例を以下に示します。

for(uint32_t h = 0; h < frame->nHands; h++){
    LEAP_HAND* hand = &frame->pHands[h];
    printf(“%s hand arm rotation:x=%8.3f, y=%8.3f, z=%8.3f, w=%8.3f\n”, (hand->type == eLeapHandType_Left ? “left” : “right”), hand->arm.rotation.x, hand->arm.rotation.y, hand->arm.rotation.z, hand->arm.rotation.w);
}

7. palmで取得可能な情報

以下、サンプルプログラム中のpalm変数(LEAP_PALM構造体)で取得可能な情報に関してまとめます。

・手の平のxyz座標

「hand->palm.position」で、手の平の中心座標を取得できます。
単位はmmです。

x座標、y座標、z座標それぞれは、以下の記述で取得できます。
 手の平のx座標:hand->palm.position.x
 手の平のy座標:hand->palm.position.y
 手の平のz座標:hand->palm.position.z

hand->palm.positionのxyz座標 == (-0.827, 222.443, 1.695)時のControl Panelの表示例を以下に示します。

hand->palm.positionのxyz座標 == (191.119, 403.736, 432.307)時のControl Panelの表示例を以下に示します。

プログラムの記述例を以下に示します。

for(uint32_t h = 0; h < frame->nHands; h++){
    LEAP_HAND* hand = &frame->pHands[h];
    printf(“%s hand palm :x=%8.3f, y=%8.3f, z=%8.3f\n”, (hand->type == eLeapHandType_Left ? “left” : “right”), hand->palm.position.x, hand->palm.position.y, hand->palm.position.z);
}

・手の平の移動速度

「hand->palm.verocity」で、手の平の移動速度を取得できます。
単位はmm/secで、xyz座標毎に取得可能です。

x座標方向、y座標方向、z座標方向それぞれの移動速度は、以下の記述で取得できます。
 手の平のx座標方向の移動速度:hand->palm.verocity.x
 手の平のy座標方向の移動速度:hand->palm.verocity.y
 手の平のz座標方向の移動速度:hand->palm.verocity.z

hand->palm.verocityのxyz座標方向の移動速度 == (54.908, -167.149, -683.297)時のControl Panelの表示例を以下に示します。この図は手の平をz軸方向手前から奥に移動させた後なので、z座標方向の移動速度がマイナス値で大きな値になっています。

hand->palm.verocityのxyz座標方向の移動速度 == (597.807, 12.661, 127.244)時のControl Panelの表示例を以下に示します。この図は手の平をx軸方向左から右に移動後なのでx座標方向の移動速度がプラス値で大きな値になっています。

プログラムの記述例を以下に示します。

for(uint32_t h = 0; h < frame->nHands; h++){
    LEAP_HAND* hand = &frame->pHands[h];
    printf(“%s hand palm verocity:x=%8.3f, y=%8.3f, z=%8.3f\n”, (hand->type == eLeapHandType_Left ? “left” : “right”), hand->palm.velocity.x, hand->palm.velocity.y, hand->palm.velocity.z);
}

・手の平のへの法線ベクトル

「hand->palm.normal」で、手の平(平面)への法線ベクトルを取得できます。
法線ベクトルのxyzの各成分として取得できます。

xyzそれぞれの係数は、以下の記述で取得できます。
 手の平への法線ベクトルのxの係数:hand->palm.normal.x
 手の平への法線ベクトルのyの係数:hand->palm.normal.y
 手の平への法線ベクトルのzの係数:hand->palm.normal.z

hand->palm.normalのxyzの各係数 == (-0.062, -0.997, 0.036)時のControl Panelの表示例を以下に示します。この図は手の平を下に向けているので、y係数が-1.0に近い値になっています。

hand->palm.normalのxyzの各係数 == (-0.049, 0.996, 0.080)時のControl Panelの表示例を以下に示します。この図は手の平を上に向けているので、y係数が+1.0に近い値になっています。

各xyz係数と手の平(または手の甲)の向きの関係は、以下の通りです。
 x係数が-1.0に近い値:手の平が左向き(手の甲が右向き)
 x係数が+1.0に近い値:手の平が右向き(手の甲が左向き)
 y係数が-1.0に近い値:手の平が下向き(手の甲が上向き)
 y係数が+1.0に近い値:手の平が上向き(手の甲が下向き)
 z係数が-1.0に近い値:手の平が奥向き(手の甲が手前向き)
 z係数が+1.0に近い値:手の平が手前向き(手の甲が奥向き)

プログラムの記述例を以下に示します。

for(uint32_t h = 0; h < frame->nHands; h++){
    LEAP_HAND* hand = &frame->pHands[h];
    printf(“%s hand palm normal:x=%8.3f, y=%8.3f, z=%8.3f\n”, (hand->type == eLeapHandType_Left ? “left” : “right”), hand->palm.normal.x, hand->palm.normal.y, hand->palm.normal.z);
}

・手の平の幅

「hand->palm.width」で、手の平の平均幅を取得できます。
単位はmmです。

hand->palm.width == 82.387時のControl Panelの表示例を以下に示します。

hand->palm.width == 82.441時のControl Panelの表示例を以下に示します。

上記2つの図のように、手の平がどのような位置・向きにあっても似た値になっています。

プログラムの記述例を以下に示します。

for(uint32_t h = 0; h < frame->nHands; h++){
    LEAP_HAND* hand = &frame->pHands[h];
    printf(“%s hand arm width:%8.3f\n”, (hand->type == eLeapHandType_Left ? “left” : “right”), hand->arm.width);
}

・手の平→指の単位方向ベクトル

「hand->palm.direction」で、手の平から指に向かう単位方向ベクトルを取得できます。
単位方向ベクトルのxyzの各成分として取得できます。

xyzそれぞれの成分は、以下の記述で取得できます。
 手の平へから指への単位方向ベクトルのx成分:hand->palm.direction.x
 手の平へから指への単位方向ベクトルのy成分:hand->palm.direction.y
 手の平へから指への単位方向ベクトルのz成分:hand->palm.direction.z

hand->palm.directionのxyzの各成分 == (-0.027, -0.010, -1.00)時のControl Panelの表示例を以下に示します。この図は手の平から指先の方向が手前から奥の方向なので、x成分が0.0に近い値、z成分が-1.0になっています。

hand->palm.directionのxyzの各成分 == (-0.997, 0.055, -0.051)時のControl Panelの表示例を以下に示します。この図は手の平から指先の方向が右から左の方向なので、x成分が-1.0に近い値、z成分が0.0に近い値になっています。

各xyz成分と手の平から指の方向の関係は、以下の通りです。
 x成分が-1.0に近い値:手の平から指の方向が右から左
 x成分が+1.0に近い値:手の平から指の方向が左から右
 y成分が-1.0に近い値:手の平から指の方向が上から下
 y成分が+1.0に近い値:手の平から指の方向が下から上
 z成分が-1.0に近い値:手の平から指の方向が手前から奥
 z成分が+1.0に近い値:手の平から指の方向が奥から手前

プログラムの記述例を以下に示します。

for(uint32_t h = 0; h < frame->nHands; h++){
    LEAP_HAND* hand = &frame->pHands[h];
    printf(“%s hand palm direction:x=%8.3f, y=%8.3f, z=%8.3f\n”, (hand->type == eLeapHandType_Left ? “left” : “right”), hand->palm.direction.x, hand->palm.direction.y, hand->palm.direction.z);
}

・手の平の方向(クォータニオン)

「hand->palm.orientation」で、手の平の方向(クォータニオン)を取得できます。
xyzwで表記します。

x,y,z,wそれぞれは、以下の記述で取得できます。
 手の平のx:hand->arm.orientation.x
 手の平のy:hand->arm.orientation.y
 手の平のz:hand->arm.orientation.z
 手の平のw:hand->arm.orientation.w

hand->palm.orientationのxyzw == (-0.034, 0.016, -0.106, 0.994)時のControl Panelの表示例を以下に示します。

hand->palm.orientationのxyzw == (0.085, 0.064, 0.994, -0.014)時のControl Panelの表示例を以下に示します。

プログラムの記述例を以下に示します。

for(uint32_t h = 0; h < frame->nHands; h++){
    LEAP_HAND* hand = &frame->pHands[h];
    printf(“%s hand palm orientation:x=%8.3f, y=%8.3f, z=%8.3f, w=%8.3f\n”, (hand->type == eLeapHandType_Left ? “left” : “right”), hand->palm.orientation.x, hand->palm.orientation.y, hand->palm.orientation.z, hand->palm.orientation.w);
}

8. digitsで取得可能な情報

digits配列(LEAP_DIGIT構造体)は下記のような記述を追加することで、取得できます。

for(int f = 0; f < 5; f++){ //★指の本数分回す
    LEAP_DIGIT finger = hand->digits[f]; //★digits配列から添え字に対応した指を取得
    … //★以下、指毎の処理
}

以下、上記プログラム中のfinger変数(LEAP_DIGITS構造体)で取得可能な情報に関してまとめます。

・指の関節のxyz座標

「hand->digits[0-4].bones[0-3].next_joint」で、各指の関節の座標を取得できます。
単位はmmです。
bones配列はLEAP_BONE構造体を使用しています(サンプルプログラム中のarm変数と同じ構造体)。

x座標、y座標、z座標それぞれは、以下の記述で取得できます。
 各指の関節のx座標:hand->digits[0-4].bones[0-3].next_joint.x
 各指の関節のy座標:hand->digits[0-4].bones[0-3].next_joint.y
 各指の関節のz座標:hand->digits[0-4].bones[0-3].next_joint.z

人差し指(digits[1])の各関節のxyz座標取得時のControl Panelの表示例を以下に示します。

プログラムの記述例を以下に示します。

for(uint32_t h = 0; h < frame->nHands; h++){
    LEAP_HAND* hand = &frame->pHands[h];
    printf(“%s hand:\n”, (hand->type == eLeapHandType_Left ? “left” : “right”));
    for(int f = 0; f < 5; f++){
        LEAP_DIGIT finger = hand->digits[f];
        for(int b = 0; b < 4; b++){
            LEAP_BONE bone = finger.bones[b];
            printf(” f[%d]_b[%d]:x=%8.3f, y=%8.3f, z=%8.3f\n”, f, b, bone.next_joint.x, bone.next_joint.y, bone.next_joint.z);
        }
    }
}

・指の伸縮

「hand->digits[0-4].isExtended」で、各指の伸ばした状態の判定結果を取得できます。
0で偽(縮んだ状態)、1で真(伸ばした状態)を意味します。

hand->digits[4].isExtended(小指)~hand->digits[0].isExtended(親指) == (1,1,1,1,1)時のControl Panelの表示例を以下に示します。

hand->digits[4].isExtended~hand->digits[0].isExtended == (0,0,1,1,0)時のControl Panelの表示例を以下に示します。

hand->digits[4].isExtended~hand->digits[0].isExtended == (1,0,0,1,1)時のControl Panelの表示例を以下に示します。

プログラムの記述例を以下に示します。

for(uint32_t h = 0; h < frame->nHands; h++){
    LEAP_HAND* hand = &frame->pHands[h];
    printf(“%s hand:\n”, (hand->type == eLeapHandType_Left ? “left” : “right”));
    for(int f = 0; f < 5; f++){
        LEAP_DIGIT finger = hand->digits[f];
        if (finger.isExtended) {
            printf( “f[%d] is extended.\n”, f);
        }
        else {
            printf(” f[%d] is shorten.\n”, f);
        }
    }
}

・指の幅

「hand->digits[0-4].bones[0-3].width 」で、指の平均幅を取得できます。
単位はmmです。
なお、各指のbones[3].width~bones[0].widthはすべて同じ値となっています。

hand->digits[4].bones[3].width(小指)~hand->digits[0].bones[3].width(親指) ==(14.532,16.360,17.192,17.505,18.326)時のControl Panelの表示例を以下に示します。

hand->digits[4].bones[3].width(小指)~hand->digits[0].bones[3].width(親指) ==(14.319,16.120,16.940,17.249,18.058)時のControl Panelの表示例を以下に示します。

上記2つの図のように、指がどのような位置・向きにあっても似た値になっています。

プログラムの記述例を以下に示します。

for(uint32_t h = 0; h < frame->nHands; h++){
    LEAP_HAND* hand = &frame->pHands[h];
    printf(“%s hand:\n”, (hand->type == eLeapHandType_Left ? “left” : “right”));
    for(int f = 0; f < 5; f++){
        LEAP_DIGIT finger = hand->digits[f];
        LEAP_BONE bone = finger.bones[3];
        printf(“ f[%d] width:%8.3f\n”, f, bone.width);
    }
}

・指の回転(クォータニオン)

「hand->digits[0-4].bones[0-3].rotation」で、腕の回転(クォータニオン)を取得できます。
xyzwで表記します。

x,y,z,wそれぞれは、以下の記述で取得できます。
 指のx:hand->digits[0-4].bones[0-3].rotation.x
 指のy:hand->digits[0-4].bones[0-3].rotation.y
 指のz:hand->digits[0-4].bones[0-3].rotation.z
 指のw:hand->digits[0-4].bones[0-3].rotation.w

各指のbones[3]のxyzw取得時のControl Panelの表示例を以下に示します。

プログラムの記述例を以下に示します。

for(uint32_t h = 0; h < frame->nHands; h++){
    LEAP_HAND* hand = &frame->pHands[h];
    printf(“%s hand:\n”, (hand->type == eLeapHandType_Left ? “left” : “right”));
    for(int f = 0; f < 5; f++){
        LEAP_DIGIT finger = hand->digits[f];
        LEAP_BONE bone = finger.bones[3];
        printf(“ f[%d] tip rotation:x=%8.3f, y=%8.3f, z=%8.3f, w=%8.3f\n”,f, bone.rotation.x, bone.rotation.y, bone.rotation.z, bone.rotation.w);
    }
}

9. おわりに

今までに説明した各種手の情報を取得する方法を1つにまとめたプログラムを以下に記載します。
SDK5.7.2のサンプルプログラム「CallBackSample.c」のOnFrame関数を下記のプログラムと入れ替えば動作します。
なお、build方法等に関しては、下記Webマガジンの「5. SDKサンプルプログラムのbuild」を参照ください。
https://www.cornestech.co.jp/tech/webmagazine/webmagazine-22579/

static void OnFrame(const LEAP_TRACKING_EVENT *frame){

#if defined(FRAMID_ENABLE) //★手を認識していない場合でも必ずFrame IDを表示したい場合は「FRAMID_ENABLE」をdefineして下さい
    if (frame->info.frame_id % 60 == 0) //★60Frame毎に1回表示、毎Frame表示したい場合はこの行を削除すること
        printf(“Frame %lli with %i hands.\n”, (long long int)frame->info.frame_id, frame->nHands);
#endif

    if(frame->nHands > 0){
        printf(“================================================================\n”);
        printf(“Frame %lli with %i hands.\n”, (long long int)frame->info.frame_id, frame->nHands);
    }

    for(uint32_t h = 0; h < frame->nHands; h++){
        LEAP_HAND* hand = &frame->pHands[h];

        if(h==1){
            printf(” —————————————————————-\n”);
            printf(“\n”);
        }

        //★handの情報
        printf(” Hand id %i is a %s.\n”, hand->id, (hand->type == eLeapHandType_Left ? “left” : “right”));
        printf(” grab_angle:%8.3f\n”, hand->grab_angle);
        printf(” grab_strength:%8.3f\n”, hand->grab_strength);
        printf(” pinch_strength:%8.3f\n”, hand->pinch_strength);
        printf(” pinch_distance:%8.3f\n”, hand->pinch_distance);

        //★armの情報
        printf(” arm elbow:x=%8.3f, y=%8.3f, z=%8.3f\n”, hand->arm.prev_joint.x, hand->arm.prev_joint.y, hand->arm.prev_joint.z);
        printf(” arm wrist:x=%8.3f, y=%8.3f, z=%8.3f\n”, hand->arm.next_joint.x, hand->arm.next_joint.y, hand->arm.next_joint.z);
        printf(” arm width:%8.3f\n”, hand->arm.width);
        printf(” arm rotation:x=%8.3f, y=%8.3f, z=%8.3f, w=%8.3f\n”,hand->arm.rotation.x, hand->arm.rotation.y, hand->arm.rotation.z, hand->arm.rotation.w);

        //★palmの情報
        printf(” palm :x=%8.3f, y=%8.3f, z=%8.3f\n”, hand->palm.position.x, hand->palm.position.y, hand->palm.position.z);
        printf(” palm verocity:x=%8.3f, y=%8.3f, z=%8.3f\n”, hand->palm.velocity.x, hand->palm.velocity.y, hand->palm.velocity.z);
        printf(” palm normal:x=%8.3f, y=%8.3f, z=%8.3f\n”, hand->palm.normal.x, hand->palm.normal.y, hand->palm.normal.z);
        printf(” palm width:%8.3f\n”, hand->palm.width);
        printf(” palm direction:x=%8.3f, y=%8.3f, z=%8.3f\n”, hand->palm.direction.x, hand->palm.direction.y, hand->palm.direction.z);
        printf(” palm orientation:x=%8.3f, y=%8.3f, z=%8.3f, w=%8.3f\n”, hand->palm.orientation.x, hand->palm.orientation.y, hand->palm.orientation.z, hand->palm.orientation.w);

        //★digits配列(=finger)の情報
        for(int f = 0; f < 5; f++){
            LEAP_DIGIT finger = hand->digits[f];
            for(int b = 0; b < 4; b++){
                LEAP_BONE bone = finger.bones[b];
                printf(” f[%d]_b[%d]:x=%8.3f, y=%8.3f, z=%8.3f\n”, f, b, bone.next_joint.x, bone.next_joint.y, bone.next_joint.z);
                if( b == 3){ //★情報量が多くなるので、bones[3](distal)時のみ表示
                    printf(” f[%d] isExtended:%d\n”, f, finger.is_extended);
                    printf(” f[%d] width:%8.3f\n”, f, bone.width);
                    printf(” f[%d] tip rotation:x=%8.3f, y=%8.3f, z=%8.3f, w=%8.3f\n”,f, bone.rotation.x, bone.rotation.y, bone.rotation.z, bone.rotation.w);
                }
            }
        }
    }

}

以上、今回はUltraleap社のHand Tracking SDKでの取得可能な情報についてにご紹介しました。

 

関連製品を見る