Friday, September 11, 2015

การตรวจจับตำแหน่งศีรษะด้วย Kinect

นอกจาก Kinect จะสามารถอ่านภาพวิดีโอ มันยังสามารถตรวจจับข้อต่อต่างๆของมนุษย์ด้วยกล้อง infrared ซึ่งจะสามารถอ่านได้ถึง 20 ข้อต่อ (Joints) ได้แก่ หัว คอ ไหล่ ข้อศอก ข้อมือ มือ เอว สะโพก ท่อนขา หัวเข่า ข้อเท้า และ เท้า

ในตอนนี้เราจะมาลองตรวจจับตำแหน่งของศีรษะกันครับ

พิกัดในการอ่านตำแหน่งของ Kinect จะมีแกนดังนี้ ถ้าวาง Kinect ไว้หน้าเรา จุดกำเนิดจะอยู่ที่กล้อง แกน X จะมีทิศไปทางขวา แกน Y จะชี้ขึ้น และแกน Z จะพุ่งจากกล้องเข้าหาเรา

นั่นคือถ้าเราขยับไปทางขวามือของเรา เมื่ออ่านค่าข้อต่อ ค่า X ก็จะเพิ่ม หรือ ถ้าเรากระโดด ค่า Y ก็จะเพิ่ม และถ้าเราถอยห่างออกจากกล้อง ค่า Z ก็จะเพิ่ม

สำหรับการตรวจจับศีรษะ เราจะสร้าง interface ของโปรแกรมให้อ่านค่าได้ประมาณนี้

ในกรณีที่กล้องจับตำแหน่งศีรษะไม่ได้

ในกรณีที่กล้องจับตำแหน่งศีรษะได้

ซึ่งค่าพิกัดที่ได้คือตำแหน่งของศีรษะเมื่อเทียบกับตัวกล้อง ในหน่วยเมตร

โค้ดของส่วน UI
<Window x:Class="HeadDetection.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Head Detection" SizeToContent="Height" Width="800" Loaded="Window_Loaded">
    <StackPanel>
        <TextBlock Name="TextHead" Text="Head Position" FontSize="72" HorizontalAlignment="Center"/>
    </StackPanel>
</Window>
โค้ดที่เหลือก็จะประมาณนี้ครับ ลองสังเกตคำอธิบายที่หมายเหตุที่แทรกไว้ได้ครับ

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 HeadTracking
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        //variables
        KinectSensor myKinect;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            //Check if there is any connecting Kinect
            if (KinectSensor.KinectSensors.Count == 0)
            {
                MessageBox.Show("No Kinect detected!", "Error");
                //end this app
                Application.Current.Shutdown();
                return;
            }

            //Try to initialize Kinect
            try
            {
                //Get the first Kinect connected to this computer
                myKinect = KinectSensor.KinectSensors[0];
                //Enable the skeleton stream
                myKinect.SkeletonStream.Enable();
                //Start the sensor
                myKinect.Start();
            }
            catch
            {
                MessageBox.Show("Initialize Kinect failed!", "Error");
                //end this app
                Application.Current.Shutdown();
            }

            //Skeleton event handler
            myKinect.SkeletonFrameReady += new EventHandler<SkeletonFrameReadyEventArgs>(myKinect_SkeletonFrameReady);
        }

        private void myKinect_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
        {
            //array of skeleton data
            Skeleton[] skeletons = null;

            using (SkeletonFrame frame = e.OpenSkeletonFrame())
            {
                string message = "No Skeleton Data";
                //if there is skeleton data
                if (frame != null)
                {
                    //create skeleton array
                    skeletons = new Skeleton[frame.SkeletonArrayLength];
                    //copy skeleton data to array
                    frame.CopySkeletonDataTo(skeletons);
                }

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

                //for each skeleton data in the skeleton array, there will be upto 6 possible skeletons (men)
                //only 2 skeletons are fully tracked, other 4 are for positions only (no joint info)
                foreach(Skeleton skeleton in skeletons)
                {
                    //focus only the trackable skeletons
                    if (skeleton.TrackingState == SkeletonTrackingState.Tracked)
                    {
                        //get the head joint
                        Joint headJoint = skeleton.Joints[JointType.Head];
                        //get the head joint's position
                        SkeletonPoint headPosition = headJoint.Position;

                        //show the head position with one decimal or 0.1 meter
                        message = string.Format("Head: X:{0:0.0} Y:{1:0.0} Z:{2:0.0}",
                            headPosition.X,
                            headPosition.Y,
                            headPosition.Z);
                    }
                }

                //show message on textblock
                TextHead.Text = message;
            }
        }
    }
}

2 comments:

  1. พี่ครับ เราสามารถหาพิกัดของตำแหน่งใดได้ไหมครับถ้าไม่ใช่พวกข้อต่ออ่ะครับ

    ReplyDelete
  2. เท่าที่ทราบค่าที่ตัวกล้องอ่านได้เลยคือข้อต่อครับ ถ้าเป็นส่วนอื่นของมนุษย์ หรือ สิ่งของ อาจจะต้องใช้ภาพวิดีโอธรรมดา (หรือ/และภาพความลึก) มาทำ image processing เพื่อทำการวิเคราะห์ตำแหน่งอีกทีครับ

    ReplyDelete