ที่มาของรูป Kinect for Windows | Human Interface Guidelines v1.8
แต่ละคนก็จะมี TrackingID เป็นตัวเลขสุ่มจำนวนเต็มที่มากกว่า 0 และค่านี้จะคงที่ตลอดถ้าคนนั้นๆยังถูกตรวจจับได้ (อยู่ในเฟรมและไม่มีอะไรบัง) แต่ถ้าเกิดการตรวจจับไม่ได้ เช่น ออกไปจากเฟรมแล้วกลับเข้ามาใหม่ Kinect ก็จะสุ่ม TrackingID ให้คนนั้นใหม่อีกที
อย่างไรก็ตาม ในบางครั้งเราต้องการตรวจจับแค่คนเดียว เช่น ในการใช้ซอฟต์แวร์หรือเล่นเกมคนเดียว ก็สามารถทำได้ครับ โดยใช้คำสั่ง
1. KinectSensor.SkeletonStream.AppChoosesSkeletons = true;
พร้อมๆกับ
2. KinectSensor.SkeletonStream.ChooseSkeletons(trackingID);
คราวนี้ Kinect ก็จะ track คนที่มี trackingID นี้ตลอด ไม่สนคนอื่นแม้ว่าจะอยู่ในเฟรมด้วย
สำหรับตัวอย่างคราวนี้ เราจะตั้งเงื่อนไขว่า
- จะ track เฉพาะคนแรกที่กล้องเจอเท่านั้น (ตอนแรกควรจะมีคนเดียวอยู่ในเฟรม)
- ต่อมาแม้ว่าจะมีคนเข้ามาในเฟรมเพิ่มก็จะไม่ track
- คนที่ถูก track จะไม่ออกจากเฟรมเสมอ และต้องไม่ถูกบดบังจนไม่สามารถ track ได้
หมายเหตุ จากการทดลองพบว่า
- Kinect จะส่ง skeleton มาหกชุดเสมอ (เป็นอาร์เรย์) คนที่ถูก track จะอยู่ในตำแหน่งสุ่ม 0-5 ของอาร์เรย์ ตำแหน่งในอาร์เรย์ไม่เกี่ยวอะไรเลยกับ trackingID
- จะมี skeleton เดียวเท่านั้นที่เราสั่งให้ track ที่มีค่า TrackingState เป็น Tracked ส่วน skeleton อื่นจะไม่ใช่สถานะนี้
อ้างอิงเนื้อหาจาก https://msdn.microsoft.com/en-us/library/jj131025.aspx
โค้ด UI
<Window x:Class="SingleDectection.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" SizeToContent="WidthAndHeight" Loaded="Window_Loaded" Closing="Window_Closing"> <StackPanel> <Canvas Height="480" Width="640"> <Image Name="kinectVideo" Height="480" Width="640"></Image> <Canvas Name="skeletonCanvas" Height="480" Width="640"></Canvas> </Canvas> <TextBlock Name="textID" Text="Tracking ID" HorizontalAlignment="Center" FontSize="22" /> </StackPanel> </Window>
โค้ด C#
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using Microsoft.Kinect; namespace SingleDectection { public partial class MainWindow : Window { KinectSensor myKinect; public MainWindow() { InitializeComponent(); } //======== Window Loaded and Closing ======== private void Window_Loaded(object sender, RoutedEventArgs e) { startKinect(); } private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { stopKinect(); } //=============== Start and Stop Kinect ========================= void startKinect() { // Check to see if a Kinect is available if (KinectSensor.KinectSensors.Count == 0) { MessageBox.Show("No Kinects detected", "Camera Viewer"); Application.Current.Shutdown(); return; } // Start the Kinect and enable both video and skeleton streams try { // Get the first Kinect on the computer myKinect = KinectSensor.KinectSensors[0]; myKinect.ColorStream.Enable(); myKinect.SkeletonStream.Enable(); myKinect.Start(); } catch { MessageBox.Show("Kinect initialise failed", "Camera Viewer"); Application.Current.Shutdown(); return; } // connect a handler to the event that fires when new frames are available myKinect.ColorFrameReady += new EventHandler<ColorImageFrameReadyEventArgs>(myKinect_ColorFrameReady); myKinect.SkeletonFrameReady += new EventHandler<SkeletonFrameReadyEventArgs>(myKinect_SkeletonFrameReady); } void stopKinect() { if (myKinect != null) myKinect.Stop(); } //================================== video ============================= byte[] colorData = null; WriteableBitmap colorImageBitmap = null; private void myKinect_ColorFrameReady(object sender, ColorImageFrameReadyEventArgs e) { using (ColorImageFrame colorFrame = e.OpenColorImageFrame()) { if (colorFrame == null) return; if (colorData == null) colorData = new byte[colorFrame.PixelDataLength]; colorFrame.CopyPixelDataTo(colorData); if (colorImageBitmap == null) { colorImageBitmap = new WriteableBitmap( colorFrame.Width, colorFrame.Height, 96, // DpiX 96, // DpiY PixelFormats.Bgr32, null); } colorImageBitmap.WritePixels( new Int32Rect(0, 0, colorFrame.Width, colorFrame.Height), colorData, // video data colorFrame.Width * colorFrame.BytesPerPixel, // stride, 0 // offset into the array - start at 0 ); kinectVideo.Source = colorImageBitmap; } } //================================== skeleton =========================== //drawing color Brush brushRed = new SolidColorBrush(Colors.Red); Brush brushGreen = new SolidColorBrush(Colors.Green); //method to draw a line between joints void drawLine(Joint j1, Joint j2) { //if any joint is not tracked if (j1.TrackingState == JointTrackingState.NotTracked || j2.TrackingState == JointTrackingState.NotTracked) { //do not draw return; } //line properties Line bone = new Line(); //line thickness bone.StrokeThickness = 5; //if any joint is inferred if (j1.TrackingState == JointTrackingState.Inferred || j2.TrackingState == JointTrackingState.Inferred) { //set color to red bone.Stroke = brushRed; } else if (j1.TrackingState == JointTrackingState.Tracked && j2.TrackingState == JointTrackingState.Tracked) { //both joints are tracked, set color to green bone.Stroke = brushGreen; } //map joint position to display location //starting point ColorImagePoint j1p = myKinect.CoordinateMapper.MapSkeletonPointToColorPoint(j1.Position, ColorImageFormat.RgbResolution640x480Fps30); bone.X1 = j1p.X; bone.Y1 = j1p.Y; //ending point ColorImagePoint j2p = myKinect.CoordinateMapper.MapSkeletonPointToColorPoint(j2.Position, ColorImageFormat.RgbResolution640x480Fps30); bone.X2 = j2p.X; bone.Y2 = j2p.Y; //add line to canvas skeletonCanvas.Children.Add(bone); } //Track a skeleton by ID int personID = 0; private void myKinect_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e) { // Remove the old skeleton skeletonCanvas.Children.Clear(); Skeleton[] skeletons = null; using (SkeletonFrame frame = e.OpenSkeletonFrame()) { if (frame != null) { skeletons = new Skeleton[frame.SkeletonArrayLength]; frame.CopySkeletonDataTo(skeletons); } } if (skeletons == null) return; //Objective: to track only the first person //Assumption: First person must be alone in the screen and be tracked at the beginning to get his/her ID //Assumption: That first person must not leave the screen or be obscured, otherwise Kinect will dispose his/her ID //textID.Text = "Person ID = " + personID; //if not get person ID yet if (personID == 0) { //textID.Text = "Skeleton size = " + skeletons.Length; //find ID of the first tracked skeleton foreach (Skeleton skeleton in skeletons) { if (skeleton.TrackingState == SkeletonTrackingState.Tracked) { personID = skeleton.TrackingId; //allow track on specific skeleton (not all) myKinect.SkeletonStream.AppChoosesSkeletons = true; //track only this skeleton myKinect.SkeletonStream.ChooseSkeletons(personID); //neglect other skeletons break; } } } else { //draw the tracked skeleton only //the skeletonFrameReady event will always return six skeletons, though we track only one //the tracked skeleton will be randomly in the skeleton array, no matter what its tracking ID is //while tracked, the tracking ID is constant //Draw only the tracked skeleton foreach (Skeleton skeleton in skeletons) { if (skeleton.TrackingState == SkeletonTrackingState.Tracked) { //Debug info textID.Text = "Tracking ID, Current=" + skeleton.TrackingId + ": Before=" + personID; //must be equal to personID //draw head to neck drawLine(skeleton.Joints[JointType.Head], skeleton.Joints[JointType.ShoulderCenter]); //draw neck to spine drawLine(skeleton.Joints[JointType.ShoulderCenter], skeleton.Joints[JointType.Spine]); //draw spine to hip center drawLine(skeleton.Joints[JointType.Spine], skeleton.Joints[JointType.HipCenter]); //draw left arm drawLine(skeleton.Joints[JointType.ShoulderCenter], skeleton.Joints[JointType.ShoulderLeft]); drawLine(skeleton.Joints[JointType.ShoulderLeft], skeleton.Joints[JointType.ElbowLeft]); drawLine(skeleton.Joints[JointType.ElbowLeft], skeleton.Joints[JointType.WristLeft]); drawLine(skeleton.Joints[JointType.WristLeft], skeleton.Joints[JointType.HandLeft]); //draw right arm drawLine(skeleton.Joints[JointType.ShoulderCenter], skeleton.Joints[JointType.ShoulderRight]); drawLine(skeleton.Joints[JointType.ShoulderRight], skeleton.Joints[JointType.ElbowRight]); drawLine(skeleton.Joints[JointType.ElbowRight], skeleton.Joints[JointType.WristRight]); drawLine(skeleton.Joints[JointType.WristRight], skeleton.Joints[JointType.HandRight]); //draw left leg drawLine(skeleton.Joints[JointType.HipCenter], skeleton.Joints[JointType.HipLeft]); drawLine(skeleton.Joints[JointType.HipLeft], skeleton.Joints[JointType.KneeLeft]); drawLine(skeleton.Joints[JointType.KneeLeft], skeleton.Joints[JointType.AnkleLeft]); drawLine(skeleton.Joints[JointType.AnkleLeft], skeleton.Joints[JointType.FootLeft]); //draw right leg drawLine(skeleton.Joints[JointType.HipCenter], skeleton.Joints[JointType.HipRight]); drawLine(skeleton.Joints[JointType.HipRight], skeleton.Joints[JointType.KneeRight]); drawLine(skeleton.Joints[JointType.KneeRight], skeleton.Joints[JointType.AnkleRight]); drawLine(skeleton.Joints[JointType.AnkleRight], skeleton.Joints[JointType.FootRight]); //neglect other not tracked skeletons break; } } }//end if-else get personID }//end skeleton frame ready method }//end class }