Leap Motion翻譯系列文章http://52coding.com/leap-motion-official-doc-translation
擷取幀資料Getting Frame Data
Leap Motion的API為你的程式提供以一系列被稱為幀對象的快照的運動追蹤資料。追蹤資料的每一幀都包含度量位置和其它在快照中關于檢測到的實體的資訊。這篇文章詳細讨論從Leap Motion控制器中捕獲幀物體(Frame objects)。
概述
從連結好的控制器擷取一幀包含追蹤資訊的對象。無論什麼時候,隻要你的應用程式準備使用控制器(Controller)類中的frame()方法來操作,就可以擷取一個幀對象:
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數值與目前幀進行比較:
int64_t lastFrameID = 0;
void processFrame( Leap::Frame frame )
{
if( frame.id() == lastFrameID ) return;
//...
lastFrameID = frame.id();
}
如果你的應用丢失了一些幀,你可以使用frame()函數裡的曆史參數來通路丢失的幀(隻要這個幀對象還在曆史緩存中):
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)對象。
使用監聽者回調函數比較複雜,因為回調是多線程的。每個回調都來自一個獨立的線程。你必須保證來自多線程中的資料都是以線程安全方式擷取的。
接下來的例子定義了一個最小的監聽子類,它可以擷取新的資料幀:
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系統追蹤到的基本對象。
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傳遞給合适類型的幀函數:
Hand hand = frame.hand(handID);
Pointable pointable = frame.pointable(pointableID);
Finger finger = frame.finger(fingerID);
Tool tool = frame.tool(toolID);
如果具有相同ID的對象無法被找到----也許一隻手或者手指移出了Leap的視野。----那麼,取而代之,會傳回一個特别的無效對象。無效對象也是類的執行個體,但是他們所有的成員都傳回0數值、0數組或者其他的無效對象。這個技術使得将方法連接配接在一起調用更友善。例如,下列的代碼計算多幀下指尖的平均位置。
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;
如果沒有無效的對象,那麼這段代碼需要每次在傳回手指對象時檢測這個幀對象。
更多相關内容,請通路http://52coding.com/category/leap-motion
