2013年8月3日 星期六

[Note] 使用Polling技術去取得kinect 骨架資訊並且畫出 in XNA

系統資訊:
版本 : Kinect SDK 1.7
IDE : Microsoft Visual Studio 2010
XNA版本 :  4.0




在原本的 SDK 裡面提供的有WPF 以及 D2D 顯示骨架資訊。



















上述在Tookit裡面可以直接打開專案來使用。

但是今天我們要在XNA裡面畫出骨架資訊。

原由: 應用於很多XNA豐富的元件以及開發環境來做出很多很棒的東西

並且在原本WPF裡面要設計一個動態的東西是不容易的。

首先 : 在原本的WPF 設計裡面。 是用Event-driven 的方式去取得骨架的frame

  private void KinectSkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
  {
                        //  以下略
  }

利用一個被動式的方法透過,事件(event)去取得frame 資料。

但是今天我們在XNA裡面,裡面的Update() 凾式。

是一直不斷地在running ,意即一直在跑無窮迴圈的意思(直到終止)

這樣我們就可以利用polling 技術去取得 frame 資料。

在XNA環境下使用polling實作起來也會比較容易些...

在原本WPF程式碼裡面的drawLine() 變成在XNA的 draw() 函式

我們先來了解一下流程吧!? (參考請點我)
















簡單一點的對照圖就如上。


開始吧!

//////////////////////////////////////////////////////////////////////////////////////////////////
一開始即宣告

首先別忘記加入kinect.dll 喔

開啟XNA 專案之後。宣告要使用的變數

        KinectSensor myKinect;
        int[] skeletonArray = new int[40];

此處宣告了Kinect 裝置變數以及我們要儲存骨架位置資訊的陣列變數

然而在 LoadContent ()裡面要開始做 讀入資源的動作

            myKinect = KinectSensor.KinectSensors[0];
            myKinect.SkeletonStream.Enable();
            myKinect.Start();

接著就是緊接著重要的部分 亦即作不斷的更新去polling取得frame

        protected override void Update(GameTime gameTime)
        {
            Skeleton[] skeletons;           
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

           
            using (SkeletonFrame frameData = myKinect.SkeletonStream.OpenNextFrame(1))
            {
                // if frame is null , then exit
                if (frameData == null)
                {
                    //System.Console.WriteLine("no data");
                    return;
                }
                skeletons = new Skeleton[frameData.SkeletonArrayLength];
                frameData.CopySkeletonDataTo(skeletons);
                foreach (Skeleton skeleton in skeletons)
                {
                    if (skeleton.TrackingState != SkeletonTrackingState.NotTracked)
                    {
                        for(int i = 0 ; i < 20 ; i++)
                        {
                            ColorImagePoint point1 = myKinect.MapSkeletonPointToColor(skeleton.Joints[(JointType)i].Position, ColorImageFormat.RgbResolution640x480Fps30);
                            skeletonArray[i * 2] = point1.X;
                            skeletonArray[i * 2 + 1] = point1.Y;
                        }
                        ColorImagePoint point2 = myKinect.MapSkeletonPointToColor(skeleton.Joints[(JointType)2].Position, ColorImageFormat.RgbResolution640x480Fps30);   
                    }
                }
            }
           
            base.Update(gameTime);
        }

在上述程式碼之中 ,首先先宣告了骨架陣列 ,

接著就是使用 SkeletonStream.OpenNextFrame(1) 來"手動"取得frame

簡單來說就是在做 polling 。

整個運作流程就是每Update()一次就去手動取得frame 一次來達到polling的效果。

接著就是儲存骨架資訊的部分了。 //此處不再補充說明 , WPF範例程式有說明。


