2013/05/12

C#.Net 分析KINECT追蹤骨架

目前可以追蹤的骨架如下圖,約20個

KINECT的骨架偵測點主要是以SPINE這個點為主,偵測到這個點才會開始判斷其他點的位置


距離太近就有可能會出現判斷方向相反,在測試時就有可能發生左腳被偵測到右腳去了,右腳跑來左腳的位置…



載入程式就觸發事件,將畫布重置,嘗試去取得KINECT的感應器的骨架資料
取得到骨架資料就由SensorSkeletonFrameReady開始來取資料,使用RenderClippedEdges去得知有沒有超過邊界,有就將紅色畫在邊框;再來使用DrawBone將A關節點到B關節點畫成直線,並將關節畫成橢圓形



using System.Windows;
using System.Windows.Media;
using Microsoft.Kinect;

namespace WpfApplication3
{
    /// <summary>
    /// MainWindow.xaml 的互動邏輯
    /// </summary>
    public partial class MainWindow : Window
    {
        //影像寬度
        private const float RenderWidth = 640.0f;

        //影像高度
        private const float RenderHeight = 480.0f;

        //關節厚度
        private const double JointThickness = 3;

        //身體中間厚度
        private const double BodyCenterThickness = 10;

        //整體區塊厚度
        private const double ClipBoundsThickness = 10;

        //身體刷子
        private readonly Brush centerPointBrush = Brushes.Blue;

        //追蹤關節筆刷
        private readonly Brush trackedJointBrush = new SolidColorBrush(Color.FromArgb(255, 68, 192, 68));

        //位置的筆刷
        private readonly Brush inferredJointBrush = Brushes.Yellow;

        //追蹤的筆
        private readonly Pen trackedBonePen = new Pen(Brushes.Green, 6);

        //位置的筆
        private readonly Pen inferredBonePen = new Pen(Brushes.Gray, 1);

        //繪圖集合
        private DrawingGroup drawingGroup;

        //繪圖影像
        private DrawingImage imageSource;

        //感應器
        private KinectSensor sensor;

        public MainWindow()
        {
            InitializeComponent();
        }

        #region 表單載入

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            //Refresh
            drawingGroup = new DrawingGroup();
            imageSource = new DrawingImage(drawingGroup);
            Image.Source = this.imageSource;
            sensor = KinectSensor.KinectSensors[0];

            if (sensor != null)
            {
                sensor.SkeletonStream.Enable();
                sensor.SkeletonFrameReady += SensorSkeletonFrameReady;
                sensor.Start();
            }
        }

        #endregion 表單載入

        #region 結束程式

        private void Window_Unloaded(object sender, RoutedEventArgs e)
        {
            sensor.Stop();
        }

        #endregion 結束程式

        #region 處理超過邊界的事件

        private void RenderClippedEdges(Skeleton skeleton, DrawingContext drawingContext)
        {
            //超過最底,則會紅線
            if (skeleton.ClippedEdges.HasFlag(FrameEdges.Bottom))
            {
                drawingContext.DrawRectangle(
                    Brushes.Red,
                    null,
                    new Rect(0, RenderHeight - ClipBoundsThickness, RenderWidth, ClipBoundsThickness));
            }

            //超過最頂端,則顯示紅線
            if (skeleton.ClippedEdges.HasFlag(FrameEdges.Top))
            {
                drawingContext.DrawRectangle(
                    Brushes.Red,
                    null,
                    new Rect(0, 0, RenderWidth, ClipBoundsThickness));
            }

            //超過最左邊,則顯示紅線
            if (skeleton.ClippedEdges.HasFlag(FrameEdges.Left))
            {
                drawingContext.DrawRectangle(
                    Brushes.Red,
                    null,
                    new Rect(0, 0, ClipBoundsThickness, RenderHeight));
            }

            //超過最右邊,則顯示紅線
            if (skeleton.ClippedEdges.HasFlag(FrameEdges.Right))
            {
                drawingContext.DrawRectangle(
                    Brushes.Red,
                    null,
                    new Rect(RenderWidth - ClipBoundsThickness, 0, ClipBoundsThickness, RenderHeight));
            }
        }

        #endregion 處理超過邊界的事件

        #region 感應器接收資料

        private void SensorSkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
        {
            Skeleton[] skeletons = new Skeleton[0];

            using (SkeletonFrame skeletonFrame = e.OpenSkeletonFrame())
            {
                if (skeletonFrame != null)
                {
                    skeletons = new Skeleton[skeletonFrame.SkeletonArrayLength];
                    skeletonFrame.CopySkeletonDataTo(skeletons);
                }
            }

            //取得目前背景
            using (DrawingContext dc = this.drawingGroup.Open())
            {
                //將背景重畫
                dc.DrawRectangle(Brushes.Black, null, new Rect(0.0, 0.0, RenderWidth, RenderHeight));

                //還有資料傳入
                if (skeletons.Length != 0)
                {
                    foreach (Skeleton skel in skeletons)
                    {
                        RenderClippedEdges(skel, dc);

                        if (skel.TrackingState == SkeletonTrackingState.Tracked)
                        {
                            DrawBonesAndJoints(skel, dc);
                        }
                        else if (skel.TrackingState == SkeletonTrackingState.PositionOnly)
                        {
                            dc.DrawEllipse(
                            centerPointBrush,
                            null,
                            this.SkeletonPointToScreen(skel.Position),
                            BodyCenterThickness,
                            BodyCenterThickness);
                        }
                    }
                }

                // prevent drawing outside of our render area
                drawingGroup.ClipGeometry = new RectangleGeometry(new Rect(0.0, 0.0, RenderWidth, RenderHeight));
            }
        }

