Kinect学习(四):提取深度数据
前言
前面試著提取了Kinect的彩色數據:Kinect學習(三):獲取RGB顏色數據。這次,要試著提取深度數據。
Depth Map(深度圖)是包含與視點的場景對象的表面的距離有關的信息的圖像或圖像通道。其中,Depth Map 類似于灰度圖像,只是它的每個像素值是傳感器距離物體的實際距離。通常RGB圖像和Depth圖像是配準的,因而像素點之間具有一對一的對應關系。
代碼
先上代碼。
#include <Windows.h> #include <iostream> #include <NuiApi.h> #include <opencv2/opencv.hpp>using namespace std; using namespace cv;int main(int argc, char *argv[]) {cv::Mat img;// 深度圖,使用灰度值來表示深度數據,越遠灰度越小則越暗img.create(480, 640, CV_8UC1);// 1、初始化NUI,使用深度數據HRESULT hr = NuiInitialize(NUI_INITIALIZE_FLAG_USES_DEPTH);if (FAILED(hr)){cout << "NuiIntialize failed" << endl;return hr;}// 2、定義事件句柄// 創建讀取下一幀的信號事件句柄,用來控制Kinect是否可以開始讀取下一幀數據HANDLE nextDepthFrameEvent = CreateEvent(NULL, TRUE, FALSE, NULL);HANDLE depthStreamHandle = NULL; // 用來保存圖像數據流的句柄,用于提取數據// 3、打開Kinect設備的深度圖數據通道,使用depthStreamHandle保存該數據流的句柄,以便于后續讀取hr = NuiImageStreamOpen(NUI_IMAGE_TYPE_DEPTH, NUI_IMAGE_RESOLUTION_640x480, 0, 2, nextDepthFrameEvent, &depthStreamHandle);if (FAILED(hr)){cout << "Could not open color image stream video" << endl;NuiShutdown();return hr;}cv::namedWindow("depthImage", CV_WINDOW_AUTOSIZE);// 4、開始讀取深度數據while (1){const NUI_IMAGE_FRAME * pImageFrame = NULL;// 4.1、無限等待新的數據,等到后就返回if (WaitForSingleObject(nextDepthFrameEvent, INFINITE) == 0){// 4.2、從剛才打開數據流的流句柄中得到該幀的數據,讀取到的數據地址存在pImageFramehr = NuiImageStreamGetNextFrame(depthStreamHandle, 0, &pImageFrame);if (FAILED(hr)){cout << "Could not get depth image" << endl;NuiShutdown();return hr;}INuiFrameTexture * pTexture = pImageFrame->pFrameTexture;NUI_LOCKED_RECT LockedRect;// 4.3、提取數據幀到LockedRect,它包括了兩個數據對象:pitch每行字節數,pBits第一個字節地址//并鎖定數據,這樣當我們讀數據的時候,kinect就不會去修改它 pTexture->LockRect(0, &LockedRect, NULL, 0);// 4.4、確認得到的數據是否有效if (LockedRect.Pitch != 0){//4.5、將數據轉換為OpenCV的Mat格式 for (int i = 0;i < img.rows;i++){uchar * ptr = img.ptr<uchar>(i);//深度圖像數據含有兩種格式,這里像素的低12位表示一個深度值,高4位未使用; //注意這里需要轉換,因為每個數據是2個字節,存儲的同上面的顏色信息不一樣,uchar *pBufferRun = (uchar*)(LockedRect.pBits) + i * LockedRect.Pitch;USHORT * pBuffer = (USHORT*)pBufferRun;for (int j = 0;j < img.cols;j++){// ptr[j] = 255 - (uchar)(255 * pBuffer[j] / 0x0fff); //直接將數據歸一化處理ptr[j] = (uchar)(255 * pBuffer[j] / 0x0fff); //直接將數據歸一化處理}}cv::imshow("depthImage", img);}else{cout << "Buffer length of received texture is bogus\r\n" << endl;}// 5、這幀已經處理完了,將其解鎖,更新下一幀數據pTexture->UnlockRect(0);// 6、釋放這一陣數據,準備接受下一幀NuiImageStreamReleaseFrame(depthStreamHandle, pImageFrame);}if (cv::waitKey(20) == 27){break;}}// 7、關閉NUI連接NuiShutdown();return 0; }運行結果
說明
基本的程序流程都與獲取彩色圖像數據的流程一樣,下面主要介紹不同點,類似的地方就不詳細說明了。
1、初始化NUI接口
// 1、初始化NUI,使用深度數據 HRESULT hr = NuiInitialize(NUI_INITIALIZE_FLAG_USES_DEPTH); if (FAILED(hr)) {cout << "NuiIntialize failed" << endl;return hr; }對NUI接口初始化,注意初始化的參數是NUI_INITIALIZE_FLAG_USES_DEPTH,代表要使用深度數據。
2、定義事件句柄
// 2、定義事件句柄 // 創建讀取下一幀的信號事件句柄,用來控制Kinect是否可以開始讀取下一幀數據 HANDLE nextDepthFrameEvent= CreateEvent(NULL, TRUE, FALSE, NULL);創建事件句柄,nextDepthFrameEvent對應信號事件,在打開Kinect設備的數據流之后,如果有信號(數據),則WaitForSingleObject(nextDepthFrameEvent, INFINITE)返回0。
3、打開Kinect設備的數據流(深度數據)
// 3、打開Kinect設備的深度圖數據通道,使用depthStreamHandle保存該數據流的句柄,以便于后續讀取 hr = NuiImageStreamOpen(NUI_IMAGE_TYPE_DEPTH, NUI_IMAGE_RESOLUTION_640x480, 0, 2, nextDepthFrameEvent, &depthStreamHandle); if (FAILED(hr)) {cout << "Could not open color image stream video" << endl;NuiShutdown();return hr; }打開深度圖的數據流,注意是:NUI_IMAGE_TYPE_DEPTH。
4、等待數據更新,若更新完成則進行下一步
// 4.1、無限等待新的數據,等到后就返回 if (WaitForSingleObject(nextDepthFrameEvent, INFINITE) == 0) { ... }訪問前面定義的信號事件:nextDepthFrameEvent,如果有數據則程序往后走,沒有數據,等待。
5、從數據流中拿出圖像數據
// 4.2、從剛才打開數據流的流句柄中得到該幀的數據,讀取到的數據地址存在pImageFrame hr = NuiImageStreamGetNextFrame(depthStreamHandle, 0, &pImageFrame); if (FAILED(hr)) {cout << "Could not get depth image" << endl;NuiShutdown();return hr; }從數據流depthStreamHandle中取出深度圖數據,保存在pImageFrame中。
6、提取數據幀并鎖定數據
INuiFrameTexture * pTexture = pImageFrame->pFrameTexture; NUI_LOCKED_RECT LockedRect;// 4.3、提取數據幀到LockedRect,它包括了兩個數據對象:pitch每行字節數,pBits第一個字節地址 //并鎖定數據,這樣當我們讀數據的時候,kinect就不會去修改它 pTexture->LockRect(0, &LockedRect, NULL, 0);INuiFrameTexture是一個保存圖像幀數據的對象,主要要用到他的下面兩個共有方法:
- LockRect:給緩沖區上鎖;
- UnlockRect:給緩沖區解鎖;
因為圖像幀是保存在緩沖區的,如果不上鎖的話,緩沖區中還有的圖像可能會導致Kinect修改要取出的圖像。
提取數據幀到LockedRect后,它包含兩個數據對象:pitch,每行字節數;pBits,第一個字節地址。
7、將數據轉換為OpenCV的Mat格式。
//4.5、將數據轉換為OpenCV的Mat格式 for (int i = 0;i < img.rows;i++) {uchar * ptr = img.ptr<uchar>(i);//深度圖像數據含有兩種格式,這里像素的低12位表示一個深度值,高4位未使用; //注意這里需要轉換,因為每個數據是2個字節,存儲的同上面的顏色信息不一樣,uchar *pBufferRun = (uchar*)(LockedRect.pBits) + i * LockedRect.Pitch; USHORT * pBuffer = (USHORT*)pBufferRun;for (int j = 0;j < img.cols;j++){// ptr[j] = 255 - (uchar)(255 * pBuffer[j] / 0x0fff); //直接將數據歸一化處理ptr[j] = (uchar)(255 * pBuffer[j] / 0x0fff); //直接將數據歸一化處理} }把LockedRect中的數據取出來,保存為OpenCV支持的Mat格式。
深度數據是用16位數據的前12位表示,這里賦值時直接做了歸一化,調整到了0-255。
參考資料
總結
以上是生活随笔為你收集整理的Kinect学习(四):提取深度数据的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Kinect学习(三):获取RGB颜色数
- 下一篇: Kinect学习(五):提取带用户ID的