やってみよう!Kinectアプリ開発 - 第5回 深度情報の利用

2012-06-29 18:31

アプリケーションの修正

それでは、Visual Studio®を使い、アプリケーションの修正を行いましょう。今回の修正はコードの変更だけですので、手順はありません。コードの変更点だけ順に説明します。

マスク画像の拡大・縮小

この修正は見ての通りでしょう。頭部の位置情報から、Z座標(これがカメラからの深度です)を取得し、画像のサイズを深度に反比例するようにします。なお、カメラから1.5m離れたときに前回と同サイズの128ピクセルになり、カメラから3m離れると半分の64ピクセル、逆に0.75mに近づくと倍の256ピクセルになるようにしています(192 / headPos.Z)。この数値は適宜変えても構いません。

それでは、実行(メニューから[デバッグ(D)]-[デバッグ実行(S)]を選択)してみましょう。

無事、深度に応じてマスク画像の拡大・縮小ができました。このように、単に骨格や関節の深度を求めるのは非常に簡単です。

深度情報の表示(画像・深度情報が同一の解像度の場合)

次に深度情報を利用して、プレイヤーのいない点を塗ってみましょう。コードの修正点は以下の通りです。

  • フィールドの追加
    MainWindowクラスの宣言の直下に以下の2つのフィールドを追加します。これらの役割は以下の通りです。
    1. depthBuffer: Depth Frameから取得した深度情報を格納するバッファ。
    2. clrPntBuffer: depthBuffer上の各点に対し、対応する画像情報上の座標を格納するバッファ。
    3. depMaskBuffer: プレイヤーのいない点を青くしたビットマップイメージ。これを背景画像上に上書きします。(従って、このビットマップは深度情報ではなく画像情報と同じ座標空間です)
  • 初期化処理
    初期化処理には、Depth Streamの有効化と追加したフィールドの初期化を追加します。これらの処理はこれまでと同様ですが、depMaskBufferは深度情報ではなく画像情報と同じサイズにしている点に注意しましょう。
  • FrameReadyイベントハンドラ
    Depth Streamを追加しましたので、using文を追加して深度フレームを取得し、描画処理に渡します。
  • 描画処理
    描画処理では、深度情報の各点を調べ、そこにプレイヤーがいない場合は(半分透明にして)青を塗ります。

    ここで追加した処理は以下の通りです。
    1. CopyPixelDataToメソッドで、深度情報をバッファにコピーする。
    2. MapDepthFrameToColorFrameメソッドで、深度情報上の各点に対応する、画像情報上の座標をバッファに格納する。
    3. depMaskBufferをゼロクリアする。(初期状態は透明にするため)
    4. depthBuffer上の各点に対して以下を行う。
      1. depthBufferからその点の深度情報を取得する。深度情報の下位3bitにはプレイヤーの情報が入っているので、それが0(プレイヤーがいない)以外の場合、次の点を処理する。
      2. 0の場合、clrPntBufferを使い、対応する画像情報上の座標を取得する。
      3. depMaskBufferの対応する点に青を(半分透明で)塗る。
    5. depMaskBufferをもとにビットマップを作成する。(透明度を持つデータなので、Bgra32形式とすることに注意)
    6. 背景画像描画後に上から5で作成したビットマップを上書きする。

それではこちらも実行(メニューから[デバッグ(D)]-[デバッグ実行(S)]を選択)してみましょう。

プレイヤーがいない部分が青くなりました。先ほども説明しましたが、深度情報を扱う場合は以下に注意が必要なことがわかると思います。

  • 画像情報の端の部分には、対応する深度情報がありません。
  • 端以外でも、(深度が大きく異なる点などは特に)深度情報がない点があります。
  • 手に持っているものなども「プレイヤー」として認識されます。この場合は、手に持っているzigsowのパンフレットも青くなっていないことが分かると思います。

深度情報の表示(画像・深度情報が異なる解像度の場合)

