やってみよう!Kinectアプリ開発 - 第11回 継続的なジェスチャー認識

2012-08-10 18:15

zigsow 長谷川 勇
 

はじめに

前々回、前回とKinect™ for Windows® SDKを使った実用的なアプリケーションのために、ポーズとジェスチャー認識の基本について説明しました。今回はポーズとジェスチャー関連の最後のトピックである、継続的なジェスチャー認識について説明します。

継続的なジェスチャー認識とは

Kinectを使った入力方法には、これまで説明したポーズやジェスチャーが考えられますが、他にはどのような入力方法があるでしょうか? 例えば、Kinectを使ったアプリケーションとして、プレイヤーの前面の空間を仮想的なキャンバスと見立て、手を動かすことで絵を描くアプリケーションがありますが、これはポーズやジェスチャーとはまた別の入力方法と言えそうです。他にも、手を動かすとその方向にマウスカーソルが動くなど、いろいろと応用例が考えられます。

これらの入力方法には以下の共通点があります。

  • 身振り手振りではあるが、何か特定のジェスチャー(右手を上げるなど)ではない
  • 連続的な動作の量を認識する。つまり「右手を上げた」「右手を下げた」といった1か0かの認識ではなく、「右手を10cm上げた」「右手を45°の方向に向けた」など、「10cm」「45°」といった連続的な値を入力として扱う。

このような入力方法について、本連載では「継続的なジェスチャー認識」と呼ぶことにします。

継続的なジェスチャー認識ロジックの考え方

継続的なジェスチャー認識では、連続的な動作の量を入力として扱うと説明しました。Kinectで認識するのですから、プレイヤーの関節を動作の対象として、その位置や角度、あるいはそれらの変化量(速度など)を検出することが、継続的なジェスチャー認識の主要なロジックとなります。このとき考えなければいけないのは、以下の2点です。

  • 対象となる関節
  • 対象となる動作の種別

