2014-10-16

[OF]Kinect v2 學習筆記(二) 環境配置與第一支程式

上一回簡單瞭解Kinect的用途,以及Kinect v2帶來的進步有哪些後
這一篇要開始著手開發Kinect v2的程式
由於自己主要還是在visual studio下進行開發
因此本篇會從SDK安裝與Visual Studio 2012的專案設定開始
到透過OF去完成簡單的Demo

以下的設定與作法,主要都是筆者參考SDK中C++的範例依樣畫葫蘆
另外OF的部份,其實ofxAddons上有一個ofxKinectV2的add-on
但使用上就是會出一些問題導致無法Compile,所以才決定從原生的SDK寫
因此建議同樣為OF的用戶,可以先嘗試看看ofxKinectV2喔

主要的材料:
  1. Windows 8以上版本的電腦 x 1
  2. USB 3.0孔 x 1
  3. Kinect v2 x 1
  4. Kinect for Windows SDK 2.0 x 1 [載點]

配料:
  1. Visual Studio 2012 x 1 (官方說法是要VS2012以上才行)
  2. Openframeworks 0.8.1 x 1 (其實只用到Display的部份)

環境配製:

=Kinect for Windows SDK=
在安裝好Kinect SDK 2.0以後,應該就能在應用程式中找到SDK Browser 2.0、Kinect Studio 2.0、Visual Gesture Builder 
SDK Browser 2.0就跟Kinect SDK 1.x的時候一樣,有許多的C#與C++的Demo可以參考
Kinect Studio 2.0的介面改了不少,可以在此軟體中測試Kinect大部分的功能,也可以將這些資訊錄製成檔案
Visual Gesture Builder...目前還沒碰過
此外,可以去環境變數中檢查一下,是否有"KINECTSDK20_DIR"這個項目


=Visual Studio 2012=
隨便新增一個Openframeworks的專案,不需要任何Addon。
首先設定Include的部分,個人是習慣設在[C/C++]>[一般]>[其他Include目錄],當然也可以選擇VC++目錄
設定的目錄為:
$(KINECTSDK20_DIR)\inc

設定完的樣子如下,別忘記Debug跟Release都要設定喔


再來是Linker的部分,位置在[連結器]>[一般]>[其他程式庫目錄]
設定的目錄為:
$(KINECTSDK20_DIR)\lib\x86;

後面"x86"部分根據你Visual Studio 2012在安裝時的版本而定,若是裝64bits則改為x64
然後再[連結器]>[輸入]>[其他相依性]中,加入:
kinect20.lib
設定完的樣子如下,一樣別忘記Debug跟Release都要設定喔





到此,就可以到你的ofApp.h試著加入
#include <Kinect.h>
如果Compile順利通過的話,就表示你的環境設定已經沒問題嘍!

=Kinect v2 Depth Demo=

環境設定都完成後,該是來寫寫第一支程式的時候了
Document的部份可以在SDK Browser 2.0中找到,懶得找的可以點開這裡
不免俗的,第一支程式當然要把最重要的Depth Map的拿到手再說
在正式開始寫之前,先簡單以Openframeworks的角度來理解的流程:

上面的圖表只是以Depth Frame作為翻例,事實上無論是Body Frame、Body Index Frame、IR Frame、Color Frame
用法上都是大同小異的!

接下來就直接看程式碼吧,首先是標頭檔的部份:

ofDepthDemo.h:
1:  #pragma once  
2:  #include "ofMain.h"  
3:  #include <Kinect.h>  
4:  class ofDepthDemo : public ofBaseApp{  
5:  public:  
6:       void setup();  
7:       void update();  
8:       void draw();  
9:       void exit();  
10:  private:  
11:       bool InitialKinectV2();  
12:       void UpdateKinectV2();  
13:  private:  
14:       ofImage                         _Display;  
15:       //Base Kinect componer  
16:       IKinectSensor*               _pKinectSensor;  
17:       //Kinect reader  
18:       IDepthFrameReader*          _pDepthFrameReader;  
19:  };  

所使用到的特殊物件只有16行IKinectSensor以及18行IDepthFrameReader
一個是用來取得Kinect Sensor,另一個則是讀取Depth Map
至於_Display則是用來在OF上顯示Depth Map用

程式檔的部份,就針對其中兩個主要的InitialKinectV2()以及UpdateKinectV2()
ofDepthDemo.cpp ─ InitialKinectV2()
1:  bool ofDepthDemo::InitialKinectV2()  
2:  {  
3:       HRESULT hr_;  
4:       hr_ = GetDefaultKinectSensor(&_pKinectSensor);  
5:       if(FAILED(hr_)){  
6:            ofLog(OF_LOG_ERROR, "Get Kinect sensor failed!");  
7:            return false;  
8:       }  
9:       if(_pKinectSensor){       
10:            hr_ = _pKinectSensor->Open();  
11:            //Depth  
12:            IDepthFrameSource* pDepthFrameSource_ = nullptr;  
13:            if(SUCCEEDED(hr_)){  
14:                 hr_ = _pKinectSensor->get_DepthFrameSource(&pDepthFrameSource_);  
15:            }  
16:            if(SUCCEEDED(hr_)){  
17:                 hr_ = pDepthFrameSource_->OpenReader(&_pDepthFrameReader);  
18:            }  
19:            if(pDepthFrameSource_ != nullptr){  
20:                 pDepthFrameSource_->Release();  
21:            }  
22:       }  
23:       if(!_pKinectSensor || FAILED(hr_)){  
24:            ofLog(OF_LOG_ERROR, "Initial failed!");  
25:            return false;  
26:       }else{  
27:            return true;  
28:       }  
29:  }  

