zoukankan      html  css  js  c++  java
  • LeapMotion(2):追踪五指

    上一篇文章,我们实现了Leap Motion的简单测试。追踪其中一个手指并用红色圆形表示其在空间的位置。

    这篇文章,我们来实现五指的追踪。

    其实,能够实现一指的追踪,那么五指的追踪自然不成问题。但是,还是有几个问题我们需要考虑一下。

    1、并不是每一帧都会包含五指的全部信息。

    比如,当前帧包含了五指信息,那么,窗口上就会显示五个红色圆。如果此时用户握拳,那么,下一帧就可能只会有一指的信息。此时,就应从窗口中移除多余的四个红色圆。

    2、手指如何和红色圆对应。

     因为Hand.Fingers集合对应的不一定是拇指、食指、中指、无名指、小指(可能对应的是小指、无名指、中指),所以,得想个办法把某个指尖和某个红色圆对应起来。幸好,Leap为每个对象都定义了ID。这样,我们就可以将指尖的ID和红色圆绑定在一起。自然地,我们会想到用Dictionary<int, Ellipse>。

    还有一点,假设上一帧检测到了拇指(id为5)、这一帧没检测到拇指,而下一帧又检测到了拇指,那么,它的id可能是5,但也有可能不是5。

    3、如何删除上一帧有的而这一帧中没有的红色圆。

    这个问题相对简单,做一个List<int>,把这一帧中id一次加进去,然后,再从Dictionary<int, Ellipse>的Keys里面删除那些不在List<int>中的id所对应的红色圆。

    OK,大部分问题都有了思路,那么,我们开始写代码吧。记得,一定要先看看上一篇文章啊。

    Step1:构造下面的用户界面。

    Step2:声明MyLeapListener类和窗口Closing事件。代码和LeapMotion(1)中的一样。

    Step3:添加成员变量Dictionary<int, Ellipse>表示手指ID和红色圆的对应,添加成员变量List<int>表示当前帧追踪到的手指编号。代码如下:

    1         private Dictionary<int, Ellipse> ellipses;
    2         private List<int> fingerIds;

    Step4:编写“连接设备”的单击事件和“断开设备”的单击事件。与之前不同的是,在“连接设备”的单击事件中,需要初始化ellipses成员变量,在“断开设备”的单击事件中,需要清空ellipses成员变量。

     1         private void connect_device_button_Click(object sender, RoutedEventArgs e)
     2         {
     3             listener = new MyLeapListener();
     4             listener.OnFrameEvent += listener_OnFrameEvent;
     5             controller = new Controller();
     6             controller.AddListener(listener);
     7 
     8             connect_device_button.IsEnabled = false;
     9             disconnect_device_button.IsEnabled = true;
    10 
    11             ellipses = new Dictionary<int, Ellipse>();
    12             fingerIds = new List<int>();
    13         }
    14 
    15         private void disconnect_device_button_Click(object sender, RoutedEventArgs e)
    16         {
    17             controller.RemoveListener(listener);
    18 
    19             connect_device_button.IsEnabled = true;
    20             disconnect_device_button.IsEnabled = false;
    21 
    22             ellipses.Clear();
    23         }

    Step5:编写OnFrameEvent事件。还是先放上事件声明。

    1         void listener_OnFrameEvent(object sender, EventArgs e)
    2         {
    3             
    4         }

    和之前一样,在事件中,我们首先要获取追踪到的手部的信息。

     1             LeapFrame frame = controller.Frame();//获取当前帧
     2             if (!frame.Hands.IsEmpty)//判断是否追踪到手部
     3             {
     4                 Hand hand = frame.Hands.FirstOrDefault();//获取追踪到的第一只手
     5                 LeapVector palmPosition = hand.PalmPosition;//获取手部位置
     6                 float palmHeight = palmPosition.y;
     7                 float detectionWidth = (float)(palmHeight * Math.Tan(75.0 / 180.0 * Math.PI) * 2);//计算当前高度的检测宽度
     8 
     9                 //将要放下面的代码
    10 
    11             }

    接下来,就需要找到追踪到的每一个指尖(是指尖,而不是笔之类的东西欧)。

    1                 foreach (Finger finger in hand.Fingers.Where(f => f.IsFinger))
    2                 {
    3                     //将要放下面的代码
    4                 }

    获取指尖id放入List<int>,然后判断Dictionary<int, Ellipse>中是否有指定id对应的ellipse。代码如下:

     1                     //获取指尖ID,放入List<int>
     2                     fingerIds.Add(finger.Id);
     3 
     4                     Ellipse ellipse = null;
     5                     if (ellipses.ContainsKey(finger.Id))//如果在Dictionary<int, Ellipse>中有,则用ellipse表示其
     6                     {
     7                         ellipse = ellipses[finger.Id];
     8                     }
     9                     else//Dictionary<int, Ellipse>中不存在,则创建一个ellipse
    10                     {
    11                         this.Dispatcher.Invoke(new Action(delegate
    12                         {
    13                             ellipse = new Ellipse();
    14                             ellipse.Width = 10;
    15                             ellipse.Height = 10;
    16                             ellipse.Fill = Brushes.Red;//10x10大小的红色圆
    17                             ellipses.Add(finger.Id, ellipse);
    18                             container_canvas.Children.Add(ellipse);
    19                         }), null);
    20                     }

    然后,就是在Canvas中设置ellipse的位置了。代码比较简单(和上一篇中的代码类似),如下:

     1                     //设置ellipse的位置
     2                     LeapVector position = finger.TipPosition;
     3 
     4                     double x = position.x;
     5                     double y = position.y;
     6 
     7                     double screenWidth = container_canvas.ActualWidth;
     8                     double screenHeight = container_canvas.ActualHeight;
     9 
    10                     x = x / detectionWidth * screenWidth + (screenWidth / 2);
    11                     y = screenHeight - y / 600 * screenHeight;
    12 
    13                     this.Dispatcher.BeginInvoke(new Action(delegate
    14                     {
    15                         Canvas.SetLeft(ellipse, x);
    16                         Canvas.SetTop(ellipse, y);
    17                     }), null);

    这样,我们就完成了指尖位置的绘制。

    但是,要记得,在Dictionary<int, Ellipse>中可能存在本帧中没有检测到的指尖的id。为此,我们需要移除Dictionary<int, Ellipse>中那些多余的Key。代码如下:

     1                 //去掉这一帧中没追踪到的手指
     2                 IEnumerable<int> deletedIds = ellipses.Keys.Except(fingerIds);
     3                 foreach (int id in deletedIds.ToList())//这里要记得ToList()一下,否则会出现异常。
     4                 {
     5                     Ellipse ellipse = ellipses[id];
     6 
     7                     this.Dispatcher.Invoke(new Action(delegate
     8                     {
     9                         container_canvas.Children.Remove(ellipse);
    10                     }), null);
    11 
    12                     ellipses.Remove(id);
    13                 }
    14 
    15                 //完成本次绘制,清空List<int>
    16                 fingerIds.Clear();

    ok,这样就完成了。运行程序看看吧。

    你会发现,基本上还是我们要的效果。但是,

    当手越高,指尖距离越近,这是为什么呢?考虑一下。

    附上源代码

  • 相关阅读:
    获取文件扩展名(后缀)
    文件字节大小显示成M,G和K
    mybatis在Mapper的xml文件中的转义字符的处理
    Java反射中method.isBridge() 桥接方法
    MyBatis框架的使用及源码分析(十三) ResultSetHandler
    MyBatis框架的使用及源码分析(十二) ParameterHandler
    MyBatis框架的使用及源码分析(十一) StatementHandler
    MyBatis框架的使用及源码分析(十) CacheExecutor,SimpleExecutor,BatchExecutor ,ReuseExecutor
    MyBatis框架的使用及源码分析(九) Executor
    MyBatis框架的使用及源码分析(八) MapperMethod
  • 原文地址:https://www.cnblogs.com/yinyy/p/3495032.html
Copyright © 2011-2022 走看看