Welcome

首页 / 软件开发 / .NET编程技术 / Kinect for Windows SDK开发入门(八)骨骼追踪进阶 上

Kinect for Windows SDK开发入门(八)骨骼追踪进阶 上2014-03-12前7篇文件我们介绍了Kinect SDK中各种传感器的各种基本知识,我们用实验的方式演示了这些基本对象和方法的如何使用,这些都是Kinect开发最基本的知识。了解了这些基本知识后,就可以开发出一个基于Kinect的简单程序了。但是这些离开发出一个好的基于Kinect的应用程序还有一段距离。后面的文章中,将会结合Kinect SDK介绍WPF以及其它第三方工具,类库来建立一个以Kinect为驱动的有较好用户体验的程序。我们将利用之前讲到的知识来进行下面一些比较复杂的话题。

Kinect传感器核心只是发射红外线,并探测红外光反射,从而可以计算出视场范围内每一个像素的深度值。从深度数据中最先提取出来的是物体主体和形状,以及每一个像素点的游戏者索引信息。然后用这些形状信息来匹配人体的各个部分,最后计算匹配出来的各个关节在人体中的位置。这就是我们之前介绍过的骨骼追踪。

红外影像和深度数据对于Kinect系统来说很重要,它是Kinect的核心,在Kinect系统中其重要性仅次于骨骼追踪。事实上,这些数据相当于一个输入终端。随着Kinect或者其他深度摄像机的流行和普及。开发者可以不用关注原始的深度影像数据,他们变得不重要或者只是作为获取其他数据的一个基础数据而已。我们现在就处在这个阶段,Kinect SDK并没有提供给开发者访问原始红外影像数据流的接口,但是其它第三方的SDK可以这么做。可能大多数开发者不会使用原始的深度数据,用到的只是Kinect处理好了的骨骼数据。但是,一旦姿势和手势识别整合到Kinect SDK并成为其一部分时,可能开发者甚至不用接触到骨骼数据了。

希望能够早日实现这种集成,因为它代表这Kinect作为一种技术的走向成熟。本篇文章和下篇文章仍将讨论骨骼追踪,但是采用不同的方法来处理骨骼数据。我们将Kinect作为一个如同鼠标,键盘或者触摸屏那样的一个最基本的输入设备。微软当初推出Kinect for Xbox的口号是“你就是控制器”,从技术方面讲,就是“你就是输入设备”。通过骨骼数据,应用程序可以做鼠标或者触摸屏可以做的事情,所不同的是深度影像数据使得用户和应用程序可以实现以前从没有过的交互方法。下面来看看Kinect控制并与用户界面进行交互的机制吧。

1. 用户交互

运行在电脑上的应用程序需要输入信息。传统的信息来自于鼠标或者键盘等这些输入设备。用户直接与这些硬件设备进行交互,然后硬件设备响应用户的操作,将这些操作转换成数据传输到计算机中。计算机接收这些输入设备的信息然后将结果以可视化的形式展现出来。大多数计算机的图像用户界面上会有一个光标(Cursor),他通常代表鼠标所在的位置,因为鼠标是最开始有个滚轮设备。但是现在,如果将这个光标指代鼠标光标的话,可能不太准确,因为现在一些触摸板或手写设备也能像鼠标那样控制光标。当用户移动鼠标或者在触摸板上移动手指时,光标也能响应这种变化。当用户将光标移动到一个按钮上时,通常按钮的外观会发生变化,提示用户光标正位于按钮上。当用户点击按钮时,按钮则为显示另一种外观。当用户松开鼠标上的按键,按钮就会出现另外一种外观。显然,简单的点击事件会涉及到按钮的不同状态。

开发者可能对这些交互界面和操作习以为常,因为诸如WPF之类的用户交互平台使得程序与用户进行交互变得非常简单。当开发网页程序时,浏览器响应用户的交互,开发者只需要根据用户鼠标的悬停状态来设置样式即可进行交互。但是Kinect不同,他作为一个输入设备,并没有整合到WPF中去,因此,作为一个开发者。对操作系统和WPF所不能直接响应的那部分工作需要我们来完成。

在底层,鼠标,触摸板或者手写设备都是提供一些X,Y坐标,操作系统将这些X,Y坐标从其在的空间坐标系统转换到计算机屏幕上,这一点和上篇文章讨论的空间变换类似。操作系统的职责是响应这些标准输入设备输入的数据,然后将其转换到图形用户界面或者应用程序中去。操作系统的图形用户界面显示光标位置,并响应用户的输入。在有些时候,这个过程没有那么简单,需要我们了解GUI平台。以WPF应用程序为例,它并没有对Kinect提供像鼠标和键盘那样的原生的支持。这个工作就落到开发者身上了,我们需要从Kinect中获取数据,然后利用这些数据与按钮,下拉框或者其他控件进行交互。根据应用程序或者用户界面的复杂度的不同,这种工作可能需要我们了解很多有关WPF的知识。

1.1 WPF应用程序中输入系统介绍

当开发一个WPF应用程序时,开发者并不需要特别关注用户输入机制。WPF会为我们处理这些机制使得我们可以关注于如何响应用户的输入。毕竟作为一个开发者,我们更应该关心如何对用户输入的信息进行分析处理,而不是重新造轮子来考虑如何去收集用户的输入。如果应用程序需要一个按钮,只需要从工具箱中拖一个按钮出来放在界面上,然后在按钮的点击事件中编写处理逻辑即可。在大多数情况下,开发者可能需要对按钮设置不同的外观以响应用户鼠标的不同状态。WPF会在底层上为我们实现这些事件,诸如鼠标何时悬停在按钮上,或者被点击。

