はつねの日記

Kinect, Windows 10 UWP, Windows Azure, IoT, 電子工作

Leap Motion - Getting Frame Data の日本語訳

Leap Motionの日本語情報は非常に限られているので公式ドキュメントをざっくり日本語訳にしました。誤訳などあったらお知らせください。

本エントリはhttps://developer.leapmotion.com/documentation/Languages/CSharpandUnity/Guides/Leap_Frames.htmlの日本語訳となります。

Getting Frame Data

一連のスナップが、フレームと呼んだように、Leap Motion APIはあなたのアプリケーションにモーショントラッキングデータを提示します。 追跡データの各フレームはそのスナップに検出された各実体の測定値と他の情報を含んでいます。 この記事はLeap MotionコントローラからFrameオブジェクトの取得方法の詳細について記載しています。

Topics:
  • Overview
  • Getting Frames by Polling
  • Getting Frames with Callbacks
  • Getting Data from a Frame
  • Using IDs to track entities across frames

Overview

Controllerオブジェクトからトラッキングデータを含むFrameオブジェクトを取得してください。 ControllerクラスのFrameメソッドを使用することで処理する準備ができているときはいつも、Frameオブジェクトを生成可能です。

VB.NET
    If controller.IsConnected Then            'controller is a Controller object
        Frame frame = controller.Frame()      'The latest frame
        Frame previous = controller.Frame(1); 'The previous frame
    End If
C#
    if (controller.IsConnected) //controller is a Controller object
    {
        Frame frame = controller.Frame(); //The latest frame
        Frame previous = controller.Frame(1); //The previous frame
    }

Frameメソッドはパラメタとして履歴番号(=1が現在フレームの直前フレーム)を指定できます。通常、最後の60個のフレームが履歴バッファで維持されています。

Getting Frames by Polling

アプリケーションが違和感のないフレームレートでループ処理を実装している時は、Controllerオブジェクトのフレームをポーリング取得するのが、最も簡単で良い結果につながります。アプリケーションがデータのフレームを処理する準備ができている間だけControllerのFrameメソッドを呼ぶだけで済みます。

ポーリングを使用する場合、もしアプリケーションのフレーム レートがLeapのフレーム レートを超えている場合は同じフレームが返ってくる場合があったり、Leapフレーム レートがアプリケーションのフレーム レートを超えている場合はフレームを取りこぼす可能性があります。

多くの場合、例えば、手の動きへの応答で画面上のオブジェクトを移動するような場合、同じフレームを連続して取得しても滑らかな動きが実現できます(アプリケーションの全体的なフレーム レートが十分に高いと仮定)。

フレームがすでに処理されているかどうかを判定するには、最後に処理したフレームのIDを保存しておいて現在のフレームのIDと比較します。

VB.NET
    Private lastFrameID As Integer = 0

    Private Sub ProcessFrame(_frame As Frame)
        If _frame.Id = lastFrameID Then return
        '...
        lastFrameID = _frame.Id
    End Sub
C#
    Int64 lastFrameID = 0;

    void processFrame( Frame frame )
    {
        if(frame.Id == lastFrameID) return;
        //...
        lastFrameID = frame.Id;
    }

アプリがフレームをスキップしてしまった場合は、履歴バッファからスキップしたフレームを処理するために履歴番号付のFrameメソッドを使用します。

VB.NET
    Private lastFrameID As Integer = 0

    Private Sub NextFrame(_controller As Controller)
        Dim currentID As Integer = _controller.Frame().Id
        For history As Integer = 0 To history < currentID - lastProcessedFrameID - 1
            ProcessFrame(_controller.Frame(history))
        Next
        lastProcessedFrameID = currentID
    End Sub

    Private Sub ProcessNextFrame(_frame As Frame)
        If _frame.IsValid Then
            '...
        End If
    End Sub
C#
    Int64 lastProcessedFrameID = 0;

    void nextFrame(Controller controller)
    {
        Int64 currentID = controller.Frame().Id;
        for( int history = 0; history < currentID - lastProcessedFrameID; history++)
        {
            processFrame(controller.Frame(history));
        }
        lastProcessedFrameID = currentID;
    }

    void processNextFrame(Frame frame)
    {
        if(frame.IsValid)
        {
            //...
        }
    }

Getting Frames with Callbacks

Leap Motion Controllerのフレームレートでフレームを取得する方法としてListenerオブジェクトを使う方法があります。Controllerオブジェクトは、新しいフレームが使用できるときにListenerのonFrameコールバック関数を呼び出します。OnFrameハンドラではFrameオブジェクト自体を呼び出すControllerのFrameメソッドを呼び出すことができます。

