1. 程式人生 > >Kinect V2開發(5)讀關節資料

Kinect V2開發(5)讀關節資料

Kinect能取得Depth(物體與感測器的距離資訊)和BodyIndex(人物索引),基於這些資料可以偵測到人體的骨骼資訊並追蹤,在Kinect V2的SDK 2.0中,它最多可以同時獲取到6個人、每個人25個關節點的資訊,並且通過深度攝像頭,可以同時獲取到這些關節點的座標。此時的座標使用的是Camera Space,也就是攝像機空間座標系,代表了物體距離深度攝像頭的距離。
Kinect的人體姿勢,是向學習了基於龐大數量的姿勢資訊的識別器裡,輸入人體區域的資訊來推定的(注:因為男女老少高矮胖瘦體形各不相同,所以必須基於神經網路的資料庫才能準確識別人體)。這個技術出自微軟在IEEE CVPR 2011(計算機視覺及模式認識領域的首位會議)發表,獲獎Best Paper。
Microsoft Research

Real-Time Human Pose Recognition in Parts from a Single Depth Image
文章我正在讀,之後有時間會傳一篇閱讀筆記。

要讀取骨骼資料,前面的步驟和之前一樣,要先通過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";
    }
}