        #endregion 感應器接收資料

        #region 畫出骨頭與關節

        private void DrawBonesAndJoints(Skeleton skeleton, DrawingContext drawingContext)
        {
            // Render Torso
            DrawBone(skeleton, drawingContext, JointType.Head, JointType.ShoulderCenter);
            DrawBone(skeleton, drawingContext, JointType.ShoulderCenter, JointType.ShoulderLeft);
            DrawBone(skeleton, drawingContext, JointType.ShoulderCenter, JointType.ShoulderRight);
            DrawBone(skeleton, drawingContext, JointType.ShoulderCenter, JointType.Spine);
            DrawBone(skeleton, drawingContext, JointType.Spine, JointType.HipCenter);
            DrawBone(skeleton, drawingContext, JointType.HipCenter, JointType.HipLeft);
            DrawBone(skeleton, drawingContext, JointType.HipCenter, JointType.HipRight);

            // Left Arm
            DrawBone(skeleton, drawingContext, JointType.ShoulderLeft, JointType.ElbowLeft);
            DrawBone(skeleton, drawingContext, JointType.ElbowLeft, JointType.WristLeft);
            DrawBone(skeleton, drawingContext, JointType.WristLeft, JointType.HandLeft);

            // Right Arm
            DrawBone(skeleton, drawingContext, JointType.ShoulderRight, JointType.ElbowRight);
            DrawBone(skeleton, drawingContext, JointType.ElbowRight, JointType.WristRight);
            DrawBone(skeleton, drawingContext, JointType.WristRight, JointType.HandRight);

            // Left Leg
            DrawBone(skeleton, drawingContext, JointType.HipLeft, JointType.KneeLeft);
            DrawBone(skeleton, drawingContext, JointType.KneeLeft, JointType.AnkleLeft);
            DrawBone(skeleton, drawingContext, JointType.AnkleLeft, JointType.FootLeft);

            // Right Leg
            DrawBone(skeleton, drawingContext, JointType.HipRight, JointType.KneeRight);
            DrawBone(skeleton, drawingContext, JointType.KneeRight, JointType.AnkleRight);
            DrawBone(skeleton, drawingContext, JointType.AnkleRight, JointType.FootRight);

            // Render Joints
            foreach (Joint joint in skeleton.Joints)
            {
                Brush drawBrush = null;

                if (joint.TrackingState == JointTrackingState.Tracked)  //追蹤中
                {
                    drawBrush = this.trackedJointBrush;
                }
                else if (joint.TrackingState == JointTrackingState.Inferred)    //大概位置
                {
                    drawBrush = this.inferredJointBrush;
                }

                //如果畫筆有被設置,畫出關節為橢圓形
                if (drawBrush != null)
                {
                    drawingContext.DrawEllipse(drawBrush, null, SkeletonPointToScreen(joint.Position), JointThickness, JointThickness);
                }
            }
        }

        #endregion 畫出骨頭與關節

        private Point SkeletonPointToScreen(SkeletonPoint skelpoint)
        {
            DepthImagePoint depthPoint = this.sensor.CoordinateMapper.MapSkeletonPointToDepthPoint(skelpoint, DepthImageFormat.Resolution640x480Fps30);
            return new Point(depthPoint.X, depthPoint.Y);
        }

        private void DrawBone(Skeleton skeleton, DrawingContext drawingContext, JointType jointType0, JointType jointType1)
        {
            Joint joint0 = skeleton.Joints[jointType0];
            Joint joint1 = skeleton.Joints[jointType1];

            //找不到任何追蹤的點就不動作
            if (joint0.TrackingState == JointTrackingState.NotTracked ||
                joint1.TrackingState == JointTrackingState.NotTracked)
            {
                return;
            }

            //找不到任何位置就不動作
            if (joint0.TrackingState == JointTrackingState.Inferred &&
                joint1.TrackingState == JointTrackingState.Inferred)
            {
                return;
            }

            //兩者都有就畫出位置
            Pen drawPen = this.inferredBonePen;
            if (joint0.TrackingState == JointTrackingState.Tracked && joint1.TrackingState == JointTrackingState.Tracked)
            {
                drawPen = trackedBonePen;
            }

            //畫出關節與關節的線
            drawingContext.DrawLine(drawPen, SkeletonPointToScreen(joint0.Position), SkeletonPointToScreen(joint1.Position));
        }
    }
}




參考資料:
http://msdn.microsoft.com/en-us/library/hh438998.aspx
http://msdn.microsoft.com/en-us/library/hh973074.aspx
http://msdn.microsoft.com/en-us/library/jj131025.aspx
http://msdn.microsoft.com/en-us/library/hh973077.aspx
http://msdn.microsoft.com/en-us/library/hh855353.aspx
http://msdn.microsoft.com/en-us/library/hh973073.aspx
http://msdn.microsoft.com/en-us/library/jj131024.aspx
http://msdn.microsoft.com/en-us/library/jj131429.aspx
http://msdn.microsoft.com/en-us/library/dn188677.aspx
http://stackoverflow.com/questions/2060478/c-sharp-redaction-drawing-on-image

參考圖片:
http://i.msdn.microsoft.com/dynimg/IC534688.png
http://i.msdn.microsoft.com/dynimg/IC584841.png