リスナーコールバックは独立したスレッドとして呼び出されるマルチスレッドコールバックのためポーリングよりも複雑な処理が必要です。複数のスレッドによりアクセスされるデータは、スレッドセーフな方法で処理されることを確認する必要があります。

次の例はデータの新しいフレームの処理を最小限のリスナーサブクラスで定義しています。

VB.NET
    Class FrameListener
        Inherits Listener

        Sub OnFrame(_controller As Controller)
        {
            Dim _frame As Frame = _controller.Frame()    'The latest frame
            Dim previous As Frame = _controller.Frame(1) 'The previous frame
            '...
        }
    End Class
C#
    class FrameListener : Listener
    {
        void onFrame(Controller controller)
        {
            Frame frame = controller.Frame(); //The latest frame
            Frame previous = controller.Frame(1); //The previous frame
            //...
        }
    };

このようにListenerオブジェクトを使ってトラッキングデータを取得する場合でも、他の処理部分はポーリングしたときと同様です。

注意:リスナーコールバックを使用する場合にも、フレームをスキップすることが可能です。onFrameコールバック関数が完了までに長時間かかる場合、履歴に次のフレームが追加されonFrameコールバックがスキップされます。時間内にフレームの処理が完了しないことが頻発するとLeapソフトウェアはそのフレームを破棄して履歴には追加しません。この問題は処理タスクが多すぎるなどのリソース不足時に発生します。

Getting Data from a Frame

Frameクラスは、フレーム内のデータへのアクセスを提供するいくつかのメンバを用意しています。

例えば、Leap Motion Systemによってトラッキングされた基本的なオブジェクトを取得する方法は次のようになります。

VB.NET
        Dim _controller As new Controller
        ' wait until Controller.isConnected() evaluates to true
        '...

        Dim _frame As Frame = _controller.Frame
        HandList hands = _frame.Hands
        PointableList pointables = _frame.Pointables
        FingerList fingers = _frame.Fingers
        ToolList tools = _frame.Tools
C#
        Controller controller = new Controller();
        // wait until Controller.isConnected() evaluates to true
        //...

        Frame frame = controller.Frame();
        HandList hands = frame.Hands;
        PointableList pointables = frame.Pointables;
        FingerList fingers = frame.Fingers;
        ToolList tools = frame.Tools;

Frameオブジェクトによって返されるオブジェクトは、すべて読み取り専用です。スレッドセーフな作りになっているため、安全にそれらを格納し、あとで利用することができます。内部的にはオブジェクトはC++ Boostライブラリの共有ポインタクラスを使って実装されています。

Using IDs to track entities across frames

もし、別フレームのエンティティIDがある場合、IDを指定して現在のフレームから該当するエンティティオブジェクトを取得できます。

VB.NET
        Dim _hand As Hand = _frame.Hand(handID)
        Dim _pointable As Pointable = _frame.Pointable(pointableID)
        Dim _finger As Finger = _frame.Finger (fingerID)
        Dim _tool As Tool = _frame.Tool(toolID)
C#
        Hand hand = frame.Hand(handID);
        Pointable pointable = frame.Pointable(pointableID);
        Finger finger = frame.Finger (fingerID);
        Tool tool = frame.Tool(toolID);

同じIDを持つオブジェクトが見つからない(おそらく手や指がLeapの検出範囲から外れるなどした場合)ときは、特別な無効オブジェクトが返却されます。無効オブジェクトは、適切なクラスのインスタンスですが、メンバー値はゼロ、ベクトル値はゼロ、もしくはIsValidプロパティ値がTrueを返します。

無効オブジェクトが発生したときの対処ですが、例えば次のコードのように複数のフレームで指の先端の位置を平均を求めて補完する方法などがあります。

VB.NET
        'Average a finger position for the last 10 frames
        Dim count As Integer = 0
        Dim average As new Vector()
        Dim fingerToAverage As Finger = frame.Fingers(0)
        For i As Integer = 0 To i < 10 - 1
            Dim fingerFromFrame As Finger = _controller.Frame(i).Finger(fingerToAverage.Id)
            if (fingerFromFrame.IsValid) Then
                average += fingerFromFrame.TipPosition
                count++
            End If
        Next
        average /= count
C#
        //Average a finger position for the last 10 frames
        int count = 0;
        Vector average = new Vector();
        Finger fingerToAverage = frame.Fingers[0];
        for (int i = 0; i < 10; i++) {
            Finger fingerFromFrame = controller.Frame(i).Finger(fingerToAverage.Id);
            if (fingerFromFrame.IsValid) {
                average += fingerFromFrame.TipPosition;
                count++;
            }
        }
        average /= count;

