やってみよう!Kinectアプリ開発 - 第4回 骨格情報の利用

2012-06-22 17:18

zigsow 長谷川 勇
 

はじめに

前回はKinect™ for Windows® SDKを使い、Kinectのカメラから画像情報を取得し画面に表示するアプリケーションを作成しました。今回はこれを修正し、よりKinectらしいアプリケーションにしましょう。通常説明の順序として次は深度センサーなのですが、ここは敢えて飛ばして骨格情報の取得をしてみます。

Skeletal Trackingによる骨格情報の取得

Kinectは深度センサーの情報を元にプレイヤーおよびその関節の情報を検出することができます。Kinect for Windows SDKの最大の特長は、人体の検出にキャリブレーションが一切不要なことです。つまり調整や事前の登録などなしに、人がKinectの前に立つだけで人体・関節が検出できます。(例えばその他のSDKには、最初にガッツポーズをとると人体を検出する方式のものもあります。)

ここでは、KinectのSkeletal Trackingでできることを簡単に紹介します。

プレイヤー・骨格の認識

Kinectでは最大6人までのプレイヤーの位置と、そのうちの2人の関節の情報を検出することができます。(これはKinect for Windows SDK v1.5時点での上限です。SDKのバージョンが1.0から1.5に上がった際に後述のSeated Modeや、Near Modeでの関節検出が追加されたように、将来もしかすると認識できる人数も増えるのかも知れません)

Kinectで取得できる関節は、「頭」「肩の中心」「右肩」など20種類の人体上の点です。ここで、Kinectの扱う「関節」とは、「首」などの実際の関節ではなく、「頭」など人体上の特定の点であることに注意しましょう。

