Leap Motion

来源:互联网 发布:烟花算法 编辑:程序博客网 时间:2024/04/17 04:25

如果你已经熟悉编程的概念,理解并在C#语言方面有一定经验,并熟悉面向对象编程思想和设计概念,了解3D图像学和向量数学知识。不妨来看看吧!

Leap Motion是什么?

一种基于计算机视觉原理的识别技术,简单来说,Leap Motion是基于双目视觉的手势识别设备。主要是利用手势控制gameobject的变换(移动、旋转等)等。

一只手上有29根骨头,29个关节,123根韧带,48根神经,30根动脉乘以2,Leap Motion已经能够非常逼真的进行模拟了。Leap Motion感知你移动的双手,让你以一种新的方式与电脑交互,它以1毫米的100分之1的精度追踪你的10根手指,这大大超过现有的运动控制技术的敏感度,这就是为什么你可以在1英寸的立方体空间内画出微型杰作的原因。

QQ截图20150716113814.jpg


Leap Motion设置和Unity 集成

首先,你需要下载Leap Motion SDK,Leap_Motion_Orion_Setup_win_3.2.0.exe。下载完后,继续安装运行时,这个SDK支持所有的平台和语言,鼓励大家去了解下这些平台和语言。

然后,为了达成集成到Unity 中的目标,要下载Leap Motion Unity Core Assets v4.1.5。

Leap Motion Core Assets

在Project窗口里,你最该注意的是Leap Motion文件夹,这个文件夹包含的OVR是Leap Motion与Oculus Rift 虚拟现实头戴式设备相结合的资源,这个在以后会涉及到。你应该花时间学习每个文件夹内的结构,更重要的是内容。其中最主要的一个核心资源是Hand Controller,这个是允许你和Leap Motion设备交互的主要预制体,它作为锚点将你的双手渲染到场景中。

QQ截图20150716114128.jpg

图4.Hand Controller属性面板

Hand Controller 预制体有一个Hand Controller 脚本附加在上面,这样就允许了你和设备的交互。看一看检视面板里面的一些属性,你会发现那里有两个关于手的属性合作来在场景中渲染出真正的手部模型,并且那里有两个物理模型,这些是碰撞,这样设计的好处是你能创建你自己的手部模型,并且用在控制器里来查看效果,并且可以自定义手势等等。

注意:Unity 和Leap Motion 都是使用的米制系统,但是有些区别:Unity 的是以米为单位,Leap Motion用的是毫米,不是什么大问题,但是当你测量坐标的时候需要知道。

另一个关键属性是Hand Movement Scale 向量,缩放值越大,设备覆盖的物理世界范围越大,你需要查看文档来找到一个合适的数值来适应你当前的应用。Hand Movement Scale向量是用来在不改变模型大小的前提下改变双手移动的范围。

你需要将Hand Controller放在摄像机前面,并且相对于摄像机往下一定数值范围内,这里没有魔法数值,你只需尝试适合你的数值即可。


using UnityEngine;using System.Collections;public class CubeInteraction : MonoBehaviour {   public Color c;   public static Color selectedColor;   public bool selectable = false;   void OnTriggerEnter(Collider c)   {      if (c.gameObject.transform.parent.name.Equals("index"))      {         if (this.selectable)         {            CubeInteraction.selectedColor = this.c;            this.transform.Rotate(Vector3.up, 33);            return;         }         transform.gameObject.GetComponent().material.color = CubeInteraction.selectedColor;      }   }}
逻辑的核心在OnTriggerEnter(Colliderc) 函数内,我们检查与可选择物体的碰撞对象是不是食指,如果是,我们设置为该颜色。

你想捡起一个物体,并且将它移动到其他地方。

