Members‎ > ‎Wataru-Fujimura‎ > ‎

XNAでKINECT!

  KIENCTを解析していて、XNAでも動いたらいいなとおもってやってみた。
  今回はボーンの位置情報のみを取得するプログラムのみです。
  (画像処理系とか期待してたらスミマセン)

  実行環境は
     windows 7
     Visual Studio 2008
     OpenNI 1.0.0.25
     Prime Sense 1.3.017
  をつかってやってみます。

  OpenNI1.0.0.25ではサンプルに、C#によって作られているUserTracker.netというサンプルがあり、こいつを解読してXNAに実装していきます。


(2012/03/08)
KINECT for Windows SDK を用いて、XNA上でKinectを動かす方法を記述しました。






  とりあえずUserTracker.netのサンプルを実行してみると
  

  こんな感じに映ります。(右腕のボーンが消えちゃってますね・・・)

  動かす事が出来たらさっそくXNAに実装していきましょう。


プロジェクトの設定
  説明が解り難いかも知れないので、プロジェクトの作成からはじめて、画像多めで説明していきます。


1.
  まずVisual Studioを立ち上げ新規プロジェクトを作成しましょう。
  VisualC#からXNA Game Studioを選び、Windowsゲームを選択。プロジェクト名に名前を付けてOKで作成されます。
  

  作られるプロジェクトは、参照されているフォルダ内に生成されます。(デフォルトなら、マイドキュメント/Visual Studioフォルダ/Projects/にあるはずです)

2.
  作られたプロジェクトフォルダ内にOpenNI.net.dllをコピーしておきます。
  (OpenNI.net.dllは  c:Program Files/OpneNI/bin/  内にあります)
  



  そうしたらプロジェクトの参照設定を右クリックして、参照の追加。参照タブを選択し、OpenNI.net.dllを追加。
  (Contentのなかにある参照設定ではないので注意!)
         



  参照されているなら下図のようになります。
  

  プロジェクトのセットはこれで完了です。

3.
  ここからプログラミングです。サンプルのソースを参考に行っていきます。
  なお参考にしているソースは、Samples/UserTracker.net/MainWindow.csです。

  まず、using xn; と宣言しましょう。これでOpenNIの変数を使えるようになります。
  次に変数の宣言・初期化を行います。

  using xn;
  namespace ○○○○○○
  {
      public class Game1 : Microsoft.Xna.Framework.Game
      {
          // 宣言
          private Context context;
          private DepthGenerator depthGenerator;                                                                       // 深度
          private UserGenerator userGenerator;                                                                           // 
          private PoseDetectionCapability poseDetectionCapability;                                       // キャリブレーションの認識
          private SkeletonCapability skeletonCapability;                                                             // 
          private string calibPose;                                                                                                   // キャリブレーションポーズの情報
          private Dictionary<uint, Dictionary<SkeletonJoint, SkeletonJointPosition>> joints;  // ジョイントの情報

          public Game1()
          {
            // 初期化
            context = new Context("SamplesConfig.xml");
            depthGenerator = context.FindExistingNode(NodeType.Depth) as DepthGenerator;

            userGenerator = new UserGenerator(context);
            userGenerator.NewUser += new UserGenerator.NewUserHandler(userGenerator_NewUser);
            userGenerator.LostUser += new UserGenerator.LostUserHandler(userGenerator_LostUser);

             poseDetectionCapability = new PoseDetectionCapability(userGenerator);
             poseDetectionCapability.PoseDetected += new PoseDetectionCapability.PoseDetectedHandler(poseDetectionCapability_PoseDetected);

             skeletonCapability = new SkeletonCapability(userGenerator);
             skeletonCapability.CalibrationEnd += new SkeletonCapability.CalibrationEndHandler(skeletonCapbility_CalibrationEnd);
             skeletonCapability.SetSkeletonProfile(SkeletonProfile.All);

             calibPose = skeletonCapability.GetCalibrationPose();

             joints = new Dictionary<uint, Dictionary<SkeletonJoint, SkeletonJointPosition>>();

             userGenerator.StartGenerating();
  
                            ・・・

  のように宣言しておきます。最低限この変数のみでKINECTの情報は取得できます。
  さて、このままの状態だと userGenerator_NewUser 、 userGenerator_LostUse 等がありませんと警告が出ていると思います。

  サンプルソースを下へ少しスクロールすると・・・

  // キャリブレーションが完了しトラッキングをするか
  void skeletonCapbility_CalibrationEnd(ProductionNode node, uint id, bool success)
  {
      if (success)
      {
          skeletonCapability.StartTracking(id);
          joints.Add(id, new Dictionary<SkeletonJoint, SkeletonJointPosition>());
      }
      else
      {
          this.poseDetectionCapability.StartPoseDetection(calibPose, id);
      }
  }

  // キャリブレーションの認識
  void poseDetectionCapability_PoseDetected(ProductionNode node, string pose, uint id)
  {
      poseDetectionCapability.StopPoseDetection(id);
      skeletonCapability.RequestCalibration(id, true);
  }

  // 新しいユーザの検出
  void userGenerator_NewUser(ProductionNode node, uint id)
  {
      poseDetectionCapability.StartPoseDetection(this.calibPose, id);
  }

  // ユーザの消滅(ロスト)
  void userGenerator_LostUser(ProductionNode node, uint id)
  {
      joints.Remove(id);
  }

  それらしいものがつらつらと書かれています。これはユーザーが新しく現れたり、キャリブレーションポーズをしたりなどのイベントを認識する、イベントハンドラです。
  これで初期化は完了です。

  
  次に、KINECTから情報をもらってきましょう。
  KINECTの情報を得るには
  try
  {
      // 深度の更新
      context.WaitOneUpdateAll(depthGenerator);
  }
  catch (Exception)
  {
  }
  // 認識しているユーザ数
  uint[] users = userGenerator.GetUsers();
  foreach (uint user in users)
  {
      // キャリブレーションしているか
      if (skeletonCapability.IsCalibrated(user))
      {
          // 各ユーザのジョイント情報を取得
          GetJoints(user);
      }
  }
  をUpDate内に記述してあげます。さて、これで情報は取得できたはずです。さっそく実行してみましょう。


  エラーが出たのならば、dllの参照やプログラムをもう一度確認しましょう。
  動いたならばとりあえずはOKです。しかし、お気づきの方もいるかもしれませんがものすごく処理が重いです。
  原因はKINECTとXNAのFPSの違いです。KINECTは30[fps]、XNAは標準で60[fps]でうごいていて、XNAの更新速度にKINECT側が追い付いていけないのが原因かと思われます。(個人的な見解です)
  よって、XNA側を30[fps]にするなどして更新速度を下げてあげれば快適に動くはずです。




Comments