zoukankan      html  css  js  c++  java
  • unity Leap Motion 手势识别

      今天突然想查一下关于unity的手势识别的资料,由于前段时间浏览器收藏的所有网址都丢失了,不敢信任,所以打算以后把所有资料放到博客中。

      此篇说的是unity通过厉动(Leap Motion)进行手势识别,不过没有写过只是看(因为没有时间。。。。),转自 https://blog.csdn.net/u012289636/article/details/46883731,以下全是从这个链接复制粘贴过来的。

    Leap Motion作为一款手势识别设备,相比于Kniect,优点在于精确度。

    在我的毕业设计《场景漫游器》的开发中,Leap Motion的手势控制作为重要的一个环节。以此,谈谈开发中使用Leap Motion进行手势识别的实现方式以及需要注意的地方。

     

    一、对Leap Motion的能力进行评估

    在设定手势之前,我们必须知道Leap Motion能做到哪种程度,以免在设定方案之后发现很难实现。这个评估依靠实际对设备的使用体验,主要从三个方面:

    1.Leap Motion提供的可视化的手势识别界面

    2.SDK文档说明

    3.Leap商店中的APP

    基本可以的得出:

    1.Leap Motion的识别对于水平方向或者以水平方向为基础手势能够较好的识别。

    2.对于握拳或者垂直的行为识别会出现误差,这种误差和具体的手势行为有关。

    3.不应该过分依赖高精确度,Leap Motion能检测到毫米级别是没错的,但是有时候会把你伸直的手指识别成弯曲的,所以要做好最坏的打算。

     

    二、实际的需要

    移动、旋转、点击按钮、缩放和旋转物体、关闭程序、暂停,基本的功能需求是这样。

    有一些原则:

    1.相同环境下的手势应该接近和方便的转换。旋转和移动的之间的转换应该设计的很自然。

    2.手势避免冲突,手势过于相似不是什么好事。比如三个伸直的手指和四个伸直的手指不应该被设计成两个手势。当然这不是绝对的,如果你进行一个缓慢的动作并且动作是面向Leap Motion的摄像头,这时候应该相信它,至少要针对这个手势做一个单独的测试。

     

    三、考虑基本的数据结构和算法的轮廓

    Leap Motion的SDK在第一部分的时候已经浏览过,最起码能知道Leap Motion可以包含的信息,从SDK看来这是非常丰富的,既然设计自己的手势,那么最好不要依赖于SKD开发包的炫酷的手势。很可能,这些手势只是官方用来演示或者炫耀的。自己设计手势的基本数据结构也有另外的好处,比如更换了体感设备,但是功能是相似的,这时候只需要更改获取数据的方式就可以了(从一个SDK更换到另一个SDK),而不要修改算法。

    算法的轮廓与基本数据有很大的关系。所以数据结构一定要尽量的精简并且允许修改(可能某个算法占据了决定性因素,但是开始没考虑到)。

     

    [csharp] view plain copy
    1. public class HandAndFingersPoint : MonoBehaviour   
    2. {  
    3.     const int BUFFER_MAX=5;  
    4.     Controller m_LeapCtrl;  
    5.   
    6.     <span style="white-space:pre">    </span>public E_HandInAboveView m_AboveView = E_HandInAboveView.None;  
    7.       
    8.     //手指-数据 ,[0]表示左手,[1]表示右手  
    9.     private Dictionary<Finger.FingerType,FingerData>[] m_FingerDatas = new Dictionary<Finger.FingerType, FingerData>[2];  
    10.     //buffer,[0]表示左手,[1]表示右手,[,n](n属于0,3,表示第n次缓存)  
    11.     private Dictionary<Finger.FingerType,FingerData>[,] m_FingerDatasBuffer=new Dictionary<Finger.FingerType, FingerData>[2,BUFFER_MAX];  
    12.     private int m_CurBufIndex=0;  
    13.     //palm 0:左手 和1:右手  
    14.     private PointData[] m_PalmDatas = new PointData[2];  
    15.       
    16.     private readonly PointData m_DefaultPointData = new PointData(Vector.Zero, Vector.Zero);  
    17.         private readonly FingerData m_DefaultFingerData = new FingerData(Vector.Zero,Vector.Zero,Vector.Zero);  

    HandAndFingersPoint类中剩下的部分是对数据的填充、清除、刷新等方法。E_HandInAboveView记录哪只手先进入Leap Motion的视野,用于设定优先级。
    另外两个基本的数据结构PointData和FingerData:

     

     

    [csharp] view plain copy
    1. //一个手指的数据包含一个指尖点数据和手指根骨的位置数据  
    2. public struct FingerData  
    3. {  
    4.     public PointData m_Point;//指尖的位置和指向  
    5.     public Vector m_Position;//手指根骨的位置,对于拇指来说是Proximal phalanges近端指骨的位置  
    6.   
    7.     public FingerData(PointData pointData, Vector pos)  
    8.     {  
    9.         m_Point = pointData;  
    10.         m_Position = pos;  
    11.     }  
    12.   
    13.     public FingerData(Vector pointPos, Vector pointDir, Vector pos)  
    14.     {  
    15.         m_Point.m_Position = pointPos;  
    16.         m_Point.m_Direction = pointDir;  
    17.         m_Position = pos;  
    18.     }  
    19.   
    20.     public void Set(FingerData fd)  
    21.     {  
    22.     m_Point = fd.m_Point;  
    23.     m_Position = fd.m_Position;  
    24.     }  
    25. }  
    26. //一个点的数据,包括方向和位置  
    27. public struct PointData  
    28. {  
    29.     public Vector m_Position;//位置  
    30.     public Vector m_Direction;//方向  
    31.   
    32.     public PointData(Vector pos,Vector dir)  
    33.     {  
    34.         m_Position = pos;  
    35.         m_Direction = dir;  
    36.     }  
    37.   
    38.     public void Set(PointData pd)  
    39.     {  
    40.         m_Position = pd.m_Position;  
    41.         m_Direction = pd.m_Direction;  
    42.     }  
    43.   
    44.     public void Set(Vector pos,Vector dir)  
    45.     {  
    46.         m_Position = pos;  
    47.         m_Direction = dir;  
    48.     }  
    49. }  
    50.   
    51. //先被看到的手  
    52. public enum E_HandInAboveView  
    53. {  
    54.     None,  
    55.     Left,  
    56.     Right  
    57. }  


    基本数据定义好之后,最好确认数据的填充是没问题的,实际通过Frame frame = Leap.Controller.Frame();来获取最新的数据。这时候并不急着写完和基本数据相关的方法,现在最终要的是手势算法的合理性。要判断是否合理,最好先写一个算法。

     

    最简单的是伸掌手势,在控制中水平的伸掌用于漫游,垂直的伸掌用于暂停。我发现手掌依赖于手指,而手指包括两个状态——伸直和弯曲。另外,其他的手势,也都是手指的伸直或者弯曲,外加方向的判定累积出各种效果。理所当然的,应该单独写出手指的弯曲和伸直判定算法:

     

    [csharp] view plain copy
    1. /// <summary>  
    2. /// 该方法提供对于单个手指匹配的算法,如伸直,弯曲  
    3. /// 以后可能的改变:对于不同的场景可能要求有所不同,这里的阈值也许会随之改变  
    4. /// </summary>  
    5. public class FingerMatch  
    6. {  
    7.     //弯曲状态的角度阈值  
    8.     static readonly float FingerBendState_Radian = Mathf.PI*4f / 18 ;//40度  
    9.     //伸直状态的角度阈值  
    10.     static readonly float FingerStrightState_Radian = Mathf.PI/12;//15度  
    11.   
    12.     /// <summary>  
    13.     /// 手指伸直的状态,当根骨-指尖的方向和指向的偏差小于阀值时,判定手指为伸直状态。  
    14.     /// 注意无效的方向为零向量,先判定是零向量  
    15.     /// </summary>  
    16.     /// <param name="adjustBorder">对阈值做的微调</param>  
    17.     /// <returns></returns>  
    18.     public static bool StrightState(FingerData fingerData, float adjustBorder=0f)  
    19.     {  
    20.         bool isStright =false;  
    21.         Vector disalDir = fingerData.m_Point.m_Direction;  
    22.         //如果指尖方向为0向量,表示无效的数据  
    23.         if (!disalDir.Equals(Vector.Zero))   
    24.         {  
    25.             Vector fingerDir = fingerData.m_Point.m_Position - fingerData.m_Position;//指尖位置减去指根位置,由指根指向指尖的向量              
    26.             float radian = fingerDir.AngleTo(disalDir);  
    27.               
    28.             if (radian < FingerStrightState_Radian + adjustBorder)  
    29.             {  
    30.                 isStright = true;  
    31.             }  
    32.         }  
    33.         return isStright;  
    34.     }  
    35.   
    36.     /// <summary>  
    37.     /// 判断一根手指是否处于弯曲状态  
    38.     /// </summary>  
    39.     /// <param name="fingerData">需要判定的手指数据</param>  
    40.     /// <param name="bandBorder">弯曲的阈值</param>  
    41.     /// <returns></returns>  
    42.     public static bool BendState(FingerData fingerData, float adjustBorder=0f)//,out float eulerAugle)  
    43.     {  
    44.         bool isBend = false;  
    45.   
    46.         //eulerAugle = -1f;  
    47.         Vector disalDir = fingerData.m_Point.m_Direction;  
    48.         if( !disalDir.Equals(Vector.Zero) )  
    49.         {  
    50.             Vector fingerDir = fingerData.m_Point.m_Position - fingerData.m_Position;//指尖位置减去指根位置,指跟到指尖的向量  
    51.   
    52.             float radian = fingerDir.AngleTo(disalDir);  
    53.             //eulerAugle = radian*180/Mathf.PI;   
    54.             //夹角超过定义的阈值时,认定为弯曲状态  
    55.             if (radian > FingerBendState_Radian + adjustBorder)  
    56.             {  
    57.                 isBend = true;  
    58.             }  
    59.         }  
    60.   
    61.         return isBend;  
    62.     }  
    63.   
    64. }  


    上面包含了一个重要的概念——阈值。它是描述到底何种程度算是伸直,何种程度算是弯曲。阈值的确定是需要实际测试来决定的。写到这里也是时候进行一次简单的测试了,毕竟算法的轮廓已经确定。我甚至没写出手掌伸直的判定算法,就确定是可行的。

     

    基本数据结构相关的操作——HandAndFingersPoint类:https://github.com/LoranceChen/Leap-Motion-In-Unity3D

    该类使用基本数据,在Unity Editor中运行会展示了一个手掌的轮廓,蓝色表示手指的方向,红色表示手指骨根到掌心和指尖的连线,黄色表示掌心到指尖的连线:

     

    四、手势实现中简要的概括

    其他代码都可以在我的GitHub:Leap Motion In Unity3D仓库中(https://github.com/LoranceChen/Leap-Motion-In-Unity3D)获取,在手势的实现中,也包含了一些小的技巧,比如对于动作的匹配要防止手指的颤抖引起的误差,采用离散的数据取样——每隔一定时间做一次取样。

    使用和观察这些脚本的方式:可以把这些脚本放在一个GameObject中,通过Leap Motion会看到脚本的属性在匹配成功时会发生变化。另外,脚本中包含了事件的注册功能,换句话说,外部可以向任意的手势注册一个事件,以便手势完成匹配或者到达某种匹配状态时做一些额外的处理。这些脚本现在并不能直接完成我们的需求,如暂停。我们需要在这些手势状态或者动作上做进一步的限定,如根据掌心的方向设定垂直向前的手掌为暂停,水平的手掌为平移之类的。


  • 相关阅读:
    LF.25.K Smallest In Unsorted Array
    LC.102. Binary Tree Level Order Traversal
    LF.236.Search Insert Position
    发生错误 1069 sqlserver
    manualresetevent的用法学习
    xml selectnodes
    Flask 路由 , 初始化 , 配置Config , 蓝图BluePrint , 装饰器
    Python垃圾回收机制
    Odoo 动作Action
    Odoo 权限简介
  • 原文地址:https://www.cnblogs.com/lingLuoChengMi/p/8919534.html
Copyright © 2011-2022 走看看