2013/12/07

C#.Net lock in a single skeleton in Microsoft Kinect SDK v 1.8

我們以這篇『C#.Net KINECT 結合影像與骨架』程式碼作為基底,以該基底延伸出新功能。
KinectSensor內有個SkeletonStreamSkeletonStream是用於接收骨架追蹤的串流,SkeletonStream類別又包含了AppChoosesSkeletons屬性,該屬性為布林值

AppChoosesSkeletons的說明為『Gets or sets a value indicating whether the application or the runtime chooses which skeletons to track.』
聰明的你一定看得出來用於鎖定骨架的第一步就設定為true

在透過ChooseSkeletons來鎖定指定的骨架,每個骨架都有它的編號,要注意ChooseSkeletons是多載的方法



  1. SkeletonStream.ChooseSkeletons ()
  2. SkeletonStream.ChooseSkeletons (Int32)
  3. SkeletonStream.ChooseSkeletons (Int32, Int32)

第一種方法僅偵測當前的骨架
第二種方法僅偵測指定的骨架ID,填入0也等同於第一種方法
第三種方法僅偵測兩個指定的骨架ID,這意謂著Kinect頂多鎖定兩個人的骨架而已

鎖定之前,要先確認AppChoosesSkeletons值要設定為True,否則會出錯


XAML:
<Window x:Class="WpfApplicationKinectTest2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" Unloaded="Window_Unloaded" Loaded="Window_Loaded">
    <Grid>
        <Image Name="Image"></Image>
    </Grid>
</Window>


Code:
using Microsoft.Kinect;
using System;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;

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

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

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

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

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

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

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

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

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

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

        /// <summary>
        /// 繪圖集合
        /// </summary>
        private DrawingGroup drawingGroup;

        /// <summary>
        /// 繪圖影像
        /// </summary>
        private DrawingImage imageSource;

        /// <summary>
        /// 感應器
        /// </summary>
        private KinectSensor sensor;

        /// <summary>
        /// Color data bytes
        /// </summary>
        private byte[] dataPixels = null;

        /// <summary>
        /// Track ID
        /// </summary>
        private int trackID = -1;

        public MainWindow()
        {
            InitializeComponent();
        }

        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.ColorStream.Enable();
                sensor.SkeletonFrameReady += SensorSkeletonFrameReady;
                sensor.ColorFrameReady += SensorColorFrameReady;
                sensor.Start();
            }
        }


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

        private void SensorColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
        {
            using (ColorImageFrame frame = e.OpenColorImageFrame())
            {
                if (frame != null)
                {
                    dataPixels = new byte[frame.PixelDataLength];
                    frame.CopyPixelDataTo(dataPixels);
                }
            }
        }

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

            using (SkeletonFrame skeletonFrame = e.OpenSkeletonFrame())
            {
                if (skeletonFrame == null)
                    return;

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

                //取得目前背景
                using (DrawingContext dc = this.drawingGroup.Open())
                {
                    BitmapSource source = BitmapSource.Create((int)RenderWidth, (int)RenderHeight, 96, 96, PixelFormats.Bgr32, null, dataPixels, 640 * 4);
                    dc.DrawImage(source, new Rect(0.0, 0.0, RenderWidth, RenderHeight));

                    //還有資料傳入
                    if (skeletons.Length != 0)
                    {
                        foreach (Skeleton skel in skeletons.Where(skl => skl.TrackingState != SkeletonTrackingState.NotTracked))
                        {

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

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

        private void LockUser(Skeleton skeleton)
        {
            if (skeleton.Joints[JointType.Head].Position.Y < skeleton.Joints[JointType.HandRight].Position.Y)
            {
                if (this.trackID == -1)
                {
                    this.trackID = skeleton.TrackingId;

                    if (this.sensor.SkeletonStream.AppChoosesSkeletons == false)
                    {
                        this.sensor.SkeletonStream.AppChoosesSkeletons = true;
                    }

                    this.sensor.SkeletonStream.ChooseSkeletons(trackID);

                    Console.WriteLine("鎖定中, ID:" + trackID);
                }
                else
                {
                    this.trackID = -1;

                    if (this.sensor.SkeletonStream.AppChoosesSkeletons == true)
                    {
                        this.sensor.SkeletonStream.ChooseSkeletons();
                        this.sensor.SkeletonStream.AppChoosesSkeletons = false;
                    }

                    Console.WriteLine("解鎖");
                }

                Thread.Sleep(700);
            }
        }


        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);
                }
            }
        }

        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/microsoft.kinect.skeletonstream_members.aspx

沒有留言:

張貼留言