Leap Motionの日本語情報は非常に限られているので公式ドキュメントをざっくり日本語訳にしました。誤訳などあったらお知らせください。
本エントリはhttps://developer.leapmotion.com/documentation/Languages/CSharpandUnity/Guides/Sample_CSharp_Tutorial.htmlの日本語訳となります。
Understanding the C# Sample Application
Topics:
Overview
Creating a Controller object
Subclassing the Listener class
Getting a Frame of data
Getting Gestures
Running the sample
Leap SDKフォルダ配下に、この資料で使う次のファイルがあります。
- LeapSDK/samples/Sample.cs ? C# sample application
- Windows:
- LeapSDK/lib/LeapCSharp.NET3.5.dll ? Leap C# library for .NET 3.5
- LeapSDK/lib/LeapCSharp.NET4.0.dll ? Leap C# library for .NET 4.0
- LeapSDK/lib/x86/LeapCSharp.dll ? 32-bit Leap C# library for Windows
- LeapSDK/lib/x64/LeapCSharp.dll ? 64-bit Leap C# library for Windows
- LeapSDK/lib/x86/Leap.dll ? 32-bit Leap library for Windows
- LeapSDK/lib/x64/Leap.dll ? 64-bit Leap library for Windows
- Mac (Mono):
Leap APIリファレンスは、LeapSDK/docs/API_Reference/annotated.html にインストールされます。
Overview
一言で言えば、Leap Motionトラッキングデバイスが検出され、手とその検出範囲内にある指を追跡します。Leapは、1度に1フレームのデータをキャプチャします。アプリケーションからこのデータにアクセスするにはLeap APIを使います。
サンプル アプリケーションはLeap API を使用して、Leapからのフレームイベントを取得する方法と、各フレームの手や指のデータにアクセスする手段を提供します。Sample.csはコマンドラインアプリケーションとなっており、検出された手や指を標準出力に出力します。
サンプルアプリケーションでは、Leap APIの主要なクラスの大部分を使用しています。
- Controller ? Leapとアプリケーション間のインターフェース
- Listener ? Leapによって送出されたイベントを処理するために使用
- Frame ? 手と指のトラッキングデータのセットが含まれています
- Hand ? 検出された手のトラッキングデータが含まれています
- Finger ? 検出された指のトラッキングデータが含まれています
- Vector ? 3Dの位置または方向ベクトルを表します
- Gesture ? 認識できたジェスチャーを表します
これらのクラスの詳細についてはLeap APIのリファレンスを参照してください。
Creating a Controller object
Controllerクラスは、Leapとアプリケーションの間の主要なインターフェイスを提供します。Controllerオブジェクトを作成すると実行中のLeapソフトウェアに接続して、Frameオブジェクトを通じて手のトラッキングデータが利用可能になります。Frameオブジェクトは、Controllerオブジェクトをインスタンス化して、Controller.Frame メソッドを呼び出すことによってアクセスできます。
アプリケーションからはループ処理によりController.Frameを呼び出して使います。ループ処理により定期的にポーリングする以外に、Controllerオブジェクトにリスナーを追加する方法もあります。Controllerオブジェクトはトラッキングデータの新たしいフレームが利用できるとき(もしくは他のLeapイベント発生時に)はListenerサブクラスで定義されているコールバックメソッドを呼び出します。
サンプルアプリケーションでは、MainメソッドでControllerオブジェクトを作成し、Controller.AddListenerメソッドでListenerのサブクラスインスタンスを追加します。
VB.NET
Class Sample Public Sub Main() ' Create a sample listener and controller Dim listener As new SampleListener Dim _controller As new Controller ' Have the sample listener receive events from the controller _controller.AddListener(listener); ' Keep this process running until Enter is pressed Console.WriteLine("Press Enter to quit..."); Console.ReadLine(); ' Remove the sample listener when done _controller.RemoveListener(listener) _controller.Dispose() End Sub End Class
C#
class Sample { public static void Main() { // Create a sample listener and controller SampleListener listener = new SampleListener(); Controller controller = new Controller(); // Have the sample listener receive events from the controller controller.AddListener(listener); // Keep this process running until Enter is pressed Console.WriteLine("Press Enter to quit..."); Console.ReadLine(); // Remove the sample listener when done controller.RemoveListener(listener); controller.Dispose(); } }
[ENTER]キーを押すまでサンプルアプリケーションは十恋し続けます。実行している間、Leapイベントが発生したときはControllerが適切なリスナーコールバックを呼び出します。上記のサンプルコードではSampleListenerクラスがリスナークラスの独自サブクラスになります。次はそのサブクラスの実装をみてみましょう。
Subclassing the Listener class
サンプル アプリケーションでは、リスナーのサブクラスであるSampleListenerでLeapからのイベントを処理するコールバックメソッドを実装しています。
イベントは次のとおりです。
- OnInit ? Listenerが登録されているControllerオブジェクトの初期化時のイベント。一度だけ発生します。
- OnConnect ? Leapに接続し、モーショントラッキングデータのフレームの送信開始の準備ができたときのイベント
- OnDisconnect ? Leapデバイスを外したりLeapソフトウェアを落としたときなど、ControllerオブジェクトがLeapから切り離されたときのイベント
- OnExit ? ControllerオブジェクトからListenerが切り離された時のイベント
- OnFrame ? モーショントラッ キングデータの新しいフレームが使用できるときのイベント
サンプルアプリケーションでは、 3つのライフサイクルイベントコールバック(OnInit OnDisconnect、OnExit)が発生すると標準出力にメッセージを出力します。OnConnect とOnFrameのイベント発生時にはメッセージ出力以外の処理もしています。 OnConnectコールバックで呼び出された関数の中では、認識したいジェスチャを使用可能にしています。OnFrameコールバックで呼び出された関数の中では、モーショントラッ キングデータの最新のフレームの内容を標準出力にメッセージ出力します。
Getting a Frame of data
Leapは、モーショントラッキングデータの新しいフレームを生成するときに、OnFrameコールバックメソッドを呼び出します。Controller.Frame()メソッドを呼び出すことによって、新たなデータにアクセスすることができます (Controllerオブジェクトへの参照がパラメータとしてコールバックに渡されます)。Frameオブジェクトには、ID、タイムスタンプおよび検出された手に対するHandオブジェクトを含むリストから構成されています。
サンプルアプリケーションのOnFrame実装の次の部分は、Controllerオブジェクトから最新のFrameオブジェクトを取得し、フレームID、タイムスタンプ、検出した手の数、検出した指の数、検出したツールの数を出力しています。
VB.NET
' Get the most recent frame and report some basic information Dim _frame As Frame = _controller.Frame() SafeWriteLine("Frame id: " + _frame.Id & ", timestamp: " & _frame.Timestamp & ", hands: " & _frame.Hands.Count & ", fingers: " & _frame.Fingers.Count & ", tools: " & _frame.Tools.Count)
C#
// Get the most recent frame and report some basic information Frame frame = controller.Frame(); SafeWriteLine("Frame id: " + frame.Id + ", timestamp: " + frame.Timestamp + ", hands: " + frame.Hands.Count + ", fingers: " + frame.Fingers.Count + ", tools: " + frame.Tools.Count);
次のコードは、手が検出できたかをチェックし、検出できた場合はリストの最初の手を取得しています。
VB.NET
If (Not _frame.Hands.Empty) Then ' Get the first hand Dim _hand As Hand = _frame.Hands(0)
C#
if (!frame.Hands.Empty) { // Get the first hand Hand hand = frame.Hands[0];
Handオブジェクトは、ID、手の物理的特性を表すプロパティおよびFingerオブジェクトリストが含まれています。各Fingerオブジェクトは、IDと指の特性を表すプロパティが含まれています。
次のコードはHandオブジェクトからFingerオブジェクトリストを取り出して、指の先端の平均的な位置を標準出力に出力します。
VB.NET
' Check if the hand has any fingers Dim fingers As FingerList = _hand.Fingers If (Not fingers.Empty) Then ' Calculate the hand's average finger tip position Dim avgPos As Vector = Vector.Zero For Each _finger In fingers avgPos += _finger.TipPosition Next avgPos /= fingers.Count SafeWriteLine("Hand has " & fingers.Count & " fingers, average finger tip position: " & avgPos) End If
C#
// Check if the hand has any fingers FingerList fingers = hand.Fingers; if (!fingers.Empty) { // Calculate the hand's average finger tip position Vector avgPos = Vector.Zero; foreach (Finger finger in fingers) { avgPos += finger.TipPosition; } avgPos /= fingers.Count; SafeWriteLine("Hand has " + fingers.Count + " fingers, average finger tip position: " + avgPos); }
次に、手の平が形作っている球体の半径と手の平の位置を出力します。
VB.NET
' Get the hand's sphere radius and palm position SafeWriteLine("Hand sphere radius: " & _hand.SphereRadius.ToString("n2") _ & " mm, palm position: " & hand.PalmPosition)
C#
// Get the hand's sphere radius and palm position SafeWriteLine("Hand sphere radius: " + hand.SphereRadius.ToString("n2") + " mm, palm position: " + hand.PalmPosition);
最後に、OnFrameメソッドは、手の法線ベクトルと方向ベクトルから手のピッチ、ロール、ヨー角を算出するベクトルを使用します。角度はラジアンから度に変換されます。
VB.NET
' Get the hand's normal vector and direction Dim normal As Vector = _hand.PalmNormal Dim direction As Vector = _hand.Direction ' Calculate the hand's pitch, roll, and yaw angles SafeWriteLine("Hand pitch: " & direction.Pitch * 180.0F / CType(Math.PI, Single) & " degrees, " _ & "roll: " & normal.Roll * 180.0F / CType(Math.PI, Single) & " degrees, " _ & "yaw: " & direction.Yaw * 180.0F / CType(Math.PI, Single) & " degrees\n")
C#
// Get the hand's normal vector and direction Vector normal = hand.PalmNormal; Vector direction = hand.Direction; // Calculate the hand's pitch, roll, and yaw angles SafeWriteLine("Hand pitch: " + direction.Pitch * 180.0f / (float)Math.PI + " degrees, " + "roll: " + normal.Roll * 180.0f / (float)Math.PI + " degrees, " + "yaw: " + direction.Yaw * 180.0f / (float)Math.PI + " degrees\n");
Getting Gestures
Leapからジェスチャーを受け取るには、最初に認識するジェスチャーを有効にする必要があります。Leapと接続したControllerオブジェクト(IsConnectedがTrue)は、いつでもジェスチャー認識を有効にできます。サンプルプログラムでは、OnConnect()コールバック関数の中でEnableGesture()Controllerクラスで定義されたメソッドを使用して認識したいすべてジェスチャを有効にしています。
VB.NET
Public Overrides Sub OnConnect(_controller As Controller) SafeWriteLine("Connected") _controller.EnableGesture(Gesture.GestureType.TYPECIRCLE) _controller.EnableGesture(Gesture.GestureType.TYPEKEYTAP) _controller.EnableGesture(Gesture.GestureType.TYPESCREENTAP) _controller.EnableGesture(Gesture.GestureType.TYPESWIPE) End Sub
C#
public override void OnConnect (Controller controller) { SafeWriteLine ("Connected"); controller.EnableGesture (Gesture.GestureType.TYPECIRCLE); controller.EnableGesture (Gesture.GestureType.TYPEKEYTAP); controller.EnableGesture (Gesture.GestureType.TYPESCREENTAP); controller.EnableGesture (Gesture.GestureType.TYPESWIPE); }
Leapは、Frameオブジェクトにおけるジェスチャーリストにジェスチャーの動きを定義したGestureオブジェクトを追加します。 サンプル・アプリケーションのOnFrame()イベントでは、検出されたジェスチャーに合わせた情報を標準出力に出力しています。
Gesture APIは、Gestureクラスをベースにして個々のジェスチャーを表すクラスに拡張されています。ジェスチャーリスト内のオブジェクトは、ジェスチャーインスタンスですので、あなたは正しいサブクラスのインスタンスにジェスチャーインスタンスを変換する必要があります。キャストはサポートされない代わりに各サブクラスは変換を実行するコンストラクターを提供します。例えば、例えば、円のジェスチャーは、以下のコードでCircleGestureインスタンスに変換できます。
VB.NET
CircleGesture circle = new CircleGesture (gesture)
C#
CircleGesture circle = new CircleGesture (gesture)
間違えているサブクラスにジェスチャインスタンスを変換する場合、コンストラクター関数は無効なジェスチャオブジェクトを返します。
ジェスチャーでは以前のフレームと現在のフレームでジェスチャーの特性を比較すると便利な場合があります。例えば、円のジェスチャーには円を何回描いたかをカウントするProgressプロパティがあります。今回のフレームの円のジェスチャーのProgressから前回のフレームの円のジェスチャーを引くことで1フレームの回数が取得できます。
VB.NET
' Calculate angle swept since last frame Dim sweptAngle As Single = 0 If (circle.State <> Gesture.GestureState.STATESTART) Then Dim previousUpdate As New CircleGesture(_controller.Frame(1).Gesture(circle.Id)) sweptAngle = (circle.Progress - previousUpdate.Progress) * 360 End If
C#
// Calculate angle swept since last frame float sweptAngle = 0; if (circle.State != Gesture.GestureState.STATESTART) { CircleGesture previousUpdate = new CircleGesture (controller.Frame (1).Gesture (circle.Id)); sweptAngle = (circle.Progress - previousUpdate.Progress) * 360; }
ジェスチャーループの完全なコードは次の通りです。
VB.NET
' Get gestures Dim gestures As GestureList = _frame.Gestures() For i As Integer = 0 To gestures.Count - 1 Dim _gesture As Gesture = gestures(i) Select Case _gesture.Type Case Gesture.GestureType.TYPECIRCLE Dim circle As New CircleGesture(_gesture) ' Calculate clock direction using the angle between circle normal and pointable Dim clockwiseness As String If (circle.Pointable.Direction.AngleTo(circle.Normal) <= Math.PI / 4) Then 'Clockwise if angle is less than 90 degrees clockwiseness = "clockwise" Else clockwiseness = "counterclockwise" End If Dim sweptAngle As Single = 0 ' Calculate angle swept since last frame If (circle.State <> Gesture.GestureState.STATESTART) Then Dim previousUpdate As New CircleGesture(_controller.Frame(1).Gesture(circle.Id)) sweptAngle = (circle.Progress - previousUpdate.Progress) * 360 End If SafeWriteLine("Circle id: " & circle.Id _ & ", " & circle.State _ & ", progress: " & circle.Progress _ & ", radius: " & circle.Radius _ & ", angle: " & sweptAngle _ & ", " & clockwiseness) Case Gesture.GestureType.TYPESWIPE Dim swipe As New SwipeGesture(_gesture) SafeWriteLine("Swipe id: " & swipe.Id _ & ", " & swipe.State _ & ", position: " & swipe.Position.ToString _ & ", direction: " & swipe.Direction.ToString _ & ", speed: " & swipe.Speed) Case Gesture.GestureType.TYPEKEYTAP Dim keytap As New KeyTapGesture(_gesture) SafeWriteLine("Tap id: " & keytap.Id _ & ", " & keytap.State _ & ", position: " & keytap.Position.ToString _ & ", direction: " & keytap.Direction.ToString) Case Gesture.GestureType.TYPESCREENTAP Dim screentap As New ScreenTapGesture(_gesture) SafeWriteLine("Tap id: " & screentap.Id _ & ", " & screentap.State _ & ", position: " & screentap.Position.ToString _ & ", direction: " & screentap.Direction.ToString) Case Else SafeWriteLine("Unknown gesture type.") End Select Next
C#
// Get gestures GestureList gestures = frame.Gestures (); for (int i = 0; i < gestures.Count; i++) { Gesture gesture = gestures [i]; switch (gesture.Type) { case Gesture.GestureType.TYPECIRCLE: CircleGesture circle = new CircleGesture (gesture); // Calculate clock direction using the angle between circle normal and pointable String clockwiseness; if (circle.Pointable.Direction.AngleTo (circle.Normal) <= Math.PI / 4) { //Clockwise if angle is less than 90 degrees clockwiseness = "clockwise"; } else { clockwiseness = "counterclockwise"; } float sweptAngle = 0; // Calculate angle swept since last frame if (circle.State != Gesture.GestureState.STATESTART) { CircleGesture previousUpdate = new CircleGesture (controller.Frame (1).Gesture (circle.Id)); sweptAngle = (circle.Progress - previousUpdate.Progress) * 360; } SafeWriteLine ("Circle id: " + circle.Id + ", " + circle.State + ", progress: " + circle.Progress + ", radius: " + circle.Radius + ", angle: " + sweptAngle + ", " + clockwiseness); break; case Gesture.GestureType.TYPESWIPE: SwipeGesture swipe = new SwipeGesture (gesture); SafeWriteLine ("Swipe id: " + swipe.Id + ", " + swipe.State + ", position: " + swipe.Position + ", direction: " + swipe.Direction + ", speed: " + swipe.Speed); break; case Gesture.GestureType.TYPEKEYTAP: KeyTapGesture keytap = new KeyTapGesture (gesture); SafeWriteLine ("Tap id: " + keytap.Id + ", " + keytap.State + ", position: " + keytap.Position + ", direction: " + keytap.Direction); break; case Gesture.GestureType.TYPESCREENTAP: ScreenTapGesture screentap = new ScreenTapGesture (gesture); SafeWriteLine ("Tap id: " + screentap.Id + ", " + screentap.State + ", position: " + screentap.Position + ", direction: " + screentap.Direction); break; default: SafeWriteLine ("Unknown gesture type."); break; } }
Running the sample
To run the sample application:
- サンプルのコンパイル
-
dows では、現在のディレクトリにある Sample.cs と LeapCSharp.NET3.5.dll または LeapCSharp.NET4.0.dll を確認します。(を使用している .NET framework の適切なライブラリ参照を使用して) コマンド ライン プロンプトで次のコマンドを実行します。
csc /reference:LeapCSharp.NET4.0.dll /platform:x86 /target:exe Sample.cs
注: .NET framework の適切なバージョンから「csc」コンパイラを使用します。
- Mac では、c# プログラムをコンパイルする、Mono プロジェクトを使用してできます。現在のディレクトリにある Sample.cs と LeapCSharp.NET3.5.dll または LeapCSharp.NET4.0.dll を確認します。
-
- LeapデバイスをUSBに繋ぎ、自分の前に設置
- Leapソフトウェアをインストールしていなかったらインストール
- Leapソフトウェアをスタート。プロンプトが表示されたら登録したメールアドレスとパスワードを入力します。準備ができたらタスクバーの通知領域に表示されたアイコンが緑色になります。
- サンプルアプリの実行
-
Windowsでは、Sample.exe、LeapCSharp.dll、Leap.dll、LeapCSharp.NET3.5.dllまたはLeapCSharp.NET4.0.dllを同じディレクトリに配置します。32ビットのプロジェクトはlib\x86ディレクトリのライブラリを使用し、64ビットプロジェクトはlib\x64ディレクトリのライブラリを使用します。コマンドラインプロンプトで次のコマンドを実行します。
Sample.exe
Visual Studioでコンパイルする場合は、プロジェクトでlib\LeapCSharp.NET4.0.dllを参照設定し、ビルドターゲットをx86にした場合はlib\x86\LeapCSharp.dllとlib\x86\Leap.dllを取り込み、コンパイルアクション「なし」、コピーアクション「常にコピーする」と各ファイルのプロパティで設定しておくと便利です。なお、ターゲットフレームワークを4.5としても動作します。
なお、ビルドターゲットをx64にした場合はlib\x64\LeapCSharp.dllとlib\x64\Leap.dllを取り込みます。 -
MacではSample.exe、 libLeapCSharp.dylib、 libLeap.dylib、LeapCSharp.NET3.5.dllまたはLeapCSharp.NET4.0.dllを同じディレクトリに配置し、ターミナルウィンドウで次のコマンドを実行して確認してください。
mono Sample.exe
-
サンプルアプリケーションを実行すると、初期化時とLeap接続時に「Initialized」と「Connected」が標準出力に出力されます。OnFrameのイベントが検出されると標準出力にフレーム情報が出力されます。Leapの上に手をかざすと指と手の平の位置情報が出力されます。
このサンプルでLeapからのモーショントラックングデータの取得方法が理解できたら、Leapを使ったC#アプリケーションの開発を始めることができます。