就跟其他Windows for C++的API一樣,用call by address的方式去取得資料
return的部分大多都是回傳HRESULT,並透過FAILED & SUCCEEDED來去檢查是否正確
特別注意pDepthFrameSource_,因為後續並沒有再使用所以使用區域變數
因此在取得_pDepthFrameReader後,就將其release

ofDepthDemo.cpp ─ UpdateKinectV2()
1:  void ofDepthDemo::UpdateKinectV2()  
2:  {  
3:       if (!_pDepthFrameReader)  
4:       {  
5:            return;  
6:       }  
7:    
8:       HRESULT hr_;  
9:       IDepthFrame* pDepthFrame_ = nullptr;  
10:    
11:       hr_ = _pDepthFrameReader->AcquireLatestFrame(&pDepthFrame_);  
12:       if (SUCCEEDED(hr_))  
13:       {  
14:            //Get the depth information  
15:            UINT iDepthBufferSize_ = 0;  
16:            UINT16 *pDepthBuffer_ = nullptr;  
17:            int iDepthWidth_ = 0;  
18:            int iDepthHeight_ = 0;  
19:            USHORT usMinDepth_ = 0;  
20:            USHORT usMaxDepth_ = USHRT_MAX;  
21:    
22:            IFrameDescription* pDepthFrameDescription_ = nullptr;  
23:            pDepthFrame_->get_FrameDescription(&pDepthFrameDescription_);  
24:            pDepthFrameDescription_->get_Width(&iDepthWidth_);  
25:            pDepthFrameDescription_->get_Height(&iDepthHeight_);  
26:            pDepthFrame_->get_DepthMinReliableDistance(&usMinDepth_);  
27:            pDepthFrame_->get_DepthMaxReliableDistance(&usMaxDepth_);  
28:            pDepthFrame_->AccessUnderlyingBuffer(&iDepthBufferSize_, &pDepthBuffer_);  
29:    
30:            //Process depth infomation  
31:            ofPixels TmpDisplay_;  
32:            TmpDisplay_.allocate(iDepthWidth_, iDepthHeight_, ofImageType::OF_IMAGE_GRAYSCALE);  
33:            unsigned char * acDisplay_ = TmpDisplay_.getPixels();  
34:              
35:            for(int iBufferIndex_ = 0; iBufferIndex_ < iDepthBufferSize_; iBufferIndex_++)  
36:            {  
37:                 acDisplay_[iBufferIndex_] = 0xff * (float)pDepthBuffer_[iBufferIndex_] / usMaxDepth_; //Intensity  
38:            }  
39:    
40:            _Display.setFromPixels(acDisplay_, iDepthWidth_, iDepthHeight_, OF_IMAGE_GRAYSCALE);  
41:       }  
42:       //Release frame  
43:       if(pDepthFrame_ != nullptr)  
44:       {  
45:            pDepthFrame_->Release();  
46:       }  
47:  }  

(首先要先承認我偷懶,沒有每一個都用HRESULT去判斷是否成功XD)

14~28:取得資料
透過IFrameDescription*可以取得一個Frame的基本資訊
除了24, 25行的width與height外,還有像是Diagonal FOV等資訊,不過這裡用不到就是了

30~40:資料處裡:
DepthFrame的資料跟Kinect v1一樣,是儲存在unsigned short裡頭(0~65535)。
為了顯示為灰階圖,得將其降unsigned char的數值(0~255)。
方法很簡單,先轉為0.0~1.0在乘上255即可!因此就得取得他的最大最小值來將他轉為0.0~1.0。
(如果懶的話,最大值可以直接採用unsigned short的最大值65535)
唯一跟OF有關的部份就是如何存在ofImage當中了
由於ofImage無法直接更動每個byte,所以是由ofPixels存好值後,再透過setFromPixels轉存到為ofImage中

※不要忘記在exit()中要把_pKinectSensor以及_pDepthFrameReader做release喔

=============分隔線==============

以上就是這次的環境配置與第一支程式
其實絕大部分都跟Kinect v1時期差不多。(個人覺得比OpenNI 2.0包裝得更好)
下一篇的目標是Body Index Frame以及Body Frame
也到了長頸鹿該出現的時候了!!

3 則留言:

  1. 作者已經移除這則留言。

    回覆刪除
  2. 哈囉,您好,我目前也在學KINECT V2體感程式設計,由於我專長不是寫程式這方面的,在書店、網路書城都找不到KINECT V2的參考書籍,請問您是否有KINECT V2的相關書籍可以推薦的,方便的話再請您推薦一下,由衷的謝謝您

    回覆刪除
  3. Programming Kinect V2 For Windows
    https://www.youtube.com/playlist?list=PL09RR9O9B-qVqPvNWXWcLxBe2oPrCswSZ

    回覆刪除