Kinect学习(三):获取RGB颜色数据
前言
在前面的文章中介紹了如何搭建Kinect開發(fā)環(huán)境:Kinect學(xué)習(xí)(一):開發(fā)環(huán)境搭建。搭建好環(huán)境后,首先要做的當(dāng)然就是試著讀取Kinect中的數(shù)據(jù)了。
Kinect有三個(gè)鏡頭,中間的是RGB攝像頭,左邊的是紅外線發(fā)射器,右邊的是紅外線CMOS攝像頭構(gòu)成的3D結(jié)構(gòu)光攝像頭,用來采集深度數(shù)據(jù)。彩色攝像頭最大支持1280*960分辨率成像,紅外攝像頭最大支持640*480成像。
接下來就要通過微軟提供的SDK來讀取Kinect中的彩色攝像頭的數(shù)據(jù)了。
代碼
先上代碼,里面有注釋,后面再詳細(xì)介紹。
#include <windows.h> #include <NuiApi.h> #include <iostream> #include <opencv2/opencv.hpp>/* 幾個(gè)常用的頭文件: 1、NuiApi.h ---包含所有的NUI(自然用戶界面) API頭文件和定義基本的初始化和函數(shù)訪問入口。這是我們C++工程的主要頭文件,它已經(jīng)包含了NuiImageCamera.h 和 NuiSkeleton.h。 2、NuiImageCamera.h ---定義了圖像和攝像頭服務(wù)的API,包括調(diào)整攝像頭的角度和仰角,打開數(shù)據(jù)流和讀取數(shù)據(jù)流等。 3、NuiSkeleton.h ---骨架有關(guān)的API,包括使能骨架跟蹤,獲取骨架數(shù)據(jù),骨架數(shù)據(jù)轉(zhuǎn)換和平滑渲染等。 4、NuiSensor.h ---音頻API,包括ISoundSourceLocalizer接口,用于返回聲源的方向(波束形成)和音頻的位置。 */using namespace std; using namespace cv;int main(int argc, char* argv[]) {cv::Mat img;img.create(480, 640, CV_8UC3);//1、初始化NUIHRESULT hr = NuiInitialize(NUI_INITIALIZE_FLAG_USES_COLOR);if (FAILED(hr)){cout << "NuiInitialize failed" << endl;return hr;}//2、定義事件句柄//創(chuàng)建讀取下一幀的信號(hào)事件句柄,控制KINECT是否可以開始讀取下一幀數(shù)據(jù)HANDLE nextColorFrameEvent = CreateEvent(NULL, TRUE, FALSE, NULL);HANDLE colorStreamHandle = NULL;//保存圖像數(shù)據(jù)流的句柄,用于提取數(shù)據(jù)//3、打開KINECT設(shè)備的彩色信息通道,并用colorStreamHandle保存該流的句柄,以便于以后讀取hr = NuiImageStreamOpen(NUI_IMAGE_TYPE_COLOR, NUI_IMAGE_RESOLUTION_640x480, 0, 2, nextColorFrameEvent, &colorStreamHandle);if (FAILED(hr)){cout << "Could not open color image stream video" << endl;NuiShutdown();return hr;}cv::namedWindow("colorImage", CV_WINDOW_AUTOSIZE);//4、開始讀取彩色圖數(shù)據(jù)while (1){const NUI_IMAGE_FRAME * pImageFrame = NULL;//4.1、無線等待新的數(shù)據(jù),等到就返回if (WaitForSingleObject(nextColorFrameEvent, INFINITE) == 0){//4.2、從剛才打開數(shù)據(jù)流的流句柄中得到該幀的數(shù)據(jù),讀取到的數(shù)據(jù)地址存于pImageFrame中hr = NuiImageStreamGetNextFrame(colorStreamHandle, 0, &pImageFrame);if (FAILED(hr)){cout << "Could not get color image" << endl;NuiShutdown();return -1;}INuiFrameTexture * pTexture = pImageFrame->pFrameTexture;NUI_LOCKED_RECT LockedRect;//4.3、提取數(shù)據(jù)幀到LockedRect(它包括兩個(gè)數(shù)據(jù)對(duì)象:pitch每行字節(jié)數(shù),pBits第一個(gè)字節(jié)地址)//并鎖定數(shù)據(jù),這樣當(dāng)我們讀取數(shù)據(jù)的時(shí)候,kinect就不會(huì)去修改它pTexture->LockRect(0, &LockedRect, NULL, 0);//4.4、確認(rèn)獲得的數(shù)據(jù)是否有效if (LockedRect.Pitch != 0){//4.5、將數(shù)據(jù)轉(zhuǎn)換為OpenCV的Mat格式for (int i = 0; i < img.rows; i++){uchar *ptr = img.ptr<uchar>(i); //第i行的指針//每個(gè)字節(jié)代表一個(gè)顏色信息,直接使用ucharuchar *pBuffer = (uchar*)(LockedRect.pBits) + i * LockedRect.Pitch;for (int j = 0;j < img.cols;j++){//內(nèi)部數(shù)據(jù)是4個(gè)字節(jié),0-1-2是BGR,第4個(gè)現(xiàn)在未使用ptr[3 * j] = pBuffer[4 * j];ptr[3 * j + 1] = pBuffer[4 * j + 1];ptr[3 * j + 2] = pBuffer[4 * j + 2];}}cv::imshow("colorImage", img); //顯示圖像}else{cout << "Buffer length of received texture is bogus\r\n" << endl;}//5、這幀已經(jīng)處理完了,所以將其解鎖pTexture->UnlockRect(0);//6、釋放本幀數(shù)據(jù),準(zhǔn)備獲取下一幀NuiImageStreamReleaseFrame(colorStreamHandle, pImageFrame);}if (cv::waitKey(20) == 27)break;}//7、關(guān)閉NUI連接NuiShutdown();return 0; }運(yùn)行結(jié)果
說明
整個(gè)程序可以分為以下流程:
1、初始化NUI接口
//1、初始化NUI HRESULT hr = NuiInitialize(NUI_INITIALIZE_FLAG_USES_COLOR);要使用微軟提供的SDK中的SDK來操作Kinect,必須先調(diào)用NUI初始化函數(shù)。
函數(shù)原型為:
dwFlags表示標(biāo)志位,有以下幾種情況:
- NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX: 提供帶用戶信息的深度圖數(shù)據(jù);
- NUI_INITIALIZE_FLAG_USES_COLOR:提供RGB彩色圖像數(shù)據(jù);
- NUI_INITIALIZE_FLAG_USES_SKELETON:提供骨骼點(diǎn)數(shù)據(jù);
- NUI_INITIALIZE_FLAG_USES_DEPTH:提供深度圖像數(shù)據(jù);
- NUI_INITIALIZE_FLAG_USES_AUDIO:提供聲音數(shù)據(jù);
- NUI_INITIALIZE_DEFAULT_HARDWARE_THREAD:初始化默認(rèn)的硬件線程;
以上的都各自對(duì)應(yīng)一個(gè)標(biāo)志位,使用時(shí)可以使用|將它們組合起來。
注意到,它還返回了一個(gè)HRESULT類型的參數(shù),通過它可以判斷初始化函數(shù)是否執(zhí)行成功。
if (FAILED(hr)) {cout << "NuiInitialize failed" << endl;return hr; }或者判斷是否等于S_OK:
if(hr == S_OK) {cout << "NuiInitialize successfully" << endl; }2、定義事件句柄
//2、定義事件句柄 //創(chuàng)建讀取下一幀的信號(hào)事件句柄,控制KINECT是否可以開始讀取下一幀數(shù)據(jù) HANDLE nextColorFrameEvent = CreateEvent(NULL, TRUE, FALSE, NULL);該函數(shù)會(huì)創(chuàng)建一個(gè)windows事件對(duì)象,創(chuàng)建成功則返回事件的句柄。這里的這個(gè)事件是用來判斷是否有新數(shù)據(jù)的。
其中有四個(gè)參數(shù):
- 第一個(gè)是安全屬性,設(shè)定為NULL的安全描述符;
- 第二個(gè)表示設(shè)置信號(hào)復(fù)位方式為自動(dòng)恢復(fù)為無信號(hào)狀態(tài)(FALSE)還是手動(dòng)恢復(fù)為無信號(hào)狀態(tài)(TRUE),設(shè)為TRUE,因?yàn)楹竺鎽?yīng)用程序會(huì)重置事件消息;
- 第三個(gè)是事件消息初始狀態(tài)的布爾值,為FALSE;
最后一個(gè)是信號(hào)名稱,可以直接設(shè)置為NULL;
3、打開Kinect設(shè)備的彩色圖像數(shù)據(jù)流
使用這個(gè)函數(shù)可以打開Kinect設(shè)備的彩色圖或是深度圖的訪問通道。也可以理解為,創(chuàng)建一個(gè)訪問彩色圖或深度圖的數(shù)據(jù)流。
函數(shù)原型:
_Check_return_ HRESULT NUIAPI NuiImageStreamOpen(_In_ NUI_IMAGE_TYPE eImageType,_In_ NUI_IMAGE_RESOLUTION eResolution,_In_ DWORD dwImageFrameFlags,_In_ DWORD dwFrameLimit,_In_opt_ HANDLE hNextFrameEvent,_Out_ HANDLE *phStreamHandle);參數(shù)說明:
4、等待數(shù)據(jù)更新,若更新完成則進(jìn)行下一步;
//4.1、無線等待新的數(shù)據(jù),等到就返回 if (WaitForSingleObject(nextColorFrameEvent, INFINITE) == 0) { ... }前面也提到了這個(gè)函數(shù),如果事件(對(duì)應(yīng)nextColorFrameEvent)有信號(hào),即有數(shù)據(jù),那么返回值為0,程序也會(huì)往下執(zhí)行;如果沒有數(shù)據(jù),就會(huì)等待。函數(shù)的第二個(gè)參數(shù)表示等待時(shí)間,單位為ms,這里設(shè)為INFINITE,表示一直等待。
5、從數(shù)據(jù)流中拿出圖像數(shù)據(jù);
//4.2、從剛才打開數(shù)據(jù)流的流句柄中得到該幀的數(shù)據(jù),讀取到的數(shù)據(jù)地址存于pImageFrame中 hr = NuiImageStreamGetNextFrame(colorStreamHandle, 0, &pImageFrame);colorStreamHandle為前面保存了Kinect設(shè)備的彩色信息通道的句柄,這個(gè)函數(shù)會(huì)從colorStreamHandle中取出RGB圖像數(shù)據(jù),并保存在pImageFrame中。第二個(gè)參數(shù),表示延時(shí)多久獲取數(shù)據(jù),直接取為0,就是不等待直接取數(shù)據(jù)。
成功調(diào)用完這個(gè)函數(shù)之后,從Kinect捕獲到的一幀圖像,會(huì)保存在一個(gè)NUI_IMAGE_FRAME結(jié)構(gòu)體中,pImageFrame為指向那個(gè)結(jié)構(gòu)體的指針,其中包含了很多信息,如:圖像類型,分辨率,圖像緩沖區(qū),時(shí)間戳等等。
6、提取數(shù)據(jù)幀并鎖定數(shù)據(jù);
INuiFrameTexture * pTexture = pImageFrame->pFrameTexture; NUI_LOCKED_RECT LockedRect;//4.3、提取數(shù)據(jù)幀到LockedRect(它包括兩個(gè)數(shù)據(jù)對(duì)象:pitch每行字節(jié)數(shù),pBits第一個(gè)字節(jié)地址) //并鎖定數(shù)據(jù),這樣當(dāng)我們讀取數(shù)據(jù)的時(shí)候,kinect就不會(huì)去修改它 pTexture->LockRect(0, &LockedRect, NULL, 0);INuiFrameTexture是一個(gè)保存圖像幀數(shù)據(jù)的對(duì)象,主要要用到他的下面兩個(gè)共有方法:
- LockRect:給緩沖區(qū)上鎖;
- UnlockRect:給緩沖區(qū)解鎖;
因?yàn)閳D像幀是保存在緩沖區(qū)的,如果不上鎖的話,緩沖區(qū)中還有的圖像可能會(huì)導(dǎo)致Kinect修改要取出的圖像。
提取數(shù)據(jù)幀到LockedRect后,它包含兩個(gè)數(shù)據(jù)對(duì)象:pitch,每行字節(jié)數(shù);pBits,第一個(gè)字節(jié)地址。
7、將數(shù)據(jù)轉(zhuǎn)換為OpenCV的Mat格式。
//4.4、確認(rèn)獲得的數(shù)據(jù)是否有效 if (LockedRect.Pitch != 0) {//4.5、將數(shù)據(jù)轉(zhuǎn)換為OpenCV的Mat格式for (int i = 0; i < img.rows; i++){uchar *ptr = img.ptr<uchar>(i); //第i行的指針//每個(gè)字節(jié)代表一個(gè)顏色信息,直接使用ucharuchar *pBuffer = (uchar*)(LockedRect.pBits) + i * LockedRect.Pitch;for (int j = 0;j < img.cols;j++){//內(nèi)部數(shù)據(jù)是4個(gè)字節(jié),0-1-2是BGR,第4個(gè)現(xiàn)在未使用ptr[3 * j] = pBuffer[4 * j];ptr[3 * j + 1] = pBuffer[4 * j + 1];ptr[3 * j + 2] = pBuffer[4 * j + 2];}}這一部分沒什么說的了,就是把LockedRect中的數(shù)據(jù)取出來,保存為OpenCV支持的Mat格式。
參考資料
后記
這個(gè)筆記總體來說不難,主要是套路,微軟官網(wǎng)的文檔早就撤了,畢竟用的還是Kinect v1.0的,靠著博客和看看源碼大概還能用用。
前段時(shí)間直到最近感覺都挺多事情的,很多筆記和寫好的代碼都沒時(shí)間去整理,還要加把勁了。這段時(shí)間又有世界杯,熬夜看球什么的真的挺“傷”的。
總結(jié)
以上是生活随笔為你收集整理的Kinect学习(三):获取RGB颜色数据的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Kinect学习(二):学习资源整理(转
- 下一篇: Kinect学习(四):提取深度数据