[Kinect SDK][C#][XNA]人を色分けしてシルエット表示する

by Teruaki Tsubokura 0 Comments
[Kinect SDK][C#][XNA]人を色分けしてシルエット表示する

Kinectをデジタルサイネージに利用するハックの第二弾です。

前回、Kinectで撮影している場所に人が居るか居ないかを取得しましたが、今回はその応用で深度カメラから取得した映像をもとにプレイヤーごとに色分けして表示をしてみます。
さらにせっかくユーザ数も取得しているので画面に表示してみましょう。

■フォントの準備

プロジェクトの作成、参照の追加までは前回のTipsと同じです。
今回はそれにプラスして画面にユーザ数の表示もするので、その表示の為のフォントを読み込みます。
ソリューションエクスプローラーのContentを右クリック→[追加(D)]→[新しい項目(W)…]を選択します。

新しい項目を追加するウィンドウが開くので、Sprite Fontを選択し任意の名前をつけてOK

これでXNAで文字を表示する準備が整いました。

 

■ソースコード

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;
using Microsoft.Research.Kinect.Nui;    //Kinect Uniの読み込み

namespace Kinect_UserColors
{
    /// <summary>
    /// 基底 Game クラスから派生した、ゲームのメイン クラスです。
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        Runtime Kinect;    //Kinectのセンサクラス
        SpriteBatch spriteBatch;
        Texture2D texture_depth = null;    //奥行テクスチャ
        Texture2D texture_User = null;    //ユーザシルエットテクスチャ
        private Color[] depthColor;    //色情報の格納
        private Color[] UserColor;    //色情報の格納
        private bool[] user_index = new bool[7];    //ユーザインデックス
        private int user_count = 0;    //ユーザの数
        private SpriteFont font = null;   //フォント

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            //画面サイズを640 X 480
            this.graphics.PreferredBackBufferWidth = 640;
            this.graphics.PreferredBackBufferHeight = 480;
        }

        #region Initialize
        protected override void Initialize()
        {
            base.Initialize();
            Kinect = Runtime.Kinects[0];    //Kinectセンサクラスの初期化
            this.font = this.Content.Load<SpriteFont>("Font1");    //フォントの読み込み

            try
            {
                //奥行の取得、トラッキング、実画像
                Kinect.Initialize(RuntimeOptions.UseDepthAndPlayerIndex |
                                RuntimeOptions.UseSkeletalTracking |
                                RuntimeOptions.UseColor);
            }
            catch (InvalidOperationException)
            {
                Console.WriteLine("Runtime initialization failed.");
                return;
            }

            try
            {
                //デプスストリームを開く
                Kinect.DepthStream.Open(ImageStreamType.Depth,
                                        2,
                                        ImageResolution.Resolution320x240,
                                        ImageType.DepthAndPlayerIndex);
            }
            catch (InvalidOperationException)
            {
                Console.WriteLine("Failed to open stream. ");
                return;
            }

            //ビデオ更新毎にKinect_DepthFrameReadyを呼び出す
            Kinect.DepthFrameReady += new EventHandler<ImageFrameReadyEventArgs>(Kinect_DepthFrameReady);
        }
        #endregion

        #region LoadContent
        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
        }
        #endregion

        #region Update
        protected override void Update(GameTime gameTime)
        {
            KeyboardState keyState = Keyboard.GetState();
            if (keyState.IsKeyDown(Keys.Escape)) this.Exit();
            base.Update(gameTime);
        }
        #endregion

        #region Draw
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            this.spriteBatch.Begin();
            //奥行画像Textureの描写
            if (this.texture_depth != null)
            {
                this.spriteBatch.Draw(this.texture_depth, new Vector2(0, 0), null,
                  Color.White, 0.0f, Vector2.Zero, 2.0f, SpriteEffects.None, 0.0f);
            }
            //ユーザシルエットTextureの描写
            if (this.texture_User != null)
            {
                this.spriteBatch.Draw(this.texture_User, new Vector2(0, 0), null,
                    Color.White, 0.0f, Vector2.Zero, 2.0f, SpriteEffects.None, 0.0f);
            }
            //画面にユーザ数を表示
            this.spriteBatch.DrawString(font, "UserCount:" + user_count, new Vector2(0, 0), Color.White);
            this.spriteBatch.End();
            base.Draw(gameTime);
        }
        #endregion

        #region 奥行き画像
        void Kinect_DepthFrameReady(object sender, ImageFrameReadyEventArgs e)
        {
            lock (this)
            {
                PlanarImage Image = e.ImageFrame.Image;
                //奥行き画像初期化
                this.depthColor = new Color[Image.Height * 2 * Image.Width * 2];
                this.texture_depth = new Texture2D(graphics.GraphicsDevice, Image.Width * 2, Image.Height * 2);
                //ユーザシルエット画像初期化
                this.UserColor = new Color[Image.Height * 2 * Image.Width * 2];
                this.texture_User = new Texture2D(graphics.GraphicsDevice, Image.Width * 2, Image.Height * 2); //テクスチャの作成

                //-- 人数をカウントする --//
                //user_index値リセット
                for (int i = 0; i < 7; i++) { user_index[i] = false; }
                //画像取得
                int no = 0;
                for (int y = 0; y < Image.Height; ++y)
                { //y軸
                    for (int x = 0; x < Image.Width; ++x, no += 2)
                    { //x軸
                        int n = (y * Image.Width + x) * 2;
                        int realDepth = (Image.Bits[n + 1] << 5) | (Image.Bits[n] >> 3);
                        byte intensity = (byte)((255 - (255 * realDepth / 0x0fff)) / 2);
                        this.depthColor[y * 640 + x] = new Color(intensity, intensity, intensity);

                        #region マスク画像生成
                        Color tmp = new Color();
                        int player = Image.Bits[(y * 320 + x) * 2] & 0x07;
                        switch (player)
                        {
                            case 0:
                                tmp = Color.Black; //背景:黒
                                break;
                            case 1:
                                tmp = new Color(intensity, intensity, 255); //Player1:青
                                user_index[0] = true;
                                break;
                            case 2:
                                tmp = new Color(intensity, 255, intensity); //Player2:緑
                                user_index[1] = true;
                                break;
                            case 3:
                                tmp = new Color(intensity, 255, 255); //Player3:水
                                user_index[2] = true;
                                break;
                            case 4:
                                tmp = new Color(255, intensity, intensity); //Player4:赤
                                user_index[3] = true;
                                break;
                            case 5:
                                tmp = new Color(255, intensity, 255); //Player5:紫
                                user_index[4] = true;
                                break;
                            case 6:
                                tmp = new Color(255, 255, intensity); //Player6:黄
                                user_index[5] = true;
                                break;
                            default:
                                tmp = Color.Black; //背景:黒
                                break;
                        }
                        //ユーザが見つかったらピクセルに色を付ける
                        if (tmp != Color.Black)
                        {
                            this.UserColor[y * 640 + x] = tmp;
                        }
                        #endregion
                    }
                }
                this.texture_depth.SetData(this.depthColor);    //texture_depthにデータを書き込む
                this.texture_User.SetData(this.UserColor);    //texture_Yserにデータを書き込む

                //見つかったユーザインデックスをカウントする
                user_count = 0;
                for (int j = 0; j < 6; j++)
                {
                    user_count += Convert.ToInt32(user_index[j]);
                }
            }
        }
        #endregion
    }
}

 

■実行結果

起動。深度カメラの映像と共に左上にユーザカウントの値が表示されています。
そしてKinectの前に人が入ると・・・

こんな感じで表示されます。
赤色で表示されているので、Player4として認識されているようです。

tmp = new Color(255, intensity, intensity); //Player4:赤 

そうなんです。一人しか居ないからといって必ずPlayer1として認識される訳ではないんですね。
今は私以外に人が居ないので一色しか出ていませんが、人が増えるごとに新しい色で表示され、最大6人まで色を分けて表示できるので試してみて下さい。

Leave a reply

Your email address will not be published.

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください