NiTE 2 的姿勢偵測


之前的文章裡,已經介紹過 NiTE 2 在 OpenNI 2 架構下的人體骨架分析的方法。而接下來這一篇,則是來介紹一下,怎樣來使用 NiTE 2 提供的姿勢偵測功能。

NiTE 2 的姿勢偵測的功能,和人體骨架分析一樣,是由 UserTracker 提供的;在目前的版本裡,只有提供兩種姿勢,一個是「POSE_PSI」、一個則是「POSE_CROSS_HAND」。而和在 OpenNI 1.x 的時代一樣,NiTE 2 依然是沒辦法提供自訂姿勢的偵測,算是有點可惜的。

而所謂的「PSI」姿勢,實際上就是如同右圖的姿勢,這個姿勢在 OpenNI 1 + NiTE 1 的時候,是用來做骨架校正的標準姿勢(參考);由於後來的 NiTE 有提供不用校正姿勢的骨架追蹤了,所以就用不太到了。也由於他最早是用來做骨架校正的姿勢,使用頻率非常高,所以要偵測到這個姿勢相當地簡單,只要擺個差不多的姿勢,就可以觸發到了~

而「Cross hand」的姿勢,理論上應該是雙手在身前交叉的動作(應該是吧?),不過相較於「PSI」姿勢,「Cross hand」這個姿勢在 Heresy 這邊測試的結果,觸發到的機率相當低…這也讓 Heresy 在懷疑,所謂的「Cross hand」姿勢到底該怎麼擺了… orz (有人知道的話記得講一下啊~)


至於要怎麼使用 UserTracker 來進行姿勢偵測呢?他的概念其實和人體骨架的追蹤一樣,都是在偵測到新的使用者的時候,針對個別使用者,要求 UserTracker 開始進行姿勢的偵測;然後接下來在每次更新的時候,去取出姿勢的資料,進行判斷、以及後續的處理。

下面就是一個簡單的範例:

// 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 user tracker
  UserTracker mUserTracker;
  mUserTracker.create();
  
  // main loop
  for( int i = 0; i < 3000; ++ i )
  {
    // 4. get user frame
    UserTrackerFrameRef mUserFrame;
    if( mUserTracker.readFrame( &mUserFrame ) != STATUS_OK )
      continue;
  
    // 5. get users' data
    const Array<UserData>& aUsers = mUserFrame.getUsers();
    for( int i = 0; i < aUsers.getSize(); ++ i )
    {
      const UserData& rUser = aUsers[i];
      const UserId& uID = rUser.getId();
  
      if( rUser.isNew() )
      {
        cout << "User " << uID << " found." << endl;
  
        // 5a. start pose detection for new user
        cout << " > Start pose detection" << endl;
        mUserTracker.startPoseDetection( uID, POSE_PSI );
        mUserTracker.startPoseDetection( uID, POSE_CROSSED_HANDS );
      }
      else if( rUser.isLost() )
      {
        cout << "User " << uID << " lost." << endl;
      }
      else
      {
        // 5b. get user pose
        const PoseData& rPosePSI = rUser.getPose( POSE_PSI );
        if( rPosePSI.isEntered() )
          cout << " > start PSI pose" << endl;
        if( rPosePSI.isExited() )
          cout << " > stop PSI pose" << endl;
  
        const PoseData& rPoseCH = rUser.getPose( POSE_CROSSED_HANDS );
        if( rPoseCH.isEntered() )
          cout << " > start Cross Hand pose" << endl;
        if( rPoseCH.isExited() )
          cout << " > stop Cross Hand pose" << endl;
      }
    }
  }
  
  // 6. shutdown
  mUserTracker.destroy();
  NiTE::shutdown();
  
  return 0;
}

這個程式基本上和人體骨架追蹤的程式非常地接近,只又在進入主迴圈後,針對每一個使用使用者個別進行處理時(5 開始)的所做動作有所不同。

在「5a」的部分,在偵測到新的使用者後,這邊是去呼叫 UserTrackerstartPoseDetection() 這個函式,針對目前的使用者(uID) ,開始偵測特定的姿勢;而在這邊,是可以針對不同的姿勢,個別進行設定的,所以如果要同時偵測「POSE_PSI」和「POSE_CROSS_HAND」的話,就需要呼叫 startPoseDetection() 兩次。

而到了「5b」的部分,就是透過 UserDatagetPose() 這個函式,來取得目前的姿勢狀態。Heresy 覺得比較特別的,是這邊也是針對個別姿勢,去取得對應的資料(有可能同時擺出兩個不同的姿勢嗎?);所以在有兩種姿勢的情況下,就需呼叫兩次了。

getPose() 所回傳的資料,是型別為 PoseData 的資料,他除了有 getType() 這個取得姿勢類型的函式外,還提供了 isEntered()isExited()isHeld() 這三個函式,用來判斷這個姿勢的狀態。

isEntered() 基本上就是代表剛擺出這個姿勢、isExited() 則是代表已經離開了這個姿勢,isHeld() 則代表目前還維持在這個姿勢。理論上,isEntered() 回傳 true 的狀態應該只會在使用者擺出這個姿勢時發生一次、再來就是持續維持 isHeld() 回傳 true 的狀態,直到使用者不再維持這個姿勢、isExited() 回傳 true。而其他時候,則就應該是三者都匯回傳 false

而實際寫成程式,大概就會像上面範例的「5b」的部分了~不過,Heresy 在這邊,並沒有去處理 isHeld() 就是了。

這個範例程式在執行後,不會有圖形介面,而在使用者被偵測到、擺出姿勢時,命令提示字元的視窗裡,都會出現對應的訊息,做為測試的結果。而整個程式會執行 3,000 個 frame,如果有需要的話,可以自行修改迴圈的部分。


OpenNI / Kinect 相關文章目錄

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

  1. Heresy您好~
    非常感謝您詳細且精闢的分享~~!!
    讓我們在openNI及NiTE的開發上少走了很多冤枉路~^^

    有關"Cross hand"的姿勢,
    我個人測試, 在任何地方手臂交叉都可以偵測的到(例如頭上, 胸前, 身左, 身右等等…)
    而在NiTE官方範例程式"UserViewer.exe"中,
    只要在骨架被追蹤的狀態下,
    擺出雙臂交叉的動作, 就會在程式左上角出現:
    “In exit pose, keep it for 2 seconds to exit" 的紅色字樣,
    維持2秒後程式就會自動關閉~可以拿來測試~
    僅提供一點小經驗, 希望能做為參考~^^

    • 恩,感謝告知。不過不知道為什麼 Heresy 自己擺這個姿勢,似乎都很難成功。
      晚點再試試看好了。

發表留言

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