使用C#开发基于Leap Motion的手势控制应用(二)



在上一篇《C#开发基于Leap Motion的手势控制应用(一)》中,我们介绍了Leap motion系统的一些特征以及如何使用Helix 3Dtoolkit打开3D模型文件。本文作为Leap Motion使用介绍的第二篇,将着重讨论Leap Motion的开发架构和手势识别所涉及的代码细节。

  1. Leap Motion的系统架构

    Leap Motion的数据采集都是基于一个系统服务(Service)。这个ServiceLeapMotion控制器通过USB3.0相连接。凡是和Leap Motion相关的应用程序都会和这个Service交互,从而获得运动的追踪数据。Leap MotionSDK提供了两大类编程接口来访问追踪数据:Native接口和Web Socket接口。所有的这些接口都可以用结合不同的编程语言来开发基于Leap Motion的应用,例如使用JSP可以开发基于WebLeap Motion应用程序。

    应用程序编程接口

Leap MotionSDK提供两类API用以追踪数据的获取。Native接口是以动态链接库的形式提供,此接口可以直接连接Leap MotionService,从而读取应用程序所需的追踪数据。这些链接库可以直接不同开发语言(例如C++或者Objective-C)的工程中加以链接使用,或者使用针对其他不同语言的封装,例如JavaC#或者Python

使用C#开发基于Leap Motion的手势控制应用(二)_第1张图片

Leap Motion系统结构的组成

  1. LeapMotion的硬件发送数据给Leap Motion Service,再由Service处理并转发给基于Leap的应用程序。应用程序在失去前台焦点以后依旧可以在后台接受Leap的数据。

  2. Leap的应用程序和Service是独立运行的,并且还有一个Leap Motion的配置程序容许用户对Leap Motion控制器进行配置等工作。

    使用C#开发基于Leap Motion的手势控制应用(二)_第2张图片

    Leap MotionConfig配置界面截图

  3. 前台的Leap应用从Service接收到运动跟踪数据。应用通过Leap MotionNative LibraryService交互。

  4. 当前台的Leap应用失去焦点以后,Service就会暂停向应用程序发发送数据。在后台工作的应用程序(或者是应用本身就当作是一个Service部署)可以申请在后台继续接收数据的许可。在后台运行的程序的行为照样可以由Leap的控制台应用程序决定。

    使用C#开发基于Leap Motion的手势控制应用(二)_第3张图片

    图 在Leap的配置应用里可以控制后台应用的行为

  1. 运行时的配置

