有了Kinect,我们就可以使用手势来控制幻灯片的播放,而不需要一边演讲一遍按键盘或者手持一个ppt控制器,我们只需要做的就是轻轻的向右或者向左挥一下手就可以控制幻灯片向前或者向后翻一页,酷吧。虽然可能在演讲的时候做这个动作可能有点奇怪,但是这也是一种控制幻灯片放映的好方法。
实现Kinect控制幻灯片播放很简单,主要思路是:使用Kinect捕捉人体动作,然后根据识别出来的动作向系统发出点击向前,向后按键的事件,从而使得幻灯片能够切换。 这里的核心功能在于手势的识别,我们在开发之前需要定义怎么样的手势算是向前或者向后切换幻灯片。手势和姿势识别在我的Kinect开发入门第九,第十,十一篇文章有详细介绍。本文仅讨论主要思路及关键代码部分。
姿势(pose)识别是通过关节点与关节点之间的相对位置关系来进行判断,相对来说比较容易,只需要通过某一帧骨骼关节点数据即可进行判断。而手势(gesture)识别则是通过对连续的一段时间内的动作来进行判断,比较复杂。但是两者对于我们需要实现特定的目的来说并没有优劣之分,就像常用的算法那样,并不是越复杂越好,有些方法就非常简单高效。
在控制ppt播放命令中,我们设定,如果右手关节点在x轴上的距离比头部关节点大于0.45的话,认为用户试图进行点击键盘上的right按钮。如果头部关节点位置在x轴方向是比左手关节点在x轴上的位置大于0.45的话,认为用户试图点击键盘上的left按钮。0.45这个值是通过反复试验的出来的,这种通过试验的方法在Kinect开发中比较常见。关键代码如下:
private void ProcessForwardBackGesture(Joint head, Joint rightHand, Joint leftHand) { if (rightHand.Position.X > head.Position.X + 0.45) { if (!isBackGestureActive && !isForwardGestureActive) { isForwardGestureActive = true; System.Windows.Forms.SendKeys.SendWait("{Right}"); } } else { isForwardGestureActive = false; } if (leftHand.Position.X < head.Position.X - 0.45) { if (!isBackGestureActive && !isForwardGestureActive) { isBackGestureActive = true; System.Windows.Forms.SendKeys.SendWait("{Left}"); } } else { isBackGestureActive = false; } }
上面的代码中,当判断到用户向右挥手动作是,执行System.Windows.Forms.SendKeys.SendWait("{Right}")语句从而发出点击键盘向右按键; 该方法执行时,要求PowerPoint程序处于当前活动的状态,这样里面的PPT才会向右键盘点击事件。需要注意的是方法中isBackGestureActive和isForwardGestureActive这两个布尔型的标志位,可以防止当用户一直处于某一个动作时会一直发送System.Windows.Forms.SendKeys.SendWait("{xx}")。
上面的方法可以放在sensor_SkeletonFrameReady事件中,首先获取头部,左手右手关节点数据,然后调用该方法。
void sensor_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e) { using (var skeletonFrame = e.OpenSkeletonFrame()) { if (skeletonFrame == null) return; if (skeletons == null || skeletons.Length != skeletonFrame.SkeletonArrayLength) { skeletons = new Skeleton[skeletonFrame.SkeletonArrayLength]; } skeletonFrame.CopySkeletonDataTo(skeletons); Skeleton closestSkeleton = (from s in skeletons where s.TrackingState == SkeletonTrackingState.Tracked && s.Joints[JointType.Head].TrackingState == JointTrackingState.Tracked select s).OrderBy(s => s.Joints[JointType.Head].Position.Z) .FirstOrDefault(); if (closestSkeleton == null) return; var head = closestSkeleton.Joints[JointType.Head]; var rightHand = closestSkeleton.Joints[JointType.HandRight]; var leftHand = closestSkeleton.Joints[JointType.HandLeft]; if (head.TrackingState != JointTrackingState.Tracked || rightHand.TrackingState != JointTrackingState.Tracked || leftHand.TrackingState != JointTrackingState.Tracked) { //Don't have a good read on the joints so we cannot process gestures return; } ProcessForwardBackGesture(head, rightHand, leftHand); } }
通过姿势识别来进行幻灯片控制简单高效,但是也存在着两个主要问题:
首先是,如果幻灯片中嵌套有视频,flash或者其他多媒体要素的话,可能不能很好的控制这些要素的播放和暂停,一种处理办法是使用动画,使得用户在点击键盘显示多媒体的时候就开始播放。还有一种方法是使用VSTO编写针对PowerPoint的插件,来监听鼠标来控制多媒体播放。
其次是误操作问题,这个问题是使用基于姿势识别存在的最大问题。有时候可能出于肢体语言表达需要,可能需要张开双臂,或者弯下腰来捡东西,这样会使得头部关节点位置和手部关节点位置的相对关系可能会满足之前我们设定的距离,从而产生误操作。
使用姿势识别的第一个问题是一个普遍存在的问题,即使使用ppt控制器也存在该问题,ppt控制器似乎也是通过发送键盘点击事件来进行幻灯片控制的。第二个问题可以使用手势识别的方式来在一定程度上避免。
手势识别通过判断在一定时间内一系列连续的动作之间的前后相关关系来进行动作的识别,在第十篇 文章中对swip这一动作如何识别有详细介绍。在Kinect for Windows Developer ToolKit v 1.5中,增加了一个名为Slideshow Gesture-WPF的例子。
我们可以直接打开其源代码,可惜的是,其中的挥动手势识别代码被封装在了一个名为Microsoft.Samples.Kinect.SwipeGestureRecognizer的dll中,
使用Reflector可以查看其实现方式,大体的原理和我之前第十篇文章中相似,不过其提供了基于手势库的识别方式,以后我会详细介绍,在这里我们可以直接使用该dll提供的方法。
要使用该dll提供的手势识别,必须先创建一个Recognizer对象。然后初始化改Recognizer。在初始化的时候注册左右挥动识别后进行的操作,该操作可以通过方法提供,由于方法体比较小,这里使用lambda表达式。
private readonly Recognizer activeRecognizer; this.activeRecognizer = this.CreateRecognizer(); private Recognizer CreateRecognizer() { // Instantiate a recognizer. var recognizer = new Recognizer(); // swipe right to press right key . recognizer.SwipeRightDetected += (s, e) => { System.Windows.Forms.SendKeys.SendWait("{Right}"); }; // swipe left to press left key .. recognizer.SwipeLeftDetected += (s, e) => { System.Windows.Forms.SendKeys.SendWait("{Left}"); }; return recognizer; }
然后在sensor_SkeletonFrameReady事件中调用即可
private Skeleton[] skeletons = new Skeleton[0]; private void sensor_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e) { // Get the frame. using (var frame = e.OpenSkeletonFrame()) { // Ensure we have a frame. if (frame != null) { // Resize the skeletons array if a new size (normally only on first call). if (this.skeletons.Length != frame.SkeletonArrayLength) { this.skeletons = new Skeleton[frame.SkeletonArrayLength]; } // Get the skeletons. frame.CopySkeletonDataTo(this.skeletons); // Pass skeletons to recognizer. this.activeRecognizer.Recognize(sender, frame, this.skeletons); } } }
把上面的代码放到程序中,运行即可看到使用左手向左挥手,使用右手向右挥手即可控制幻灯片向左向右切换。如前面的文章所述,使用手势识别动作有一个时间阈值和一定的规则,如果该动作在某一时间内没有完成则识别失败。所以在本例中,如果挥手动作过慢,可能导致识别不出。而不像之前使用姿势识别例子中的那样,只需保持某个动作即可完成识别操作,这在一定程度上减少了第一种情况下出现误识别的概率。
运行效果如下:
本文介绍了使用Kinect实现控制ppt播放的两种方法,一种是基于姿势识别一种是基于手势识别,并介绍了这两种方法的优缺点以及实现的部分代码。其实利用Kinect完全可以实现常用ppt控制器上的功能,比如说LED指示灯,使用Kinect也可以实现该功能,就是把用户手势所在的位置映射到屏幕上,并以红色显示。当然仅当娱乐的话,您也可以使用语音来控制PPT的播放。相信您看了之前我写的一些文章在加上上面给出的代码,实现Kinect控制幻灯片播放应该很容易了。这里就不提供原代码了。希望本文对您了解Kinect中的姿势和手势识别有所帮助。