深度画像の取得
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