WPF有一个健全的输入系统来从输入设备中获取用户的输入信息,并响应这些输入信息所带来的控件变化。这些API位于System.Windows.Input命名空间中(Presentation.Core.dll),这些API直接从操作系统获取输入设备输入的数据,例如,名为Keyboard,Mouse,Stylus,Touch和Cursor的这些类。InputManager这个类负责管理所有输入设备获取的信息,并将这些信息传递到表现框架中。

WPF的另一类组件是位于System.Windows命名空间(PresentationCore.dll)下面的四个类,他们是UIElement,ContentElement,FrameworkElement以及FrameworkContentElement 。FrameworkElement继承自UIElement,FrameworkContentElement继承自ContentElement。这几个类是WPF中所有可视化元素的基类,如Button,TextBlock及ListBox。更多WPF输入系统相关信息可以参考MSDN文档。

InputManager监听所有的输入设备,并通过一系列方法和事件来通知UIElement和ContentElement对象,告知这些对象输入设备进行了一些有关可视化元素相关的操作。例如,在WPF中,当鼠标光标进入到可视化控件的有效区域时就会触发MouseEnterEvent事件。UIElement和ContentElement对象也有OnMouseEnter事件。这使得任何继承自UIElement或者ContentElement类的对象也能够接受来自输入设备的所触发的事件。WPF会在触发任何其它输入事件之前调用这些方法。在UIElement和ContentElement类中也有一些类似的事件包括MouseEnter,MouseLeave,MouseLeftButtonDown,MouseLeftButtonUp,TouchEnter,TouchLeave,TouchUp和TouchDown。

有时候开发者需要直接访问鼠标或者其他输出设备,InputManager对象有一个称之为PrimaryMouseDevice的属性。他返回一个MouseDevice对象。使用MouseDevice对象,能够在任何时候通过调用GetScreenPositon来获取鼠标的位置。另外,MouseDevice有一个名为GetPositon的方法,可以传入一个UI界面元素,将会返回在该UI元素所在的坐标空间中的鼠标位置。当需要判断鼠标悬停等操作时,这些信息尤其重要。当Kinect SDK每一次产生一幅新的SkeletonFrame帧数据时,我们需要进行坐标空间转换,将关节点位置信息转换到UI空间中去,使得可视化元素能够直接使用这些数据。当开发者需要将鼠标作为输入设备时, MouseDevice对象中的GetScreenPositon和GetPosition方法能提供当前鼠标所在点的位置信息。

在有些情况下,Kinect虽然和鼠标相似,但是某些方面差别很大。骨骼节点进入或者离开UI上的可视化元素这一点和鼠标移入移出行为类似。换句话说,关节点的悬停行为和鼠标光标一样。但是,类似鼠标点击和鼠标按钮的按下和弹起这些交互,关节点与UI的交互是没有。在后面的文章中,可以看到使用手可以模拟点击操作。在Kinect中相对于实现鼠标移入和移出操作来说,对鼠标点击这种支持相对来说较弱。

Kinect和触摸板也没有太多相同的地方。触摸输入可以通过名为Touch或者TouchDevice的类来访问。单点的触摸输入和鼠标输入类似,然而,多点触控是和Kinect类似的。鼠标和UI之间只有一个交互点(光标)但是触摸设备可以有多个触控点。就像Kinect可以有多个游戏者一样。从每一个游戏者身上可以捕捉到20个关节点输入信息。Kinect能够提供的信息更多,因为我们知道每一个输入点是属于游戏者身体的那个部位。而触控输入设备,应用程序不知道有多少个用户正在触摸屏幕。如果一个程序接收到了10个输入点,无法判断这10个点是一个人的10个手指还是10个人的一个手指触发的。 虽然触控设备支持多点触控,但这仍然是一种类似鼠标或者手写板的二维的输入。然而,触控输入设备除了有X,Y点坐标外,还有触控接触面积这个字段。毕竟,用户用手指按在屏幕上没有鼠标光标那样精确,触控接触面积通常大于1个像素。

当然,他们之间也有相似点。Kinect输入显然严格地符合WPF 所支持的任何输入设备的要求。除了有其它输入设备类似的输入方式外,他有独特的和用户进行交互的方式和图形用户界面。核心上,鼠标,触控板和手写板只传递一个像素点位置嘻嘻你。输入系统确定该点在可见元素上下文中的像素点位置,然后这些相关元素响应这个位置信息,然后进行响应操作。

期望是在未来Kinect能够完整的整合进WPF。在WPF4.0中,触控设备作为一个单独的模块。最开始触控设备被作为微软的Surface引入。Surface SDK包括一系列的WPF控件,诸如SurfaceButton,SurfaceCheckBox,和SurfaceListBox。如果你想按钮能够响应触摸事件,最好使用SurfaceButton控件。

能够想象到,如果Kinect被完整的整合进WPF,可能会有一个称之为SkeletonDevice的类。他和Kinect SDK中的SkeletonFrame对象类似。每一个Skeleton对象会有一个称之为GetJointPoint的方法,他和MouseDevice的GetPositon和TouchDevice的GetTouchPoint类似。另外,核心的可视化元素(UElement, ContentElement, FrameworkElement, FrameworkContentElement) 有能够相应的事件或者方法能够通知并处理骨骼关节点交互。例如,可能有一个JointEnter,JointLeave,和JointHover事件。更进一步,就像触控类有一个ManipulationStarted和ManipulationEnded事件一样,在Kinect输入的时候可能伴随GetstureStarted和GestureEnded事件。

目前,Kinect SDK和WPF是完全分开的,因此他和输入系统没有在底层进行整合。所以作为开发者的我们需要追踪骨骼关节点位置,并判断节点位置是否和UI界面上的元素有交互。当关节点在对应的UI坐标系可视化界面的有效范围内时,我们必须手动的改变这些可视化元素的外观以响应这种交互。