それらの関節に関して、以下の3つの情報を取得できます。

  • 種別: 「頭」などの関節の種別。詳しくはSDKのドキュメント( http://msdn.microsoft.com/en-us/library/microsoft.kinect.jointtype )参照。
  • 位置: Kinectから見た三次元空間上の座標(Kinectセンサーから見て左がX軸、上がY軸、正面がZ軸、単位はメートルです) 。http://msdn.microsoft.com/en-us/library/hh973078
  • 状態: Kinectが関節を認識している"Tracked"状態、認識できていないが位置を推測している"Inferred"状態、認識できていない"NotTracked"状態のいずれか。

関節の向き(Joint Orientation)

Kinect for Windows SDK v1.5から、「関節の向き」を取得できるようになりました。接続元の関節と接続先の関節を結ぶ線を「ボーン」と呼び、「関節の向き」とはボーンの指す方向を言います。たとえば「右ひじの向き」は、右肩と右ひじを結ぶ線を考え(これが「右ひじのボーン」です)、その向きを指します。(図1)

図1: 関節・ボーンと関節の向き

※ SDKのドキュメントでは、"Joint Orientation"や"Bone Orientation"という表記がありますが、あまり区別せず使っているようなので、本連載では「関節の向き」で統一します。

Kinect for Windows SDKでは、これらの関節の向きを、関節の位置と同様のカメラ視点の座標系で表したもの(AbsoluteRotation)と、接続元の関節の方向から相対的に表したもの(HierarchicalRotation)の2種類、さらにそれぞれの「向き」をクォータニオンまたは行列の形式で取得できます。(相対的な向きや、クォータニオンは、3DCGになじみのない人には分かりにくいかもしれません。関節の向きを使う場合は、まずはAbsoluteRotationを行列で扱うのがよいかもしれません。)

Seated Mode

Kinect for Windows SDK v1.5からSeated Modeが追加されました。 Seated Modeでは認識できる関節が、頭、肩の中心、両腕の関節の計10個になる代わりに、座っているプレイヤーも認識できるモードです。

Near Mode

Kinect for Windows SDK v1.0では、Near Modeではプレイヤーの位置のみ検出でき、骨格情報は取得できませんでした。v1.5からは、Near Modeでも骨格情報を取得できるようになりました。機能的には全身の20点の関節を認識できますが、Near Modeではプレイヤーの全身が入らないことが多いので、前述のSeated Modeと組み合わせるのがよいでしょう。

作成するアプリケーションについて

前回はKinectのカメラを使い、取得した画像をウィンドウに表示するアプリケーションを作成しました。今回はこれを基にしてよりKinectらしいアプリケーションにしましょう。今回作成するのは、骨格情報を利用し、画面内のプレイヤーの顔の位置に別途用意した画像を上書きして表示するアプリケーションです。

上書きする画像は何でも構いませんので、みなさん好きな画像ファイルを用意してください。WPFの機能で拡大縮小しますので、解像度も自由です。ここで紹介する例では、みなさんご存じのタナカズ博士®の顔を上書きしてみます。

アプリケーションの概要

今回のアプリケーションの概要を図2に示します。

図2: 作成するアプリケーションの概要

基本的な処理の流れは前回のアプリケーションと同様ですが、イベントハンドラ(AllFramesReady)の処理が変わります。 AllFrameReadyメソッドでは以下の処理を行います。

  1. 骨格情報から、Kinectセンサーの範囲内の人の頭部を認識し、その位置を取得
  2. カメラから背景画像情報を取得し背景として描画
  3. 1で取得した頭部の位置情報から、背景上の人の顔の位置にマスク画像を上書き

※ 以降紛らわしいので、Kinectのカメラから取得する背景となる画像を「背景画像」、背景画像上の人物の顔の位置に上書きする画像を「マスク画像」と呼びます。

 

4件の質問スレッド
  • バージョンアップで? kazgb 2012-10-31 22:05 SDKがバージョンアップされて、
    警告みたいなのがでていたので気になりました。

    >= kinect.MapSkeletonPointToColor(headPos, rgbFormat);

    大雑把でアレですが上の部分。。。
    一応動くみたい?ですが、[使用しないでください]と記述されていました。

    調べて、以下に書き換えました。

    = kinect.CoordinateMapper.MapSkeletonPointToColorPoint(headPos, rgbFormat);

    動いているので問題なさそうですが、、、大丈夫・・・ですよね? うん。
  • 解説のDrawingVisualについて claw 2012-08-09 17:03 はじめまして。
    現在、大学でプログラミングを学びはじめた者です。

    非常に細かいことで申し訳ないのですが、
    2ページ目、fillBitmapメソッドの解説の部分、

    > DrawingVisualのRenderOpenメソッドでDrawingContextオブジェクトを作成します。

    となっていますが、
    DrawingVisualのRenderOpenメソッドでDrawContextオブジェクトを作成します。
    の間違い(?)ではないでしょうか?

    読んでいて少し混乱してしまったので……
    1件のコメント
    • kinection.jp管理人 clawさん

      ご質問を理解できていないかも知れませんが、RenderOpenメソッドでDrawingContextクラスのオブジェクトを作成しているので、意図通りです(DrawContextというクラスはありません)。
      もし、用語として「<クラス名>オブジェクト」ではなく「<変数名>オブジェクト」とすべき、という意味であれば、例えばgoogleで"DrawingContext-object"で検索すれば普通に使われているのがわかると思います。
      2012-08-10 10:00
  • 保存について 柏木 2012-07-17 10:06 初めまして。
    初心者なのですが、取得した骨格情報というのはテキストなどに保存可能でしょうか?
    全20ポイントのスケルトンのx,y,z座標を30フレームで取得し続け、10分程度のデータを保存することは可能でしょうか?
    続いて、取得したRBG画像を動画として保存することは可能でしょうか?
    環境は
    VisualStudio2010でc#
    Kinect for Windows v.1.5
    Windows7
    です。

    よろしくお願いいたします。
    2件のコメント
    • 柏木 コメントありがとうございます!えと・・・そうなると、たとえば
      (1)
      void kinect_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
      {
      try
      {
      using (SkeletonFrame skeletonFrame = e.OpenSkeletonFrame())
      {
      if (skeletonFrame != null)
      {
      DrawSkeleton(skeletonFrame);
      }
      }
      }
      catch (Exception ex)
      {
      MessageBox.Show(ex.Message);
      }
      }
      private void DrawSkeleton(SkeletonFrame skeletonFrame)
      {
      // スケルトンのデータを取得する
      Skeleton[] skeletons = new Skeleton[skeletonFrame.SkeletonArrayLength];
      skeletonFrame.CopySkeletonDataTo(skeletons);

      SkeletonCanvas.Children.Clear();

      // スケルトンのジョイントを一つ一つ取得
      foreach (Skeleton skeleton in skeletons)
      {
      // スケルトンがトラッキング状態の場合は、ジョイントを取得
      if (skeleton.TrackingState == SkeletonTrackingState.Tracked)
      {
      // ジョイントを保存する
      foreach (Joint joint in skeleton.Joints)
      {
      // ジョイントがトラッキングされていなければ次へ
      if (joint.TrackingState == JointTrackingState.NotTracked)
      {
      continue;
      }

      // ジョイントの座標を保存する
      JointRecord(joint.Position);
      }
      }
      }
      }
      とでもして、JointRecord部分で・・・どう作ればいいのかわからなくなってしまっております。Console.WriteLineで保存可能なのでしょうか?

      (2)
      void kinect_ColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
      {
      try
      {
      // RGBカメラのフレームデータを取得する
      using (ColorImageFrame colorFrame = e.OpenColorImageFrame())
      {
      if (colorFrame != null)
      {
      // RGBカメラのピクセルデータを取得する
      byte[] colorPixel = new byte[colorFrame.PixelDataLength];
      colorFrame.CopyPixelDataTo(colorPixel);

      // ピクセルデータをビットマップに変換する
      RGBCameraImage.Source = BitmapSource.Create(colorFrame.Width, colorFrame.Height, 96, 96,
      PixelFormats.Bgr32, null, colorPixel, colorFrame.Width * colorFrame.BytesPerPixel);
      }
      }
      }
      catch (Exception ex)
      {
      MessageBox.Show(ex.Message);
      }
      }
      たとえばこの状態だとして、GridのImageを動画で保存するにはどのようにすればいいのでしょうか?

      色々検索はしてみたのですが、画像保存の場合くらいしか見つからず、よく分かりませんで。

      どうぞよろしくお願いいたします。
      2012-07-20 10:40
    • 初音玲 (1)
      保存先の書き込みスピードにもよりますがテキストデータとしての保存は可能です。

      (2)
      取得したRGB画像を動画として保存することは可能です。
      2012-07-19 02:19
  • バグ修正 kinection.jp管理人 2012-07-03 09:38 かおるんさんご指摘の画像が反転するバグの修正です。

    Matrix rot = new Matrix(-headMtrx.M11, headMtrx.M12,
    -headMtrx.M21, headMtrx.M22,
    headPt.X, headPt.Y);

    M11 と M12 のところの符号を反転させています。

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

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