Leap Motion控制面板中的很多选项都可以在配置文件中完成,这些配置文件包括:服务控制和功能控制。当启动Leap MotionService的时候,这些在配置文件中的控制选项可以在运行时动态的通过Config类来完成。这些设置上的更改会影响使用这一Service的全部应用,所以在使用Config做设置的时候需要格外谨慎。此部分不作为具体介绍(文中connectHandler函数有所涉及,可做参考),有兴趣的读者可以自行了解。file:///YOUR_LEAPMOTION_SDK_PATH/LeapSDK/docs/csharp/devguide/Leap_Configuration.html

 

  1. 具体应用的相关代码

  1. 首先定义一个接口(ILeapEventDelegate)和类(LeapEventListener。在类的实现中,完成了主要的Leap Motion事件的响应函数(实际上是对接口Listener中的事件处理函数原型的实现)。并通过委托发送给真正的消息处理函数(消息被路由)

publicinterfaceILeapEventDelegate

   {

       voidLeapEventNotification(string EventName);

   }

 

   publicclassLeapEventListener :Listener

   {

       ILeapEventDelegate eventDelegate;

 

       publicLeapEventListener(ILeapEventDelegate delegateObject)

       {

           this.eventDelegate =delegateObject;

       }

       publicoverridevoid OnInit(Controller controller)

       {

           this.eventDelegate.LeapEventNotification("onInit");

       }

       publicoverridevoid OnConnect(Controller controller)

       {

           controller.SetPolicy(Controller.PolicyFlag.POLICY_IMAGES);

           controller.EnableGesture(Gesture.GestureType.TYPE_SWIPE);

           this.eventDelegate.LeapEventNotification("onConnect");

}

                  publicoverridevoid OnFrame(Controller controller)

       {

           this.eventDelegate.LeapEventNotification("onFrame");

       }

       publicoverridevoid OnExit(Controller controller)

       {

           this.eventDelegate.LeapEventNotification("onExit");

       }

       publicoverridevoid OnDisconnect(Controller controller)

       {

           this.eventDelegate.LeapEventNotification("onDisconnect");

       }

}

  1. 定义Leap motion相关的变量,并将其初始化

privateController controller;//Leapmotion控制器对象定义

  privateLeapEventListener listener; //Leap Motion的事件对象定义

并在构造函数中初始化:

this.controller =newController();//初始化控制器对象

       this.listener =newLeapEventListener(this);//初始化listener对象

//将自定义的Listener对象和控制器相关关联,控制器的实例的所有事件都会dispatch//这个listener对象,并完成处理。

controller.AddListener(listener);

这里实际上实现了一个LeapMotion Controller的订阅者模式,凡是被订阅者注册的controller的消息都会被接受,并最终用委托的相识发送给消息路由。

delegatevoidLeapEventDelegate(string EventName);

publicvoidLeapEventNotification(string EventName)

       {

           if (this.CheckAccess())

           {

               switch (EventName)

               {

                   case"onInit"://当初始化发生的时候,只打印一个Debug信息

                       Debug.WriteLine("Init");

                       break;

                   case"onConnect"://当(每次连接只会发生一次)和控制器连接的时候,调//connectHandler()函数

                       this.connectHandler();

                       break;

                   case"onFrame"://每当有新的真数据到来,就调用newFrameHandler函数

                       if (!this.isClosing)

                           this.newFrameHandler(this.controller.Frame());

                       break;

               }

           }

           else

           {

  Dispatcher.Invoke(newLeapEventDelegate(LeapEventNotification),newobject[] { EventName});//其他的消息依旧会被送回这个“路由”函数,但永远不会得到处理。

           }

       }

3)具体的消息处理函数

  • ConnectHandler

void connectHandler()

       {

           this.controller.SetPolicy(Controller.PolicyFlag.POLICY_IMAGES);

           this.controller.EnableGesture(Gesture.GestureType.TYPE_SWIPE);

           this.controller.EnableGesture(Gesture.GestureType.TYPE_KEY_TAP);

           this.controller.EnableGesture(Gesture.GestureType.TYPE_CIRCLE);

           this.controller.EnableGesture(Gesture.GestureType.TYPESCREENTAP);

           this.controller.EnableGesture(Gesture.GestureType.TYPE_SCREEN_TAP);

           this.controller.Config.SetFloat("Gesture.Swipe.MinLength", 100.0f);

       }

connectHandler主要是在连接控制器的时候打开要识别的手势类型,并在最后使用Config配置动作Swipe的最小移动距离。具体的手势名称和对应的TYPE信息可以参考SDK文档,此处不做赘述。

  • newFrameHandler函数

    此函数主要处理新的数据帧中的手势信息,其基本的结构如下:

voidnewFrameHandler(Leap.Frame frame)

       {

           //Parse thegestures here

           for (int i = 0; i

           {

               Gesture gesture =frame.Gestures()[i];

 

               switch (gesture.Type)

               {

                   caseGesture.GestureType.TYPE_CIRCLE:

 

                       CircleGesture circle =newCircleGesture(gesture);

}

}

实际上,有些动态手势并不能在一帧图形中识别出来,例如TYPE_CIRCLE(用手指在空中画圈),这样的动作其实多个帧之间判断手指尖的初始位置和结束位置。然而有些手势却能在一帧数据里识别出来,例如,手势“V”,类似的手势只需在一帧的数据中判断指尖和几个特殊关节的位置,就可以多出对静态手势的识别。有兴趣的读者可以下载(https://github.com/iamwmh/ModelViwer.git)配合本文的工程源代码源代码,结合代码里的手势识别处理用例,更多的了解手势处理的细节。





你可能感兴趣的:(使用C#开发基于Leap Motion的手势控制应用(二))