深度画像の取得

Windowsフォームアプリケーションでの深度画像取得方法です。

(作成日:2012/02/28)

1.プロジェクトを作る

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

2.参照設定を追加

[Microsoft.Kinect]、[System.Runtime.Serialization]のほかに[PresentationCore]を追加してください。

3.アプリケーションのデザイン

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

4.プログラミング

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

From1.cs

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Windows.Forms;

// ビットマップを生成するため

using System.Drawing.Imaging;

// Kinectのセンサクラス

// Microsoft.Kinectを参照に追加

using Microsoft.Kinect;

// メモリ管理の最適化

// System.Runtime.Serializationを参照に追加

using System.Runtime.InteropServices;

// BitMap関係のもの

// presentationCoreを参照に追加

using System.Windows.Media;

using System.Windows.Media.Imaging;

namespace fromgetdepthimage

{

public partial class Form1 : Form

{

/// <summary>

/// kinectのセンサクラス

/// </summary>

KinectSensor kinect;

/// <summary>

/// kinectから取得した深度データ

/// (short型配列)

/// </summary>

short[] depthData;

/// <summary>

/// kinectの深度情報

/// (byte型配列)

/// </summary>

byte[] depthFrame32;

/// <summary>

/// 深度画像

/// (ビットマップ形式)

/// </summary>

Bitmap depthBitMap;

/// <summary>

/// 認識ユーザーごとにつける色

/// </summary>

static readonly int[] IntensityShiftByPlayerR = { 1, 2, 0, 2, 0, 0, 2, 0 };

static readonly int[] IntensityShiftByPlayerG = { 1, 2, 2, 0, 2, 0, 0, 1 };

static readonly int[] IntensityShiftByPlayerB = { 1, 0, 2, 2, 0, 2, 0, 2 };

/// <summary>

/// ビットシフトに用いる

/// </summary>

static readonly int Bgr32BytesPerPixel = (PixelFormats.Bgr32.BitsPerPixel + 7) / 8;

/// <summary>

/// depthFrame32にデータを挿入する順番

/// (BGRの順で挿入される)

/// </summary>

const int RedIndex = 2;

const int GreenIndex = 1;

const int BlueIndex = 0;

public Form1()

{

InitializeComponent();

// ピットマップの初期化

depthBitMap = new Bitmap(640, 480);

// kinectの初期化

kinect = KinectSensor.KinectSensors[0];

// 深度画像の取得を開始する

kinect.DepthFrameReady += new EventHandler<DepthImageFrameReadyEventArgs>(DepthFrameReady);

kinect.DepthStream.Enable(DepthImageFormat.Resolution640x480Fps30);

// スケルトントラッキングを開始する

// これを行わないとユーザーを認識しない

kinect.SkeletonStream.Enable();

// Kinectを起動する

kinect.Start();

}

/// <summary>

/// 深度画像の取得

/// </summary>

void DepthFrameReady(object sender, DepthImageFrameReadyEventArgs e)

{

// kinectから深度情報を取得

DepthImageFrame depthImage = e.OpenDepthImageFrame();

if (depthImage != null)

{

// imageDataの初期化

depthData = new short[depthImage.PixelDataLength];

depthFrame32 = new byte[depthImage.Width * depthImage.Height * Bgr32BytesPerPixel];

// depthImageのピクセルデータをdepthDataへコピーする

depthImage.CopyPixelDataTo(depthData);

// 深度ごとに色づけを行う

byte[] convertedDepthBits = ConvertDepthFrame(depthData, ((KinectSensor)sender).DepthStream);

// convertedDepthBitsからビットマップへ変換する

depthBitMap = toBitmap(convertedDepthBits, depthBitMap.Width, depthBitMap.Height);

// ピクチャーボックスへ反映

pictureBox1.Image = depthBitMap;

}

}

/// <summary>

/// 深度ごとに色づけ&認識ユーザーの色づけ

/// </summary>

private byte[] 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.depthFrame32.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の間?(近すぎ?)

depthFrame32[i32 + RedIndex] = 255;

depthFrame32[i32 + GreenIndex] = 255;

depthFrame32[i32 + BlueIndex] = 255;

}

else if (player == 0 && realDepth == tooFarDepth)

{

// 紫色

// 5m以上を意味する?(遠すぎる?)

depthFrame32[i32 + RedIndex] = 66;

depthFrame32[i32 + GreenIndex] = 0;

depthFrame32[i32 + BlueIndex] = 66;

}

else if (player == 0 && realDepth == unknownDepth)

{

// 茶色

// 深度画像が取得できていない?

depthFrame32[i32 + RedIndex] = 66;

depthFrame32[i32 + GreenIndex] = 66;

depthFrame32[i32 + BlueIndex] = 33;

}

else

{

// 認識ユーザーごとに色をつける

depthFrame32[i32 + RedIndex] = (byte)(intensity >> IntensityShiftByPlayerR[player]);

depthFrame32[i32 + GreenIndex] = (byte)(intensity >> IntensityShiftByPlayerG[player]);

depthFrame32[i32 + BlueIndex] = (byte)(intensity >> IntensityShiftByPlayerB[player]);

}

}

return depthFrame32;

}

/// <summary>

/// 取得データをビットマップデータに変換

/// </summary>

/// <param name="pixels">kinectで取得したbyte[]配列</param>

/// <param name="width">横サイズ</param>

/// <param name="height">縦サイズ</param>

/// <returns></returns>

public static Bitmap toBitmap(byte[] pixels, int width, int height)

{

// pixelsに何も入っていない場合nullを返す

if (pixels == null)

return null;

// ビットマップの初期化

var bitmap = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppRgb);

// システムメモリへロック

var data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);

// メモリデータのコピー(メモリ管理を最適化?)

Marshal.Copy(pixels, 0, data.Scan0, pixels.Length);

// システムメモリのロック解除

bitmap.UnlockBits(data);

return bitmap;

}

}

}

実行結果

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

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

取得できる種類

Resolution320x240Fps30

Resolution640x480Fps30(デフォルト)

Resolution80x60Fps30