無効オブジェクトを使用しない場合、Fingerオブジェクトをチェックする前に各Frameオブジェクトを確認する必要があります。

 

Leap Motion - Overview の日本語訳

Leap Motionの日本語情報は非常に限られているので公式ドキュメントをざっくり日本語訳にしました。誤訳などあったらお知らせください。

本エントリはhttps://developer.leapmotion.com/documentation/Languages/CSharpandUnity/Guides/Leap_Overview.htmlの日本語訳となります。

Leap Overview

Leapは装置近くの手、指、および指状のツールを高い精度で検出し追跡します。

Leapソフトウェアは検出した物体を分析し、手、指、ツールなどの位置関係、ジェスチャー、および動きを認識します。 Leapの検出範囲は、装置の中心を頂点とした逆ピラミッドです。 検出距離は約25~600ミリメートルなっています。

Topics:

座標系

モーショントラッキングデータ

  • フレーム
    • トラッキングデータのリスト
      • フレームモーション
    • 手のモデル
      • 手の属性
        • 手のモーション
          • 指とツール
        • 指とツールのモデル
        • ジェスチャー
          • まる
            • スワイプ
              • タップ
            • キータップ
            • 画面タップ

座標系

Leapは右利きのデカルト座標系を使います。 Leap Motionの中心を基点としてそこからの距離がmm(ミリメートル)単位で取得できます。

Leap Motionの右側がX座標のプラス値、情報がY座標のプラス値、手前側がZ座標のプラス値となり、その反対方向がそれぞれマイナス値となります。

モーショントラッキングデータ

Leapが手、指およびツールを追跡している間、更新セットまたはデータフレームとして追跡結果を取得できます。各フレームは、ジェスチャーやモーションなどの基本的なトラッキングデータのリストを含んでいます。

手、指、ツールまたはジェスチャーを検出するとき、LeapはユニークなID指示子をそれに割り当てます。 その実体がセンサー検出範囲に残っている限り同じIDが振り続けられます。ただし、センサー検出範囲から外れたりトラッキングが途切れた場合は、同じ実態に対して新しいIDを割り当てるかもしれません。これは、Leapソフトウェアが再検出したときに同じものであるかの判断ができないからです。

注意: 私たちは、Leapが消費者にリリースされる前に、あなたのアプリケーションに提供された動作追跡データを高めるのを計画しています。 Leap SDKの今後のリリースでは、私たちは、時代を通して、より詳細な追跡データと連続を提供するために骨格の手のモデルを紹介するのを計画しています。

フレーム

フレームオブジェクトは、センサーが検出した追跡データ、ジェスチャーおよび要素のリストを提供します。

トラッキングデータのリスト

  • Hands -- 検出されたすべての手
  • Pointables -- 検出されたすべての指と指状のツール
  • Fingers -- 検出されたすべての指
  • Tools -- 検出されたすべての指状のツール
  • Gestures -- 検出されたすべてジェスチャーの開始、終了、変化

検出されたPointableオブジェクトに対して3つのpointablesリスト(Pointables、指およびツール)が取得できます。例えば、検出した手はHandsオブジェクトを通して関連しているpointablesにアクセスできます。 Handsオブジェクトでは、Leap検出範囲内にある指やツールとは関連していない可能性があることに注意してください。

フレームからフレームの間の指のように個々のオブジェクトをトラッキングする場合、新しいフレームで関連付けられたIDを使用できます。

以下の機能を使用して、IDに対するオブジェクトを取得してください。

  • Frame.Hand()
  • Frame.Finger()
  • Frame.Tool()
  • Frame.Pointable()
  • Frame.Gesture()

現在のフレームに存在しているなら、これらの機能は対応するオブジェクトの参照を返します。 オブジェクトがもう存在していないならnullが返却されます。nullはトラッキングデータの記録に含まないようにすることで、Leapトラッキングの参照量を減らすことが出来ます。

フレームモーション

Leapは、移動、回転、拡大縮小などすべてのモーションを分析します。 例えば、両手をLeap検出範囲内で左に動かすと、フレームには「移動」の情報が含まれます。 手をひねればフレームには「回転」の情報が含まれます。手の距離を変えればフレームには「拡大縮小」の情報が含まれます。

