NiTE 2 的手勢偵測

在之前的文章,已經針對 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 下。在開始使用前,要先呼叫 NiTEinitialize() 來完成整個環境的初始化,結束時則需要呼叫 shutdown() 來做終止的動作。(請參考《NiTE2 基本使用》)

在完成整體環境的初始化後,則是要建立出我們所需要的 NiTE 模組,這邊就是 HandTracker。這邊首先先宣告出 HandTracker 的物件 mHandTracker,然後呼叫他的 create() 函式,來完成 HandTracker 的建立(3 的部分)

UserTracker 一樣,HandTrackercreate() 這個函式,實際上是可以透過指定一個 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 );」這行拿掉,不要針對這個手勢進行偵測會比較好。

6 thoughts on “NiTE 2 的手勢偵測”

  1. main loop为什么是3000次的loop,而不是其他的呢:per::per:

  2. to wolf

    那個是隨意設定的,可以自行修改。
    只是單純看你要讓程式跑多久而已。

  3. 您好我是初學者!!請問一下mHandTracker.create()失敗,
    Can’t create hand tracker,這種情形要怎麼解決呢?謝謝

  4. to John

    建議請先確認在你的環境下,NiTE 官方的範例可以正常運作。
    並且確定檔案的配置都沒有問題。

  5. 您好,openni手势识别中,总有疑似手的其他物体干扰,有什么好的解决办法吗?

  6. to Avril

    個人會建議先在背景較單純的環境試試看,看看還會不會有問題。
    如果是背景太複雜的話,應該就是得想辦法過濾背景環境了。

發佈回覆給「heresy」的留言 取消回覆

發佈留言必須填寫的電子郵件地址不會公開。