接下來就是Draw的部分了。

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            // draw head to neck
            spriteBatch.DrawSingleLine(new Vector2(skeletonArray[6], skeletonArray[7]), new Vector2(skeletonArray[4], skeletonArray[5]), Color.DarkRed, 4);
            spriteBatch.DrawSingleLine(new Vector2(skeletonArray[4], skeletonArray[5]), new Vector2(skeletonArray[2], skeletonArray[3]), Color.DarkRed, 4);
            spriteBatch.DrawSingleLine(new Vector2(skeletonArray[2], skeletonArray[3]), new Vector2(skeletonArray[0], skeletonArray[1]), Color.DarkRed, 4);                  
            // draw nack to left arm
            spriteBatch.DrawSingleLine(new Vector2(skeletonArray[4], skeletonArray[5]), new Vector2(skeletonArray[8], skeletonArray[9]), Color.DarkRed, 4);
            spriteBatch.DrawSingleLine(new Vector2(skeletonArray[8], skeletonArray[9]), new Vector2(skeletonArray[10], skeletonArray[11]), Color.DarkRed, 4);
            spriteBatch.DrawSingleLine(new Vector2(skeletonArray[10], skeletonArray[11]), new Vector2(skeletonArray[12], skeletonArray[13]), Color.DarkRed, 4);
            spriteBatch.DrawSingleLine(new Vector2(skeletonArray[12], skeletonArray[13]), new Vector2(skeletonArray[14], skeletonArray[15]), Color.DarkRed, 4);
            // draw nack to right arm
            spriteBatch.DrawSingleLine(new Vector2(skeletonArray[4], skeletonArray[5]), new Vector2(skeletonArray[16], skeletonArray[17]), Color.DarkRed, 4);
            spriteBatch.DrawSingleLine(new Vector2(skeletonArray[16], skeletonArray[17]), new Vector2(skeletonArray[18], skeletonArray[19]), Color.DarkRed, 4);
            spriteBatch.DrawSingleLine(new Vector2(skeletonArray[18], skeletonArray[19]), new Vector2(skeletonArray[20], skeletonArray[21]), Color.DarkRed, 4);
            spriteBatch.DrawSingleLine(new Vector2(skeletonArray[20], skeletonArray[21]), new Vector2(skeletonArray[22], skeletonArray[23]), Color.DarkRed, 4);
            // draw center to left leg
            spriteBatch.DrawSingleLine(new Vector2(skeletonArray[0], skeletonArray[1]), new Vector2(skeletonArray[24], skeletonArray[25]), Color.DarkRed, 4);
            spriteBatch.DrawSingleLine(new Vector2(skeletonArray[24], skeletonArray[25]), new Vector2(skeletonArray[26], skeletonArray[27]), Color.DarkRed, 4);
            spriteBatch.DrawSingleLine(new Vector2(skeletonArray[26], skeletonArray[27]), new Vector2(skeletonArray[28], skeletonArray[29]), Color.DarkRed, 4);
            spriteBatch.DrawSingleLine(new Vector2(skeletonArray[28], skeletonArray[29]), new Vector2(skeletonArray[30], skeletonArray[31]), Color.DarkRed, 4);
            // draw center to right leg
            spriteBatch.DrawSingleLine(new Vector2(skeletonArray[0], skeletonArray[1]), new Vector2(skeletonArray[32], skeletonArray[33]), Color.DarkRed, 4);
            spriteBatch.DrawSingleLine(new Vector2(skeletonArray[32], skeletonArray[33]), new Vector2(skeletonArray[34], skeletonArray[35]), Color.DarkRed, 4);
            spriteBatch.DrawSingleLine(new Vector2(skeletonArray[34], skeletonArray[35]), new Vector2(skeletonArray[36], skeletonArray[37]), Color.DarkRed, 4);
            spriteBatch.DrawSingleLine(new Vector2(skeletonArray[36], skeletonArray[37]), new Vector2(skeletonArray[38], skeletonArray[39]), Color.DarkRed, 4);
            base.Draw(gameTime);
        }

其實在跟WPF程式碼部分沒有特別不同,我們這邊特別用了

第三方寫的class去使用畫筆的部分。

因為在XNA裡面畫基本圖形是比較困難複雜的,必須要考量到3D世界以及攝影機觀點。

去做向量繪圖。 因此簡單的2D繪圖也會相當的困難許多。(API 點我)

結果畫面 :



( 完整程式碼請點我 )

參考資料 :

Kinect 體感程式設計入門

XNA PC / Xbox 360 C# 遊戲程式設計

Tigran's Blog

沒有留言:

張貼留言