連載:Leap Motion開発入門(C#編)
Leap SDKで指を検出してみよう(Tracking Hands, Fingers, and Tools)
Leap Motionの最大の特長である手・指を検出するには? Leap Motion Developer SDKを活用した開発方法を詳しく解説。※C++編の同名タイトルと基本的な内容は同じです。
今回はLeap Motionの最大の特長である手・指の検出について解説する。Leap Motion Developer SDK(以下、Leap SDKとする)の特長は手や指の扱い方がシンプルであること、座標や方向のベクトルを表すVector
クラス(Leap名前空間)が使いやすいことが挙げられるだろう。これらの情報を組み合わせて、アプリケーションを開発することになるだろう。
全体のコードは次のリンク先で公開しているので、参照しながら以下の解説を読み進めてほしい。
Leap Motionで取得できるもの
Leap Motionで取得できる種類には次のものがある。
- 手
- 指
- ツール(=棒状のもの)
指およびツールはPointable
なものとしてまとめて扱うこともできる。
それぞれに、位置や方向、速さや向きといったパラメーターを持っている。
では取得方法から順に見てみよう。
手/指/ツールを取得する
手/指/ツールを取得する方法はいくつかあり。手の取得方法と、指/ツールの取得方法について、それぞれ下記に示す。
【手/指/ツール共通の取得方法】
●現在のフレームから、検出された指およびツールの一覧を取得し、その中から所望のものを探す(どの手の指かというフィルターはなく、検出した指やツールが全て取得される)
・手の一覧はLeap.Frame.Hands
プロパティで取得する
・指の一覧はLeap.Frame.Fingers
プロパティで取得する
・ツールの一覧はLeap.Frame.Tools
で取得する
・ポインター(=指とツール)の一覧はLeap.Frame.Pointables
プロパティで取得する
●現在のフレームで、以前に取得したIDを渡すことで取得する
●現在のフレームで、一番左/右/手前の手を取得する
【指/ツールのみの取得方法】
●現在のフレームで、検出した手に属する指やツールを取得する
では、それぞれの取得方法を見てみよう。
現在のフレームで検出した手などの一覧を取得する
まずは、現在のフレームで検出した全ての手/指/ツールを取得してみよう。
上述の通り、Leap.Frame
クラスのHands
/Fingers
/Tools
/Pointables
プロパティで一覧を取得する。次のプログラムでは、取得した手などの数を表示する。なお、v2では指を全て追跡するため、伸びている指だけを取得したい場合にはFingers.Extended().Count
を使用する(詳しくは「これは快適! Leap Motion v2で格段に良くなったSkeletal Tracking機能」を参照)。
HandList hands = frame.Hands;
FingerList fingers = frame.Fingers;
ToolList tools = frame.Tools;
PointableList pointables = frame.Pointables;
Console.WriteLine( string.Format(
"Frame Data : Hands : {0} Fingers : {1} Extended Fingers : {2} Tools : {3} Pointers : {4}",
hands.Count, fingers.Count, fingers.Extended().Count, tools.Count, pointables.Count ) );
|
リスト1を実行すると図1~3のようになる。片手、両手や手をグーパーしてみると動作が分かる。ツールは太めのペンを使うとよいだろう。
なお、手およびツール(棒)を検出させる際には[Leap Motionコントロール パネル]の[トラッキング]タブから[道具の追跡]および[手の追跡]のチェックボックスがチェックされていることを確認する(図4)。
現在のフレームで、以前に取得したIDを渡すことで取得する
次に、IDを利用した取得方法を解説する。
手などを検出すると個別に一意のIDが振られる。このIDをキーとして目的のデータを取得する。検出した手などを、フレームをまたいで追跡し続ける場合などに利用する方法だ。
次のプログラムでは、手のIDを取得し、そのIDの手を取得し続けるものだ。Leap SDKでは手などを検出しなかった場合でも、既定の無効値のデータが返される。そのため、インデックスのチェックなどは不要となっている。有効なIDを取得できた場合には、そのIDでフレームからデータを取得し、表示するようにしている。
if ( handId == -1 ) {
handId = frame.Hands[0].Id;
}
else {
Hand hand = frame.Hand( handId );
handId = hand.Id;
// 手の情報を表示する
Console.WriteLine( string.Format( "ID : {0} 位置 : {1} 速度 : {2} 法線 : {3} 向き : {4}",
hand.Id, hand.PalmPosition, hand.PalmVelocity, hand.PalmNormal, hand.Direction ) );
}
|
現在のフレームで、一番左/右/手前の手を取得する
例えば、両手を使った操作、2本の指でマルチタッチを模すような操作で、左右それぞれの手を取得したい場合に利用する。Leap.HandList
/Leap.FingerList
/Leap.ToolList
/Leap.PointableList
というクラスそれぞれにLeftmost
/Rightmost
/Frontmost
プロパティが定義されている。例えば、左右の手の位置を取得したい場合には、Leap.HandList.Leftmost
およびLeap.HandList.Rightmost
で取得できる。もし手を1つしか検出していない場合には、左右で同じIDが返ってくるので問題ない。
HandList hands = frame.Hands;
Hand leftMost = hands.Leftmost;
Hand rightMost = hands.Rightmost;
Hand frontMost = hands.Frontmost;
Console.WriteLine( string.Format( "左 : {0} 右 : {1} 手前 : {2}",
leftMost.PalmPosition, rightMost.PalmPosition, frontMost.PalmPosition ) );
|
現在のフレームで、手の情報を取得する
v2で追加になった左右の手の検出、ピンチおよびグラブ(いずれの意味も後述)の開閉度合い、手の信頼性を取得する。
右手や左手の判別には、左右の手を出すとよい。手を回転させたり開閉させたりしてみよう。手のひらをLeap Motionに向けた場合と手の甲を向けた場合の値についても見てみよう。
ピンチは親指と人差し指の開閉度合い、グラブは手全体の開閉度合いだ(グー、パーする動作)。信頼性は、手を回転させてみると値が変わる。信頼性パラメーターの値によって、手の確実性をフィルタリングできる。
Hand hand = frame.Hands[0];
Console.WriteLine(
string.Format("右手 : {0} ピンチ : {1} グラブ : {2} 信頼性 : {3}",
hand.IsRight, hand.PinchStrength, hand.GrabStrength, hand.Confidence ) );
|
※2015/09/04修正: 詳しくは末尾の【更新履歴】をご参照ください。
現在のフレームで、検出した手に属する指やツールを取得する
これは、ポインター(=手とツール)についての取得方法だ。
指のデータを利用する際に、「どの手の指か」ということを知りたい場合があるだろう。その場合には、検出した手から指を取得することで実現できる。ツールについても同様だが、ツールは細長い棒状のものになるので、必ずしも手に属しているわけではないことに注意されたい(フレーム内で独立している)。
次のプログラムでは、検出した全ての手に対して、その手に属するポインター/指/ツールの数を表示している。実際にこのプログラムを実行すると、先のフレームからの取得では、例えば両指10本が取得されるが、このプログラムでは2つの検出した手に対して5本ずつの指が取得できるのが分かるだろう。
// 手に属している指とツールを取得する
foreach ( var hand in frame.Hands ) {
Console.WriteLine( string.Format( "ID : {0} ポインタ : {1} 指: {2} ツール : {3}",
hand.Id, hand.Pointables.Count, hand.Fingers.Count, hand.Tools.Count ) );
}
|
※2015/09/04修正: 詳しくは末尾の【更新履歴】をご参照ください。
現在のフレームで、検出した指の情報を取得する
いよいよ指の情報を取得する。伸びている指について表示する。Type
やTipPosition
、TipVelocity
、Direction
で指の種類、指先の位置、指の動作速度、指の向きを取得できる。位置の座標系は後述するLeap Motionの3次元座標系である。これらのデータを使って指を使ったアプリケーションを開発する。
// 指の情報を表示する
foreach ( var finger in frame.Fingers ) {
Console.WriteLine( string.Format( "ID : {0} 種類 : {1} 位置 : {2} 速度 : {3} 向き : {4}",
finger.Id, finger.Type, finger.TipPosition, finger.TipVelocity, finger.Direction ) );
}
|
例えば、チョキ(=人差し指と中指)を出すと次のようになる。指の種類は親指がFinger.FingerType.TYPE_THUMB、人差し指がFinger.FingerType.TYPE_INDEX、中指がFinger.FingerType.TYPE_MIDDLE、薬指がFinger.FingerType.TYPE_RING、小指がFinger.FingerType.TYPE_PINKYとなる。
検出した指の関節情報を取得する
最後に指の関節の情報を取得する。Leap Motion SDKではBone
クラスとして定義されており「指骨」を表す。指骨の場所はLeap.Bone.BoneType
列挙体で次の4つが定義されている。
値 | 意味 |
---|---|
TYPE_METACARPAL | 中手骨 |
TYPE_PROXIMAL | 基節骨 |
TYPE_INTERMEDIATE | 中節骨 |
TYPE_DISTAL | 末節骨 |
指骨の位置はPrevJoint
、Center
、NextJoint
で取得でき、指骨の場所と位置の関係は次のようになっている。
これらをコードにすると次のようになる。例えばPrevJoint
のみを描画していけば、Leap Motionのビジュアライザーのような手のモデルを表示できる。
// 指の関節情報を取得する
foreach ( var finger in frame.Fingers ) {
// 末節骨(指先の骨)
var bone = finger.Bone( Bone.BoneType.TYPE_DISTAL );
Console.WriteLine(string.Format("種類 : {0} 中心 : {1} 上端 : {2} 下端 : {3}",
bone.Type, bone.Center, bone.PrevJoint, bone.NextJoint ));
}
|
なお、親指については骨が3つとなっている。下記のコードで、どの骨が有効であるかを確認できる。実行させるとTYPE_METACARPAL(中手骨)の長さが0であるため、Leap MotionとしてはTYPE_PROXIMAL(基節骨)、TYPE_INTERMEDIATE(中節骨)、TYPE_DISTAL(末節骨)から構成されていることが分かる。
// 親指の定義を確認する
foreach ( var finger in frame.Fingers ) {
if ( finger.Type == Finger.FingerType.TYPE_THUMB ) {
for ( int t = (int)Bone.BoneType.TYPE_METACARPAL; t <= (int)Bone.BoneType.TYPE_DISTAL; t++ ) {
var bone = finger.Bone( (Bone.BoneType)t );
Console.WriteLine( string.Format( "種類 : {0} 長さ : {1}", bone.Type, bone.Length ) );
}
}
}
|
手や指から得られる情報
手などの取得方法を理解したところで、そこからどのような情報を得られるのかを見てみよう。Leap.Hnad
クラスから得られる主な情報を次の表に示す。
プロパティ | 得られる情報 |
---|---|
PalmPosition | 手の位置 |
PalmVelocity | 手の動作速度(ミリメートル/秒) |
PalmNormal | 手の法線ベクトル |
Direction | 手の向き |
SphereCenter | 手にフィットする球体の中心座標 |
SphereRadius | 手にフィットする球体の半径 |
指およびツールはそれぞれLeap.Finger
クラスとLeap.Tool
クラスであり、両方ともポインターを表すLeap.Pointable
クラスを継承している。Leap.Pointable
クラスで取得できる主な情報を次の表に示す。
プロパティ | 得られる情報 |
---|---|
TipPosition | ポインターの位置 |
TipVelocity | ポインターの動作速度(ミリメートル/秒) |
Direction | ポインターの向き |
IsFinger | ポインターが指かどうか |
IsTool | ポインターがツールかどうか |
これらの座標系は次の図に示すLeap Motionの座標系で取得できる。この座標を任意の形に加工してアプリケーションで使うことになるだろう。
手にフィットする球体の座標および半径は、次の図の情報だ。
[補足]Leap.Vectorクラス
手の位置や、法線、速度はベクトルとして返される。ベクトルはLeap.Vector
クラスで表されるが、標準で距離や内積、外積、四則計算など、よく使う機能がすでに実装されている。
より詳細な機能は、「Leap.Vectorのリファレンス」を参照してもらいたいが、このようにSDKレベルで細かい機能が提供されていることは非常にうれしい(と感じるのは、Kinect SDKやPerC SDKなど、Vector
クラスが最低限の座標データを保持する機能しか持っていない環境の利用経験が長かったからだろうか……)。
まとめ
このようにLeap SDKでは手や指のさまざまなデータを簡単に扱える。扱い方もほぼ同じなので、1つを覚えれば簡単に他のデータも扱えるだろう。また、Vector
クラスが非常に使いやすいので、他のSDKでも使いたいものだ。
【更新履歴】2015/09/04
以下の誤りがありましたので、お詫びして訂正させていただきます。
リスト4がC++で掲載されていましたが、C#に修正しました。
リスト5にhand.Fingers.Count
が2つありましたが、1つはhand.Tools.Count
の誤りでした。
1. C#によるLeap Motion v2開発の全体像
Leap Motion Developer SDKを利用してC#でLeap Motionのアプリケーションを開発する方法を解説する連載(2015年改訂版)。今回はC#の開発環境など、開発の基礎を紹介。
2. 【現在、表示中】≫ Leap SDKで指を検出してみよう(Tracking Hands, Fingers, and Tools)
Leap Motionの最大の特長である手・指を検出するには? Leap Motion Developer SDKを活用した開発方法を詳しく解説。※C++編の同名タイトルと基本的な内容は同じです。
5. フレームのいろいろな使い方
Leap Motion公式のサンプルを題材に、さまざまなフレームの扱い方についてコードを交えて解説。※C++編の「Leap SDKのいろいろな使い方」と基本的な内容は同じです。