Deep Insider の Tutor コーナー
>>  Deep Insider は本サイトからスピンオフした姉妹サイトです。よろしく! 
Leap Motionの座標系の変換とスクリーンを理解する[C#]

Leap Motionの座標系の変換とスクリーンを理解する[C#]

  • このエントリーをはてなブックマークに追加

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

【obsolete: 旧コンテンツ】 Leap Motionからのタッチ入力を実装するための前準備として、Leap Motionの座標系の変換やスクリーンについて解説する。※C++編の同名タイトルの記事と基本的な内容は同じです。

Natural Software 中村 薫
2013年10月21日
本稿はLeap Motion SDK バージョン1に基づいた記事です。すでにバージョン2の連載記事に改訂しており、その改訂の際に本稿の内容はカットされました。同じLeap Motionデバイスで、最新のSDKバージョン2を利用できますので、最新版SDKをご利用ください。

 前回は、Leap Motionを利用してタッチ入力を認識するアプリケーションを作成した。今回はタッチ入力の際にも利用した、Leap Motionの座標系の変換やスクリーンについて解説する。

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

Leap Motion上で認識されるスクリーン

 座標系を解説する前に、Leap Motionが認識しているスクリーン(=ディスプレイ)について知る必要がある。

 Leap.ControllerクラスにはLocatedScreensプロパティというスクリーン一覧(=Leap.ScreenListオブジェクト)を取得する機能がある。ちなみに、同様の機能を持つプロパティとしてCalibratedScreensプロパティがあるが、これは“Deprecated”(=推奨されない)とされ、「LocatedScreensプロパティを利用するように」と記載されている。

 次のようなコードで、スクリーン情報を表示できる。

C#
foreach ( var screen in leap.LocatedScreens ) {
  TextScreen.Text = string.Format(
    "Screen Pixels: {1},{2}{0}Horizontal axis: {3}{0}Vertical axis: {4}{0}Bottom left corner: {5}", Environment.NewLine,
    screen.WidthPixels, screen.HeightPixels,
    screen.HorizontalAxis,
    screen.VerticalAxis,
    screen.BottomLeftCorner );
スクリーン情報を表示する(MainWindow.xaml.cs)

このコードを実際に試したい場合は、前回のコードのコンストラクタ()MainWindow()の最後にこれを追記すればよい。また、XAMLにはTextScreenという名前でTextBlockを配置してください。

 Leap.ScreenListクラスは、個別のスクリーン情報を持つLeap.Screenクラスのリストを持ち、これをforまたはforeach文で回すことで、スクリーン情報を表示できる。上記のコードではLocatedScreensプロパティ以外に5つの座標に関するプロパティが使われている。それぞれの意味を次に示す。

  • WidthPixels : スクリーンの幅(ピクセル単位)
  • HeightPixels : スクリーンの高さ(ピクセル単位)
  • HorizontalAxis : スクリーンの物理的な幅(Leap Motionコントローラーを中心とした物理的な位置。ミリメートル単位)
  • VerticalAxis : スクリーンの物理的な高さ(Leap Motionコントローラーを中心とした物理的な位置。ミリメートル単位)
  • BottomLeftCorner : スクリーンの左下の位置座標(Leap Motionコントローラーを中心とした物理的な位置。ミリメートル単位)

 このようにLeap Motionで利用されている座標の単位は2つあり、ピクセル単位(以降、px)とミリメートル単位(以降、mm)となる。

 例えば筆者が利用している1920×1080のディスプレイを持つPCおよび、1600×900のディスプレイを持つPCで上記のコードを動作すると、次のような結果になる。

Screen Information
Screen Pixels: 1920, 1080
Horizontal axis: (400, 0, 0)
Vertical axis: (0, 250, 0)
Bottom left corner: (-200, 50, -200)
スクリーン情報を表示するコードを、1920×1080のディスプレイを持つPCで動作させた例
Screen Information
Screen Pixels: 1600, 900
Horizontal axis: (400, 0, 0)
Vertical axis: (0, 250, 0)
Bottom left corner: (-200, 50, -200)
スクリーン情報を表示するコードを、1600×900のディスプレイを持つPCで動作させた例

 例えば1920x1080のマシンの出力結果を見ると、WidthPixelsプロパティおよびHeightPixelsプロパティは、「幅: 1920」「高さ: 1080」(ピクセル単位)というスクリーン・サイズの値を返している。HorizontalAxisプロパティおよびVerticalAxisプロパティの出力を見ると、スクリーンの物理的な幅と高さは「幅: 400mm」「高さ: 250mm」となっている。

 BottomLeftCornerプロパティが返す値には特に注意が必要だ。繰り返しになるが、この値はLeap Motionを中心としてスクリーンの左下の位置を表す。Leap Motionの座標系は右手座標系であり、それぞれの方向は次のようになっている。上の2つの出力結果では「(-200, 50, -200)」となっているため、「左: 200mm(Leap Motion中心から右がプラス、左がマイナス)」「上: 50mm(上がプラス、下がマイナス)」「奥: 200mm(手前がプラス、奥がマイナス)」の位置ということになる。

Leap Motionの座標系(Leap Motion SDKのAPIドキュメントから引用)
Leap Motionの座標系(Leap Motion SDKのAPIドキュメントから引用)

さまざまな座標変換

 ここから本題に入る。Leap Motionから取得できる座標の変換方法は下記の3種類がある。

 (1)InteractionBoxオブジェクトを利用した座標変換
 (2)インターセクション・ポイント(Intersection Point)の座標変換
 (3)プロジェクション・ポイント(Projection Point)の座標変換

 座標の種類は、先に解説した「ピクセル単位」および「ミリメートル単位」のものになる。「ピクセル単位」の座標系に変換する場合は、指をどのように認識するかによって、「(1)(前回紹介した)インタラクション・ボックスを使用した座標変換」と「(2)インターセクション・ポイントの座標変換」のうち、適切な手段を選択する。「ミリメートル単位」の座標系に変換する場合は、「(3)プロジェクション・ポイント」だ。

 それぞれについて詳しく説明しよう。

(1)InteractionBoxオブジェクトを利用した座標変換

 InteractionBoxオブジェクトを使えば、指の位置をピクセル座標系に変換し、指がスクリーンのどの位置にあるかという値を取得できる。次のコードはその利用例である。

C#
Leap.Vector normalizedPosition = interactionBox.NormalizePoint( pointable.StabilizedTipPosition );
 
float tx = normalizedPosition.x * windowWidth;
float ty = windowHeight - normalizedPosition.y * windowHeight;
InteractionBoxオブジェクトを利用した座標変換

(2)インターセクション・ポイント(Intersection Point)の座標変換

 Leap.Screen.Intersectメソッドを使えば、指の位置に加えて指の方向を認識されて、指が向いている先のスクリーン・パネルと交差する点の座標(=インターセクション・ポイント)を取得できる。次の図の「A」と「B」はスクリーンと交差している緑色の○がインターセクション・ポイントになる。

インターセクション・ポイント(Leap Motion SDKのAPIドキュメントから引用)
インターセクション・ポイント(Leap Motion SDKのAPIドキュメントから引用)

ポインターAは、物理スクリーン領域外にあるスクリーン・パネルとのインターセクション。
ポインターBは、スクリーンとの直接的なインターセクション。
ポインターCは、スクリーン・パネルと交差しない。

 次のコードは、インターセクション・ポイントの座標変換を行う例だ。

C#
Leap.Vector normalizedPosition = locatedScreen.Intersect( pointable, true );
 
float tx = normalizedPosition.x * windowWidth;
float ty = windowHeight - normalizedPosition.y * windowHeight;
Leap.Screen.Intersectメソッドを利用したインターセクション・ポイントの座標変換

 「(1)(前回紹介した)インタラクション・ボックスを使用した座標変換」と「(2)インターセクション・ポイントの座標変換」のどちらも、次の図のように左下を原点とする座標となっている。実行して座標を確認してみると、指を正面に向けているときは両方の座標がほぼ同じ位置を指し、指を上下左右に動かしてみると、両方の座標は変化する。特にインターセクション・ポイントは指先方向を見るため、手の位置を固定しても指の動きだけで座標が大きく変化することが分かるだろう。

Leap Motionのスクリーン座標系(Leap Motion SDKのAPIドキュメントから引用)

(3)プロジェクション・ポイント(Projection Point)の座標変換

 プロジェクション・ポイントでは、次の図に示すように、スクリーン・パネル上に投影された指の点を「ミリメートル単位」の座標に変換する。

プロジェクション・ポイント(Leap Motion SDKのAPIドキュメントから引用)
プロジェクション・ポイント(Leap Motion SDKのAPIドキュメントから引用)

ポイントAは、スクリーンに直接、投影されている。
ポイントBは、物理スクリーン領域外にあるスクリーン・パネルに投影されている。

 プロジェクション・ポイントは次のコードのように利用する。

C#
Leap.Vector normalizedPosition = locatedScreen.Project( pointable.TipPosition, false );
 
float tx = normalizedPosition .x;
float ty = normalizedPosition .y;
Leap.Screen.Projectメソッドを利用したプロジェクション・ポイントの座標変換

 こちらは原点がLeap Motionの位置になるため、一般的なLeap Motionをスクリーンの中心に置いた場合、X座標の「0」がスクリーンの中心、Y座標の値がLeap Motionからの高さの位置になる。

まとめ

 このようにLeap Motionで変換できる座標系および手・指の扱いにはいくつかの種類がある。目的のアプリケーションに最適な動きを採用していただきたい。

サイトからのお知らせ

Twitterでつぶやこう!