在之前的文章,已經針對 NiTE 2 的人體骨架追蹤,以及姿勢的偵測,以範例的方法做了說明。而接下來這篇,則是來說明 NiTE 2 提供的手勢偵測的使用方法。
在 NiTE 2 裡,手勢偵測的功能是由 HandTracker 這個類別提供的;而除了手勢偵測之外,HandTracker 另一個重要的功能,就是手部位置的追蹤。他的基本使用概念,和人體骨架追蹤、姿勢偵測的 UserTracker 基本上是相似的。
而由於手部位置的偵測,一般來說是要搭配手勢偵測使用的,所以在這邊 Heresy 就先針對手勢偵測的部分,以簡單的範例來做說明。
首先,在基本功能面上,NiTE 2 所提供的手勢偵測的功能,基本上和 OpenNI 1.x 的時候(參考)一樣,都是不針對特定使用者,而是對整個畫面做分析,來找到符合的手勢的。
而在目前的 NiTE 2 裡面,他支援的手勢只有三種,就是「GESTURE_WAVE」、「GESTURE_CLICK」和「GESTURE_HAND_RAISE」,代表的意義分別是「揮手」、「往前推再縮回」、「手舉起」;每個手勢可以個別設定是否要進行偵測,不過和姿勢偵測一樣,NiTE 2 並沒有提供自訂手勢的功能,這點還是覺得滿可惜的。
下面,則是一個簡單的 NiTE 2 的手勢偵測的範例:
// STL Header #include <iostream> // 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 ) { const GestureData& rGesture = aGestures[i]; // 5b. check gesture type cout << "Detect gesture "; switch( rGesture.getType() ) { case GESTURE_WAVE: cout << "[wave]"; break; case GESTURE_CLICK: cout << "[click]"; break; case GESTURE_HAND_RAISE: cout << "[hand raise]"; break; } // 5c. get gesture position const Point3f& rPos = rGesture.getCurrentPosition(); cout << " at " << rPos.x << ", " << rPos.y << ", " << rPos.z; // 5d. check gesture status if( rGesture.isComplete() ) cout << " is complete"; if( rGesture.isInProgress() ) cout << " is in progress"; cout << endl; } } // 6. shutdown mHandTracker.destroy(); NiTE::shutdown(); return 0; }
和在使用 UserTracker 時一樣,要使用 NiTE 2,一樣是需要 include NiTE.h 這個 header 檔,而所有相關的東西,都會在 nite 這個 namespace 下。在開始使用前,要先呼叫 NiTE 的 initialize() 來完成整個環境的初始化,結束時則需要呼叫 shutdown() 來做終止的動作。(請參考《NiTE2 基本使用》)
在完成整體環境的初始化後,則是要建立出我們所需要的 NiTE 模組,這邊就是 HandTracker。這邊首先先宣告出 HandTracker 的物件 mHandTracker,然後呼叫他的 create() 函式,來完成 HandTracker 的建立(3 的部分)。
和 UserTracker 一樣,HandTracker 的 create() 這個函式,實際上是可以透過指定一個 openni::Device 的指標,來指定要分析哪個感應器的深度影像的;而如果沒有特別要指定的話,就不用給,這樣 HandTracker 就會自己去找一個合適的來用。
在建立出 HandTracker 後,接下來則是要透過 HandTracker 提供的 startGestureDetection() 這個函式,來指定要偵測那些手勢(4 的部分);目前 NiTE 所支援的手勢,就是「GESTURE_WAVE」、「GESTURE_CLICK」和「GESTURE_HAND_RAISE」這三種,Heresy 在這邊是都加進來了,如果不想要的話,也可以不要偵測。而如果中途想要停止偵測特定手勢的話,也可以呼叫 stopGestureDetection() 來停止對特定手勢的偵測。
再來,進入主迴圈之後,一樣是透過 readFarme() 這個函式,來把當下這個畫面的分析結果寫到 mHandFrame 這個型別為 HandTrackerFrameRef 的物件裡。這個物件除了提供了時間等 metadata 外,在手勢偵測這方面,主要是提供了 getGestures() 這個函式,可以取得出目前的畫面裡,有偵測到的手勢資料的陣列(5a 的部分)。
getGestures() 這個函式回傳的資料,會是 Array<GestureData> 這個型別的陣列物件,陣列裡面每一筆 GestureData 資料,都代表是偵測到的一個手勢。而為了要分析裡面所有的資料,這邊是用一個迴圈,去個別針對筆一筆資料作處理。
GestureData 相當地簡單,他只有四個函式可以使用。透過 getType() 這個函式,可以知道他是哪一種手勢;透過 isComplete() 和 isInProgress() 則可以知道手勢目前的狀態。而透過 getCurrentPosition(),則可以知道手勢出現的位置;他回傳的位置會是在世界座標系統、型別為 Point3f 的點,有需要的話,可以透過 HandTracker 提供的 convertHandCoordinatesToDepth() 這個函式,把他轉換到深度影像的座標系統上。
而在上面的範例程式裡面,Heresy 是先透過 getType() 來做判斷,輸出手勢的名稱(5b 的部分),然後再把透過 getCurrentPosition() 這個函式取得的位置做輸出(5c 的部分)。最後,則是去透過 isComplete() 和 isInProgress() 做狀態的判斷(5d 的部分)。
這樣的程式在執行後,並不會有圖形介面,而在有偵測到手勢的時候,就會在文字介面裡面,輸出偵測到的手勢的相關資訊了~不過,由於「GESTURE_HAND_RAISE」被觸發到的機率相當地高,所以可能會覺得畫面有被洗版的感覺,所以個人會建議可以把「mHandTracker.startGestureDetection( GESTURE_HAND_RAISE );」這行拿掉,不要針對這個手勢進行偵測會比較好。
[…] PrimeSense NiTE 2 的 UserTacker 和 HandTracker,也都有提供 NewFrameListener 可以使用,所以有需要的話,NiTE […]
讚讚
[…] NiTE 2 的手勢偵測 […]
讚讚
[…] PrimeSense 的 NiTE 主要是提供了人的骨架追蹤,以及手部追蹤的功能,基本上應該算是在 OpenNI 這個架構下,最重要、也最常被使用的 middleware library 了。不過由於他的手部追蹤的功能,僅能提供位置的追蹤、和簡單的動態手勢辨識,並沒有辦法去針對手的狀態、或是手指作分析,算是比較可惜的地方。 […]
讚讚
話說問一下似乎我實驗一下HandTracker不能和UserTracker同時使用否則會Exception?不知道是否正確?
讚讚
Heresy 自己測試過,應該是沒問題。不知道你的程式是怎樣的錯誤?
讚讚
我剛剛又試了一下用OpenNI2,如果user跟hand在同個thread進行readFrame會發生異常存取,不太確定是不是OpenNI2不可以同時開啟user跟hand的tracker device?話說我用VC9
http://codepad.org/zBmlQlX8
讚讚
幾個點:
1. 先把 OpenNI 的東西都先設定好,再呼叫 NiTE::Initialize()
2. setImageRegistrationMode() 應該是要在 VideoStream 都建立出來才有用
3. VideoStream 的 setVideoMode() 要在 create() 之後
4. 建議在 HandTracker / UserTracker 的 create() 裡,把要使用的 Device 傳進去
這樣再試試看。
BTW, 自己測試的結果,HandTracker 一定要比 UserTracker 早建立,不然會出問題。
讚讚
[…] 這篇基本上算是之前《NiTE 2 的手勢偵測》和《NiTE 2 的手部追蹤》這兩篇文章的延伸。主要,是透過 OpenCV 來寫個簡單的範例,示範如何把 NiTE2 抓到的手部相關資料畫出來。 […]
讚讚
你好,我想要請教一下我要如何得知NITE2中是如何去定義
GESTURE_WAVE、GESTURE_CLICK 、GESTURE_HAND_RAISE
這三個動作呢?
因為我下載了NITE2之後並實際的去運作了,
HandViewer、SimpleHandTracker這兩個程式,
發現明明都是做「CLICK」的動作,
但是HandViewer卻很輕鬆的可以偵測到,
反之SimpleHandTracker相當的遲鈍,通常都做了好幾十次才會偵測到(WAVE也是),
不知道能不能看到其中的CODE進而了解呢?
讚讚
由於 NiTE 不是開放原始碼的函式庫,所以無從得知其內部定義。
讚讚
恩恩,了解。
再請教一下,關於「WAVE」的動作,
我與同學之間都做過測試,
發現這個動作的觸發實在有點困難?
我們是用手掌面對Xtion左右快速揮手(小幅),
通常要大約來回5、6次才會觸發,
不知道有沒有更簡單的方式?
另外,如果希望自訂動作的話,
是不是只能自製MIDDLEWARE了呢?
讚讚
NiTE 的手勢偵測方法,並沒有參數可以調整。
所以如果不合用的話,基本上只能試著去用其他 middleware libraryu 看看了。
讚讚
[…] 這篇是延續前一篇的《NiTE 2 的手勢偵測》,繼續來講 NiTE 2 所提供的手部追蹤功能。NiTE 2 所提供的手部追蹤功能,和手勢偵測一樣,是由 HandTracker 提供的,而在使用上則是和在 OpenNI 1.x 的時候(參考)一樣,都是要先偵測到手的位置,然後再針對這個位置進行追蹤;所以一般在使用上,大多是搭配手勢偵測,找到手的位置後,再開始追蹤。 […]
讚讚