Leap Motionの座標系の変換とスクリーンを理解する[C#]
連載:Leap Motion開発入門(C#編)
【obsolete: 旧コンテンツ】 Leap Motionからのタッチ入力を実装するための前準備として、Leap Motionの座標系の変換やスクリーンについて解説する。※C++編の同名タイトルの記事と基本的な内容は同じです。
前回は、Leap Motionを利用してタッチ入力を認識するアプリケーションを作成した。今回はタッチ入力の際にも利用した、Leap Motionの座標系の変換やスクリーンについて解説する。
今回のサンプル・コードは前回のコードに加える形になっている。サンプル・コードは次のリンク先で公開している。Visual Studio Express 2012 for Windows Desktopでの動作確認を行い、プロジェクト・ファイルを含めてすぐに利用できるようにしてある。
Leap Motion上で認識されるスクリーン
座標系を解説する前に、Leap Motionが認識しているスクリーン(=ディスプレイ)について知る必要がある。
Leap.ControllerクラスにはLocatedScreensプロパティというスクリーン一覧(=Leap.ScreenListオブジェクト)を取得する機能がある。ちなみに、同様の機能を持つプロパティとしてCalibratedScreensプロパティがあるが、これは“Deprecated”(=推奨されない)とされ、「LocatedScreensプロパティを利用するように」と記載されている。
次のようなコードで、スクリーン情報を表示できる。
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には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) |
Screen Information Screen Pixels: 1600, 900 Horizontal axis: (400, 0, 0) Vertical axis: (0, 250, 0) Bottom left corner: (-200, 50, -200) |
例えば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から取得できる座標の変換方法は下記の3種類がある。
(1)InteractionBoxオブジェクトを利用した座標変換
(2)インターセクション・ポイント(Intersection Point)の座標変換
(3)プロジェクション・ポイント(Projection Point)の座標変換
座標の種類は、先に解説した「ピクセル単位」および「ミリメートル単位」のものになる。「ピクセル単位」の座標系に変換する場合は、指をどのように認識するかによって、「(1)(前回紹介した)インタラクション・ボックスを使用した座標変換」と「(2)インターセクション・ポイントの座標変換」のうち、適切な手段を選択する。「ミリメートル単位」の座標系に変換する場合は、「(3)プロジェクション・ポイント」だ。
それぞれについて詳しく説明しよう。
(1)InteractionBoxオブジェクトを利用した座標変換
InteractionBoxオブジェクトを使えば、指の位置をピクセル座標系に変換し、指がスクリーンのどの位置にあるかという値を取得できる。次のコードはその利用例である。
Leap.Vector normalizedPosition = interactionBox.NormalizePoint( pointable.StabilizedTipPosition );
float tx = normalizedPosition.x * windowWidth;
float ty = windowHeight - normalizedPosition.y * windowHeight;
|
(2)インターセクション・ポイント(Intersection Point)の座標変換
Leap.Screen.Intersectメソッドを使えば、指の位置に加えて指の方向を認識されて、指が向いている先のスクリーン・パネルと交差する点の座標(=インターセクション・ポイント)を取得できる。次の図の「A」と「B」はスクリーンと交差している緑色の○がインターセクション・ポイントになる。
ポインターAは、物理スクリーン領域外にあるスクリーン・パネルとのインターセクション。
ポインターBは、スクリーンとの直接的なインターセクション。
ポインターCは、スクリーン・パネルと交差しない。
次のコードは、インターセクション・ポイントの座標変換を行う例だ。
Leap.Vector normalizedPosition = locatedScreen.Intersect( pointable, true );
float tx = normalizedPosition.x * windowWidth;
float ty = windowHeight - normalizedPosition.y * windowHeight;
|
「(1)(前回紹介した)インタラクション・ボックスを使用した座標変換」と「(2)インターセクション・ポイントの座標変換」のどちらも、次の図のように左下を原点とする座標となっている。実行して座標を確認してみると、指を正面に向けているときは両方の座標がほぼ同じ位置を指し、指を上下左右に動かしてみると、両方の座標は変化する。特にインターセクション・ポイントは指先方向を見るため、手の位置を固定しても指の動きだけで座標が大きく変化することが分かるだろう。
(3)プロジェクション・ポイント(Projection Point)の座標変換
プロジェクション・ポイントでは、次の図に示すように、スクリーン・パネル上に投影された指の点を「ミリメートル単位」の座標に変換する。
ポイントAは、スクリーンに直接、投影されている。
ポイントBは、物理スクリーン領域外にあるスクリーン・パネルに投影されている。
プロジェクション・ポイントは次のコードのように利用する。
Leap.Vector normalizedPosition = locatedScreen.Project( pointable.TipPosition, false );
float tx = normalizedPosition .x;
float ty = normalizedPosition .y;
|
こちらは原点がLeap Motionの位置になるため、一般的なLeap Motionをスクリーンの中心に置いた場合、X座標の「0」がスクリーンの中心、Y座標の値がLeap Motionからの高さの位置になる。
まとめ
このようにLeap Motionで変換できる座標系および手・指の扱いにはいくつかの種類がある。目的のアプリケーションに最適な動きを採用していただきたい。