之前已經寫過《使用 OpenCV 顯示深度影像》和《讀取彩色影像與紅外線影像》,基本上,算是把把 Kinect 能拿到的影像資料的原始資料,都介紹過了。 而實際上,除了深度、彩色、紅外線影像這些算是原始資料外,Kinect for Windows SDK 能提供的影像資料,還有一種,那就是經過分析的結果:Body Index。 Body Index 在 K4W SDK v2 裡面,他的 frame source 的介面是 IBodyIndexFrameSource(MSDN),基本上是用來代表人體在深度畫面中,所佔位置的影像資料。在 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)提供的函式來做轉換。這部分就之後再做說明了~
|