動きを分析するとき、Leapは検索範囲内の物体すべてをチェックします。 片手を検出するだけなら片手の手の動きをフレームモーション要素のベースとし、2本の手を検出するならフレームモーション要素として両手の動きをベースにし、同時にHandオブジェクトから各手のモーションを要素を別々に得ることができます。

フレームモーションは、指定された以前のフレームと現在のフレームを比べることによって導出されます。

  • RotationAxis — 回転軸の指示ベクトル
  • RotationAngle — 回転軸の周りの傾き(右手の法則を使用して時計回りで)
  • RotationMatrix — 回転を表現する変換マトリクス
  • ScaleFactor — 拡大・縮小
  • Translation — 直線的な運動を表現するベクトル

アプリケーションで利用する場合、複数フレーム間で手と指を個々にトラッキングする必要はなく、モノを操作するためのモーション要素を適用できます。

手のモデル

手のモデルは手に関連している指と指状のツールのリストと同様に検出された手の位置、特性、および動きの情報を提供します。

Leap APIは手のできるだけ多くの情報を提供します。 しかしながら、Leapはあらゆるフレームのすべての手の属性を決定できるかもしれないというわけではありません。 例えば、手をグーにしたときなどは、指がLeapで検出できないので指のリストは空になります。よってこのようなケースを考慮してコードで指のリストが空のときでも正常に動作するように考慮した方がよいでしょう。

Leapは、手が左か右の手であるかどうか決定しません。 また、視点に1人以上の人の手か他の手のような物があるなら、2本以上の手がリストに現れます。このように2本以上の手が検出されたときも、最適なモーショントラッキングのクオリティを確保するためにLeap Motion Controllerの検出範囲にあるすべての手についてトラッキングすることをお勧めします。

手の属性

Handオブジェクトは検出された手の物理的な特性を報告するいくつかの属性を提供します。
  • PalmPosition -- 手のひらの真ん中の位置をミリメートルで測定
  • PalmVelocity -- 手のひらの速度をmm/secで測定
  • PalmNormal -- 手のひらの中心から垂直に下向きを指すベクトル(図参照)
  • Direction -- 手のひらの中心から指に向かって指すベクトル(図参照)
  • SphereCenter -- ボールを持ったときの中心位置
  • SphereRadius -- 手に持ったボールの半径をミリメートルで測定
「Direction」と「palmNormal」は、Leap座標系に関して手の向きを説明するベクトル情報です。

「SphereCenter」と「SphereRadius」は手のひらに収まっているボールについての情報です。

手のモーション

Handオブジェクトはフレームの間の検出された手の動きを報告するいくつかの属性を盛っています。 Leapはその手の動き、関連する指、および指状のツールを分析して、移動、回転および拡大縮小を報告します。 手をLeap検出範囲内で動かすと移動情報が生成され、手をターンしたりねじったり傾けると回転情報が生成され、向かい合ったまたは距離のはなれた指やツールを動かすと拡大縮小データが生成されます。

手のモーションは、指定された以前のフレームと現在のフレームの手の特性を比べることによって導出されます。

  • RotationAxis — 回転軸の指示ベクトル
  • RotationAngle — 回転軸の周りの傾き(右手の法則を使用して時計回りで)
  • RotationMatrix — 回転を表現する変換マトリクス
  • ScaleFactor — 拡大・縮小
  • Translation — 直線的な運動を表現するベクトル

指とツール

  • Pointables — Pointableオブジェクトとしての指とツール
  • Fingers — 指のみ
  • Tools — ツールのみ

このリストを使って前のフレームで得られたID値を使用することで個々の指かツールを見つけることができます。?

前のフレームで検出したものが現在のフレームでも存在しているならば対応するオブジェクトの参照を返します。 検出できなかったときはnullを返します。

指とツールのモデル

Leapは検出範囲の中で指とツールの両方を検出してトラッキングします。 形に従って、Leapは指のようなオブジェクトを分類します。 ツールは、より長く、より薄く、指よりまっすぐです。

Leapモデルでは、指とツールの物理的な特性はPointableオブジェクトに抜き取られています。 指とツールはPointableオブジェクトのタイプです。 Pointableオブジェクトの物理的な特性は以下のとおり。

  • Length -- 指の長さ
  • Width -- 指の平均した幅
  • Direction -- 指の向いている方向を指すユニット指示ベクトル
  • TipPosition -- Leapから指の先端までの距離(mm:ミリメートル単位)
  • TipVelocity -- 指先の移動速度(mm/sec)

Leapは指かツールのどちらかとして検出されたPointableオブジェクトを分類します。 Pointable.IsToolの特性を使用して、Pointableオブジェクトがどれを表すか決定してください。