最後に、上記のアプリケーションを深度情報の解像度を小さくして試してみましょう。コードの修正は以下の1点だけです。説明も不要でしょう。

それではこちらも実行(メニューから[デバッグ(D)]-[デバッグ実行(S)]を選択)してみましょう。

このように、深度情報がマッピングできる点が4分の1程度なので、色が薄くなったことが分かると思います。なお、単純に4点に1点の割合というわけではなく、格子状の模様が出るのが興味深いですね。

まとめ

前回までのカメラ・骨格情報の利用に続き、今回は深度情報の利用を紹介しました。これでKinectの画像に関する基本的な機能は一通り紹介が終わりました。次回からは音源に関する情報の利用方法を紹介します。

商標について

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

3件の質問スレッド
  • depMaskBuffer 工藝 太郎 2014-11-19 14:22 上に書いてあるプログラムを打って、エラーは出なかったのですが、

    depMaskBuffer[clrIdx ⋇ 4] = 255; の部分に

    「IndexOutOfRangeExceptionはハンドルされませんでした。」 ”インデックスが配列の境界外です。”と出ました。
    どうすればいいのか教えてください。
    トラブルシューティングのヒント:
    リストの最大インデックスが、リストサイズよりも小さくなることを確認します。
    インデックスが負の数でないことを確認します。
    データ列名が適切であることを確認します。
    この例外に関する一般的なヘルプを参照します。
    と書いてありました。
  • マスク画像の拡大縮小のプログラミング のぎむ 2012-08-22 09:17 自分のペースでこちらのkinect開発を進めています!

    前回で行った「第四回の骨格情報」のおまけで頭の傾きをマスク画像に反映させるプログラムを習いました。

    そして、今回その続きからプログラミングをするようになっています。なんとか、今回の第五回目も動作させる事が出来て一安心でした。

    そこで、前回までの復習に、傾きを加えずに拡大縮小させてみようと、第四回目のおまけ前のソースで試してみました。

    しかし、Item1などがエラーが出てしまい、(Mtrix4を追加したら消えたのですが)どこを変えれば動作するのかわかりません。

    どうかご教授いただけたらと思います。よろしくお願いします。
    4件のコメント (すべて表示)
    • のぎむ 某支配人さん、ありがとうございます。

      教えて頂いたおかげで、
      Item1のエラーは消えました。
      しかし、
      //頭の位置を保存
      results.add(head.Position);
      の部分が消えません。

      ListをList<Tuple<SkeletonPoint, Matrix4>> に変更したので、

      // 頭の位置を保存
      results.Add(Tuple.Create(head.Position, 〇〇));

      という形になると思うのですが、Item2を要求され、エラーが出てしまいました。

      実際のサンプルは、Item2に傾きが入っていた為、

      // 頭の位置を保存
      results.Add(Tuple.Create(head.Position, headMtrx));

      となっていたのですが、今回は傾きを入れないのでheadMtrxは出てきていません。
      当然、入力するとエラーがでます。

      // 頭の位置を保存
      results.Add(Tuple.Create(head.Position,   ここ ));
      ここにはなにを入れたらよいのでしょうか?


      度々、申し訳ありません。
      2012-09-09 12:24
    • 某支配人 私も理解出来てるつもりは無い部分なのですが、
      Item1が無いってことなら

      // RGBカメラの画像情報に対して、顔の位置にマスクを上書きして描画する  // 追加 第4回
          private void fillBitmap(KinectSensor kinect,ColorImageFrame imgFrame,
                      List<SkeletonPoint> headList)
          {



      // RGBカメラの画像情報に対して、顔の位置にマスクを上書きして描画する  // 追加 第4回
          private void fillBitmap(KinectSensor kinect, ColorImageFrame imgFrame,
                      List<Tuple<SkeletonPoint, Matrix4>> headList)  //
          {

      このあたりが怪しいと思います。
      合わせて

      private List<Tuple<SkeletonPoint, Matrix4>> getHeadPoints(SkeletonFrame skelFrame)   // 変更

      ここも候補となります。

      きちんとしたアドバイスにならなくてごめんなさいネ
      2012-08-28 23:45
    • のぎむ 返答遅れてしまい、申し訳ありません。

      某支配人さん、丁寧な回答して下さりありがとうございます。
      某支配人さんのレビューを参考にさせて頂いていたので、回答を頂いて驚きました。

      拝見して改めて認識したのですが、
      "Itemの定義が含まれていない"
      とエラーが出てしまいます。


      第四回のP2までに作成したソースで打ち込んだ為、
      必要な文が足りていない事に気づき、
      自分なりに解釈してMatrix4がxとyだけでなくzも取得する構文なのではと考え、
      「処理結果のリストを空状態で作成」(第四回のP3、上段)
      var results = new List<Tuple<SkeletonPoint, Matrix4>>();
      を追加しました。

      しかし、打ち込んでみたところ次々とエラーが出力され、
      すべてのエラーを消す為に頭の向きを取得まで打つ始末。

      Itemのエラーを無くすにはMatrix4を追記する必要があるのでしょうか?

      その場合、
      //頭の位置・向きを保存(第四回のP3)
      results.Add(Tuple.Create(head.Position, headMtrix));

      では、どのように変える必要があるのでしょうか?

      全くの初心者で申し訳ありません。
      よろしくお願いします。
      2012-08-28 17:21
    • 某支配人       // getHeadPointsで取得した、各東部の位置毎にループ    // 追加 第4回2nd
            for (int idx = 0; headList != null && idx < headList.Count; ++idx)
            {
              // 骨格の座標から画像情報の座標に変換
              SkeletonPoint headPos = headList[idx].Item1;    // 追加 第4回2nd
              ColorImagePoint headPt
                = kinect.MapSkeletonPointToColor(headPos, rgbFormat);  // 追加 第4回2nd

              // 距離に応じてサイズを決定   // 追加 第5回
              int size = (int)(192 / headPos.Z);

              // 頭の位置にマスク画像を描画
              Matrix4 headMtrx = headList[idx].Item2;
              Matrix rot = new Matrix(-headMtrx.M11, headMtrx.M12,
                          -headMtrx.M21, headMtrx.M22,
                          headPt.X, headPt.Y);
              drawContext.PushTransform(new MatrixTransform(rot));
              //Rect rect = new Rect(- 64, - 64, 128, 128);
              Rect rect = new Rect(-size / 2, -size / 2, size, size);  // 変更 第5回
              drawContext.DrawImage(maskImage, rect);
              drawContext.Pop();
            }
            // 画面に表示するビットマップに描画
            drawContext.Close();
            bmpBuffer.Render(drawVisual);

      この部分で// 変更 第5回となっているあたりを参考にしていただければ、サイズの変更は可能だと思います。
      2012-08-23 11:31
  • OutOfMemoryの解決方法 mkama 2012-08-11 19:30 ジグソー運営事務局にお願いして、2週間遅れでレビュー書いています。

    入院2週間目で、やっと抜糸(抜鉤25本)しました。
    kinect + ディスクトップを病室に持ち込んで、やっとレビュー開始しました。

    連載 第5回目レビューでOutOfMemoryが発生して困っていましたが、解決方法がわかりました。

    ガーベージコレクションを強制するとエラーが発生しません。

    レビューアーの方で他の方法で解決された方、いらっしゃいましたか?

    private void fillBitmap()の最後に追加します。

    // 画面に表示するビットマップに描画
    drawConText.Close();
    bmpBuffer.Render(drawVisual);

    GC.Collect(); //★これを追加
    1件のコメント
    • kazgb 自分もこの1行でOutOfMemoryの発生が無くなりました。
      他の方法は良く分かっていないので試せていないのですが・・・
      2012-08-12 10:54

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

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