【翻译】Leapmotion-python开发官方文档(3)

(由于本人英语水平有限,翻译有失误之处还望指出)

数据帧(Frames)

    LeapMotion API 将运动追踪的数据作为一系列数据帧(frames)传输到你的程序中。每帧数据都包括位置信息以及LeapMotion捕捉到的其他实体的信息。这篇文章主要探讨从LeapMotion控制器获得的Frame对象的一些具体问题。

概览

    每一Frame对象均包含由LeapMotion记录的现场的实时快照(snapshot)。手、手指、工具是LeapMotion系统追踪到的最基本的物理实体。
    从已连接的Controller对象中获得一个包含追踪信息的Frame对象。你能在你的程序准备好处理数据的时候,用Controller中的frame()方法获得一个数据帧。
if(controller.is_connected): #controller is a Leap.Controller object
    frame = controller.frame() #The latest frame
    previous = controller.frame(1) #The previous frame

   frame()方法有一个history形参,用于指出回溯多少个数据帧(即找出历史数据)。在历史缓冲区中存放着最新的六十个数据帧。(不要依赖这个数值的精确性,在未来这个值可能会发生改变。)
   注意:通常连续的数据帧会有连续的ID值。但是,在某些情况下,数据帧的ID值会有跳跃。在资源有限的电脑上,LeapMotion软件可能会发生丢帧现象。另外,当LeapMotion启动强健模式来补偿红外亮度,那么Frame对象会以二增长。(每个Frame对象需要两个摄像头捕捉的帧生成)。最后,如果你使用Listener对象的回调函数获取数据帧,回调函数直到第一次调用产生返回的时候才会调用第二次。这样,如果你的回调函数需要花很长的时间处理数据,也会发生丢帧的现象。不过在这种情况下,丢失的数据帧会保存在历史缓冲区。

从数据帧获取数据 (Getting Data from a Frame)

    Frame类定义了一些提供访问数据帧中数据的函数。比如,下面的代码就展示了如何获得由LeapMotion系统追踪到的基本对象
controller = Leap.Controller()
# wait until Controller.isConnected() evaluates to true
#...

frame = controller.frame()
hands = frame.hands
pointables = frame.pointables
fingers = frame.fingers
tools = frame.tools
    由Frame对象返回的对象均是只读的。你可以安全的保存他们然后在讲来使用他们。他们都是线程安全的。这些对象的内部实现都使用了C++ Boost 中的shared_ptr模板类。

以轮询的方式获取帧(Getting Frames by Polling)

    当你的程序本身就有帧率的时候(natural frame rate)采用轮询Controller对象获取数据帧是最简单也是最佳的方式。你只需要在你的程序准备好处理一帧数据时调用Controller对象的frame()函数。
    当你使用轮询的方式时,会有可能同样的数据帧被获取了两次。(当你程序的帧率比LeapMotion的帧率快的时候),或者跳过某些帧(当LeapMotion的帧率比你程序的帧率快时)。在许多情况下,丢失和重复数据帧不是很重要。比如,如果你使用屏幕上一个对象作为对手运动的响应。整个过程仍然是很流畅的(假设你程序的所有帧率都足够高)。在这些情况下丢帧和帧重复不是什么要紧的事,你可以使用frame()函数的history形参找回丢失的帧。
    为了判断是否处理过一个数据帧,保存上一已经处理过的数据帧的ID值然后与当前帧进行比较。
    
lastFrameID = 0;
def processFrame(self, frame):
    if(frame.id == self.lastFrameID):
        return
    #...
    self.lastFrameID = frame.id
    如果你的程序已经跳过了某些帧,使用frame()函数的history参数访问那些被跳过的数据帧(要保证不会超过历史缓冲区的长度)
  
lastProcessedFrameID = 0
def nextFrame(self, controller):
    currentID = controller.frame().id;
    for history in range(0, currentID - self.lastProcessedFrameID):
        self.processFrame(controller.frame(history))
        self.lastProcessedFrameID = currentID;

def processNextFrame(self, frame ):
    if(frame.is_valid):
        #...process

使用回调函数获取数据帧(Getting Frames with Callbacks)

    同样的,你也可以使用一个Listener对象在LeapMotion控制器的帧率下获取帧。Controller 对象在新的数据帧准备好时候调用Listener的 on_frame()函数。在on_frame()内部,你可以调用Controller.function()来获得Frame对象。
    使用回调函数会更复杂一些,因为这些回调函数都是多线程的。每一个回调都会启动一个独立的线程。你必须保证参与多线程的程序数据都是线程安全的。即使你从API获得的追踪数据是线程安全的,其他由你程序产生的数据则不一定是线程安全的。一个普遍的问题是,由GUI层组建拥有的对象只能由特定的线程进行修改。在这种情况下,你必须在相应的线程中修改那些非线程安全的部分,而不是在回调函数的线程中。
    下面的例子定义了一个小型Listener的子类用于处理新的数据帧:
class FrameListener(Leap.Listener):

    def onFrame(self, controller):
        frame = controller.frame() #The latest frame
        previous = controller.frame(1) #The previous frame
        #...

    正如你所见,使用Listener获得追踪数据和采用轮询的方式获取数据是一样的。

    注意,在使用Listener的回调函数时仍然有可能发生跳帧现象。如果你的on_frame回调函数花费时间太长,那么接下来的一帧就会放入历史缓冲区但是跳过回调函数读取的过程。少数情况下如果LeapMotion软件无法及时的处理完一个数据帧,这个数据帧就会被丢弃也不会放入到历史缓冲区。当电脑处于过多的计算任务时,这个问题就可能发生。

从数据帧中获取实体(Following entities across frames)

    如果你从另外一个数据帧中得到了一个实体的ID,你可以利用ID在当前帧中获得代表这个实体的对象。通过将ID值传入Frame的适当函数。

<span style="font-weight: normal;">hand = frame.hand(handID)
pointable = frame.pointable(pointableID)
finger = frame.finger(fingerID)
</span><p><span style="font-weight: normal;">tool = frame.tool(toolID)</span></p>

    如果同样的ID无法找到,有可能是这个手移出了LeapMotion的可视范围。然后返回一个特殊的,无效的对象。无效对象是相应的类的实例,但是所有成员都返回0值,0向量,或其他无效对象。这种技术方便了一起调用的连锁方法。比如,下面的代码段用来在一系列数据帧中求出平均的指尖位置。

#Average a finger position for the last 10 frames
count = 0
average = Leap.Vector()
finger_to_average = frame.fingers[0]
for i in range(0,9):
    finger_from_frame = controller.frame(i).finger(finger_to_average.id)
    if(finger_from_frame.is_valid):
        average = average + finger_from_frame.tip_position
        count += 1
average = average/count

    没有无效对象,这代码就必须在检查返回的Finger对象前检查每个Frame对象。无效对象减少了当获取LeapMotion的追踪数据时你不得不去做的大量无效检查(null checking)

序列化(Serialization)

    Frame对象提供serialize和deserialize()函数使你能够存储从数据帧中获得的数据和将其重构成有效的Frame对象。
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页