Kinect 2.0 + OpenCV 顯示深度數據、骨架信息、手勢狀態和人物二值圖

1.前言

Kinect 2.0實測比第一代性能提升非常多!

本來想簡單地找個教程複製黏貼一下,居然還沒有人寫過C++版的Kinect 2.0教程,自己摸索了一下,現在把結果拿出來和大家分享。

實現的功能是:深度數據(Depth Data),骨架信息(Body Data),手勢狀態(Hand State)和人物二值圖(就是圖1的那個東西,微軟官方稱法是Body Index Data)的提取和顯示。

效果如下:

  

圖1 骨架信息,人物二值圖和手勢狀態



圖2 深度信息


2.安裝

Kinect 2.0的安裝要求非常苛刻,可以在官網看。具體如何安裝非常簡單,就不多說了,有問題可以留言。

提一句:我是Mac,本來 一直用Parallels Desktop跑虛擬機槓槓的,結果Kinect 2.0由於對GPU有要求,而虛擬機下顯示的GPU不是真實的GPU,導致我的Kinect 2.0無法在虛擬機下安裝。如果各位有解決這一問題的,請務必賜教!!感激不盡。


3.代碼剖析

如果要自己研究的話,目前基本就兩個研究來源。一個是Kinect 2.0 SDK提供的範例代碼,另一個是微軟Kinect官網(尤其建議閱讀其中的“read technical docs”鏈接)。我就是結合這兩個資料,摸索出來的。

具體的代碼我會附在最後,但首先我們要了解Kinect 2.0的工作機制,瞭解了這個以後代碼理解起來超級容易!Kinect 2.0真心比1.0的函數接口簡單太多。

3.1 運行機理

在Kinect 2.0中,每個類型的數據都有三個類與之對應:Source,Reader和Frame。比如如果要讀取骨架,就有IBodyFrameSource,IBodyFrameReader,IBodyFrame這三個類,而要讀取深度數據,就有IDepthFrameSource,IDepthFrameReader,IDepthFrame這三個類,以此類推其他的如Body Index,Infrared,Color數據也是這樣命名的。

那麼這三個接口是什麼意思呢?

3.1.1 Source

在我們初始化並打開了Kinect後,我們需要請求Kinect打開一個源,我們將從這個源不斷獲得信息。其代碼爲:

m_pKinectSensor->get_BodyFrameSource(&pBodyFrameSource);

其中m_pKinectSensor是我們的Kinect總端口,pBodyFrameSource是一個IBodyFrameSource類。

3.1.2 Reader

由於Source是Kinect端擁有的,不是我們電腦擁有的,所以我們需要創建一個讀口,這個讀口和上述的源綁定,之後我們讀取信息都通過調用這個Reader來獲得。其代碼爲:

pBodyFrameSource->OpenReader(&m_pBodyFrameReader);

其中m_pBodyFrameReader是一個IBodyFrameReader類。

3.1.3 Frame

Frame是真正存儲數據的類,每一次都讓Reader把數據讀到Frame中,然後我們再從Frame中提取各種各樣最後使用的數據。代碼爲:

m_pBodyFrameReader->AcquireLatestFrame(&pBodyFrame);

其中pBodyFrame是一個IBodyFrame類。

3.2 如何從Frame中獲得數據

請求Source和創建Reader對於每一個數據類型都是一模一樣的,但是從Frame中提取信息則各有不同。下面講講深度信息、骨架信息、手勢狀態和人物二值圖信息的提取方法。

3.2.1 深度信息:

在Kinect 2.0中,深度座標空間的範圍是(高*寬 = 424*512)(官網有說明)。從深度信息Frame中提取數據,主要就是把Frame中的數據轉存到一個數組中(官網鏈接)。代碼爲:

pBodyIndexFrame->CopyFrameDataToArray(cDepthHeight * cDepthWidth, bodyIndexArray);
這裏cDepthHeight是424,cDepthWidth是512,bodyIndexArray就是一個424*512大小的16位unsigned int數組,用來存儲深度數據。

3.2.2 骨架信息:

kinect 2.0可以同時追蹤六個人的骨架,因此每次我們需要先調用函數,獲得六個骨架信息(如果沒有人,那麼那個骨架類就是空指針)。代碼爲:

pBodyFrame->GetAndRefreshBodyData(_countof(ppBodies), ppBodies);
這裏ppBodies是一個長度爲6的IBody數組,IBody是用來存儲追蹤到的骨架信息的類。

在獲得了這個類後,我們需要進一步從類中提取骨架位置,對於ppBodies中的每一個元素pBody,代碼爲:

pBody->GetJoints(_countof(joints), joints);

這裏的joints是一個長度爲25的數組,每一個元素就是骨架的位置信息。然而, 這個骨架位置信息是照相機座標系(camera view)下的位置,x和y的範圍都是-1到1。因此我們需要將它轉化到深度座標系中。這裏要用到一個coordinateMapper類,具體代碼爲:

m_pCoordinateMapper->MapCameraPointToDepthSpace(joints[j].Position, &depthSpacePosition[j]);
coordinateMapper類的創建非常簡單,具體可以參考代碼。depthSpacePosition是一個長度也爲25的數組,每一個元素是DepthSpacePoint,這個元素包含了在深度座標系下的x和y座標。

3.2.3 手勢狀態:

在上面的骨架信息的pBody中,同時也包含了追蹤到的人的手勢狀態信息,具體代碼爲:

pBody->get_HandLeftState(&leftHandState);
pBody->get_HandRightState(&rightHandState);
其中leftHandState和rightHandState都是HandState類,這個類有五種狀態:open,closed,lasso,not tracked,unknown(官網鏈接)。其中前三種是特殊手勢,後兩種大同小異,反正就是無法識別了。比較特殊的是lasso,這個手勢在官網有說明,具體形狀是:伸出剪刀手,然後把食指和中指合併在一起,就是lasso狀態了。事實上只用食指伸出來也可以被判斷爲lasso,但是食指和中指一起用,追蹤效果更佳穩定。

3.2.4 人物二值圖:

人物二值圖數據的獲取和深度數據獲取非常類似,具體代碼爲:

pBodyIndexFrame->CopyFrameDataToArray(cDepthHeight * cDepthWidth, bodyIndexArray);
只不過,bodyIndexArray是一個424*512的8位unsigned char數組。如果某個點被判斷爲屬於人的一部分,就是黑色,否則(背景部分)就是白色。

4.源代碼

源代碼比較長,我做了一定程度的註釋。請移步我的github下載查看。地址:otnt's github

注意:請手動配置工程,需要包含kinect和opencv的庫。

如有任何問題和建議,非常歡迎在下面留言,我會隨時關注的~ 


2014.11.04


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章