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