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

深度画像の取得

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

ċ
fromgetdepthimage.zip
(47k)
Wataru Fujimura,
Feb 28, 2012, 3:09 AM
Comments