如果你觉得文档很有用,你还可以去我的淘宝购买此文档最新的docx、pdf、html版本。
Leap Motion翻译系列文章汇总http://brightguo.com/leap-motion-official-doc-translation
获取帧数据Getting Frame Data
Leap Motion的API为你的程序提供以一系列被称为帧对象的快照的运动追踪数据。追踪数据的每一帧都包含度量位置和其它在快照中关于检测到的实体的信息。这篇文章详细讨论从Leap Motion控制器中捕获帧物体(Frame objects)。
概述
从链接好的控制器获取一帧包含追踪信息的对象。无论什么时候,只要你的应用程序准备使用控制器(Controller)类中的frame()方法来操作,就可以获取一个帧对象:
1 2 3 4 5 |
if( controller.isConnected()) //controller is a Controller object { Frame frame = controller.frame(); //The latest frame Frame previous = controller.frame(1); //The previous frame } |
frame()方法输入一个历史(history)参数标示可以重新得到多少以前的帧。[应该就是缓存空间大小吧]一般的,过去的60帧内容都保存在这个历史缓存中。
通过轮询获取帧
通过对控制器物体的轮询获得帧对象的方式,是当你应用运行在自然帧率下最简单以及往往最好的策略。当你设备处理一帧数据时,你只需要调用一下控制器的frame()函数。
但你使用轮询方法,有一定概率你会在同一行内(如果应用个帧率快于Leap的帧率)获取同一帧的内容,又或者跳过一帧(当Leap帧率快于你应用的帧率)。在大部分情况下,丢失或者重复的帧对象都是不重要的。比如,如果你在屏幕前用手移动物体,物体的运动依旧是平滑的(假设你应用的总体帧率是足够高的)。[不知道它表达的什么意思,我觉得只要它数据是平滑变化的就好]
为了检测你是否正在处理了一个已经处理过的帧对象了,将已保存最后处理过帧的ID数值与当前帧进行比较:
1 2 3 4 5 6 7 8 |
int64_t lastFrameID = 0; void processFrame( Leap::Frame frame ) { if( frame.id() == lastFrameID ) return; //... lastFrameID = frame.id(); } |
如果你的应用丢失了一些帧,你可以使用frame()函数里的历史参数来访问丢失的帧(只要这个帧对象还在历史缓存中):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
int64_t lastProcessedFrameID = 0; void nextFrame( Leap::Controller controller ) { int64_t currentID = controller.frame().id(); for( int history = 0; history < currentID - lastProcessedFrameID; history++) { processFrame( controller.frame(history) ); } lastProcessedFrameID = currentID; } void processNextFrame( Leap::Frame frame ) { if( frame.isValid() ) { //... } } |
通过回调函数获取帧
同样的,你还可以使用一个监听(Listener)对象来获取Leap Motion控制器的帧对象。只要一个新的帧到来,这个控制器对象就会调用监听者的onFrame()函数。在onFrame处理中,你可以调用控制器(Controller)的frame()函数来获取他自己的帧(Frame)对象。
使用监听者回调函数比较复杂,因为回调是多线程的。每个回调都来自一个独立的线程。你必须保证来自多线程中的数据都是以线程安全方式获取的。
接下来的例子定义了一个最小的监听子类,它可以获取新的数据帧:
1 2 3 4 5 6 7 8 9 |
class FrameListener : Leap::Listener { void onFrame(Controller &controller) { Frame frame = controller.frame(); //The latest frame Frame previous = controller.frame(1); //The previous frame //... } }; |
正如你所见,通过一个监听对象获取追踪数据和轮询模式几乎一样。
注意,跳过一帧是很必要的,即使当我们使用监听回调函数。如果你的onFrame回调函数用了过多的时间才执行完毕,那么下一帧被被添加到历史记录中,但是onFrame回调函数跳过去了。不太相同的,如果一个Leap软件不能及时处理一帧数据,那个帧会被抛弃,并且不会被加入到历史中。当计算机由于需要运行太多任务导致停滞,就会导致这个问题。
从帧中获取数据
帧类定义了一些如何访问帧中数据的函数。例如,下面的代码描述了如何获取Leap Motion系统追踪到的基本对象。
1 2 3 4 5 6 7 8 |
Leap::Controller controller = Leap::Controller(); // wait until Controller.isConnected() evaluates to true //... Leap::Frame frame = controller.frame(); Leap::HandList hands = frame.hands(); Leap::PointableList pointables = frame.pointables(); Leap::FingerList fingers = frame.fingers(); Leap::ToolList tools = frame.tools(); |
这个从帧对象中返回的对象都是只读的。你可以安全的保存它们并在未来使用它们。它们是线程安全的。内部的,这些对象使用C++ Boost库共享指针类。
使用IDs来追踪帧间实体
如果你获取到了一个来自不同帧下实体的ID,你可以获取对象在当前帧的描述信息。将ID传递给合适类型的帧函数:
1 2 3 4 |
Hand hand = frame.hand(handID); Pointable pointable = frame.pointable(pointableID); Finger finger = frame.finger(fingerID); Tool tool = frame.tool(toolID); |
如果具有相同ID的对象无法被找到—-也许一只手或者手指移出了Leap的视野。—-那么,取而代之,会返回一个特别的无效对象。无效对象也是类的实例,但是他们所有的成员都返回0数值、0数组或者其他的无效对象。这个技术使得将方法连接在一起调用更方便。例如,下列的代码计算多帧下指尖的平均位置。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
int count = 0; Leap::Vector average = Leap::Vector(); Leap::Finger fingerToAverage = frame.fingers()[0]; for( int i = 0; i < 10; i++ ) { Leap::Finger fingerFromFrame = controller.frame(i).finger(fingerToAverage.id()); if( fingerFromFrame.isValid() ) { average += fingerFromFrame.tipPosition(); count++; } } average /= count; |
如果没有无效的对象,那么这段代码需要每次在返回手指对象时检测这个帧对象。