在之前的文章裡,已經介紹過 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」的部分,在偵測到新的使用者後,這邊是去呼叫 UserTracker 的 startPoseDetection() 這個函式,針對目前的使用者(uID) ,開始偵測特定的姿勢;而在這邊,是可以針對不同的姿勢,個別進行設定的,所以如果要同時偵測「POSE_PSI」和「POSE_CROSS_HAND」的話,就需要呼叫 startPoseDetection() 兩次。
而到了「5b」的部分,就是透過 UserData 的 getPose() 這個函式,來取得目前的姿勢狀態。Heresy 覺得比較特別的,是這邊也是針對個別姿勢,去取得對應的資料(有可能同時擺出兩個不同的姿勢嗎?);所以在有兩種姿勢的情況下,就需呼叫兩次了。
getPose() 所回傳的資料,是型別為 PoseData 的資料,他除了有 getType() 這個取得姿勢類型的函式外,還提供了 isEntered()、isExited() 和 isHeld() 這三個函式,用來判斷這個姿勢的狀態。
isEntered() 基本上就是代表剛擺出這個姿勢、isExited() 則是代表已經離開了這個姿勢,isHeld() 則代表目前還維持在這個姿勢。理論上,isEntered() 回傳 true 的狀態應該只會在使用者擺出這個姿勢時發生一次、再來就是持續維持 isHeld() 回傳 true 的狀態,直到使用者不再維持這個姿勢、isExited() 回傳 true。而其他時候,則就應該是三者都匯回傳 false。
而實際寫成程式,大概就會像上面範例的「5b」的部分了~不過,Heresy 在這邊,並沒有去處理 isHeld() 就是了。
這個範例程式在執行後,不會有圖形介面,而在使用者被偵測到、擺出姿勢時,命令提示字元的視窗裡,都會出現對應的訊息,做為測試的結果。而整個程式會執行 3,000 個 frame,如果有需要的話,可以自行修改迴圈的部分。
您好,我想问一下用kinect 的骨骼识别跟踪时,当两个关节点重合的时候,画面显示的骨架模型就会一直抖动,这个问题有办法解决么?
开始我使用的是SDK 1.6开发,但是代码不是开源的,没有办法知道SDK的骨骼平滑是怎么做的;
现在使用的是OPENNI 1.5 但是发现骨骼数据提取是NITE提供的,好像代码也不是开源的;
那kinect 骨骼提取部分,一直就没有源代码么?是么?
to vivi
是的,NiTE 和 K4W SDK 的骨架追蹤都不是 opensource 的,所以無法知道他們內部的實作方法。
那想问您一下,当两个关节点重合的时候,骨骼的跟踪就不准确了,这个有办法解决么?
to vivi
基本上這是演算法的問題,無解。
还想咨询您一下,骨骼坐标、深度坐标和彩色图像坐标之间如何转换呢
to vivi
請參考之前的文章
例如:
http://viml.nchc.org.tw/blog/paper_info.php?CLASS_ID=1&SUB_ID=1&PAPER_ID=434
博主你好,我在运行示例代码时,程序停在mUserTracker.create();这里,后面的代码都没有运行,请问这是什么原因?
to hank
建議請先確認官方的範例可以正確地執行。
再來,就是 NiTE 的相關檔案是否有準備好?
如果想要在人體骨架旁用點座標標點,這該如何做?
to 程式新手
抱歉,不太懂你的問題。
如何在影像裡給定座標,繪製座標點?
to 程式新手
如果你是要繪製的範例的話,請參考
http://viml.nchc.org.tw/blog/paper_info.php?CLASS_ID=1&SUB_ID=1&PAPER_ID=435