主要內容:
一、姿勢探測識別
1.1 內容介紹
NITE 2的姿勢探測識別功能和人體骨骼跟蹤一樣,是由UserTracker提供的,在NiTE 2.0版本中,提供了兩種姿勢:“POSE_PSI”(我稱它為“投降姿勢”)和“POSE_CROSS_HAND”(稱之為“雙手抱胸”),除此之外,我們沒辦法提供自己設定的特定姿勢的探測和識別。
在之前的版本中,由於“POSE_PSI”是用來做骨架跟蹤校正的標志姿勢使用的,但后來NITE提供了不用校正姿勢的情況下就可以進行骨架跟蹤了,所以在沒有特殊使用的情況下,本人(談談NITE 2與OpenCV結合的第二個程序(提取人體骨骼坐標))覺得在骨骼跟蹤上,姿勢的探測已然成為了多余的了;但對於使用姿勢識別有關方面的研究的,我想這個姿勢探測識別應該還是重要的,沒准在NITE后面的版本中提供”自制的特定姿勢“跟蹤識別了。
由於在NITE中主要提供的是人體跟蹤和手的跟蹤,而人體姿勢探測屬於前者,所以姿勢探測識別和人體骨骼跟蹤一樣,都是使用UserTracker。首先通過獲得新的使用者信息;然后根據指定使用者,利用UserTracker開始進行姿勢的探測;最后根據每次探測到最新的姿勢資料,進行判定識別,以及開始我們自己需要的處理工作。
1.2 代碼介紹
同樣的,直接上代碼說明如何利用UserTracker進行姿勢探測。
#include "stdafx.h" #include <iostream> // 載入NiTE頭文件 #include <NiTE.h> // using namespace using namespace std; using namespace nite; int main( int argc, char** argv ) { // 初始化NiTE環境 NiTE::initialize(); // 創建UserTracker跟蹤器 UserTracker mUserTracker; mUserTracker.create(); while(true) { // 讀取幀信息 UserTrackerFrameRef mUserFrame; mUserTracker.readFrame( &mUserFrame); // 通過幀信息,獲得用戶數據UserData 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; // 為每一個新用戶進行姿勢探測 cout << "Start pose detection " << uID<< endl; // 探測uID的兩種姿勢 mUserTracker.startPoseDetection( uID, POSE_PSI ); mUserTracker.startPoseDetection( uID, POSE_CROSSED_HANDS ); } else if( rUser.isLost() ) { cout << "User " << uID << " lost." << endl; } else { // 讀取用戶的“POSI_PSI”的姿勢狀態 const PoseData& rPosePSI = rUser.getPose( POSE_PSI ); // 當做了“POSI_PSI”時: if( rPosePSI.isEntered() ) cout << " 開始---投降姿勢(PSI pose)" << endl; if( rPosePSI.isHeld() ) cout << " 保持---投降姿勢(PSI pose)" << endl; // 當做完了“POSI_PSI”后,雙手放下時: if( rPosePSI.isExited() ) cout << "停止---投降姿勢(PSI pose)" << endl; // 同樣的讀取“POSE_CROSSED_HANDS”的姿勢狀態 const PoseData& rPCH = rUser.getPose( POSE_CROSSED_HANDS ); if( rPCH.isEntered() ) cout << " 開始---雙手抱胸(Cross Hand pose)" << endl; if( rPCH.isHeld() ) cout << " 保持---雙手抱胸(Cross Hand pose)" << endl; if( rPCH.isExited() ) cout << " 停止---雙手抱胸(Cross Hand pose)" << endl; } } } // 關閉UserTracker跟蹤 mUserTracker.destroy(); // 關閉NITE環境 NiTE::shutdown(); return 0; }
上圖:
當開始姿勢探測時,只要雙手舉起,PSI就會觸發,使rPosePSI.isEntered()值為true;當保持着這個姿勢一段時間,就會使rPosePSI.isHeld()值為true,表示目前的狀態為保持着投降姿勢;當雙手放下時,rPosePSI.isExited()值為true,表示不再保持着投降姿勢。同樣的道理,當你做出”雙手抱胸“的姿勢(POSE_CROSSED_HANDS)時,也同樣提供isEntered()、isHeld()和isExited()三個函數來表示當前探測的姿勢的狀態情況。
注:當我無論如何做出”雙手抱胸“的姿勢(POSE_CROSSED_HANDS)時,都無法觸發這一姿勢的跟蹤,所以我的結論是我還不知道怎么去”雙手抱胸“~~~
1.3 總結
對於姿勢探測識別,主要包括以下幾個步驟:
- 初始化NiTE環境:NiTE::initialize();
- 創建UserTracker跟蹤器:UserTracker::create();
- 讀取跟蹤器幀信息:UserTracker::readFrame( &UserTrackerFrameRef);
- 通過幀信息,獲得用戶數據UserData:mUserFrame::getUsers();
- 對特定用戶開始姿勢探測(包括”投降姿勢“和”雙手抱胸姿勢“的探測):UserTracker::startPoseDetection(UserId user, PoseType type);
- 讀取用戶的指定姿勢的狀態信息:PoseData& getPose(PoseType type);
- 主要有isEntered()、isHeld()、isExited()三個函數來表示當前探測的姿勢的狀態情況;
- 停止UserTracker跟蹤器:UserTracker::destroy();
- 最后停止NiTE環境:NiTE::shutdown();
二、手勢探測識別
2.1 內容介紹
在NITE中,手勢探測識別主要是由HandTracker類提供的,和UserTracker一樣,HandTracker還提供的手部位置的跟蹤(談談NITE 2的第一個程序HandViewer和談談NITE 2與OpenCV結合的第一個程序,以及談談NITE 2與OpenCV結合提取指尖坐標等處都做了介紹了)。根據目前的NITE提供的手勢跟蹤和之前的版本個人感覺差不多。首先都是不針對指定用戶的手勢識別(這點和姿勢探測識別不一樣),而是針對整個界面幀信息進行分析,找到符合的手勢;其次探測識別的手勢只有三個:”GESTURE_WAVE“(揮手)、”GESTURE_CLICK“(手掌前推在縮回來)和”GESTURE_HAND_RAISE“(手舉起)。最后目前都沒有提供自制的手勢探測(這點和姿勢探測一樣)。
2.2 代碼說明
#include "stdafx.h" #include <iostream> // 載入NiTE頭文件 #include <NiTE.h> // using namespace using namespace std; using namespace nite; int main( int argc, char** argv ) { // 初始化NiTE環境 NiTE::initialize(); // 創建HandTracker跟蹤器 HandTracker mHandTracker; mHandTracker.create(); // 設定手勢探測(GESTURE_WAVE、GESTURE_CLICK和GESTURE_HAND_RAISE) mHandTracker.startGestureDetection( GESTURE_WAVE ); mHandTracker.startGestureDetection( GESTURE_CLICK ); mHandTracker.startGestureDetection( GESTURE_HAND_RAISE ); while(true) { // 讀取幀信息 HandTrackerFrameRef mHandFrame; mHandTracker.readFrame( &mHandFrame ); // 整個界面幀信息進行分析,找到符合的手勢 const Array<GestureData>& aGestures = mHandFrame.getGestures(); for( int i = 0; i < aGestures.getSize(); ++ i ) { const GestureData& rGesture = aGestures[i]; // 對找到的手勢進行類型判斷,並輸出類型 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; } // 得到的手勢信息中還包含了當前手勢的坐標位置 const Point3f& rPos = rGesture.getCurrentPosition(); cout << " 手勢位置為: (" << rPos.x << ", " << rPos.y << ", " << rPos.z << ")" << endl; // 以及手勢狀態,完成狀態和進行狀態 if( rGesture.isComplete() ) cout << " 手勢完成"; if( rGesture.isInProgress() ) cout << " 手勢正在進行"; cout << endl; } } // 關閉HandTracker跟蹤 mHandTracker.destroy(); // 關閉NITE環境 NiTE::shutdown(); return 0; }
上圖:
注:三個手勢中”舉起“手勢太好識別了,直接掩蓋了其它兩個手勢,所以在實際的手勢識別中,我建議不用檢測”舉起“這個手勢~~~。我把上面代碼中mHandTracker.startGestureDetection( GESTURE_HAND_RAISE );給注釋了,再看運行結果:
注:排除了”舉起“手勢的搗亂之后,發現”前推並收回“手勢(click),動作最好掌控,識別過程和效果也是最好(個人覺得);同時我怎么”搖手“(wave),好像都出不了檢測結果,具體什么原因現在我也不知道(估計又是我手勢做的不對),有知道的煩請教教我,謝謝~~~
2.3 總結
具體手勢探測識別流程和1.3的相似,看了上面的代碼肯定知道,所以這里就不寫了。最后我的建議是:如果要進行手勢探測識別的話,我提議是探測”click“手勢~~~
寫的粗糙~~~