Members‎ > ‎Wataru-Fujimura‎ > ‎Kinect Tips‎ > ‎

XNAによる深度画像取得

XNAでの深度画像取得方法です。
(作成日:2012/03/01)


1.プロジェクトを作る

前回と同じように作成してください。


2.参照設定を追加

[.NET]のタブを選択し、[Microsoft.Kinect]を追加します。
さらに[PresentationCore]も追加してください。



追加されるとこのように表示されます。




3.プログラミング

以下のコードのように入力してください。

Game1.cs

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

// kinectのセンサクラス
// Microsoft.kinectを参照設定に追加
using Microsoft.Kinect;

//////////////////////////////////////////////////
// 参照設定にPresentationCoreを設定しておくこと //
// (深度画像処理に使うため)                   //
//////////////////////////////////////////////////

namespace getdepthimage
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        /// <summary>
        /// 深度画像テクスチャ
        /// 640x480
        /// </summary>
        private Texture2D depthTexture;

        /// <summary>
        /// kinectから取得した深度データ
        /// (short型配列)
        /// </summary>
        private short[] depthData;

        /// <summary>
        /// 深度データから色情報に変換されたデータ
        /// (byte型配列)
        /// </summary>
        private byte[] depthImageData;

        /// <summary>
        /// ユーザーごとにつける色
        /// </summary>
        private static readonly int[] IntensityShiftByPlayerR = { 1, 2, 0, 2, 0, 0, 2, 0 };
        private static readonly int[] IntensityShiftByPlayerG = { 1, 2, 2, 0, 2, 0, 0, 1 };
        private static readonly int[] IntensityShiftByPlayerB = { 1, 0, 2, 2, 0, 2, 0, 2 };

        /// <summary>
        /// ビットシフトに用いる
        /// </summary>
        private static readonly int Bgr32BytesPerPixel = (System.Windows.Media.PixelFormats.Bgr32.BitsPerPixel + 7) / 8;

        /// <summary>
        /// 深度データを配列に挿入する順番
        /// </summary>
        private const int RedIndex = 0;
        private const int GreenIndex = 1;
        private const int BlueIndex = 2;
        private const int AlphaIndex = 3;

        /// <summary>
        /// KINECTのセンサクラス
        /// </summary>
        private KinectSensor kinect;

        /// <summary>
        /// キーボードの状態
        /// </summary>
        private KeyboardState key;

        /// <summary>
        /// 1frame前のキーボードの状態
        /// </summary>
        private KeyboardState oldkey;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            // ウィンドウサイズの指定
            graphics.PreferredBackBufferHeight = 480;
            graphics.PreferredBackBufferWidth = 640;
        }

        protected override void Initialize()
        {
            // kinectの初期化
            kinect = KinectSensor.KinectSensors[0];

            // 深度画像の取得を開始する
            // 私の手法では640x480で処理を行うとすごく重くなった
            kinect.DepthFrameReady += new EventHandler<DepthImageFrameReadyEventArgs>(DepthFrameReady);
            kinect.DepthStream.Enable(DepthImageFormat.Resolution640x480Fps30);

            // スケルトントラッキングを開始する
            kinect.SkeletonStream.Enable();

            // Kinectを起動する
            kinect.Start();

            base.Initialize();
        }

        protected override void LoadContent()
        {
            // 新規の SpriteBatch を作成します。これはテクスチャーの描画に使用できます。
            spriteBatch = new SpriteBatch(GraphicsDevice);

        }

        protected override void UnloadContent() { }

        protected override void Update(GameTime gameTime)
        {
            // キーボードの状態を取得
            key = Keyboard.GetState();

            // Escapeキーが押されたらプログラムを終了する
            if (key.IsKeyDown(Keys.Escape))
                Exit();

            // F1が押されたら[ウィンドウモード⇔フルスクリーンモード]の切り替え
            if (key.IsKeyDown(Keys.F1) && oldkey.IsKeyUp(Keys.F1))
                graphics.ToggleFullScreen();
            
            // このフレームのキーボードの状態を記憶しておく
            oldkey = key;

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // スプライトバッチの使用開始
            spriteBatch.Begin();

            // 深度画像の描写
            if (depthTexture != null)
                spriteBatch.Draw(depthTexture, new Vector2(0, 0), null, Color.White, 0.0f, Vector2.Zero, 1.0f, SpriteEffects.None, 0.0f);

            // スプライトバッチの使用終了
            spriteBatch.End();

            base.Draw(gameTime);
        }

        /// <summary>
        /// 深度画像の取得
        /// </summary>
        void DepthFrameReady(object sender, DepthImageFrameReadyEventArgs e)
        {
            // kinectから深度情報を取得
            DepthImageFrame depthImage = e.OpenDepthImageFrame();

            if (depthImage != null)
            {
                // depthDataの初期化
                depthData = new short[depthImage.PixelDataLength];
                depthImageData = new byte[depthImage.Width * depthImage.Height * Bgr32BytesPerPixel];

                // depthImageのピクセルデータをdepthDataへコピーする
                depthImage.CopyPixelDataTo(depthData);

                // 深度ごとに色づけを行う
                ConvertDepthFrame(depthData, ((KinectSensor)sender).DepthStream);

                // depthTextureの初期化
                depthTexture = new Texture2D(GraphicsDevice, depthImage.Width, depthImage.Height);

                // depthTextureにimageDataを反映する
                depthTexture.SetData(depthImageData);
            }
        }

        /// <summary>
        /// 深度ごとに色づけ&認識ユーザーの色づけ
        /// ほぼサンプルコピペ(余分な部分は削除してある)
        /// </summary>
        private void ConvertDepthFrame(short[] depthFrame, DepthImageStream depthStream)
        {
            int tooNearDepth = depthStream.TooNearDepth;
            int tooFarDepth = depthStream.TooFarDepth;
            int unknownDepth = depthStream.UnknownDepth;

            for (int i16 = 0, i32 = 0; i16 < depthFrame.Length && i32 < this.depthImageData.Length; i16++, i32 += 4)
            {
                int player = depthFrame[i16] & DepthImageFrame.PlayerIndexBitmask;
                int realDepth = depthFrame[i16] >> DepthImageFrame.PlayerIndexBitmaskWidth;

                // 13ビットの深度情報を表示に適した8ビットへ変換する(最上位ビットを無視する)
                byte intensity = (byte)(~(realDepth >> 4));

                if (player == 0 && realDepth == 0)
                {
                    // 白色
                    // 40~80cmの間?(近すぎ?)
                    depthImageData[i32 + RedIndex] = 255;
                    depthImageData[i32 + GreenIndex] = 255;
                    depthImageData[i32 + BlueIndex] = 255;
                    depthImageData[i32 + AlphaIndex] = 255;
                }
                else if (player == 0 && realDepth == tooFarDepth)
                {
                    // 紫色
                    // 5m以上を意味する?(遠すぎる?)
                    depthImageData[i32 + RedIndex] = 66;
                    depthImageData[i32 + GreenIndex] = 0;
                    depthImageData[i32 + BlueIndex] = 66;
                    depthImageData[i32 + AlphaIndex] = 255;
                }
                else if (player == 0 && realDepth == unknownDepth)
                {
                    // 茶色
                    // 深度画像が取得できていない?
                    depthImageData[i32 + RedIndex] = 66;
                    depthImageData[i32 + GreenIndex] = 66;
                    depthImageData[i32 + BlueIndex] = 33;
                    depthImageData[i32 + AlphaIndex] = 255;
                }
                else
                {
                    // ユーザーごとに色づけ
                    depthImageData[i32 + RedIndex] = (byte)(intensity >> IntensityShiftByPlayerR[player]);
                    depthImageData[i32 + GreenIndex] = (byte)(intensity >> IntensityShiftByPlayerG[player]);
                    depthImageData[i32 + BlueIndex] = (byte)(intensity >> IntensityShiftByPlayerB[player]);
                    depthImageData[i32 + AlphaIndex] = 255;
                }
            }
        }
    }
}





実行結果

このような感じで表示されるはずです。




ソースコード内の「 kinect.DepthStream.Enable(DepthImageFormat.Resolution640x480Fps30); 」を変更することで、取得するサイズ・FPSなど変更できます。

取得できる種類
Resolution320x240Fps30
Resolution640x480Fps30(デフォルト)
Resolution80x60Fps30

ċ
xnagetdepthimage.zip
(60k)
Wataru Fujimura,
Mar 1, 2012, 12:09 AM
Comments