対象となる関節とは、これまでも説明してきた、Kinectによる骨格情報の関節、「頭」「肩の中心」など20種類の人体上の点(http://msdn.microsoft.com/en-us/library/microsoft.kinect.jointtype)のうち、どの点を対象とするかです。また、対象となる動作の種別は大きく「位置」と「角度」に分かれますが、これらの位置や角度は相対的な値ですので、何を基準とするかを考える必要があります。例えば、以下が考えられるでしょう。

  • 体の中心を基準とした相対的な位置
  • 背景の一点を基準とした相対的な位置
  • 直前のフレームからの相対的な位置(これは単位時間あたりの移動量であり、速度と考えることもできます)

具体例で考えてみましょう。例えば、先ほど例で挙げた仮想的なキャンバス上で絵を描くアプリケーションを考えましょう。プレイヤーの前面30cmの距離に仮想的なキャンバスがあり、その仮想的なキャンバス上で右手を動かした軌跡が、アプリケーション上のキャンバスに描かれるとします。(図1)

図1: 仮想的なキャンバス上で絵を描くアプリケーション

ここで必要な入力は、プレイヤーの右手が仮想的なキャンバス上でどこにあるかを示す位置の情報です。位置の情報は、仮想的なキャンバスの中心(つまりプレイヤーの体の中心から前面へ30cmの位置)からの相対的な位置として取得できます。これをアプリケーション上のキャンバスでの中心からの位置とすることで、軌跡を描くことができます。(図2)

図2: 仮想的なキャンバス上の相対位置

つまり、このアプリケーションでは、以下の情報から継続的なジェスチャー認識を行います。

  • 対象となる関節
    • 右手
  • 対象となる動作の種別
    • キャンバスの中心(※)からの相対的な位置
      ※ただし、キャンバスの中心とは、プレイヤーの体の中心から全面に30cmの点

継続的なジェスチャー認識を利用したアプリケーション例

上記の考え方を基にして、継続的なジェスチャー認識を利用したアプリケーションを作成してみましょう。

ここでは例として、手を使わずに首を傾けることでマウスを動かすアプリケーションを作ってみます。利用イメージは、お菓子を食べたり、他のことをしながらウェブブラウジングできるようにということで、名前は「ものぐさマウス」にしましょう。もしかしたら、その他にもキーボードを打ちながらマウスカーソルを動かすといった応用方法も考えられるかも知れませんね。

上記と同様にして、認識ロジックを考えてみましょう。

  • 対象となる関節:
    • 「首を傾けること」が入力なので、対象の関節は「首」になります。
  • 対象となる動作の種別:
    • 同様に「首を傾けること」が入力なので、首の角度が対象です。
    • 首を真上に向けているのを基準として、どちらの方向に何度傾けているかを測定します。

なお、こうして取得した首の角度をマウスの動きにする場合、2通りの考え方がありますが、今回は1の速度に対応させます。

  1. 首の角度をマウスの速度に対応させる
    首を傾けた方向にマウスを動かしますが、首をわずかに傾けた場合はゆっくり動かし、大きく傾けた場合は速く動かします。
  2. 首の角度をマウスの位置に対応させる
    首をまっすぐ上に向けた場合はマウスポインタを画面中央に置き、首を右にもっとも傾けると、画面右端にマウスポインタを置きます。

次に、このアプリケーションのユーザインタフェースを考えます。ここでは、図3のようなインタフェースを考えます。

図3: 手を使わず首を傾けることでマウスを動かすアプリケーション

  • 画像
    首の角度を緑の点で表します。
  • 上下反転
    首を下に向けたときにマウスを上に動かすか、下に動かすかを切り替えます。
  • 遊び
    首を少し動かしただけでマウスが動かないよう、ここで指定した数値以下の傾きは無視します(自動車のハンドルなどの「遊び」のイメージです)。
  • 倍率
    首の角度とマウスの移動速度の比率です。この値を大きくするとマウスが速く動きます。

アプリケーションの作成

アプリケーションの作成方法の復習も兼ね、0から作ってみましょう。(復習となる部分が多いので、今回は後で直接ソースを掲載します。すでに理解されている方はこれらを直接コピペしてご利用ください。また、手順に沿っても同じものが作れますので、興味のある方は手順を試してみてください)

プロジェクトの作成

まずはアプリケーションのソースファイルなどの入れ物になるプロジェクトを作成します。手順は連載第3回の[プロジェクトの作成]を行ってください。ただし、プロジェクト名は”MonogusaMouse”にしましょう。

また、Kinectセンサーの初期化・終了処理のために、Kinect Toolkitを使えるようにしましょう。こちらの手順は連載第8回の[Kinect Toolkitの追加]を行ってください。

図のようになっていれば、プロジェクトの作成は完了です。”Microsoft.Kinect.Toolkit”プロジェクトが追加され、”MonogusaMouse”プロジェクトの参照設定に”Microsoft.Kinect.Toolkit”が追加されていることを確認してください。

メインウィンドウのレイアウト設定

今回のアプリケーションではGridを使ったレイアウトを紹介します。Gridはウィンドウを表の様に行と列で区切り、表のセル上にコントロールを配置するようなイメージでレイアウトができます。

まず、必要となる、行・列数を考えましょう。図3の画面を見ると、上から順に「画像」「上下反転」「遊び」「倍率」の4つに分割されていますので、4行必要です。また、列に関しては「画像」のように1行に1つだけコントロールがある行や、「遊び」「倍率」のように3つのコントロールが1行に配置されている行があります。ここでは、一番多い3列用意します。

4行3列のGridを作成する手順は以下の通りです。

  1. MainWindow.xaml上で画面上のGridを右クリックして[プロパティ(E)]を選択
  2. [プロパティ]から、"RowDefinisions"の右にある[...]と書かれたボタンをクリック
  3. [コレクション エディター: RowDefinisions]が開くので、[追加(A)]ボタンをクリックし、"RowDefinision"を4つ作成
    ※ このとき、2, 3, 4つ目のRowDefinitionの"Height"プロパティは"Auto"にします
  4. 同様に、"ColumnDefinision"も3つ作成します。ただし、こちらはColumnDefinitionの"Width"プロパティを上から順に、"Auto", "*"(デフォルトのまま), "64"にします。

メインウィンドウへのコントロールの追加

Gridの設定ができましたので、図3上の各コントロールを配置しましょう。コントロールの追加は連載第3回[コントロールの追加]を行ってください。以下に追加するコントロールとそのプロパティを示します。なお、レイアウトの指定は、コントロールの”Grid.Column”、”Grid.Row”プロパティに値を設定することで指定できます。

  • Image
    1. Grid.Column="0" Grid.ColumnSpan="3" Grid.Row="0" Name="rgbImage"
      ※ 0行・0列目に、3列にまたがって配置することを意味します。
    2. 次のプロパティの値はリセットする
      Height, HorizontalAlignment, Margin, Stretch, VerticalAlignment, Width
  • CheckBox
    1. Grid.Column="0" Grid.ColumnSpan="3" Grid.Row="1" Content="上下反転" Margin="8,4,0,4" Name="checkBoxInvY"
    2. 次のプロパティの値はリセットする
      Height, HorizontalAlignment, VerticalAlignment
  • Label
    1. Content="遊び:" Grid.Column="0" Grid.Row="2" Margin="4,4,0,8"
    2. 次のプロパティの値はリセットする
      Height, HorizontalAlignment, VerticalAlignment
  • Slider (見当たらない場合、すべてのWPFコントロールを見てみてください)
    1. Grid.Column="1" Grid.Row="2" Margin="0,4,0,8" Name="sliderPlay" Maximum="0.5" Value="0.075" TickFrequency="0.005" IsSnapToTickEnabled="True" LargeChange="0.1" SmallChange="0.005"
    2. 次のプロパティの値はリセットする
      Height, HorizontalAlignment, VerticalAlignment, Width
  • TextBox
    1. Grid.Column="2" Grid.Row="2" Margin="0,4,8,8" Name="textBoxPlay"
    2. 次のプロパティの値はリセットする
      Height, HorizontalAlignment, VerticalAlignment, Width
  • Label
    1. Content="倍率:" Grid.Column="0" Grid.Row="3" Margin="4,4,0,8"
    2. 次のプロパティの値はリセットする
      Height, HorizontalAlignment, VerticalAlignment
  • Slider
    1. Grid.Column="1" Grid.Row="3" Margin="0,4,0,8" Name="sliderAmp" Maximum="100" Value="30" TickFrequency="1" IsSnapToTickEnabled="True"
    2. 次のプロパティの値はリセットする
      Height, HorizontalAlignment, VerticalAlignment, Width
  • TextBox
    1. Grid.Column="2" Grid.Row="3" Margin="0,4,8,8" Name="textBoxAmp"
    2. 次のプロパティの値はリセットする
      Height, HorizontalAlignment, VerticalAlignment, Width

データバインディングの設定

データバインディングとは、コントロールのもつ値やコード中の値などのデータ同士を結び付けることを言います。今回のアプリケーションでは、「倍率」などスライダーとテキストボックスの2つのコントロールを持っていますが、これらのコントロールで設定したいのは単一の「倍率」の値です。そこでデータバインディングを使い、これらのコントロール間でデータを結びつけると、スライダーの動きに応じてテキストボックスの数値を変化させたり、逆にテキストボックスに数値を入力するとスライダーを該当箇所に移動させたりすることができます。

ここでは、コントロールのデータ同士をデータバインディングで結び付ける手順を紹介します。

  • sliderPlayとtextBoxPlay のデータバインディング
    1. textBoxPlayと名前を設定したTextBoxのTextプロパティを右クリックして[データバインドの適用...]を選択し、"ElementName", "sliderPlay"を順に選択する
    2. [パス:]をクリックし、"Value"を選択する
  • sliderAmpとtextBoxAmpのデータバインディング
    1. textBoxAmpと名前を設定したTextBoxのTextプロパティを右クリックして[データバインドの適用...]を選択し、"ElementName", "sliderAmp"を順に選択する
    2. [パス:]をクリック"Value"を選択する

メインウィンドウのプロパティ設定

メインウィンドウのプロパティ・イベントハンドラを設定します。手順は連載第3回[イベントハンドラの設定]を参照してください。設定する内容は以下の通りです。

  • プロパティ
    • Title="ものぐさマウス" SizeToContent="WidthAndHeight"
    • 次のプロパティの値はリセットする
      Height, Width
  • イベント
    • Loaded="WindowLoaded" Closed="WindowClosed"

ソースの追加

今回のアプリケーションでは、マウスカーソルを動かしますが、これは.NET Frameworkだけでは実現できません。C#からWin32 APIを呼び出し、マウスカーソルを動かします。手順は以下の通りです。

  1. [ソリューション エクスプローラー]からプロジェクト(ここでは"MonogusaMouse")を右クリックし、[追加(D)]-[新しい項目(W)...]を選択します。
  2. [新しい項目の追加]で[クラス]を選択し、[名前(N):]に"NativeWrapper.cs"、[追加(A)]をクリックします
  3. NativeWrapper.csのソースコードは後程示しますので、それをコピペしてください。

このC#からのWin32 API呼び出しは今回の連載の範囲を超えてしまうので、詳細は割愛します。外部のライブラリを使うと思って中身は気にせず使ってみてください。ポイントは、sendMouseMoveというメソッドに、マウスの移動量を与えて呼び出すとマウスが動くことです。

ソースの修正

MainWindows.csの修正は以下の通りです。これまでの連載と同じような部分が多いので、ポイントだけ説明します。

  • 初期化、終了処理
    WindowLoaded, WindowClosed, KinectChanged, InitKinectSensor メソッドです。内容は、第8回で説明した初期化・終了処理とおおむね同様の処理ですが、Near Mode, Seated Mode に設定しています。
  • イベントハンドラ
    AllFramesReadyメソッドです。このメソッドから、背景の描画と、骨格情報の処理を呼び出します。
  • 背景の描画
    drawBaseメソッドです。図3の画像部分を描画します。
  • 骨格情報の処理
    processSkeleton, processHeadDirメソッドです。processSkeletonメソッドで、骨格情報から首の向きを取得し、processHeadDirメソッドで角度を元にマウスカーソルを動かします。

 

ジグソープレミアムレビュー

  • やってみようKinect(キネクト)アプリ開発 - ラボクルー集まれ!