Kinect產生的景深資料作用有限,要利用Kinect建立真正意義上互動,有趣和難忘的應用,還需要除了深度資料之外的其他資料。這就是骨骼追蹤技術的初衷,骨骼追蹤技術通過處理景深資料來建立人體各個關節的座標,骨骼追蹤能夠確定人體的各個部分,如那部分是手,頭部,以及身體。骨骼追蹤產生X,Y,Z資料來確定這些骨骼點。在上文中,我們討論了景深影像處理的一些技術。骨骼追蹤系統採用的景深影像處理技術使用更複雜的演算法如矩陣變換,機器學習及其他方式來確定骨骼點的座標。
本文首先用一個例子展示骨骼追蹤系統涉及的主要對象,然後在此基礎上詳細討論骨骼追蹤中所涉及的物件模型。
1. 擷取骨骼資料
本節將會建立一個應用來將擷取到的骨骼資料繪製到UI介面上來。在開始編碼前,首先來看看一些基本的對象以及如何從這些對象中如何擷取骨骼資料。在進行資料處理之前瞭解資料的格式也很有必要。這個例子很簡單明了,只需要骨骼資料對象然後將擷取到的資料繪製出來。
彩色影像資料,景深資料分別來自ColorImageSteam和DepthImageStream,同樣地,骨骼資料來自SkeletonStream。訪問骨骼資料和訪問彩色影像資料、景深資料一樣,也有事件模式和 “拉”模式兩種方式。在本例中我們採用基於事件的方式,因為這種方式簡單,代碼量少,並且是一種很普通基本的方法。KinectSensor對象有一個名為SkeletonFrameReady事件。當SkeletonStream中有新的骨骼資料產生時就會觸發該事件。通過AllFramesReady事件也可以擷取骨骼資料。在下一節中,我們將會詳細討論骨骼追蹤物件模型,現在我們只展示如何從SkeletonStream流中擷取骨骼資料。SkeletonStream產生的每一幀資料都是一個骨骼對象集合。每一個骨骼對象包含有描述骨骼位置以及骨骼關節的資料。每一個關節有一個唯一標示符如頭(head)、肩(shoulder)、肘(dlbow)等資訊和3D向量資料。
現在來寫代碼。首先建立一個新的wpf工程檔案,添加Microsoft.Kinect.dll。添加基本尋找和初始化感應器的代碼,這些代碼參考之前的文章。在開始啟動感應器之前,初始化SkeletonStream資料流,並註冊KinectSensor對象的SkeletonFrameReady事件,這個例子沒有使用彩色攝像機和紅外攝像機產生的資料,所以不需要初始化這些資料流。UI介面採用預設的,將Grid的名稱改為LayoutRoot,之後就再Grid裡面繪製。代碼如下:
<Window x:Class="KinectSkeletonTracking.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"> <Grid x:Name="LayoutRoot" Background="White"> </Grid></Window>
後台邏輯代碼如下:
private KinectSensor kinectDevice;private readonly Brush[] skeletonBrushes;//繪圖筆刷private Skeleton[] frameSkeletons; public MainWindow(){ InitializeComponent(); skeletonBrushes = new Brush[] { Brushes.Black, Brushes.Crimson, Brushes.Indigo, Brushes.DodgerBlue, Brushes.Purple, Brushes.Pink }; KinectSensor.KinectSensors.StatusChanged += KinectSensors_StatusChanged; this.KinectDevice = KinectSensor.KinectSensors.FirstOrDefault(x => x.Status == KinectStatus.Connected); } public KinectSensor KinectDevice{ get { return this.kinectDevice; } set { if (this.kinectDevice != value) { //Uninitialize if (this.kinectDevice != null) { this.kinectDevice.Stop(); this.kinectDevice.SkeletonFrameReady -= KinectDevice_SkeletonFrameReady; this.kinectDevice.SkeletonStream.Disable(); this.frameSkeletons = null; } this.kinectDevice = value; //Initialize if (this.kinectDevice != null) { if (this.kinectDevice.Status == KinectStatus.Connected) { this.kinectDevice.SkeletonStream.Enable(); this.frameSkeletons = new Skeleton[this.kinectDevice.SkeletonStream.FrameSkeletonArrayLength]; this.kinectDevice.SkeletonFrameReady += KinectDevice_SkeletonFrameReady; this.kinectDevice.Start(); } } } }} private void KinectSensors_StatusChanged(object sender, StatusChangedEventArgs e){ switch (e.Status) { case KinectStatus.Initializing: case KinectStatus.Connected: case KinectStatus.NotPowered: case KinectStatus.NotReady: case KinectStatus.DeviceNotGenuine: this.KinectDevice = e.Sensor; break; case KinectStatus.Disconnected: //TODO: Give the user feedback to plug-in a Kinect device. this.KinectDevice = null; break; default: //TODO: Show an error state break; }}