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


之前已經大該把 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 提供的其他資訊,就之後再提吧~


Kinect for Windows v2 C++ 程式開發目錄

Advertisements

關於 Heresy
https://kheresy.wordpress.com

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

  1. libra says:

    想請問當kinect 抓取骨架後,要如何將左右影像做相反的動作(因要做人跟手臂做互動,抓取的影像必須要像鏡射一樣),但不知從何下手

    喜歡

    • Heresy says:

      這應該要看你是用什麼框架、函示庫在做。
      比如說 OpenCV 的話,它本身就有鏡像的功能了。

      喜歡

      • libra says:

        那使用OPEN CV的話,需要如何加入到主程式當中(因為是初學者,不好意思)
        如需要程式碼的話,可以提供給你

        喜歡

        • Heresy says:

          這部分是看你到底要做什麼,如果本來不是用 OpenCV、而是用其他方法來顯示,透過 OpenCV 來做鏡像,搞不好會拖效能。建議還是看你現在搭配的是什麼,再決定怎麼做。

          另外,OpenCV 網路上可以找到許多教學,建議先自己試試看。

          喜歡

  2. 希望 says:

    版大您好,已經多次拜讀您的這一系列文章了,寫得非常好,非常感謝您無私的寫出來,想請教一下,怎樣用kinect2.0拍攝到得深度數據來追蹤一個移動的物體(例如羽毛球),得到羽毛球尾部中心的坐標,然後如果想得到這個點的攝像機的三維坐標,是不是通過MapDepthPointsToCameraSpace()來實現深度二維坐標變成攝像機三維坐標的?拜託版大了

    喜歡

    • Heresy says:

      Kinect for Windows SDK 並沒有提供針對人以外的物體追蹤,所以如果要追蹤其他物體,請自行找其他合適的演算法來作。

      喜歡

      • 希望 says:

        謝謝Heresy,再問一下哈,如果是彩色影像,可以用幀差法,光流法,背景減除法來追蹤移動物體,但很明顯光流法和背景減除法應該不適合用於深度影像,但幀差法覺得應該可以,還沒有試過,能先請教一下Heresy,就是你只有從kinect2.0得到的深度影像,可以用幀差法來追蹤移動物體嗎?如果不可以,請問一下Heresy知道有什麼其他演算法可以用於處理深度影像來追蹤移動的物體。麻煩Heresy您了(≧▽≦)/

        喜歡

        • Heresy says:

          個人建議你都先試試看吧。
          基本上,你說的這三種方法都有人用在深度影像上,並不是完全不合適的。

          喜歡

          • 希望 says:

            好的,謝謝Heresy,手頭的事忙完后就來試試看,下次來再把我試的結果告訴您。

            喜歡

  3. 通告: Visual Gesture Builder C++ API | Heresy's Space

  4. 通告: 建立 Kinect 的姿勢辨識資料庫:Visual Gesture Builder 工具(一) | Heresy's Space

  5. SONY says:

    如果要一次顯示出全部關節的座標
    請問要怎麼撰寫程式

    喜歡

  6. 通告: K4W v2 C++ Part 7a:繪製人體骨架 | Heresy's Space

  7. ps says:

    請問如果要從SDK v2彩色影像的範例擷取座標資料,該怎麼運用這段語法? 範例的名稱是Color Basics

    喜歡

    • Heresy says:

      抱歉,不太確定你的問題。
      不過如果你是在問怎麼計算彩色影像中的空間座標的話,請參考這篇
      https://kheresy.wordpress.com/2015/02/11/k4w-v2-part-6-draw-with-opengl/

      喜歡

  8. Sony says:

    請教一下
    如何擷取當下骨架位置的資料

    喜歡

    • Heresy says:

      這篇的內容已經是這個的教學了啊?
      程式裡的 rJointPos.Position 就是骨架個別關節點的位置了。
      不知道是哪邊有問題?

      喜歡

      • Sony says:

        是要加在程式的哪一行?

        喜歡

        • Heresy says:

          麻煩先把範例程式看完,確認你知道範例程式在幹嘛、或是哪裡搞不懂。

          喜歡

  9. 王俊程 says:

    請問一下,我想用kinect製作翻身偵測,但不曉得從何下手

    喜歡

    • Heresy says:

      抱歉,Heresy 沒做這方面的研究,所以應該沒辦法給你什麼建議。

      喜歡

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

您的留言將使用 WordPress.com 帳號。 登出 / 變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 / 變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 / 變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 / 變更 )

連結到 %s

%d 位部落客按了讚: