之前已經大該把 Kinect for Windows SDK 一些基本的資料讀取都講過,而這一篇,就來整理一下,怎麼透過 K4W SDK v2 來取得人體骨架的資料吧~
在 K4W SDK v2 裡面,用來進行人體骨架分析的介面,是被稱為「Body」的這一群介面,包含了 IBodyFrameSource、IBodyFrameReader、IBodyFrame、IBody;前面三個以「BodyFrame」為名稱的介面,在使用上大致上都和其他的 Frame 介面有相同的操作邏輯。
不過由於 IBodyFrame 實際上並不是一個畫面,所以他的介面就和其他類型資料的 farme 有一定程度的差異。
首先,要讀取人體骨架的資料,是要使用 IBodyFrame 提供的 GetAndRefreshBodyData() 這個函式(MSDN);執行這個函式時,需要給他一個 IBody 的指標陣列,讓他把人體骨架的資訊寫進去。
而要先建立這個 IBody 的指標陣列的話,就需要先透過 IBodyFrameSource 的 get_BodyCount() 這個函式,來取得 K4W SDK v2 能處理的人體骨架數量(目前版本是 6),並將初始值都設定成 nullprt;這樣的程式可以寫成下面的形式:
pFrameSource->get_BodyCount(&iBodyCount);
IBody** aBody = new IBody*[iBodyCount];
for (int i = 0; i < iBodyCount; i)
aBody[i] = nullptr;
其中,pFrameSource 是 IBodyFrameSource 的指標,而 iBodyCount 就是能追蹤的骨架數量、aBody 就是之後讓 K4W SDK v2 紀錄骨架資料的陣列。
而之後在取得 IBodyFrame 的物件後、就可以透過 GetAndRefreshBodyData() 這個函式,把最新的骨架資料、寫到 aBody 裡;程式大致上可以寫成下面的樣子:
{
//…
}
而在運作正確的狀況下,aBody 裡面就會儲存了 iBodyCount 個最新的人體骨架資料。
但是由於在場景裡,並不一定隨時都有這麼多個使用者,所以實際上要個別 IBody 的物件前,要先透過他提供的 get_IsTracked() 這個函式,來確認這個 IBody 是否正被追蹤。
這邊程式的寫法可以寫成下面的樣子:
if ((pBody->get_IsTracked(&bTracked) == S_OK) && bTracked)
{
//…
}
當確定 IBody 正在被追蹤的情況下,接下來就可以使用他提供的各種函式(MSDN)、來讀取我們需要的資料了。
那 IBody 到底可以提供那些資訊呢?
基本上,一般會用到的,主要應該就是可以透過 GetJoints() 來取得所有關節點的位置資訊,或透過 GetJointOrientations() 來取得所有關節點的方向性。另外,K4W SDK v2 也可以透過 get_HandRightState() 和 get_HandLeftState() 這兩個函式,來取得兩手的狀態。
而在骨架的關節的部分,K4W SDK v2 總共定義了 25 個關節點,並透過 JointType 來列舉(MSDN);這 25 個關節點的示意圖,基本上就是右邊的樣子。
身體中心的五個關節點,由上而下依序是:
- JointType_Head(頭)
- JointType_Neck(脖子)
- JointType_SpineShoulder(脊椎肩膀中間)
- JointType_SpineMid(脊椎中央)
- JointType_SpineBase(脊椎底)
手部的話,以右手來說,由上而下,則依序是:
而分支出去的一個,則是 JointType_ThumbRight,代表右手的拇指。
至於腿的部分,以右腿來說,由上而下則依序是:
另外,在 JointType 裡面,也有另外定義「JointType_Count」,來代表總共的關節數目。
而要讀取關節位置資訊時,K4W SDK v2 是把它定義成「Joint」這個型別,裡面記錄了三個資料,一個是 JointType、代表自己是哪個關節點,一個是 Position、是用 CameraSpacePoint 來記錄這個關節點在「攝影機空間座標系統」裡的位置(請參考《使用 OpenGL 繪製場景》);最後則是 TrackingState、用來記錄這個關節的追蹤狀態(MSDN)。
要讀取關節位置的程式,基本上可以寫成像下面這樣:
pBody->GetJoints(JointType::JointType_Count, aJoints);
也就是先宣告一個大小為 JointType_Count 的 Joint 陣列,然後再透過 IBody 的 GetJoints() 這個函式,來把資料寫進去了。
之後,就可以直接去存取不同的關節點的位置資訊了~例如要取得右手的位置並輸出的話,就是:
if (rJointPos.TrackingState != TrackingState_NotTracked)
{
cout << rJointPos.Position.X << "/"
<< rJointPos.Position.Y << "/"
<< rJointPos.Position.Z << endl;
}
可以看到,這邊在輸出位置的資訊前,有先去檢查關節的 TrackingState。之所以要這樣做,是由於一個人的骨架不見得所有的關節點都可以被 Kinect 感應器完整地看到、追蹤,所以有的時候會有局部的關節是無法被正確追蹤的;所以在使用關節的資料前,最好先檢查一下關節的 TrackingState、確認這個關節點的可以用性,而如果要更嚴謹的話,應該是連同 TrackingState_Inferred 狀態都排除會更好。
至於關節方向性的部分,K4W SDK v2 則是使用 JointOrientation 這個結構來紀錄的。在它裡面設紀錄了 JointType 和 Orientation 兩種資料;前者是記錄自己是哪個關節點,後者則是以 Vector4 的形式、來記錄這個關節點的方向性。
雖然官方文件似乎沒有明確地說明這個紀錄方向性的 Vector4 的意義,不過就 Heresy 個人判斷,應該和 NiTE 2 一樣,是採用 quaternion 的形式(參考《處理 NiTE2 的骨架關節點方向性資訊:Quaternion》)吧?不過,這點 Heresy 自己還沒下去驗證就是了。
而如果把讀取關節資訊的程式整個寫出來的,應該會變得像下面的樣子:
if (pFrameReader->AcquireLatestFrame(&pFrame) == S_OK)
{
pFrame->GetAndRefreshBodyData(iBodyCount, aBody);
for (int i = 0; i < iBodyCount; i)
{
IBody* pBody = aBody[i];
// check if is tracked
BOOLEAN bTracked = false;
if ((pBody->get_IsTracked(&bTracked) == S_OK) && bTracked)
{
// get joint position
Joint aJoints[JointType::JointType_Count];
pBody->GetJoints(JointType::JointType_Count, aJoints);
// get joint orientation
JointOrientation aOrientations[JointType::JointType_Count];
pBody->GetJointOrientations(JointType::JointType_Count, aOrientations);
// output information
JointType eJointType = JointType::JointType_Head;
const Joint& rJointPos = aJoints[eJointType];
const JointOrientation& rJointOri = aOrientations[eJointType];
if (rJointPos.TrackingState != TrackingState_NotTracked)
{
cout << rJointPos.Position << "\n" << rJointOri.Orientation << endl;
}
}
}
// 4e. release frame
pFrame->Release();
}
整個完成的程式碼,則可以參考 Heresy 放在 GitHub 上的檔案(連結)。
另外一提,在使用方向性資訊的時候,可能也需要注意一下的,是有的關節點似乎還是沒有方向性資訊的。像是 Heresy 在試著讀取頭部(JointType_Head)的資訊的時候,他的方向性資訊就永遠都是 (0,0,0,0);這點應該也是在撰寫程式時,可能要預先作檢查的地方。
至於 K4W SDK v2 的 Body 提供的其他資訊,就之後再提吧~
[i]加入斜體文字[/i]:per::lol::lol::lol::lol::lol::lol::lol::x:cry::cry::x:per::o:o:(:D:(:(:o:per::lol::lol::cry::cry::x:x:lol::per::per::o
[i]加入斜體文字[/i]:per::lol::lol::lol::lol::lol::lol::lol::x:cry::cry::x:per::o:o:(:D:(:(:o:per::lol::lol::cry::cry::x:x:lol::per::per::o
[i]加入斜體文字[/i]:per::lol::lol::lol::lol::lol::lol::lol::x:cry::cry::x:per::o:o:(:D:(:(:o:per::lol::lol::cry::cry::x:x:lol::per::per::o
[i]加入斜體文字[/i]:per::lol::lol::lol::lol::lol::lol::lol::x:cry::cry::x:per::o:o:(:D:(:(:o:per::lol::lol::cry::cry::x:x:lol::per::per::o