K4W v2 C++ Part 7:偵測、追蹤人體骨架

| | 4 Comments| 11:07
Categories:

之前已經大該把 Kinect for Windows SDK 一些基本的資料讀取都講過,而這一篇,就來整理一下,怎麼透過 K4W SDK v2 來取得人體骨架的資料吧~

在 K4W SDK v2 裡面,用來進行人體骨架分析的介面,是被稱為「Body」的這一群介面,包含了 IBodyFrameSourceIBodyFrameReaderIBodyFrameIBody;前面三個以「BodyFrame」為名稱的介面,在使用上大致上都和其他的 Frame 介面有相同的操作邏輯。

不過由於 IBodyFrame 實際上並不是一個畫面,所以他的介面就和其他類型資料的 farme 有一定程度的差異。

首先,要讀取人體骨架的資料,是要使用 IBodyFrame 提供的 GetAndRefreshBodyData() 這個函式(MSDN);執行這個函式時,需要給他一個 IBody 的指標陣列,讓他把人體骨架的資訊寫進去。

而要先建立這個 IBody 的指標陣列的話,就需要先透過 IBodyFrameSourceget_BodyCount() 這個函式,來取得 K4W SDK v2 能處理的人體骨架數量(目前版本是 6),並將初始值都設定成 nullprt;這樣的程式可以寫成下面的形式:

INT32 iBodyCount = 0;
pFrameSource->get_BodyCount(&iBodyCount);
IBody** aBody = new IBody*[iBodyCount];
for (int i = 0; i < iBodyCount; i)
    aBody[i] = nullptr;

其中,pFrameSourceIBodyFrameSource 的指標,而 iBodyCount 就是能追蹤的骨架數量、aBody 就是之後讓 K4W SDK v2 紀錄骨架資料的陣列。

而之後在取得 IBodyFrame 的物件後、就可以透過 GetAndRefreshBodyData() 這個函式,把最新的骨架資料、寫到 aBody 裡;程式大致上可以寫成下面的樣子:

if (pFrame->GetAndRefreshBodyData(iBodyCount, aBody) == S_OK)
{
    //…
}

而在運作正確的狀況下,aBody 裡面就會儲存了 iBodyCount 個最新的人體骨架資料。

但是由於在場景裡,並不一定隨時都有這麼多個使用者,所以實際上要個別 IBody 的物件前,要先透過他提供的 get_IsTracked() 這個函式,來確認這個 IBody 是否正被追蹤。

這邊程式的寫法可以寫成下面的樣子:

BOOLEAN bTracked = false;
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_ShoulderRight(右肩)
  • JointType_ElbowRight(右肘)
  • JointType_WristRight(右腕)
  • JointType_HandRight(右手)
  • JointType_HandTipRight(右手尖端)

而分支出去的一個,則是 JointType_ThumbRight,代表右手的拇指。

至於腿的部分,以右腿來說,由上而下則依序是:

  • JointType_HipRight(右臀)
  • JointType_KneeRight(右膝蓋)
  • JointType_AnkleRight(右腳踝)
  • JointType_FootRight(右腳)

另外,在 JointType 裡面,也有另外定義「JointType_Count」,來代表總共的關節數目。

而要讀取關節位置資訊時,K4W SDK v2 是把它定義成「Joint」這個型別,裡面記錄了三個資料,一個是 JointType、代表自己是哪個關節點,一個是 Position、是用 CameraSpacePoint 來記錄這個關節點在「攝影機空間座標系統」裡的位置(請參考《使用 OpenGL 繪製場景》);最後則是 TrackingState、用來記錄這個關節的追蹤狀態(MSDN)。

要讀取關節位置的程式,基本上可以寫成像下面這樣:

Joint aJoints[JointType::JointType_Count];
pBody->GetJoints(JointType::JointType_Count, aJoints);

也就是先宣告一個大小為 JointType_CountJoint 陣列,然後再透過 IBodyGetJoints() 這個函式,來把資料寫進去了。

之後,就可以直接去存取不同的關節點的位置資訊了~例如要取得右手的位置並輸出的話,就是:

const Joint& rJointPos = aJoints[JointType::JointType_HandRight];
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 這個結構來紀錄的。在它裡面設紀錄了 JointTypeOrientation 兩種資料;前者是記錄自己是哪個關節點,後者則是以 Vector4 的形式、來記錄這個關節點的方向性。

雖然官方文件似乎沒有明確地說明這個紀錄方向性的 Vector4 的意義,不過就 Heresy 個人判斷,應該和 NiTE 2 一樣,是採用 quaternion 的形式(參考《處理 NiTE2 的骨架關節點方向性資訊:Quaternion》)吧?不過,這點 Heresy 自己還沒下去驗證就是了。

而如果把讀取關節資訊的程式整個寫出來的,應該會變得像下面的樣子:

IBodyFrame* pFrame = nullptr;
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 提供的其他資訊,就之後再提吧~

4 thoughts on “K4W v2 C++ Part 7:偵測、追蹤人體骨架”

  1. [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

  2. [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

  3. [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

  4. [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

Leave a Reply

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *