やってみよう!Kinectアプリ開発 - 第9回 ポーズ認識

2012-07-27 17:23

zigsow 長谷川 勇
 

はじめに

Kinect™ for Windows® SDKを使った実用的なアプリケーションを開発する場合、その入力としてポーズやジェスチャーを利用することが多いでしょう。そこで、今回から数回に分けてこれらのポーズやジェスチャーの認識を考えます。今回はポーズ認識の基本について説明します。

ポーズの認識

ポーズとジェスチャー

そもそも「ポーズ」と「ジェスチャー」の違いとは何でしょうか?ポーズはジェスチャーの一種とみなせるかも知れませんし、言葉本来の意味では明確な線引きは無いかも知れません。しかし、NUI(ナチュラルユーザーインタフェース)の文脈では、「ポーズ」とは特定の1つの姿勢のことを指し、「ジェスチャー」とはある姿勢に付随する動きを含めたものを指すようです。なおKinect for Windows SDKに付属のHuman Interface Guideでは、「ポーズ」のことを"static gesture", "pose", "posture"と呼び、「ジェスチャー」のことを"dynamic gesture"と呼んでいるようです。例えばポーズの例としてOpenNIが認識するガッツポーズを考えると「両腕を広げ、ひじを90度曲げ、手を上に向けた姿勢」を指します。またジェスチャーの例としては、例えばKinect for Windows SDKのサンプルアプリケーションでは「右手を左から右に動かす動作」を認識しています。

ポーズの認識ロジックの考え方

実際のアプリケーションの開発においてポーズの認識を行いたい場合は、ポーズやジェスチャー認識のライブラリを使うのが手軽でしょう。しかし、ポーズやジェスチャーの認識がどのように行われているのか全く分からないと、そのライブラリを使うことで、使いたいポーズやジェスチャーを実際に認識できるのか判断ができなかったり、ライブラリをうまく使えなかったりということになってしまいます。このため、ここではKinect for Windows SDKだけを使い、ポーズを認識する際の基本を学びましょう。

ポーズの認識の実装は、以下の手順で行います。

  1. ポーズの特徴を抜き出す。
  2. それらの特徴を認識するロジックを作る。

Kinect for Windows SDKではプレイヤーの骨格を認識し、関節の位置を取得できることを第2回で説明しました。これを使うことで2のポーズの特徴を認識するロジックを作ることができます。このため、1で抜き出すポーズの特徴も、骨格や関節の情報で判定できるような特徴を考えるのがよいでしょう。

例えば旗揚げゲームのようなものを考え、右手を上げて左手を下げたポーズを認識したいとします。この場合、まずポーズの特徴として以下を抜き出します。

  • 右手を上げている ⇒ 右手が頭より上にある
  • 左手を下げている ⇒ 左手が左肩より下にある

そして、これらの特徴を認識するロジックを実装し、それらを全て満たすならば対象のポーズをとっていると判断できます。

以下では、まずロジックを実装するための予備知識として、Kinect for Windows SDKの関節の向きから方向ベクトルを取得する方法と、関節の向き同士の角度を調べるために内積を計算する方法を説明します。それらを基にして、具体例を交えてポーズの認識方法と、実際のアプリケーションの作成を行います。

補足1: 関節の向きからの方向ベクトルの取得

関節の向きについては第3回で軽く説明しただけですので、実際に利用するにあたり必要な点を補足します。

ポーズの特徴の認識ロジックでは「関節の向き」を方向ベクトルで扱うのが簡単です。方向ベクトルとは、方向を「原点を始点とする長さ1のベクトル(矢印)」で表したものです。例えば、頭が真上を向いている場合は、(Y軸が真上方向なので)その方向ベクトルは(0,1,0)になります。同様に、右手がカメラから見て奥方向を向いているということは、その方向ベクトルは(0,0,1)になります。(図1)


図1: 方向ベクトル

ただし、Kinect for Windows SDKから取得する関節の向きは行列(またはクォータニオン)なのでそのままでは方向ベクトルとして使えません。以下の手順で方向ベクトルを取り出します。

  1. 行列形式の関節の向きを取得
    Matrix4 headDirMatrix = skeleton.BoneOrientations[JointType.Head].AbsoluteRotation.Matrix;
  2. 行列から方向ベクトルを取得
    (headDirMatrix.M12, headDirMatrix.M22, headDirMatrix.M32)

補足2: 内積による角度の判定

2つの「関節の向き」の間の角度を調べるには、それぞれの方向ベクトルの内積を計算するのが簡単です。2つのベクトル (x1,y1,z1) と (x2,y2,z2) の内積は、以下の式で計算できます。

内積 = (x1 × x2) + (y1 × y2) + (z1 × z2)

このとき、2つのベクトルの長さが1であれば(幸い上で説明した方向ベクトルは長さが1です!)、内積は以下の特徴を持ちます。

  • 内積の値が1 ⇒ 2つのベクトルの角度は0° (同じ方向)
  • 内積の値が0 ⇒ 2つのベクトルの角度は90° (直角)
  • 内積の値が-1 ⇒ 2つのベクトルの角度は180° (正反対の向き)

例えば、先ほど例として挙げた真上を向く方向ベクトル(0,1,0)と、奥を向く方向ベクトル(0,0,1)で考えると、その内積は

(0 × 1) + (1 × 0) + (0 × 1) = 0

