Windowsフォームアプリケーションでの認識人物の実画像取得方法です。
(作成日:2012/02/28)
前回の深度画像取得の段階で、認識ユーザーに色づけを行っていました。
認識ユーザーの色づけ処理
depthFrame32[i32 + RedIndex] = (byte)(intensity >> IntensityShiftByPlayerR[player]);
depthFrame32[i32 + GreenIndex] = (byte)(intensity >> IntensityShiftByPlayerG[player]);
depthFrame32[i32 + BlueIndex] = (byte)(intensity >> IntensityShiftByPlayerB[player]);
IntensityShiftByPlayerR、IntensityShiftByPlayerG、IntensityShiftByPlayerBでユーザーに色を付けていましたが、ここに実画像のデータのRGBを挿入すればおわりです。
認識ユーザーの実画像取得処理
if(player != 0)
{
// 認識ユーザーの実画像データを取得
depthFrame32[i32 + RedIndex] = imageData[i32 + RedIndex];
depthFrame32[i32 + GreenIndex] = imageData[i32 + GreenIndex];
depthFrame32[i32 + BlueIndex] = imageData[i32 + BlueIndex];
}
実行結果
ただこの処理方法では実画像のサイズと深度画像のサイズが一致していなくてはなりません。
深度画像の処理を640x480で行うと意外と処理が重いです(私の手法が悪いかもしれませんが・・・)。
ソースコード
Form1.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 formgetuserimage
{
public partial class Form1 : Form
{
/// <summary>
/// kinectのセンサクラス
/// </summary>
KinectSensor kinect;
/// <summary>
/// kinectから取得したRGBデータ
/// (byte型配列)
/// </summary>
byte[] imageData;
/// <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.ColorFrameReady += new EventHandler<ColorImageFrameReadyEventArgs>(ColorImageReady);
kinect.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30);
// 深度画像の取得を開始する
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>
void ColorImageReady(object sender, ColorImageFrameReadyEventArgs e)
{
// kinectからカラーイメージを取得
ColorImageFrame image = e.OpenColorImageFrame();
// imageがnullだった場合処理しない
if (image != null)
{
// imageData配列の初期化
imageData = new byte[image.PixelDataLength];
// imageのピクセルデータをpixelDataへコピーする
image.CopyPixelDataTo(imageData);
}
}
/// <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)
{
// 認識ユーザーの実画像データを取得
depthFrame32[i32 + RedIndex] = imageData[i32 + RedIndex];
depthFrame32[i32 + GreenIndex] = imageData[i32 + GreenIndex];
depthFrame32[i32 + BlueIndex] = imageData[i32 + BlueIndex];
}
}
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;
}
}
}