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 );」這行拿掉,不要針對這個手勢進行偵測會比較好。


OpenNI / Kinect 相關文章目錄

對「NiTE 2 的手勢偵測」的想法

  1. […] PrimeSense 的 NiTE 主要是提供了人的骨架追蹤,以及手部追蹤的功能,基本上應該算是在 OpenNI 這個架構下,最重要、也最常被使用的 middleware library 了。不過由於他的手部追蹤的功能,僅能提供位置的追蹤、和簡單的動態手勢辨識,並沒有辦法去針對手的狀態、或是手指作分析,算是比較可惜的地方。 […]

  2. 話說問一下似乎我實驗一下HandTracker不能和UserTracker同時使用否則會Exception?不知道是否正確?

        • 幾個點:
          1. 先把 OpenNI 的東西都先設定好,再呼叫 NiTE::Initialize()
          2. setImageRegistrationMode() 應該是要在 VideoStream 都建立出來才有用
          3. VideoStream 的 setVideoMode() 要在 create() 之後
          4. 建議在 HandTracker / UserTracker 的 create() 裡,把要使用的 Device 傳進去

          這樣再試試看。

          BTW, 自己測試的結果,HandTracker 一定要比 UserTracker 早建立,不然會出問題。

  3. 你好,我想要請教一下我要如何得知NITE2中是如何去定義
    GESTURE_WAVE、GESTURE_CLICK 、GESTURE_HAND_RAISE
    這三個動作呢?
    因為我下載了NITE2之後並實際的去運作了,
    HandViewer、SimpleHandTracker這兩個程式,
    發現明明都是做「CLICK」的動作,
    但是HandViewer卻很輕鬆的可以偵測到,
    反之SimpleHandTracker相當的遲鈍,通常都做了好幾十次才會偵測到(WAVE也是),
    不知道能不能看到其中的CODE進而了解呢?

      • 恩恩,了解。
        再請教一下,關於「WAVE」的動作,
        我與同學之間都做過測試,
        發現這個動作的觸發實在有點困難?
        我們是用手掌面對Xtion左右快速揮手(小幅),
        通常要大約來回5、6次才會觸發,
        不知道有沒有更簡單的方式?
        另外,如果希望自訂動作的話,
        是不是只能自製MIDDLEWARE了呢?

        • NiTE 的手勢偵測方法,並沒有參數可以調整。
          所以如果不合用的話,基本上只能試著去用其他 middleware libraryu 看看了。

  4. […] 這篇是延續前一篇的《NiTE 2 的手勢偵測》,繼續來講 NiTE 2 所提供的手部追蹤功能。NiTE 2 所提供的手部追蹤功能,和手勢偵測一樣,是由 HandTracker 提供的,而在使用上則是和在 OpenNI 1.x 的時候(參考)一樣,都是要先偵測到手的位置,然後再針對這個位置進行追蹤;所以一般在使用上,大多是搭配手勢偵測,找到手的位置後,再開始追蹤。 […]

發表留言

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料