Leap Motionの日本語情報は非常に限られているので公式ドキュメントをざっくり日本語訳にしました。誤訳などあったらお知らせください。
本エントリはhttps://developer.leapmotion.com/documentation/Languages/CSharpandUnity/Guides/Leap_Tracking.htmlの日本語訳となります。
Tracking Hands, Fingers, and Tools
手、指およびツールはLeap Motion Systemにおけるトラッキング基本エンティティです。本稿ではこれらのエンティティを表すオブジェクトの取得方法と使用方法の詳細を説明します。
Topics:
- Overview
- Hand, and Pointable Lists
- Hands
- Getting the Hand Characteristics
- Getting the Fingers and Tools
- Computing the Hand Orientation
- Transforming Finger Coordinates into the Hand's Frame of Reference
- Pointables
- Converting a Pointable Object to a Finger or Tool
- Calculating the Position of the Base of a Finger
Overview
Leap APIはトラッキング基本オブジェクトを表すクラスを定義しています。
Frameオブジェクトは、手、指およびツールのリストへのアクセスを提供します。例えばPointableオブジェクトである指とツールはPointableListとして一緒に扱うかFingerListとToolListクラスを使用して個別に扱うことができます。手のオブジェクトは指とツール(PointableListとして一緒に、または別個に)へのアクセスを提供します。
手、指およびツールの物理的特性はLeap座標系(mm単位で測定)の値として取得できます。Leap SDKは位置と方向を表すVectorクラスを提供します。Vectrorクラスを扱うための数学関数を提供します。
Listクラスはすべて同じ構造を持っています。ベクトル形式の配列のように振る舞うし、イテレータ(反復子)をサポートしています。Leap APIから受信したメンバーオブジェクトを削除したり変更することはできませんが、同じオブジェクトタイプのリストを組み合わせる事ができます。
反復処理を行って例は次のようになります。
VB.NET
For Each Hand hand in handList
Console.WriteLine(hand.ToString())
Next
C#
foreach (Hand hand in handList) {
Console.WriteLine(hand.ToString());
}
手、Pointable、指およびツールリストは、Leap座標系の相対位置に基づいて、リストのメンバーを取得するためのleftmost()、rightmost()そしてfrontmostなどが定義されています。次のスニペットではこれらの関数を使用するいくつかの方法を示しています。
VB.NET
Dim farLeft As Finger = _frame.Fingers.Leftmost
Dim mostForwardOnHand As Finger = _frame.Hands(0).Fingers.Frontmost
Dim rightTool As Tool = _frame.Tools.Rightmost
C#
Finger farLeft = frame.Fingers.Leftmost;
Finger mostForwardOnHand = frame.Hands[0].Fingers.Frontmost;
Tool rightTool = frame.Tools.Rightmost;
より複雑な例では、検出されたすべてのPointableオブジェクトを含むバウンディングボックスの壁を計算してみます。APIには含まれていない機能なので、下記の例では、上、下を取得するための独自の関数及びリアPointablesを定義します。
VB.NET
Private left As single = frame.Pointables.Leftmost.TipPosition.x
Private right As single = frame.Pointables.Rightmost.TipPosition.x
Private front As single = frame.Pointables.Frontmost.TipPosition.z
Private back As single = backmost(frame.Pointables).TipPosition.z
Private top As single = topmost(frame.Pointables).TipPosition.y
Private bottom As single = bottommost(frame.Pointables).TipPosition.y
Private Function backmost(pointables As PointableList) As Pointable
If pointables.Count = 0 Then
return Pointable.Invalid
End If
Dim backmost As Pointable = pointables(0)
For p = 1 As Integer To p < pointables.Count - 1
If pointables[p].TipPosition.z > backmost.TipPosition.z Then
backmost = pointables(p)
End If
Next
Return backmost
End Function
Private Function topmost(pointables As PointableList) As Pointable
If pointables.Count = 0 Then
return Pointable.Invalid
End If
Dim topmost As Pointable = pointables(0)
For p = 1 As Integer To p < pointables.Count - 1
If pointables[p].TipPosition.y > topmost.TipPosition.y Then
topmost = pointables(p)
End If
Next
Return topmost
End Function
Private Function bottommost(pointables As PointableList) As Pointable
If pointables.Count = 0 Then
return Pointable.Invalid
End If
Dim bottommost As Pointable = pointables(0)
For p = 1 As Integer To p < pointables.Count - 1
If pointables[p].TipPosition.y > bottommost.TipPosition.y Then
bottommost = pointables(p)
End If
Next
Return bottommost;
End Function
C#
float left = frame.Pointables.Leftmost.TipPosition.x;
float right = frame.Pointables.Rightmost.TipPosition.x;
float front = frame.Pointables.Frontmost.TipPosition.z;
float back = backmost(frame.Pointables).TipPosition.z;
float top = topmost(frame.Pointables).TipPosition.y;
float bottom = bottommost(frame.Pointables).TipPosition.y;
Pointable backmost(PointableList pointables)
{
if(pointables.Count == 0) return Pointable.Invalid;
Pointable backmost = pointables[0];
for( int p = 1; p < pointables.Count; p++ )
{
if( pointables[p].TipPosition.z > backmost.TipPosition.z)
backmost = pointables[p];
}
return backmost;
}
Pointable topmost(PointableList pointables)
{
if(pointables.Count == 0) return Pointable.Invalid;
Pointable topmost = pointables[0];
for( int p = 1; p < pointables.Count; p++ )
{
if( pointables[p].TipPosition.y > topmost.TipPosition.y)
topmost = pointables[p];
}
return topmost;
}
Pointable bottommost(PointableList pointables)
{
if(pointables.Count == 0) return Pointable.Invalid;
Pointable bottommost = pointables[0];
for( int p = 1; p < pointables.Count; p++ )
{
if( pointables[p].TipPosition.y < bottommost.TipPosition.y )
bottommost = pointables[p];
}
return bottommost;
}
Hands
HandクラスはLeapによって検出された物理的な手を表しています。HandオブジェクトはPointablesリストだけではなく、手の位置、向き、動きなどの属性へのアクセスを提供します。
次の例ではFrameからHandオブジェクトを取得してます。
VB.NET
Dim _frame As Frame = controller.Frame ‘ controller is a Controller object
Dim hands As HandList = _frame.Hands
Dim firstHand AS Hand = hands(0)
C#
Frame frame = controller.Frame(); // controller is a Controller object
HandList hands = frame.Hands;
Hand firstHand = hands[0];
Frameの中での相対的な手の位置を取得するには次のようなコードを記述します。
VB.NET
Dim _frame As Frame = controller.Frame ‘ controller is a Controller object
Dim hands As HandList = frame.Hands
Dim leftmost As Hand = hands.Leftmost
Dim rightmost As Hand = hands.Rightmost
Dim frontmost As Hand = hands.Frontmost
C#
Frame frame = controller.Frame(); // controller is a Controller object
HandList hands = frame.Hands;
Hand leftmost = hands.Leftmost;
Hand rightmost = hands.Rightmost;
Hand frontmost = hands.Frontmost;
注意:Leftmost()とRightmost()は、もっとも左または右に離れている手を識別することに注意してください。位置関係の判定であり左手や右手を認識しているわけではありません。
Getting the Hand Characteristics
手に対しては、位置、方向、動きを取得できます。
手の位置はLeap Motinからミリメートル単位で掌の中心点の3次元座標を含むベクターで表された掌位置属性で指定されます。手の方向は、掌の中心から指の方向と垂直方向の2つのベクトルによって与えられます。
手の動きはmm/secで手の瞬間的な動きがベクトルとして取得できます。また、2つの指定したフレーム間での手の平行移動、回転、拡大縮小値を変換するモーションファクターを取得できます。
次のコードスニペットでは、フレームからHandオブジェクトを取得し、その基本的な属性にアクセスする方法を示しています。
VB.NET
Dim _hand As Hand = frame.Hands.Rightmost
Dim position As Vector = hand.PalmPosition
Dim velocity As Vector = hand.PalmVelocity
Dim direction As Vector = hand.Direction
C#
Hand hand = frame.Hands.Rightmost;
Vector position = hand.PalmPosition;
Vector velocity = hand.PalmVelocity;
Vector direction = hand.Direction;
Getting the Fingers and Tools
IDを使ってリストや個別情報から手と一緒に指やツールも取得できます。
リストの例は次のようになります。
VB.NET
' hand is a Hand object
DIm pointables As PointableList = hand.Pointables ' Both fingers and tools
Dim fingers As FingerList = hand.Fingers
Dim tools As ToolList = hand.Tools
C#
// hand is a Hand object
PointableList pointables = hand.Pointables; // Both fingers and tools
FingerList fingers = hand.Fingers;
ToolList tools = hand.Tools;
以前のフレームのIDを使った例は次のようになります。
VB.NET
Dim knownPointable As Pointable = hand.Pointable(pointableID)
C#
Pointable knownPointable = hand.Pointable(pointableID);
Leapの検出範囲内での指やツールの位置を取得するのに、listクラスのrightmost、leftmostおよびfrontmostが使えます。
VB.NET
' hand is a Hand object
Dim leftPointable As Pointable = hand.Pointables.Leftmost
Dim rightFinger As Finger = hand.Fingers.Rightmost
Dim frontTool As Tool = hand.Tools.Frontmost
C#
// hand is a Hand object
Pointable leftPointable = hand.Pointables.Leftmost;
Finger rightFinger = hand.Fingers.Rightmost;
Tool frontTool = hand.Tools.Frontmost;
注意:手自体ではなくLeap Motionの中心点に対して相対的なことに注意してください。手を基準にして指を取得するにはLeap Matrixクラスを使用して変換します。
Transforming Finger Coordinates into the Hand's Frame of Reference
参照している手のフレームと手の指の座標が一緒に取得できると便利な場合が多いです。なぜなら指を並べたときに指の位置の分析を簡略化できるからです。指の位置と方向を変換するためLeap Matrixクラスを使用して変換行列を作成することができます。手の基準は、2つの間のクロス積で定義されている3番目の軸と手の方向と掌の法線ベクトルを使用して定義できます。
VB.NET
Dim _frame As Frame = leap.Frame
For h As Integer = 0 To h < frame.Hands.Count - 1
Hand leapHand = frame.Hands(h)
Vector handXBasis = leapHand.PalmNormal.Cross(leapHand.Direction).Normalized
Vector handYBasis = -leapHand.PalmNormal
Vector handZBasis = -leapHand.Direction
Vector handOrigin = leapHand.PalmPosition
handTransform As Matrix = new Matrix(handXBasis, handYBasis, handZBasis, handOrigin)
handTransform = handTransform.RigidInverse()
For f As Integer = 0 To f < leapHand.Fingers.Count - 1
Dim leapFinger As Finger = leapHand.Fingers(f)
Dim transformedPosition As Vector = handTransform.TransformPoint(leapFinger.TipPosition)
Dim transformedDirection As Vector = handTransform.TransformDirection(leapFinger.Direction)
' Do something with the transformed fingers
Next
Next
C#
Frame frame = leap.Frame();
for( int h = 0; h < frame.Hands.Count; h++ )
{
Hand leapHand = frame.Hands[h];
Vector handXBasis = leapHand.PalmNormal.Cross(leapHand.Direction).Normalized;
Vector handYBasis = -leapHand.PalmNormal;
Vector handZBasis = -leapHand.Direction;
Vector handOrigin = leapHand.PalmPosition;
Matrix handTransform = new Matrix(handXBasis, handYBasis, handZBasis, handOrigin);
handTransform = handTransform.RigidInverse();
for( int f = 0; f < leapHand.Fingers.Count; f++ )
{
Finger leapFinger = leapHand.Fingers[f];
Vector transformedPosition = handTransform.TransformPoint(leapFinger.TipPosition);
Vector transformedDirection = handTransform.TransformDirection(leapFinger.Direction);
// Do something with the transformed fingers
}
}
Pointables
Pointableオブジェクトは指とツール、すなわち指し示すものを表します。指や手のオブジェクトから特定の手に関連付けられているツール(つまり手に持っている道具)を得ることができます。また、Frameオブジェクトから検出されたすべてのPointablesを得ることができます。Pointablesは必ずしもHandオブジェクトに関連付けられていません。例えば、物理的な手自体がLeap検出範囲の外に当ても良いし、別の手でブロックされている場合もあるでしょう)。したがってFrameからのPointablesリストには関連付けられていないLeapが検出できなかった指やツールを含めることができます。
Pointableオブジェクトは指やツールの特性を記述した多くの属性があります。
- TipPosition:Leap Motionからの先端までの距離をmm単位で表す
- TipVelocity:先端の移動速度(mm/sec)
- StabilizedTipPosition:手ぶれ防止フィルタをかけた位置と速度
- Direction:先端が差す方向
- Length:指またはツールの見かけ上の長さ
- Width:平均幅
- TouchDistance:仮想タッチ面からの正規化した距離
- TouchZone:先端と仮想タッチ面の関係
次の例は、FrameからPointableオブジェクトを取得し、その基本的な特性にアクセスする方法を示しています。
VB.NET
Dim _pointable As Pointable = _frame.Pointables.Frontmost
Dim direction As Vector = _pointable.Direction
Dim length As Single = _pointable.Length
Dim width As Single = _pointable.Width
Dim stabilizedPosition As Vector = _pointable.StabilizedTipPosition
Dim position As Vector = _pointable.TipPosition
Dim speed As Vector = _pointable.TipVelocity
Dim touchDistance As Single = _pointable.TouchDistance
Dim zone As Pointable.Zone = _pointable.TouchZone
C#
Pointable pointable = frame.Pointables.Frontmost;
Vector direction = pointable.Direction;
float length = pointable.Length;
float width = pointable.Width;
Vector stabilizedPosition = pointable.StabilizedTipPosition;
Vector position = pointable.TipPosition;
Vector speed = pointable.TipVelocity;
float touchDistance = pointable.TouchDistance;
Pointable.Zone zone = pointable.TouchZone;
Converting a Pointable Object to a Finger or Tool
Pointableオブジェクトを適切なFingerサブクラスやToolサブクラスに変換するには、適切なコンストラクター(Leapクラスのコンストラクタを使う必要があります)を使ってください。
VB.NET
If (_pointable.IsTool) Then
Dim _tool As new Tool(_pointable)
else
Dim _finger As new Finger(_pointable)
End If
C#
if (pointable.IsTool) {
Tool tool = new Tool(pointable);
} else {
Finger finger = new Finger(pointable);
}
Calculating the Position of the Base of a Finger
指の起点を位置を計算する場合は、次のように指の先端の位置と方向を使用します。
VB.NET
Vector basePosition = -pointable.Direction * pointable.Length
basePosition += pointable.TipPosition
C#
Vector basePosition = -pointable.Direction * pointable.Length;
basePosition += pointable.TipPosition;