在之前的文章,已經針對 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 );」這行拿掉,不要針對這個手勢進行偵測會比較好。
main loop为什么是3000次的loop,而不是其他的呢:per::per:
to wolf
那個是隨意設定的,可以自行修改。
只是單純看你要讓程式跑多久而已。
您好我是初學者!!請問一下mHandTracker.create()失敗,
Can’t create hand tracker,這種情形要怎麼解決呢?謝謝
to John
建議請先確認在你的環境下,NiTE 官方的範例可以正常運作。
並且確定檔案的配置都沒有問題。
您好,openni手势识别中,总有疑似手的其他物体干扰,有什么好的解决办法吗?
to Avril
個人會建議先在背景較單純的環境試試看,看看還會不會有問題。
如果是背景太複雜的話,應該就是得想辦法過濾背景環境了。