Kinect開發學習筆記之(六)帶遊戲者ID的深度資料的提取
【原文:http://blog.csdn.net/zouxy09/article/details/8151044】
Kinect開發學習筆記之(六)帶遊戲者ID的深度資料的提取
我的Kinect開發平臺是:
Win7x86 + VS2010 + Kinect for Windows SDK v1.6 + OpenCV2.3.0
開發環境的搭建見上一文:
http://blog.csdn.net/zouxy09/article/details/8146055
本學習筆記以下面的方式組織:程式設計前期分析、程式碼與註釋和重要程式碼解析三部分。
要實現目標:通過微軟的SDK提取帶遊戲者ID的深度資料並用OpenCV顯示,不同使用者,顯示的顏色不同
一、程式設計前期分析
我們在上一文中提到的是不帶遊戲者ID的深度資料的提取,具體見下面:
Kinect開發學習筆記之(五)不帶遊戲者ID的深度資料的提取
http://blog.csdn.net/zouxy09/article/details/8146719
首先,Kinect感測器核心是發射紅外結構光,並探測紅外光反射,從而可以計算出視場範圍內每一個畫素的深度值。從深度資料中最先提取出來的是物體主體和形狀,以及每一個畫素點的遊戲者索引資訊。然後用這些形狀資訊來匹配人體的各個部分,最後計算匹配出來的各個關節在人體中的位置。而Kinect具有一次識別多達6個遊戲者的能力,並能跟蹤最多兩個人的骨骼(對於XBOX360來說,就是可以同時兩個人玩遊戲了)。
可能有點奇怪哦,這一個帶遊戲者ID,一個不帶,還得那麼嚴肅地給它單獨開一文來學習。究竟啥來頭啊。呵呵,實際上,既然微軟提供了這種差別,那麼它的存在肯定是有意義的,所謂存在即合理嘛。多個選擇嘛。需要用到遊戲者ID的時候就用,不需要的時候就不用費那麼大勁。也不能說費勁,就是使用遊戲者ID的時候,我們需要再做一些工作,去把不同遊戲者的輪廓找出來,然後為了區別,標上不同的顏色,這就是本文想實現的。有點囉嗦了。
上一文中,我們講到,Kinect的深度影象資料有兩種格式,一種是帶遊戲者ID的,一種是不帶的。兩種格式都是用兩個位元組來儲存一個畫素的深度值,而兩方式的差別在於:
(1)唯一表示深度值:那麼畫素的低12位表示一個深度值,高4位未使用;
(2)既表示深度值又含有遊戲者ID:Kinect為每一個追蹤到的遊戲者編號作為索引。而這個方式中,畫素值的高13位儲存了深度值,低三位儲存使用者序號,7 (0000 0111)這個位掩碼能夠幫助我們從深度資料中獲取到遊戲者索引值。
要注意的是,不要對特定的遊戲者索引位進行編碼,因為他們是會變化的。實際的遊戲者索引位並不總是和Kinect前面的遊戲者編號一致。啥意思呢?例如,Kinect視野中只有一個遊戲者,但是返回的遊戲者索引位值可能是3或者4。也就是說有時候第一個遊戲者的遊戲者索引位可能不是1。還有,如果走進Kinect視野再走出去,然後再走進來,雖然你還是你,但是Kinect給你的索引ID可能就和原來的不一樣了,例如之前返回的索引位是1,走出去後再次走進,可能索引位變為其他值了。所以開發Kinect應用程式的時候應該注意到這一點。
說得有點亂哦,我們們還是看程式碼吧。
二、程式碼與註釋
- #include <windows.h>
- #include <iostream>
- #include <NuiApi.h>
- #include <opencv2/opencv.hpp>
- using namespace std;
- using namespace cv;
- //處理深度資料的每一個畫素,如果屬於同一個使用者的ID,那麼畫素就標為同種顏色,不同的使用者,
- //其ID不一樣,顏色的標示也不一樣,如果不屬於某個使用者的畫素,那麼就採用原來的深度值
- RGBQUAD shortDepth2RGBquad( USHORT depthID )
- {
- //每畫素共16bit的資訊,其中最低3位是ID(所捕捉到的人的ID),剩下的13位才是資訊
- USHORT realDepth = (depthID & 0xfff8) >> 3; //提取距離資訊,高13位
- USHORT player = depthID & 0x07 ; //提取ID資訊 ,低3位
- //因為提取的資訊是距離資訊,為了便於顯示,這裡歸一化為0-255
- BYTE depth = 255 - (BYTE)(256*realDepth/0x0fff);
- RGBQUAD q;
- q.rgbRed = q.rgbBlue = q.rgbGreen = 0;
- //RGB三個通道的值都是相等的話,就是灰度的
- //Kinect系統能夠處理辨識感測器前多至6個人物的資訊,但同一時刻最多隻有2個玩家可被追蹤(即骨骼跟蹤)
- switch( player )
- {
- case 0:
- q.rgbRed = depth / 2;
- q.rgbBlue = depth / 2;
- q.rgbGreen = depth / 2;
- break;
- case 1:
- q.rgbRed = depth;
- break;
- case 2:
- q.rgbGreen = depth;
- break;
- case 3:
- q.rgbRed = depth / 4;
- q.rgbGreen = depth;
- q.rgbBlue = depth;
- break;
- case 4:
- q.rgbRed = depth;
- q.rgbGreen = depth;
- q.rgbBlue = depth / 4;
- break;
- case 5:
- q.rgbRed = depth;
- q.rgbGreen = depth / 4;
- q.rgbBlue = depth;
- break;
- case 6:
- q.rgbRed = depth / 2;
- q.rgbGreen = depth / 2;
- q.rgbBlue = depth;
- break;
- case 7:
- q.rgbRed = 255 - ( depth / 2 );
- q.rgbGreen = 255 - ( depth / 2 );
- q.rgbBlue = 255 - ( depth / 2 );
- }
- return q;
- }
- int main(int argc, char *argv[])
- {
- Mat image;
- image.create(240, 320, CV_8UC3);
- //1、初始化NUI,注意這裡是DEPTH_AND_PLAYER_INDEX
- HRESULT hr = NuiInitialize(NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX);
- if (FAILED(hr))
- {
- cout<<"NuiInitialize failed"<<endl;
- return hr;
- }
- //2、定義事件控制程式碼
- //建立讀取下一幀的訊號事件控制程式碼,控制KINECT是否可以開始讀取下一幀資料
- HANDLE nextColorFrameEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
- HANDLE depthStreamHandle = NULL; //儲存影象資料流的控制程式碼,用以提取資料
- //3、開啟KINECT裝置的彩色圖資訊通道,並用depthStreamHandle儲存該流的控制程式碼,以便於以後讀取
- hr = NuiImageStreamOpen(NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX, NUI_IMAGE_RESOLUTION_320x240,
- 0, 2, nextColorFrameEvent, &depthStreamHandle);
- if( FAILED( hr ) )//判斷是否提取正確
- {
- cout<<"Could not open color image stream video"<<endl;
- NuiShutdown();
- return hr;
- }
- namedWindow("depthImage", CV_WINDOW_AUTOSIZE);
- //4、開始讀取深度資料
- while(1)
- {
- const NUI_IMAGE_FRAME * pImageFrame = NULL;
- //4.1、無限等待新的資料,等到後返回
- if (WaitForSingleObject(nextColorFrameEvent, INFINITE)==0)
- {
- //4.2、從剛才開啟資料流的流控制程式碼中得到該幀資料,讀取到的資料地址存於pImageFrame
- hr = NuiImageStreamGetNextFrame(depthStreamHandle, 0, &pImageFrame);
- if (FAILED(hr))
- {
- cout<<"Could not get depth image"<<endl;
- NuiShutdown();
- return -1;
- }
- 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<image.rows; i++)
- {
- uchar *ptr = image.ptr<uchar>(i); //第i行的指標
- //其二是既表示深度值又含有人物序號,則畫素值的高13位儲存了深度值,低三位儲存使用者序號,
- //注意這裡需要轉換,因為每個資料是2個位元組,儲存的同上面的顏色資訊不一樣,
- uchar *pBufferRun = (uchar*)(LockedRect.pBits) + i * LockedRect.Pitch;
- USHORT * pBuffer = (USHORT*) pBufferRun;
- for (int j=0; j<image.cols; j++)
- {
- //對於每一個畫素,我們通過它的深度資料去修改它的RGB值;
- RGBQUAD rgb = shortDepth2RGBquad(pBuffer[j]);
- ptr[3*j] = rgb.rgbBlue;
- ptr[3*j+1] = rgb.rgbGreen;
- ptr[3*j+2] = rgb.rgbRed;
- }
- }
- imshow("depthImage", image); //顯示影象
- }
- else
- {
- cout<<"Buffer length of received texture is bogus\r\n"<<endl;
- }
- //5、這幀已經處理完了,所以將其解鎖
- pTexture->UnlockRect(0);
- //6、釋放本幀資料,準備迎接下一幀
- NuiImageStreamReleaseFrame(depthStreamHandle, pImageFrame );
- }
- if (cvWaitKey(20) == 27)
- break;
- }
- //7、關閉NUI連結
- NuiShutdown();
- return 0;
- }
三、程式碼解析
首先,這裡基本上和上一文說的深度資料的獲取的流程和API都是一樣的,具體的話,參考上一文。只是有幾個點需要說明下:
(1)初始化和開啟深度資料流的時候傳入的引數是不同的,這個需要注意下,我們需要的是DEPTH_AND_PLAYER_INDEX資料;
(2)每個畫素的深度資料由兩個位元組來儲存,高13位儲存了深度值,低三位儲存使用者序號。
(3)具體顯示的時候我們是這樣處理的:
對於每一個畫素,我們通過它的深度資料去修改它的RGB值,如果屬於同一個使用者的ID,那麼畫素就標為同種顏色,不同的使用者,其ID不一樣,顏色的標示也不一樣,如果不屬於某個使用者的畫素,那麼就採用原來的深度值。
首先,這裡涉及到了:
USHORTrealDepth = (depthID & 0xfff8) >> 3; //提取距離資訊,高13位
USHORTplayer = depthID & 0x07 ; //提取ID資訊,低3位
然後RGBQUAD是一個結構體,其儲存一個畫素點的RGB值,定義如下:
typedef struct tagRGBQUAD {
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
} RGBQUAD;
至此,目標達成。
下面是結果,感覺似乎如果兩個人靠得太近的話,也會被識別為同一個使用者,標示同樣的顏色,這點感覺有點不太穩定,這種情況應該挺容易避免的啊,是我高估了Kinect,還是我高估了我。
相關文章
- Kinect開發學習筆記之(五)不帶遊戲者ID的深度資料的提取筆記遊戲
- Kinect開發學習筆記之(七)骨骼資料的提取筆記
- Kinect開發學習筆記之(二)Kinect開發學習資源整理筆記
- Kinect開發學習筆記之(四)提取顏色資料並用OpenCV顯示筆記OpenCV
- Kinect開發學習筆記之(三)Kinect開發環境配置筆記開發環境
- Kinect開發學習筆記之(八)彩色、深度、骨骼和使用者摳圖結合筆記
- Kinect開發學習筆記之(一)Kinect介紹和應用筆記
- 大資料學習筆記(六)大資料筆記
- android學習筆記六Android筆記
- Android 開發學習筆記Android筆記
- 《Android藝術開發探索》學習筆記之View的工作原理Android筆記View
- 《Android藝術開發探索》學習筆記之IPCAndroid筆記
- 遊戲資料的捕捉(鬱金香學習筆記)遊戲筆記
- 深度學習讀書筆記之RBM深度學習筆記
- 《Android藝術開發探索》學習筆記之Activity的生命週期Android筆記
- 深度學習——loss函式的學習筆記深度學習函式筆記
- Python深度學習(處理文字資料)--學習筆記(十二)Python深度學習筆記
- hive學習筆記之六:HiveQL基礎Hive筆記
- 《Android藝術開發探索》學習筆記之View的事件體系(一)Android筆記View事件
- 飛機的 PHP 學習筆記之資料庫篇PHP筆記資料庫
- 學習筆記:深度學習中的正則化筆記深度學習
- Javascript 學習 筆記六JavaScript筆記
- swoft 學習筆記之資料庫操作筆記資料庫
- 大資料之 Hadoop學習筆記大資料Hadoop筆記
- 安卓開發學習-Intent攜帶資料安卓Intent
- 深度學習keras筆記深度學習Keras筆記
- 深度學習 筆記一深度學習筆記
- laravel學習筆記之開發環境搭建Laravel筆記開發環境
- Django之“學習筆記”網站開發1Django筆記網站
- Open CV 學習開發筆記之概述(一)筆記
- Deep Learning(深度學習)學習筆記整理系列之(一)深度學習筆記
- Android學習筆記之IntentAndroid筆記Intent
- 【深度學習】大牛的《深度學習》筆記,Deep Learning速成教程深度學習筆記
- 《Mastering Delphi 6》學習筆記之六 (轉)AST筆記
- android開發學習筆記系列(1)-android起航Android筆記
- 《Android藝術開發探索》學習筆記之View的事件體系(View的滑動)Android筆記View事件
- 深度學習入門筆記——Transform的使用深度學習筆記ORM
- mediasoup 學習筆記 【三】 Producer 資料生產者筆記