と計算され、内積は0になり、2つのベクトルの角度は90°であることが分かります。(図1からも2つの方向ベクトルのなす角は90°であることが分かります)

※ただし実際にはポーズの特徴として「直角」だという場合も、完全に90°ではないので、「直角」という判定は「内積が-0.1~0.1の範囲」など余裕を持たせた方がよいでしょう。

ポーズ認識の具体例

それでは、具体例として、以下の図2のポーズの認識を考えてみましょう。

図2: ポーズの例

まず、このポーズの特徴を抜き出すと以下が考えられます。

  1. 右手は頭より上にある
  2. 右腕は真上ではなく斜め上に上げている
  3. 右ひじがまっすぐ伸びている
  4. 左手は下げている

※ どこまでを対象のポーズの範囲に含めるかによって、どれを判定するか変わってきます。例えば左手は上げていても下げていても構わないのであれば、4番目の判断は不要ですし、右腕がまっすぐ伸びてなくてもよいのであれば、3番目の判断は不要です。

次に、これらの特徴を判定するロジックを考えます。(以下で「右手」という表記は、Kinect for Windows SDKの認識する関節としての右手を指します)

  1. 右手は頭より上にある
    ⇒「右手首」のY座標が「頭」のY座標より大きい
  2. 右手は真上ではなく斜め上に上げている
    ⇒「右手首」の関節の向きのx成分またはz成分が0ではない
  3. 右ひじがまっすぐ伸びている
    ⇒「右ひじ」の関節の向きと「右手首」の関節の向きが同じ
  4. 左手は下げている
    ⇒「左ひじ」のY座標が「左肩」のY座標よりも小さい

※ ここで紹介したロジックは一例であり、他のロジックも考えられます。例えば4番目は『「左ひじ」の関節の方向のy成分がマイナス』というロジックでもよいでしょう。

※ お気づきの方もいるかもしれませんが、図2のポーズでは右手を握っていますが、この条件は入っていません。これはKinect for Windows SDKの機能だけでは簡単には判定できないので、今回はあきらめています。

この1~4の条件を全て満たす場合、図2のポーズをとっていると判断することができます。

アプリケーションの修正

それでは、上で説明した図2のポーズの認識ロジックを実際に使ってみましょう。ここでは、連載の第8回までで作成したアプリケーションに機能を追加し、図2のポーズをとったときに、マスク画像のサイズを2倍にしてみます。

今回もコードの修正だけですので、順に見ていきましょう。

  • ポーズの認識メソッド(checkPosture)の追加

    このメソッドは引数として骨格情報を受け取り、その骨格がとっているポーズの番号を返します(この実装では1種類だけなので、ポーズをとっていれば1、判定できなければ0を返します)。処理の内容は、関節情報の取得・チェックの後、以下を行います。
    1. 右手が頭より上にあることのチェック:
      右手首と頭の関節情報から、それらのY座標を比較
    2. 右手を斜めに上がっていることのチェック:
      右手首の関節の方向から方向ベクトルを取得し、そのX成分とZ成分が0でないことをチェック(実際は0と比較すると厳密な真上以外全てOKになるので、少し幅を持たせて絶対値の和が0.3より大きい場合を斜めと判定)
    3. 右ひじがまっすぐ伸びていることのチェック:
      右手首と右ひじの関節の方向が同じであることを調べる。これは前述の方向ベクトルの内積が1であることをチェック(これも完全に方向が一致しないと1にはならないので、0.9より大きければ同じ方向と判定)
    4. 左手を下げていることのチェック:
      左ひじと左肩の関節情報から、それらのY座標を比較
    1~4が全て満たされれば対象のポーズをとっていると判定します。
  • 骨格情報から頭部の位置を取得するメソッド(getHeadPoints)に、ポーズの認識メソッドの呼び出しを追加し、ポーズの情報を結果保持用の変数(results)に追加します。

  • 描画処理(fillBitmapメソッド)で、骨格のポーズによってサイズを変更します。

実行

修正は以上です。メニューから[デバッグ(D)]-[デバッグ実行(S)]を選択し、実行してみましょう。実行したら、以下を試してみてください。正しく動作していれば、1だけ対象のポーズとして認識され、他は認識されないはずです。

  1. 右腕を頭より上に斜め方向に伸ばす
  2. 1の状態から少し右ひじを曲げる
  3. 1の状態から右腕を真上に伸ばす
  4. 1の状態から左手も上げる


図2のポーズ(右腕をまっすぐ斜め上に上げる)をとると、マスク画像が2倍になり、ひじを少し曲げると(ポーズと認識されないため)マスク画像が元に戻りました。

まとめ

今回は応用編の初回として、ポーズ認識の基本について説明しました。次回以降はジェスチャーの認識などのテーマについて説明します。

商標について

Kinect、Visual Studio、Windowsは、米国Microsoft Corporationの米国およびその他の国における登録商標または商標です。

1件の質問スレッド
  • マスク画像ではなく、音を出すには? matayoshinao 2014-10-14 22:23 初めまして。
    プログラム初心者です。なんとか第9回目のポーズ認証まで進めることができました。

    ポーズ認証について質問です。

    ポーズをとるとマスク画像が2倍になるのではなく、サウンドを出るようにしたい場合どのようなソースコードになりますか?

    よろしくお願いします。

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

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