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>表示当前帧追踪到的手指编号。代码如下:
private Dictionary<int, Ellipse> ellipses;
private List<int> fingerIds;
Step4:编写“连接设备”的单击事件和“断开设备”的单击事件。与之前不同的是,在“连接设备”的单击事件中,需要初始化ellipses成员变量,在“断开设备”的单击事件中,需要清空ellipses成员变量。
private void connect_device_button_Click(object sender, RoutedEventArgs e)
{
listener = new MyLeapListener();
listener.OnFrameEvent += listener_OnFrameEvent;
controller = new Controller();
controller.AddListener(listener); connect_device_button.IsEnabled = false;
disconnect_device_button.IsEnabled = true; ellipses = new Dictionary<int, Ellipse>();
fingerIds = new List<int>();
} private void disconnect_device_button_Click(object sender, RoutedEventArgs e)
{
controller.RemoveListener(listener); connect_device_button.IsEnabled = true;
disconnect_device_button.IsEnabled = false; ellipses.Clear();
}
Step5:编写OnFrameEvent事件。还是先放上事件声明。
void listener_OnFrameEvent(object sender, EventArgs e)
{ }
和之前一样,在事件中,我们首先要获取追踪到的手部的信息。
LeapFrame frame = controller.Frame();//获取当前帧
if (!frame.Hands.IsEmpty)//判断是否追踪到手部
{
Hand hand = frame.Hands.FirstOrDefault();//获取追踪到的第一只手
LeapVector palmPosition = hand.PalmPosition;//获取手部位置
float palmHeight = palmPosition.y;
float detectionWidth = (float)(palmHeight * Math.Tan(75.0 / 180.0 * Math.PI) * );//计算当前高度的检测宽度 //将要放下面的代码 }
接下来,就需要找到追踪到的每一个指尖(是指尖,而不是笔之类的东西欧)。
foreach (Finger finger in hand.Fingers.Where(f => f.IsFinger))
{
//将要放下面的代码
}
获取指尖id放入List<int>,然后判断Dictionary<int, Ellipse>中是否有指定id对应的ellipse。代码如下:
//获取指尖ID,放入List<int>
fingerIds.Add(finger.Id); Ellipse ellipse = null;
if (ellipses.ContainsKey(finger.Id))//如果在Dictionary<int, Ellipse>中有,则用ellipse表示其
{
ellipse = ellipses[finger.Id];
}
else//Dictionary<int, Ellipse>中不存在,则创建一个ellipse
{
this.Dispatcher.Invoke(new Action(delegate
{
ellipse = new Ellipse();
ellipse.Width = ;
ellipse.Height = ;
ellipse.Fill = Brushes.Red;//10x10大小的红色圆
ellipses.Add(finger.Id, ellipse);
container_canvas.Children.Add(ellipse);
}), null);
}
然后,就是在Canvas中设置ellipse的位置了。代码比较简单(和上一篇中的代码类似),如下:
//设置ellipse的位置
LeapVector position = finger.TipPosition; double x = position.x;
double y = position.y; double screenWidth = container_canvas.ActualWidth;
double screenHeight = container_canvas.ActualHeight; x = x / detectionWidth * screenWidth + (screenWidth / );
y = screenHeight - y / * screenHeight; this.Dispatcher.BeginInvoke(new Action(delegate
{
Canvas.SetLeft(ellipse, x);
Canvas.SetTop(ellipse, y);
}), null);
这样,我们就完成了指尖位置的绘制。
但是,要记得,在Dictionary<int, Ellipse>中可能存在本帧中没有检测到的指尖的id。为此,我们需要移除Dictionary<int, Ellipse>中那些多余的Key。代码如下:
//去掉这一帧中没追踪到的手指
IEnumerable<int> deletedIds = ellipses.Keys.Except(fingerIds);
foreach (int id in deletedIds.ToList())//这里要记得ToList()一下,否则会出现异常。
{
Ellipse ellipse = ellipses[id]; this.Dispatcher.Invoke(new Action(delegate
{
container_canvas.Children.Remove(ellipse);
}), null); ellipses.Remove(id);
} //完成本次绘制,清空List<int>
fingerIds.Clear();
ok,这样就完成了。运行程序看看吧。
你会发现,基本上还是我们要的效果。但是,
当手越高,指尖距离越近,这是为什么呢?考虑一下。
附上源代码。
LeapMotion(2):追踪五指的更多相关文章
- LeapMotion控制器 java语言开发笔记--(LeapMotion控制器简介)
(1)LeapMotion系统识别和追踪手,手指,以及根手指类似的工具,这个设备运行在一个极小的范围,这个范围拥有个高精度,高跟踪频率可以记录离散的点,手势,和动作. (2)LeapMotion控制器 ...
- 基于OpenCV的车辆检测与追踪的实现
最近老师布置了一个作业,是做一个基于视频的车辆检测与追踪,用了大概两周的时间做了一个简单的,效果不是很理想,但抑制不住想把自己的一些认识写下来,这里就把一些网络上的博客整理一下分享给大家,希望帮助到大 ...
- 基于Cat的分布式调用追踪
Cat是美团点评出的一款APM工具,同类的产品也有不少,知名的开源产品如zipkin和pinpoint:国内收费的产品如oneapm.考虑到Cat在互联网公司的应用比较广,因此被纳入选型队列,我也有幸 ...
- Office 365 如何使用powershell查询邮件追踪
如何使用Powershell 对office365的邮件进行查询追踪 1. 首先链接到Exchange Online 管理上面 $UserCredential = Get-Credential $Se ...
- java性能调优及问题追踪--Btrace的使用
在生产环境中经常遇到格式各样的问题,如OOM或者莫名其妙的进程死掉.一般情况下是通过修改程序,添加打印日志:然后重新发布程序来完成.然而,这不仅麻烦,而且带来很多不可控的因素.有没有一种方式,在不修改 ...
- 追踪记录每笔业务操作数据改变的利器——SQLCDC
对于大部分企业应用来用,有一个基本的功能必不可少,那就是Audit Trail或者Audit Log,中文翻译为追踪检查.审核检查或者审核记录.我们采用Audit Trail记录每一笔业务操作的基本信 ...
- 2016-1-30 Servlet中Session管理(Sesssion追踪)
Session管理(Sesssion追踪)是Web应用程序开发中非常重要的一个主题.这是因为HTTP是无状态的,在默认情况下,Web服务器不知道一个HTTP请求是来自初次用户,还是来自之前已经访问过的 ...
- Metaio获取当前追踪的对象的方法
重写 onTrackingEvent获取TrackingValues集合,然后通过TrackingValues的state属性的isTrackingState()方法判断是否为追踪状态,或者直接使用s ...
- 基于zipkin分布式链路追踪系统预研第一篇
本文为博主原创文章,未经博主允许不得转载. 分布式服务追踪系统起源于Google的论文“Dapper, a Large-Scale Distributed Systems Tracing Infras ...
随机推荐
- iOS - UI - UIStepper
7.UIStepper //计数器控件 固定宽高 UIStepper * stepper = [[UIStepper alloc] initWithFrame:CGRectMake(100, 10 ...
- 重构23-Introduce Parameter Object(参数对象)
有 时当 使用 一个 包含 多 个 参 数 的 方 法 时 , 由 于参 数 过 多 会 导 致 可 读 性 严 重 下 降 , 如 : 有 时当 使用 一个 包含 多 个 参 数 的 方 法 时 , ...
- jQuery中each的break和continue
each实质上是一个for循环,那么能不能像普通的for循环那样break和continue呢? 参考http://bevisoft.iteye.com/blog/641195做了个实验,可以的, 代 ...
- PHP trim()函数的一些用法
string trim ( string $str [, string $charlist ] ) - 去除字符串首尾处的空白字符(或者其他字符) trim()函数当第二个参数为空时,默认去掉空格.制 ...
- codeforces 680B B. Bear and Finding Criminals(水题)
题目链接: B. Bear and Finding Criminals //#include <bits/stdc++.h> #include <vector> #includ ...
- poj 1390 动态规划
思路: 黑书的例题 #include<iostream> #include<cstring> #include<algorithm> #include<cma ...
- ASP.Net 验证控件 RangeValidator
RangeValidator 定义和用法 RangeValidator 控件用于检测用户输入的值是否介于两个值之间.可以对不同类型的值进行比较,比如数字.日期以及字符. 注释:如果输入控件为空,验证不 ...
- 【ERROR】---Error executing "adb devices":ADB server didn't ACK
搭建环境 ionic emulate android 的时候出现问题 端口占用,找到占用端口的程序,结束进程再启动 重新ionic emulate android 还是报错,闪了一下说fail ...
- php nginx fastdfs 下载文件重命名
其实fdfs可以传入一个filename参数,指定文件名.. M00/00/00/fwAAAVGMateAafjTAAAABBW-xbM368.txt?filename=test.txt 这样下载时用 ...
- (转)一网打尽当下NoSQL类型、适用场景及使用公司
摘要:对比传统关系型数据库,NoSQL有着更为复杂的分类——键值.面向文档.列存储以及图数据库.这里就带你一览NoSQL各种类型的适用场景及一些知名公司的方案选择. 在过去几年,关系型数据库一直是数据 ...