【翻譯】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對象。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章