ジェスチャー

Leapは、ある動作パターンがジェスチャーであるかどうかを認識します。 Leapレポートジェスチャーは、モーショントラッキングデータの同じようにフレームで検出されます。検出された各ジェスチャーのために、LeapはGestureオブジェクトをフレームに追加します。 よって、認識されたジェスチャーはFrameからGestureオブジェクトとして入手できます。

以下の動作パターンはLeapによって認識されます:

  • まる -- 円をたどる単一の指
  • スワイプ -- 手の直線的な運動
  • キータップ -- キーボードを叩くような指の動き
  • 画面タップ -- 正面の画面をタップするような指の動き

Leapが最初にジェスチャーとして動作パターンを分類したときに、フレームにGestureオブジェクトが追加されます。 ジェスチャーが続くなら、LeapはアップデートされたGestureオブジェクトをフレームに追加し続けます。 ジェスチャーの「まる」と「スワイプ」は連続して発生します。 Leapはそれぞれがまとめるこれらのジェスチャーの進み具合をアップデートします。

タップは単発的なジェスチャーです。 Leapは単一のGestureオブジェクトとしてフレームに追加します。

重要

あなたのアプリケーションで身振りを交える前に、あなたは、あなたが使用するつもりである各ジェスチャーのための認識を可能にしなければなりません。 Controllerのクラスには、あなたが使用するジェスチャーのタイプのために認識を可能にするのに使用できるEnableGesture()メソッドがあります。

まる

LeapはCircleジェスチャーとして、指先で「まる」を描く指の動きを認識します。

あなたはどんな指やツールでも「まる」を描くことができます。 「まる」のジェスチャーは連続しています。 ジェスチャーがいったん始まると、ジェスチャーが終わるまで、Leapは状況を更新し続けます。 「まる」の動きからはずれたり、動きがゆっくりになると「まる」のジェスチャー検出が終了します。

詳しい情報に関するAPIリファレンスでCircleGestureを見てください。

スワイプ

LeapはSwipeジェスチャーとして、指先直線的な動きを認識します。

「スワイプ」ジェスチャーはどの指をどの方向に動かしても検出できます。「スワイプ」ジェスチャーは連続して発生します。 ジェスチャーがいったん始まると、ジェスチャーが終わるまで、Leapは状況を更新します。 指の移動方向が変わったり、動きがゆっくりになると「スワイプ」のジェスチャー検出が終了します。

詳しい情報に関するAPIリファレンスでSwipeGestureを見てください。

タップ

Leapは、 下向きのキータップと前後の画面タップの合計2つのタイプのタップを認識します。

キータップ

Leapは「キータップ」ジェスチャーとして指かツールで迅速で、下向きのタップを認識します。

ピアノの鍵盤を押すかのように、下向きに叩くことによって、キータップジェスチャを認識させることができます。タップジェスチャーは単発的なので、単一のGestureオブジェクトだけがタップジェスチャー単位で加えられます。

詳しい情報に関するAPIリファレンスでKeyTapGestureを見てください。

画面タップ

Leapは「画面タップ」ジェスチャーとして指かツールで迅速で、前後のタップを認識します。

をまるで垂直なタッチスクリーンに触れているかのように、前向きに叩くことによって、画面タップジェスチャを認識させることができます。タップジェスチャーは単発的なので、単一のGestureオブジェクトだけがタップジェスチャー単位で加えられます。

詳しい情報に関するAPIリファレンスでScreenTapGestureを見てください。

------

Copyright © 2012-2013 Leap Motion, Inc. All rights reserved.

Leap Motion proprietary and confidential. Not for distribution. Use subject to the terms of the Leap Motion SDK Agreement available at https://developer.leapmotion.com/sdk_agreement, or another agreement between Leap Motion and you, your company or other organization.

LeapMotionがやってきた

PreOrderが失敗して早期出荷がだめだったLeap Motionですが、入手日こそ1週間遅れましたが無事Pre価格で入手できました。すばらしい!

2013/05/22 Leap Motion Pre-Order

2013/07/22 Leap Motion出荷開始

2013/07/25 Important: We're trying to ship your Leap Motion Controller but need your help

2013/07/25 Your Leap Motion Controller is on the way

2013/07/28 入手完了

Pre予約から製品出荷まではかなり日数がありましたが出荷開始からはちゃんと出荷在庫を確保し、評判を聞く限りでは初期機能不足的な事もなさそうで、まさにハードはこうでなくちゃとおもえる状態ですね。 先進的なハードでもそれが絵に描いた餅じゃ仕方がない訳ですし。