Kinect V2開發(5)讀關節資料
Kinect能取得Depth(物體與感測器的距離資訊)和BodyIndex(人物索引),基於這些資料可以偵測到人體的骨骼資訊並追蹤,在Kinect V2的SDK 2.0中,它最多可以同時獲取到6個人、每個人25個關節點的資訊,並且通過深度攝像頭,可以同時獲取到這些關節點的座標。此時的座標使用的是Camera Space,也就是攝像機空間座標系,代表了物體距離深度攝像頭的距離。
Kinect的人體姿勢,是向學習了基於龐大數量的姿勢資訊的識別器裡,輸入人體區域的資訊來推定的(注:因為男女老少高矮胖瘦體形各不相同,所以必須基於神經網路的資料庫才能準確識別人體)。這個技術出自微軟在IEEE CVPR 2011(計算機視覺及模式認識領域的首位會議)發表,獲獎Best Paper。
Microsoft Research
文章我正在讀,之後有時間會傳一篇閱讀筆記。
要讀取骨骼資料,前面的步驟和之前一樣,要先通過IKinectSensor
來取得 IBodyFrameSource
,然後開啟 IBodyFrameReader
,之後再在主迴圈裡取得 IBodyFrame
裡面的資料,但是在IBodyFrame
裡面實際上包括了所有人的資料,需要通過GetAndRefreshBodyData()
這個函式寫入IBody
這個類裡面再進行個別讀取。可以另外設定一個變數代表一個IBody陣列,寫入資料後即可以讀取每個人的骨架資料。
通過IBodyFrameSource
get_BodyCount()
可以取得iBodyCount
代表可以讀取到追蹤的人數,目前來說最多就是6個人。 通過
IBody
中的get_IsTracked()
這個函式可以判斷某個人是否正在被追蹤 通過
IBody
中的GetJoints()
這個函式可以得到所有關節點的位置資訊。 位置資訊被定義成
Joint
這個類別,裡面包含三個引數,第一個是JointType
,代表是哪個關節點,我們可以在JointType Enumeration中看到具體的列舉;第二個是Position
,是用CameraSpacePoint
來記錄這個關節點在攝像頭空間座標系裡的位置(如果要用來在2D影象中顯示,需要做座標轉換);第三個是TrackingState
通過IBody
中的GetJointOrientations()
這個函式可以得到關節點的方向
IBody
中還有兩個函式get_HandRightState()
和 get_HandLeftState()
可以用來獲取兩手的狀態資料。
我做了一個上半身骨骼資訊讀取,效果圖如下:
使用者編號是看起來是隨機的,我自己測試的時候每一次編號都不太一樣,但是在0-5範圍,位置變化的時候不一定能讀取到幾個關節,離Kinect比較近的話只能讀到一個關節。然後這個位置和方向的資料是讀出來了,但是是否正確還不知道怎麼驗證。
程式碼如下:
#include <iostream>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <Kinect.h>
using namespace std;
using namespace cv;
const string get_name(int n); //此函式判斷出關節點的名字
int main(void)
{
// 1a.獲取感應器
IKinectSensor* pSensor = nullptr;
GetDefaultKinectSensor(&pSensor);
// 1b. 開啟感應器
pSensor->Open();
/****************2.開啟深度影象閱讀器************************/
// 取得深度資料
IDepthFrameSource* pDepthSource = nullptr;
pSensor->get_DepthFrameSource(&pDepthSource);
// 取得深度資料的描述資訊(寬、高)
int iDepthWidth = 0;
int iDepthHeight = 0;
IFrameDescription* pDepthDescription = nullptr;
pDepthSource->get_FrameDescription(&pDepthDescription);
pDepthDescription->get_Width(&iDepthWidth);
pDepthDescription->get_Height(&iDepthHeight);
// 開啟深度資料閱讀器
IDepthFrameReader* pDepthReader = nullptr;
pDepthSource->OpenReader(&pDepthReader);
pDepthDescription->Release();
pDepthDescription = nullptr;
// 釋放變數pDepthSource
pDepthSource->Release();
pDepthSource = nullptr;
/*******************3.開啟Body資料的閱讀器*******************/
// 取得Body資料
IBodyFrameSource* pBodySource = nullptr;
pSensor->get_BodyFrameSource(&pBodySource);
// 取得Body資料的描述資訊(數量)
int iBodyCount = 0;
pBodySource->get_BodyCount(&iBodyCount);
IBody** aBody = new IBody*[iBodyCount];
for (int i = 0; i < iBodyCount; ++i)
aBody[i] = nullptr;
// 開啟Body資料閱讀器
IBodyFrameReader* pBodyReader = nullptr;
pBodySource->OpenReader(&pBodyReader);
// 釋放變數pBodySource
pBodySource->Release();
pBodySource = nullptr;
/*******************4.為顯示深度影象做準備******************/
Mat img16(iDepthHeight, iDepthWidth, CV_16UC1);
Mat img8(iDepthHeight, iDepthWidth, CV_8UC1);
while (1)
{
// 4a. 深度影象的轉化以及顯示
IDepthFrame * pDepthFrame = nullptr;
while (pDepthReader->AcquireLatestFrame(&pDepthFrame) != S_OK);
pDepthFrame->CopyFrameDataToArray(iDepthWidth * iDepthHeight, (UINT16 *)img16.data);
img16.convertTo(img8, CV_8UC1, 255.0 / 4500);
imshow("Depth Img", img8);
// 4b. 獲取Body資料
IBodyFrame* pBodyFrame = nullptr;
while (pBodyReader->AcquireLatestFrame(&pBodyFrame) != S_OK);
if (pBodyFrame->GetAndRefreshBodyData(iBodyCount, aBody) == S_OK)
{
int iTrackedBodyCount = 0;
// 4c. 遍歷每個人
for (int i = 0; i < iBodyCount; ++i)
{
IBody* pBody = aBody[i];
// 判斷這個人是不是正在被追蹤
BOOLEAN bTracked = false;
if ((pBody->get_IsTracked(&bTracked) == S_OK) && bTracked)
{
++iTrackedBodyCount;
cout << "User " << i << " is under tracking!" << endl;
// 獲取關節位置
int count = 0;
Joint aJoints[JointType::JointType_Count];
if (pBody->GetJoints(JointType::JointType_Count, aJoints) != S_OK)
{
cerr << "Get joints fail" << endl;
}
// 獲取關節方向
JointOrientation aOrientations[JointType::JointType_Count];
if (pBody->GetJointOrientations(JointType::JointType_Count, aOrientations) != S_OK)
{
cerr << "Get joints fail" << endl;
}
// 輸出資訊
for (int j = 0; j < JointType_Count; j++)
{
//判斷該點是否被追蹤
if (aJoints[j].TrackingState == TrackingState_Tracked)
continue;
//獲取關節的名字
string rt = get_name(aJoints[j].JointType);
//輸出關節資訊
if (rt != "NULL")
{
count++;
cout << " " << rt << " tracked" << endl;
cout << "\n\t position: " << aJoints[j].Position.X <<"," << aJoints[j].Position.Y << "," << aJoints[j].Position.Z
<< "\n\t orientation: " << aOrientations[j].Orientation.w<< ","<< aOrientations[j].Orientation.x << "," << aOrientations[j].Orientation.y << "," << aOrientations[j].Orientation.z <<endl;
}
}
cout << count << "joints tracked" << endl << endl;
}
}
//判斷這一時刻有幾個人在被追蹤
if (iTrackedBodyCount > 0)
cout << "Total " << iTrackedBodyCount << " bodies in this time\n" << endl;
else
{
cerr << "Can't read body data" << endl;
}
}
// 4d. release frame
pDepthFrame->Release();
pBodyFrame->Release();
if (waitKey(30) == VK_ESCAPE)
break;
//為避免資料刷太快,每秒鐘更新一次
Sleep(1000);
}
delete[] aBody;
// 4e. release frame
pDepthReader->Release();
pBodyReader->Release();
// 1c.關閉感應器
pSensor->Close();
// 1d.釋放感應器
pSensor->Release();
pSensor = nullptr;
return 0;
}
const string get_name(int n)
{
switch (n)
{
case 2:return "Neck"; break;
case 3:return "Head"; break;
case 4:return "Left shoulder"; break;
case 8:return "Right shoulder"; break;
case 7:return "Left hand"; break;
case 11:return "Right hand"; break;
case 22:return "Left thumb"; break;
case 24:return "Right thumb"; break;
default :return "NULL";
}
}