Deep Insider の Tutor コーナー
>>  Deep Insider は本サイトからスピンオフした姉妹サイトです。よろしく! 
連載:Leap Motion開発入門(C#編)

連載:Leap Motion開発入門(C#編)

Leap Motionでのタッチ操作はどう開発するのか?

2015年8月28日 改訂 (初版:2013/10/7)

Leapアプリのタッチ操作の認識方法と開発方法を説明。※C++編の同名タイトルの記事と基本的な内容は同じです。

Natural Software 中村 薫
  • このエントリーをはてなブックマークに追加

 第1回および第2回Leap Motionを利用したアプリケーションの基本的な開発方法および手指の検出について解説した。今回は、よりLeap Motionらしいアプリケーションとして、タッチ操作を扱う。

 サンプルコードは、Leap Motionの開発者サイトにある「Touch Emulation」を利用する。

 今回のサンプルコードも次のリンク先で公開している。Visual Studio Express 2013 for Windows Desktopでの動作確認を行い、プロジェクトファイルを含めてすぐに利用できるようにしてある。

Touch Emulation

 このプログラムを実行し、Leap Motion上に手をかざして指を後ろから前に動かすと緑の丸が現れ、さらに前に動かすと赤に変わる。丸の色は緑●がホバー状態を表し、赤●に変わった位置がタッチを表す。Leap Motionのような非接触のユーザーインターフェースをタッチに利用する場合には、タッチしようとする位置が分かりづらいため、ホバーを利用してタッチしようとしている位置をユーザーに教えて上げることで操作に対するストレスが軽減される。図1は、サンプルの実行例である。

図1 Touch Emulationの実行画面
図1 Touch Emulationの実行画面

 タッチの検出は、C++編第1回で紹介したジェスチャーのScreenTapGestureクラスでも可能だが、ScreenTapGestureは(筆者の感覚で)認識が難しいこと、瞬間的なイベントであるため、事前のホバー状態、事後のタッチし続けている状態といった一連のタッチ操作を検出できないことから、Leap Motionを利用したタッチ操作には、今回紹介するLeap.Pointableクラスを使うことが望ましいと考えている。

Leap Motionを利用したタッチの認識

 まずはLeap Motionがどのようにタッチを認識しているかを解説する。

 次の図が動作イメージであり、「仮想的なタッチパネル(The virtual touch surface)を持っている」と考えると分かりやすいだろう。仮想的なタッチパネルの前後に状態を判定する空間があり、手前側が「ホバー状態(hovering)」、奥側が「タッチ状態(touching)」を表す。空間の範囲は前後1-1となっているが正確な単位については確認できていない。

図2 Leap Motionのタッチ検出イメージ(Leap Motion SDKのAPIドキュメントから引用)
図2 Leap Motionのタッチ検出イメージ(Leap Motion SDKのAPIドキュメントから引用)

コードの解説

 本サンプルコードでの注目点はprotected void Update(object sender, EventArgs e)というメソッドの中身のみとなり、ここに全てが実装されている。

フレームデータを取得する

 このフレームでのLeap.FrameオブジェクトおよびLeap.InteractionBoxオブジェクトを取得する。

C#
Frame frame = leap.Frame();
InteractionBox interactionBox = leap.Frame().InteractionBox;
リスト1 フレームデータを取得する

 Leap.PointableListオブジェクトは、名前の通りLeap.Pointableオブジェクトのリストであり、指やツール(=棒状のもの)をひとまとめにして表している。Leap.FingerクラスおよびLeap.Toolクラスは、Leap.Pointableクラスを継承している。ここではタッチの位置を取得するためにPointableListオブジェクトを利用する。

 Leap.InteractionBoxオブジェクト(=インタラクションボックス)は、Leap Motionで認識できる可動範囲となり、下記のイメージだ。InteractionBoxオブジェクトを利用することで指やツールの位置を実際のディスプレイの座標系に変換できる。

図3 インタラクションボックス(Leap Motion SDKのAPIドキュメントから引用)
ポイントされた座標を取得する

 PointableListオブジェクトをループで順に処理する。その処理コードは、次のようになっている。

C#
foreach ( Pointable pointable in leap.Frame().Pointables )
{
  ……省略……
  Leap.Vector normalizedPosition = interactionBox.NormalizePoint( pointable.StabilizedTipPosition );
  float tx = normalizedPosition.x * windowWidth;
  float ty = windowHeight - normalizedPosition.y * windowHeight;
  ……省略……
}
リスト2 ポインターの位置をスクリーン座標に変換する

 Leap.InteractionBox.NormalizePoint()メソッドにLeap.Pointable.StabilizedTipPositionプロパティを渡してポインターの画面上の位置を取得する。取得した画面上の位置はLeap.Vector型となり、画面サイズ上でのポインターの位置が0.01.0の間で表される。これを実際の画面サイズで割り出すが、Leap Motionのスクリーン座標系の原点が左下であるため(図4を参照)、Y座標は高さの値から算出した値を引くことで、左上を原点とする座標にしている。

図4 Leap Motionのスクリーン座標系(Leap Motion SDKの過去のAPIドキュメントから引用。現在この画像はAPIドキュメントからは削除されている)
タッチの状態に合わせた処理を行う

 続けてループ内で、タッチの状態に合わせた処理を実施する。この際、タッチの状態はLeap.Pointable.TouchDistanceプロパティおよびLeap.Pointable.TouchZoneプロパティで取得する。

 今回のサンプルコードでは、(リスト3のように)取得された状態と座標によって表示される丸の色および位置を変えている。ホバーはタッチされていないが指を認識している状態で、「指を押し込むと、ここがタッチされる」という場所を示すものだ。

C#
// ホバー状態
if ( pointable.TouchDistance > 0 && pointable.TouchZone != Pointable.Zone.ZONENONE ) {
  alpha = 255 - (int)(255 * pointable.TouchDistance);
  touchIndicator.Color = Color.FromArgb( (byte)alpha, 0x0, 0xff, 0x0 );
}
// タッチ状態
else if ( pointable.TouchDistance <= 0 ) {
  alpha = -(int)(255 * pointable.TouchDistance);
  touchIndicator.Color = Color.FromArgb( (byte)alpha, 0xff, 0x0, 0x0 );
}
// タッチ対象外
else {
  alpha = 50;
  touchIndicator.Color = Color.FromArgb( (byte)alpha, 0x0, 0x0, 0xff );
}
リスト3 ポインターの位置をスクリーン座標に変換する

 TouchDistanceプロパティは下の図の+10-1の値を返す。また、TouchZoneプロパティはLeap.Pointable.Zone列挙体の値を返し、この値は「ホバー中(ZONEHOVERING)」「タッチ中(ZONETOUCHING)」「なし(ZONENONE)」、いずれかの状態を指す。

図5 Leap Motionのタッチ検出(Leap Motion SDKのAPIドキュメントから引用)
図5 Leap Motionのタッチ検出(Leap Motion SDKのAPIドキュメントから引用)

 TouchDistanceプロパティおよびTouchZoneプロパティの状態は同期しており、次のようになっている(以下の説明を読んで、上の図を見直すとよりよく理解できるだろう)。

  • TouchDistanceプロパティが「1」であれば、TouchZoneプロパティは「ZONENONE」
  • TouchDistanceプロパティが「1」以下、かつ「0」より大きい値であれば、TouchZoneプロパティは「ZONEHOVERING」
  • TouchDistanceプロパティが「0」以下、かつ「-1」より大きい値であれば、TouchZoneプロパティは「ZONETOUCHING」
伸びている指やツールのみを検出させる

 「これは快適! Leap Motion v2で格段に良くなったSkeletal Tracking機能」でLeap Motion SDK v2について解説した。

 V2では指を開いても閉じても5本の指を検出し続けるようになった。これによって指を閉じた(グーの状態)で手を突き出してもタッチとして認識してしまう。これは多くの場合、意図しない動作だろう。そこで伸びている指およびツールのみタッチとして認識するようにさせる。

 先の解説にも書いたが、Leap.Pointable.IsExtendedプロパティで伸びているかどうかの状態を取得できる。これを使って次のようにコードを追加することで、意図する動作に変更できる。なお、Leap.Fingerクラス(指)およびLeap.Toolクラス(ツール)はLeap.Pointableクラスを継承しているため、同様にIsExtendedプロパティを使うことができる。

C#
foreach ( Pointable pointable in leap.Frame().Pointables ) {
  // ここから追加
  // 伸びている指、ツール以外は無視する
  if ( !pointable.IsExtended ) {
    continue;
  }
  // ここまで追加
  ……省略……
}
リスト4 伸びている指およびツールのみタッチとして認識させる

 これによって、人差し指1本だけを伸ばしたタッチでは1つの円のみが表示される(図6)。先ほど追加したコードを入れたり外したりして、動作の違いを確認するとよい。

図6 伸びている指およびツールのみタッチとして認識させた実行画面
図6 伸びている指およびツールのみタッチとして認識させた実行画面

まとめ

 以上で今回の解説は終了だ。モーション・センサー・デバイスのSDKでは、ユーザーインターフェースへの活用が多いため、デバイスの座標系からスクリーンの座標系に変換する実装を多く行う。Leap Motion Developer SDKではこの座標系変換についても、より簡単に行える。実装者はLeap Motionの視野角を含めた見え方と、それをスクリーンに投影する際の変換イメージを持つことで、より簡単に扱えるだろう。

連載:Leap Motion開発入門(C#編)
1. C#によるLeap Motion v2開発の全体像

Leap Motion Developer SDKを利用してC#でLeap Motionのアプリケーションを開発する方法を解説する連載(2015年改訂版)。今回はC#の開発環境など、開発の基礎を紹介。

連載:Leap Motion開発入門(C#編)
2. Leap SDKで指を検出してみよう(Tracking Hands, Fingers, and Tools)

Leap Motionの最大の特長である手・指を検出するには? Leap Motion Developer SDKを活用した開発方法を詳しく解説。※C++編の同名タイトルと基本的な内容は同じです。

連載:Leap Motion開発入門(C#編)
3. 【現在、表示中】≫ Leap Motionでのタッチ操作はどう開発するのか?

Leapアプリのタッチ操作の認識方法と開発方法を説明。※C++編の同名タイトルの記事と基本的な内容は同じです。

連載:Leap Motion開発入門(C#編)
4. Leap Motionのカメラ画像を取得する

Leapアプリのタッチ操作の認識方法と開発方法を説明。新規書き下ろし。※C++編の同名タイトルの記事と基本的な内容は同じです。

連載:Leap Motion開発入門(C#編)
5. フレームのいろいろな使い方

Leap Motion公式のサンプルを題材に、さまざまなフレームの扱い方についてコードを交えて解説。※C++編の「Leap SDKのいろいろな使い方」と基本的な内容は同じです。

サイトからのお知らせ

Twitterでつぶやこう!