Friday, October 23, 2015

การวาด Skeleton จาก Kinect ตอนที่ 1: วาดแนวสันหลัง

เมื่อเราสามารถตรวจจับตำแหน่งของ Skeleton และข้อต่อได้แล้ว เราสามารถแสดงผลค่าดังกล่าวออกมาเป็นภาพได้โดยการวาดเส้น

สำหรับการวาดเส้นตรงก็ต้องอาศัยจุดสองจุด คือจุดเริ่มต้นกับจุดสิ้นสุด ซึ่งในที่นี้ เราจะลองลากเส้นเชื่อมระหว่างหัวและสะโพก ซึ่งก็คือแนวกระดูกสันหลังนั่นเอง

ความยากอยู่ตรงที่ ตำแหน่งของข้อต่อต่างๆที่ได้จาก Kinect จะวัดเทียบกับแกนของ Kinect เอง (จุดกำเนิดอยู่ที่ตัว Kinect) และตำแหน่งของข้อต่อก็จะอยู่ในหน่วยของเมตร แต่ การแสดงผลบนหน้าจอจะเป็นพิกัดของหน้าต่าง (จุดกำเนิดอยู่มุมบนซ้าย) และขนาดของหน้าจอก็มักจะเท่ากับขนาดของภาพวีดิโอ เช่น 640x480

ดังนั้น จะต้องมีกระบวนการทำ Coordinate Mapping ซึ่งจริงๆแล้วก็ทำได้ไม่ยาก (อาศัยความรู้เรื่อง Computer Graphics) อย่างไรก็ตาม ตัว Kinect SDK เองก็มีฟังก์ชันสำเร็จรูปเพื่อทำงานนี้มาให้เช่นกัน คือ KinectSensor.CoordinateMapper.MapSkeletonPointToColorPoint() หรือ ถ้าเป็นคำสั่งเก่าจะใช้ KinectSensor.MapSkeletonPointToColor()

ตัวอย่างเช่น ถ้าเราต้องการทำ Mapping จากพิกัดของศีรษะใน Kinect ไปยังหน้าต่างขนาด 640x480 พิกเซล ก็จะใช้คำสั่ง
ColorImagePoint headPoint = myKinect.CoordinateMapper.MapSkeletonPointToColorPoint(headJoint.Position, ColorImageFormat.RgbResolution640x480Fps30);

ลองมาดูตัวอย่างกันครับ

ผลลัพธ์ที่ต้องการ (เส้นแนวกระดูกสันหลังที่เชื่อมต่อระหว่างหัวกับสะโพก)

โค้ด UI
<Window x:Class="SkeletonDrawing.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">
    <Canvas Name="skeletonCanvas" HorizontalAlignment="Center" Height="480" Width="640"/>
</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 SkeletonDrawing
{
    public partial class MainWindow : Window
    {
        KinectSensor myKinect;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // Check to see if a Kinect is available
            if (KinectSensor.KinectSensors.Count == 0)
            {
                MessageBox.Show("No Kinects detected", "Camera Viewer");
                Application.Current.Shutdown();
                return;
            }

            // Get the first Kinect on the computer
            myKinect = KinectSensor.KinectSensors[0];

            // Start the Kinect running and select the depth camera
            try
            {
                myKinect.SkeletonStream.Enable();
                myKinect.Start();
            }
            catch
            {
                MessageBox.Show("Kinect initialise failed", "Camera Viewer");
                Application.Current.Shutdown();
            }

            // connect a handler to the event that fires when new frames are available

            myKinect.SkeletonFrameReady += new EventHandler<SkeletonFrameReadyEventArgs>(myKinect_SkeletonFrameReady);
        }

        private void myKinect_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
        {
            // Remove the old skeleton (if exists)
            skeletonCanvas.Children.Clear();

            Skeleton[] skeletons = null;

            //copy skeleton data to skeleton array
            using (SkeletonFrame frame = e.OpenSkeletonFrame())
            {
                if (frame != null)
                {
                    skeletons = new Skeleton[frame.SkeletonArrayLength];
                    frame.CopySkeletonDataTo(skeletons);
                }
            }

            //if no skeleton data
            if (skeletons == null) 
                return;

            //for each skeleton
            foreach (Skeleton skeleton in skeletons)
            {
                //if skeleton is tracked
                if (skeleton.TrackingState == SkeletonTrackingState.Tracked)
                {
                    //Head joint
                    Joint headJoint = skeleton.Joints[JointType.Head];
                    //Hip joint
                    Joint hipJoint = skeleton.Joints[JointType.HipCenter];

                    //Map head position to canvas
                    //ColorImagePoint headPoint = myKinect.MapSkeletonPointToColor(headJoint.Position, ColorImageFormat.RgbResolution640x480Fps30);
                    ColorImagePoint headPoint = myKinect.CoordinateMapper.MapSkeletonPointToColorPoint(headJoint.Position, ColorImageFormat.RgbResolution640x480Fps30);
                    //Map hip position to canvas
                    //ColorImagePoint hipPoint = myKinect.MapSkeletonPointToColor(hipCenter.Position, ColorImageFormat.RgbResolution640x480Fps30);
                    ColorImagePoint hipPoint = myKinect.CoordinateMapper.MapSkeletonPointToColorPoint(hipJoint.Position, ColorImageFormat.RgbResolution640x480Fps30);

                    //Prepare a line
                    Line backBone = new Line();
                    backBone.Stroke = new SolidColorBrush(Colors.Red);
                    backBone.StrokeThickness = 5;

                    //Draw a line from head to hip
                    backBone.X1 = headPoint.X;
                    backBone.Y1 = headPoint.Y;
                    backBone.X2 = hipPoint.X;
                    backBone.Y2 = hipPoint.Y;

                    //Add this line to canvas
                    skeletonCanvas.Children.Add(backBone);
                }
            }//end for
        }//end method
    }//end class
}

No comments:

Post a Comment