K4W v2 C++ Part 4:讀取人體位置(Body Index)


之前已經寫過《使用 OpenCV 顯示深度影像》和《讀取彩色影像與紅外線影像》,基本上,算是把把 Kinect 能拿到的影像資料的原始資料,都介紹過了。

而實際上,除了深度、彩色、紅外線影像這些算是原始資料外,Kinect for Windows SDK 能提供的影像資料,還有一種,那就是經過分析的結果:Body Index。

Body Index 在 K4W SDK v2 裡面,他的 frame source 的介面是 IBodyIndexFrameSourceMSDN),基本上是用來代表人體在深度畫面中,所佔位置的影像資料。在 K4W SDK 目前的系統下,最多能追蹤六個人,而 Body Index 就是用來告訴程式開發者,現在這六個人個別在畫面裡的哪個位置的。

而這資料能用來幹嘛呢?最直接的用法,就是在程式執行時,可以讓操作只知道自己在感應器的視角裡的樣子、確定偵測到的是對的。再來,對於開發者來說,也可以用來做操作者的簡單的位置偵測、或是去背的動作。

它的使用方法基本上和其他的影像類型的資料一樣,都是 Frame Source – Frame Reader – Frame 這樣的三層式的架構;而這三層的介面,分別是:

  • IBodyIndexFrameSource
  • IBodyIndexFrameReader
  • IBodyIndexFrame

而他所讀取出來的畫面,每個像素則是一個 BYTE(實際上是 unsigned char),代表的是該點的人的編號;由於 K4W SDK v2 只能追蹤六個人,所以這編號只有 0 – 5 是有用的,如果他的值不再這個範圍內的話,則代表這點是背景、不是正在被追蹤的人體。

如果要用 OpenCV 來顯示的話,程式大致上可以寫成下面的樣子:

// Standard Library
#include <iostream>

// OpenCV Header
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>

// Kinect for Windows SDK Header
#include <Kinect.h>

using namespace std;

int main(int argc, char** argv)
{
    // 1a. Get default Sensor
    IKinectSensor* pSensor = nullptr;
    GetDefaultKinectSensor(&pSensor);

    // 1b. Open sensor
    pSensor->Open();

    // 2a. Get frame source
    IBodyIndexFrameSource* pFrameSource = nullptr;
    pSensor->get_BodyIndexFrameSource(&pFrameSource);

    // 2b. Get frame description
    int        iWidth = 0;
    int        iHeight = 0;
    IFrameDescription* pFrameDescription = nullptr;
    pFrameSource->get_FrameDescription(&pFrameDescription);
    pFrameDescription->get_Width(&iWidth);
    pFrameDescription->get_Height(&iHeight);
    pFrameDescription->Release();
    pFrameDescription = nullptr;

    // 3a. get frame reader
    IBodyIndexFrameReader* pFrameReader = nullptr;
    pFrameSource->OpenReader(&pFrameReader);

    // 2c. release Frame source
    cout << "Release frame source" << endl;
    pFrameSource->Release();
    pFrameSource = nullptr;

    // Prepare OpenCV data
    cv::Mat    mImg(iHeight, iWidth, CV_8UC3);
    cv::namedWindow("Body Index Image");

    // color array
    cv::Vec3b aColorTable[7] = {
        cv::Vec3b(255,0,0),
        cv::Vec3b(0,255,0),
        cv::Vec3b(0,0,255),
        cv::Vec3b(255,255,0),
        cv::Vec3b(255,0,255),
        cv::Vec3b(0,255,255),
        cv::Vec3b(0,0,0),
    };

    // Enter main loop
    while (true)
    {
        // 4a. Get last frame
        IBodyIndexFrame* pFrame = nullptr;
        if (pFrameReader->AcquireLatestFrame(&pFrame) == S_OK)
        {
            // 4c. Fill OpenCV image
            UINT uSize = 0;
            BYTE* pBuffer = nullptr;
            pFrame->AccessUnderlyingBuffer(&uSize, &pBuffer);
            for (int y = 0; y < iHeight; ++y)
            {
                for (int x = 0; x < iWidth; ++x)
                {
                    int uBodyIdx = pBuffer[x + y * iWidth];
                    if (uBodyIdx < 6)
                        mImg.at<cv::Vec3b>(y, x) = aColorTable[uBodyIdx];
                    else
                        mImg.at<cv::Vec3b>(y, x) = aColorTable[6];
                }
            }
            cv::imshow("Body Index Image", mImg);
        
            // 4e. release frame
            pFrame->Release();
        }

        // 4f. check keyboard input
        if (cv::waitKey(30) == VK_ESCAPE){
            break;
        }
    }

    // 3b. release frame reader
    pFrameReader->Release();
    pFrameReader = nullptr;

    // 1c. Close Sensor
    pSensor->Close();

    // 1d. Release Sensor
    pSensor->Release();
    pSensor = nullptr;

    return 0;
}