using UnityEngine;using UnityEngine.UI;using System.Collections;using Leap;public class GrabMyCube : MonoBehaviour {   public GameObject cubePrefab;   public HandController hc;   private HandModel hm;   public Text lblNoDeviceDetected;   public Text lblLeftHandPosition;   public Text lblLeftHandRotation;   public Text lblRightHandPosition;   public Text lblRightHandRotation;   // Use this for initialization   void Start()   {      hc.GetLeapController().EnableGesture(Gesture.GestureType.TYPECIRCLE);      hc.GetLeapController().EnableGesture(Gesture.GestureType.TYPESWIPE);      hc.GetLeapController().EnableGesture(Gesture.GestureType.TYPE_SCREEN_TAP);   }     private GameObject cube = null;   // Update is called once per frame   Frame currentFrame;   Frame lastFrame = null;   Frame thisFrame = null;   long difference = 0;   void Update()   {      this.currentFrame = hc.GetFrame();      GestureList gestures = this.currentFrame.Gestures();      foreach (Gesture g in gestures)      {         Debug.Log(g.Type);           if (g.Type == Gesture.GestureType.TYPECIRCLE)         {            // create the cube ...            if (this.cube == null)            {               this.cube = GameObject.Instantiate(this.cubePrefab,                                                  this.cubePrefab.transform.position,                                                  this.cubePrefab.transform.rotation) as GameObject;            }         }         if (g.Type == Gesture.GestureType.TYPESWIPE)         {            if (this.cube != null)            {               Destroy(this.cube);               this.cube = null;            }         }      }        foreach (var h in hc.GetFrame().Hands)      {         if (h.IsRight)         {            this.lblRightHandPosition.text = string.Format("Right Hand Position: {0}", h.PalmPosition.ToUnity());            this.lblRightHandRotation.text = string.Format("Right Hand Rotation: ", h.Direction.Pitch, h.Direction.Yaw, h.Direction.Roll);              if (this.cube != null)               this.cube.transform.rotation = Quaternion.EulerRotation(h.Direction.Pitch, h.Direction.Yaw, h.Direction.Roll);              foreach (var f in h.Fingers)            {               if (f.Type() == Finger.FingerType.TYPE_INDEX)               {                  // this code converts the tip position from leap motion to unity world position                  Leap.Vector position = f.TipPosition;                  Vector3 unityPosition = position.ToUnityScaled(false);                  Vector3 worldPosition = hc.transform.TransformPoint(unityPosition);                    //string msg = string.Format("Finger ID:{0} Finger Type: {1} Tip Position: {2}", f.Id, f.Type(), worldPosition);                  //Debug.Log(msg);               }            }           }         if (h.IsLeft)         {            this.lblLeftHandPosition.text = string.Format("Left Hand Position: {0}", h.PalmPosition.ToUnity());            this.lblLeftHandRotation.text = string.Format("Left Hand Rotation: ", h.Direction.Pitch, h.Direction.Yaw, h.Direction.Roll);              if (this.cube != null)               this.cube.transform.rotation = Quaternion.EulerRotation(h.Direction.Pitch, h.Direction.Yaw, h.Direction.Roll);         }        }     }}

我们定义了一个Hand Controller对象hc,这里就引用了Hand Controller对象,我们就可以在此脚本中调用此对象中的功能,第一件事是,我们需要用HandController对象注册手势,这在Start()方法中完成,这里有一些预先定义好的手势,所以我们将使用其中的一些,在这个示例中,我们注册了食指转圈,扫动,屏幕轻触手势类型。

还定义了两个GameObject变量,名为cubePrefab和cube,cubePrefab是我们引用的一个代表我们的立方体的预制体,这个预制体有对应材质和其他一些组件附加其上。

让我们看看Update()函数,这是所有魔法发生的地方,我们在这里查找手势类型为typecircle,这将实例出一个我们预先制作好的名为cubePrefab的预制体,所以,第一件事是将当前帧对象传递到Hand Controller对象,一个帧对象包含了所有有关手部运动的信息,下一步是获得由传感器检测到的所有手势并储存为一个列表。

下一步我们将循环遍历每一个手势,并检测它的类型,如果我们检测到CIRCLE手势,我们就检查,我们是否已经实例化了我们的立方预制体,如果没有,实例化它,如何下一个手势类型是SWIPE,这将销毁我们实例化的预制体。

下一个循环基本上遍历检测完所有的手,检测它是左手还是右手,基于哪只手在执行特定的操作,在这个例子里,我们只是获得手的位置和旋转,并且根据手部的旋转来旋转我们实例化的立方体,没什么奇怪的。

双目视觉就是有两个摄像头,利用双目立体视觉成像原理,通过两个摄像机来提取包括三维位置在内的信息进行手势的综合分析判断,建立的是手部的立体模型。这种方法对于用户手势的输入限制较小,可以实现更加自然的人机交互,但由于需要进行立体匹配,且由于立体模型的复杂性,需要处理大量的数据,计算相对来说较复杂。

要实现双目手势识别首先需要对双目摄像头做标定,即是计算空间中左右两台摄像机位置的几何关系。首先是对单摄像机的标定,其主要任务是计算摄像机的内部参数(包含摄像机的投影变换矩阵和透镜畸变参数)和外部参数(包含相对于某个世界坐标系的旋转矩阵和平移向量),形象点说,摄像机本身存在畸变,如果不经过标定过程,摄像机所拍摄出的影响是存在畸变的,即可能将原本的矩形显示成不规则的圆角四边形。然后是标定,即计算空两台摄像机在空间中的相对的几何位置关系(包含旋转矩阵和平移向量),通俗讲就是使得两台摄像机所成的影像显示在同一水平线上。

接下来就是具体的手势识别过程了。

首先从双目摄像头采集操作者手势动作的左右视觉图像,通过立体视觉算法生成深度图像。具体过程:经过立体标定后获取经过校准的立体图像对后,进行立体匹配,获得视差图像,再利用摄像机的内参数及外参数进行三角计算获取深度图像。

深度:Leap Motion手势识别大揭秘

然后对左(或右)视觉图像使用手势分割算法处理,分割出的人手所在的初始位置信息,并将该位置作为手势跟踪算法的起始位置。

再使用手势跟踪算法对人手运动进行跟踪。

再根据跟踪得到的结果进行手势的识别。需要说明的是,如果跟踪目标消失,则重新进行手势分割,再重复上述步骤。

双目手势识别流程图如下:

深度:Leap Motion手势识别大揭秘






0 0