這篇是延續前一篇的《NiTE 2 的手勢偵測》,繼續來講 NiTE 2 所提供的手部追蹤功能。NiTE 2 所提供的手部追蹤功能,和手勢偵測一樣,是由 HandTracker 提供的,而在使用上則是和在 OpenNI 1.x 的時候(參考)一樣,都是要先偵測到手的位置,然後再針對這個位置進行追蹤;所以一般在使用上,大多是搭配手勢偵測,找到手的位置後,再開始追蹤。
而寫成程式的話,基本上就會是像下面這樣子:
// STL Header #include <iostream> #include <vector> // 1. include NiTE Header #include <NiTE.h> // using namespace using namespace std; using namespace nite; int main( int argc, char** argv ) { // 2. initialize NiTE NiTE::initialize(); // 3. create hand tracker HandTracker mHandTracker; mHandTracker.create(); // 4. set gesture mHandTracker.startGestureDetection( GESTURE_WAVE ); mHandTracker.startGestureDetection( GESTURE_CLICK ); mHandTracker.startGestureDetection( GESTURE_HAND_RAISE ); // main loop for( int i = 0; i < 3000; ++ i ) { // 4. get new frame HandTrackerFrameRef mHandFrame; mHandTracker.readFrame( &mHandFrame );// 5a. get gesture data const Array<GestureData>& aGestures = mHandFrame.getGestures(); for( int i = 0; i < aGestures.getSize(); ++ i ) { // 5b. start hand tracking const Point3f& rPos = aGestures[i].getCurrentPosition(); HandId mHandID; mHandTracker.startHandTracking( rPos, &mHandID ); cout << "Found hand and start tracking" << endl; }// 6. Get hands data const Array<HandData>& aHands = mHandFrame.getHands(); for( int i = 0; i < aHands.getSize(); ++ i ) { const HandData& rHand = aHands[i]; cout << "Hand " << rHand.getId(); if( rHand.isNew() ) cout << " Start tracking"; else if( rHand.isLost() ) cout << " Lost"; if( rHand.isTracking() ) { const Point3f& rPos = rHand.getPosition(); cout << " at " << rPos.x << ", " << rPos.y << ", " << rPos.z; } cout << endl; if( rHand.isTouchingFov() ) cout << " Touch FOV" << endl; }} // 6. shutdown mHandTracker.destroy(); NiTE::shutdown(); return 0; }
上面的範例程式,在大架構下都和之前的手勢偵測的相同,有改到的地方,基本上就是主迴圈內,也就黃底(5 和 6)的部分了。
首先,在偵測到手勢之後,如果要開始追蹤這支手,就是要去呼叫 HandTracker 的 startHandTracking() 這個函式,要求 NiTE 開始追蹤現在這個點。這個函式需要兩個參數,第一個就是要追蹤的位置,基本上就是直接把偵測到手勢的位置(rPos)給他就可以了;而第二個參數,則是輸出用的,它可以用來知道現在開始追蹤的手的編號(HandID),可以做之後的管理之用。
接下來,則是透過 HandTrackerFrameRef 的 getHands() 這個函式,來取得目前有再追蹤的手的陣列;其中,每一個手的資料,都是用 HandData 這個型別來儲存的。HandData 有提供六個函式,可以用來取得手部的相關資料,透過 getId() 可以取得手的編號(就是前面 startHandTracking() 的編號),而透過 getPosition(),則可以取得手部目前現在的位置。
而除了這兩個 get 的函式外,他還提供了 isNew()、isLost()、isTracking(),可以用來判斷這隻手的狀態。另外比較特別的,是它還有提供一個 isTouchingFov() 的函式,可以用來判斷手部是否已經碰到攝影機視角的邊緣,在許多時候,也是相當有用的~
上面的範例程式在執行後,會在偵測到手勢後,就自動開始做手部的追蹤,並把手部的位置做文字的輸出。而由於有針對 GESTURE_HAND_RAISE 做偵測,所以應該手隨便動一下,就會開始偵測了。而開始追蹤後,就會一直輸出手部所在的位置,直到手離開畫面、被判定已經停止追蹤(lost)。
至於有圖形介面的範例,就等之後有時間再補上吧。
新手發問:
請問一下在vs2012跑這個範例時,在5b 跟 6這兩個地方的 Point3f&
都有出現錯誤error C2872: ‘Point3f’ : 模稜兩可的符號的訊息,請問該怎麼解決呢?
謝謝
讚讚
這個錯誤應該是因為有其他同樣命名為 Point3f 的 class 造成的。
你還有 include 其他 header 檔嗎?有可能是你用的其他函式庫,也有同名的類別,所以會造成編譯器不知道該用哪個。(例如:OpenCV)
如果是這個狀況的話,請不要用 using namespace 的方法省略 namespace 就可以了。
讚讚
[…] NiTE 2 的手部追蹤 […]
讚讚
Heresy您好:
我想請問一下,一定要手勢偵測才能偵測手部的位置嗎?
我有嘗試把手勢偵測拿掉,可是他就偵測不到手部了。
讚讚
文中有說:「要先偵測到手的位置,然後再針對這個位置進行追蹤」
如果你知道手的位置所在,也可以強制指定從該點開始追蹤。
讚讚
您好 H大大
請問這個NITE2 手部追蹤的程序 是否可以改為簡單的滑鼠模擬事件 ?
是否可以像在之前的這篇 <>中一樣加入CVMouse 這個 class ?
我是初學者 希望H大大可以給我一點提示 ;)
謝謝
讚讚
可以,原理和之前 OpenNI 1.x 的時候基本上是完全相同的。
讚讚
謝謝
讚讚
[…] NiTE 2 的手部追蹤 / 使用 OpenCV 繪製 NiTE2 的手部資料 […]
讚讚
[…] PrimeSense 的 NiTE 主要是提供了人的骨架追蹤,以及手部追蹤的功能,基本上應該算是在 OpenNI 這個架構下,最重要、也最常被使用的 middleware library 了。不過由於他的手部追蹤的功能,僅能提供位置的追蹤、和簡單的動態手勢辨識,並沒有辦法去針對手的狀態、或是手指作分析,算是比較可惜的地方。 […]
讚讚
[…] 這篇,算是簡單的概念實作測試吧…Heresy 是試著在 OpenCV 的環境下,透過 NiTE 的手部追蹤的功能,來時做體感的按鈕。由於只是概念實作,所以在圖形的部分,算相當地簡單就是了~如果有需要的話,也可以自己根據需求作加強。 […]
讚讚
[…] 2 的手勢偵測》和《NiTE 2 的手部追蹤》這兩篇文章的延伸。主要,是透過 OpenCV 來寫個簡單的範例,示範如何把 […]
讚讚