在前面取得 frame source、frame reader 的部分,基本上都和之前的一樣,所以這邊就不解釋了。不過這邊的程式碼基本上省略了錯誤處理的部分,如果是要看比較完整的程式碼的話,請參考 GitHub 上的檔案

而比較特別,是由於 body index 的資料算是索引值的影像,而 OpenCV 本身並不直接支援這類型的資料,所以會需要自行把每一個像素轉換成顏色。

為了做這件事,這邊在「color array」的地方,建立了一個大小為 7 的 cv::Vec3b 的陣列 aColorTable,裡面記錄了七種顏色,用來各自代表 0 – 5 的使用者,以及背景。

在「4c」的地方,則是一樣、透過 AccessUnderlyingBuffer() 來讀取原始的 body index 資料,然後再透過兩層迴圈、去掃過畫面裡的每一個像素、並從 aColorTable 裡面取出對應的色彩、填到 OpenCV 的影像(mImg)裡面。

而這個程式在執行後,如果有偵測到人的話,應該就會顯示類似下面的畫面;其中黃色的部分,就代表是他認為是人體的部分了~

如果把這個資料結合彩色影像,基本上就可以做出簡單的去背效果了~

不過由於 K4W SDK 所提供的深度影像資料和彩色影像的座標不一致,所以還會需要透過 ICoordinateMapper 這個介面(MSDN)提供的函式來做轉換。這部分就之後再做說明了~


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

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

16 Responses to K4W v2 C++ Part 4:讀取人體位置(Body Index)

  1. Krystal 說道:

    Heresy老师您好,请问colorspace和cameraspace的区别是什么

    喜歡

  2. H.S.C 說道:

    老師你好,我想詢問讓第一個被偵測的人是黃色的方法

    喜歡

    • Heresy 說道:

      請自行修改 aColorTable 這個顏色對應表,或自己去處理哪個使用者要對應到哪個顏色。

      喜歡

      • 114 說道:

        老师请问这句是怎么理解的呢?
        if (uBodyIdx < 6)
        mImg.at(y, x) = aColorTable[uBodyIdx];

        他是怎么判断不同的人呢?

        喜歡

        • Heresy 說道:

          判斷方法是微軟的演算法決定的。
          基本上應該就是靠空間中的位置是否有連接。

          喜歡

  3. jimmy 說道:

    老師你好,想請問一下,該如何從Body Index,取得個個被追蹤的人的深度,麻煩了

    喜歡

    • Heresy 說道:

      請根據 Body Index 的資訊,回到深度影像畫面做反查。

      喜歡

      • jimmy 說道:

        老師你好,我該如何提取Body IndeX 的資料

        喜歡

        • Heresy 說道:

          這篇文章就是在寫如何存取 body index 的資料。
          如果你想要寫 Kinect for Windows 2.0 的程式,建議你先看過全系列的問題,然後確認你目前能做到什麼、不知道什麼,再把問題提出來。

          喜歡

  4. Y.C SYU 說道:

    大神

    請問一下
    int uBodyIdx = pBuffer[x + y * iWidth];

    這行程式,是提取BodyIndexFrame 每一個pixel位置嗎?(由上至下 ,左至右)

    喜歡

    • Heresy 說道:

      這是取得 x, y 這點的值

      喜歡

  5. Y.C SYU 說道:

    大神

    請問Body Index 的呈現,除了像此範例.可以用不同顏色呈現
    有沒有辦法使用像深度影像的方式呈現(灰階值方式,依據不同距離而呈現不同灰階值)??

    另外,Kinect V2 Body Index 理論公式 是否有相關Paper可以讀?

    謝謝

    喜歡

    • Heresy 說道:

      1. 自己修改要顯示的顏色,讓他去參考原始的深度影像不就可以了嗎?
      2. 第一代 Kinect 的時候,微軟有發過相關論文,不過標題忘了,建議請自己找看看。

      喜歡

  6. 通告: K4W v2 C++ Part 6:使用 OpenGL 繪製場景 | Heresy's Space

  7. 通告: K4W v2 C++ Part 5:簡單的去背程式 | Heresy's Space

發表迴響

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

WordPress.com Logo

你正使用 WordPress.com 帳號留言。 登出 / 變更 )

Twitter picture

你正使用 Twitter 帳號留言。 登出 / 變更 )

Facebook照片

你正使用 Facebook 帳號留言。 登出 / 變更 )

Google+ photo

你正使用 Google+ 帳號留言。 登出 / 變更 )

連結到 %s

%d 位部落客按了讚: