NiTE(官網)是 PrimeSense 針對 OpenNI 這個深度感應器程式開發 Framework 所推出的一套 middleware,他主要的功能,包括了使用者的偵測、人體骨架的分析與追蹤、手部的追蹤、姿勢手勢辨識等等。
在 OpenNI 1.x 的時候,雖然 OpenNI 是提供了一個開放的框架,讓開發者自行根據規格來實作各自的 middleware;但是實際上,到最後還是只有 NiTE 這一套而已…或許是這個原因,在 OpenNI 2 的新架構下,OpenNI 的框架不再去定義 middleware 的介面和功能,而是把本來屬於 plug-in 的 middleware,改成讓使用者直接去使用的函式庫的形式。
在這種模式下,OpenNI 2 的 middleware library 的功能變得更自由,不再被 OpenNI 定義的介面綁死,在開發上也更彈性了~而在 OpenNI 2 發布的同時,也就已經有不少由其他開發者提供的 middleware library 跟著一起上線了(連結)~
而當然,PrimeSense 還是有持續維護他們的 NiTE、針對 OpenNI 2 推出新的 NiTE 2(連結),繼續提供本來就有的手部、骨架追蹤,以及姿勢和手勢的偵測。不過由於整個架構的改變,使用方法和 OpenNI 1.x 的時候也有很大的差別;這邊,就大概來講一下怎麼在 OpenNI 2 的架構下,使用 NiTE 2 吧~
專案設定
首先,在專案設定的部分,要設定的項目和 OpenNI 2 基本上是相同的,包括了:
-
「C/C++」的「Gerenal」(一般)裡的「Additional Include Directories」(其他 Include 目錄)要加上 $(NITE2_INCLUDE) 或 $(NITE2_INCLUDE64)。
-
「Linker」(連結器)的「Gerenal」(一般)裡的「Additional Library Directories」(其他程式庫目錄)要加上 $(NITE2_LIB) 或 $(NITE2_LIB64)。
- 「Linker」(連結器)的「Input」(輸入)裡的「Additional Dependencies」(其他相依性)要加上 NiTE2.lib。
比較完整的圖文設定說明,請參考《OpenNI 2 基本程式範例》一文,基本上,只是修改要甜的東西、把 OpenNI2 改成 NITE2 而已。不過另外要注意的就是,由於 NiTE 會使用到 OpenNI,所以本來 OpenNI 2 的設定也是需要加上去的~
再來,就是和 OpenNI 2 一樣,在程式執行的時候,會需要使用到 NiTE 的 Redist 目錄(預設是在 C:\Program Files\PrimeSense\NiTE2\Redist)下的檔案,所以也是一樣,需要讓程式找到的這些檔案。也因為會同時需要 OpenNI 2 和 NiTE 2 兩個函式庫的 Redist 資料夾,Heresy 會建議把這兩個資料夾的檔案,複製出來、放到同一個資料夾,然後把這個資料夾設定成為這個專案的「Working Directory」(工作路徑),這樣程式才可以正常執行;如果沒有處理好的話,可能就會出現類似下面的錯誤訊息:
Failed to initialize OpenNI Found no files matching '.\OpenNI2\Drivers\*.dll'
Could not find data file .\NiTE2/s.dat current working directory = C:\Program Files\OpenNI2\Redist
如果遇到「遺失 dll」的錯誤訊息,或是執行時有上面的錯誤訊息的話,請確認程式工作路徑下,要有下列的檔案:NiTE.ini、NiTE2.dll、OpenNI.ini、OpenNI2.dll、PS1080.ini ,以及 NiTE2 和 OpenNI2 這兩個資料夾。這些檔案,基本上都是在 OpenNI 和 NiTE 的 Redist 資料夾裡面的。
基本架構
NiTE 2 的架構、和使用概念,基本上都和 OpenNI 2(請參考《OpenNI 2 簡介》)非常地相似,所以如果已經知道 OpenNI 2 的程式怎麼寫,應該很容易就可以上手。比較大略性的來看,會使用來控制的類別,主要包括了:
-
nite::NiTE
NiTE 2 的整體環境控制的類別,基本上是用來控制 NiTE 的初始化和停止。
和 openni::OpenNI 一樣,他所有的函示都是 static 的,使用時不需要實體化變數出來。 -
nite::UserTracker
用來追蹤使用者的類別,包括了使用者的管理,以及骨架追蹤、姿勢偵測等功能;性質和 OpenNI 1.x 的 User Generator 類似。
讀取出來的資料類型,是 nite::UserTrackerFrameRef。 -
nite::HandTracker
用來進行手部追蹤的類別,除了追蹤手部位置外,也包含了手勢的偵測。基本上算是把 OpenNI 1.x 的 Hands Generator 和 Gesture Generator 的功能加在一起。
讀取出來的資料類型,是 nite::HandTrackerFrameRef。
基本上,NiTE 只又在程式開始、結束的時候需要用到,中間基本上是用不到的;使用概念和 openni::OpenNI 一樣。而 UserTracker 和 HandTracker 的使用概念,則也和 openni::VideoStream 相似。更完整的說明,可以參考官方的文件,檔案預設是在 C:\Program Files\PrimeSense\NiTE2\Documentation。
基本使用
NiTE 2 雖然需要 OpenNI 2 的功能(NiTE.h 就會去使用 OpenNI.h),但是實際上,如果只是單純要使用 NiTE 所提供的功能的話,在程式碼裡面,是可以完全不出現 OpenNI 的東西的~他最基本的使用流程,大致如下:
-
include NiTE.h 這個 header 檔,之後 NiTE C++ API 的東西,都會在 nite 這個 namespace 下。
-
呼叫 nite::NiTE::initialize() 來進行整個 NiTE 環境的初始化。
-
宣告出一個 NiTE 的物件,如果是要做使用者/骨架的追蹤的話,就是使用 nite::UserTracker,如果是要做手部相關的處理的話,則是用 nite::HandTracker。
之後再透過他所提供的 create() 函式,來完成 NiTE Tracker 的建立。
-
create() 這個函式有一個參數,可以指定要使用哪一個 openni::Device,如果不指定的話,NiTE 會自己去找一個可以用的,自己開啟來使用。
-
-
和 OpenNI 的 VideoStream 不同,NiTE 2 提供的兩個 Tracker,都沒有 start() 和 stop() 的函示可以用來控制開始和結束。所以要使用的話,就是直接進入主迴圈,透過 readFrame() 這個函式,來取得對應的 frame reference(nite::UserTrackerFrameRef 或 nite::HandTrackerFrameRef);而之後會用到的資料,基本上都在讀取到的 frame reference 中。
-
程式結束時,呼叫 Tracker 的 destory() 函式,把 Track 關掉。
-
最後,呼叫 nite::NiTE::shutdown(),關閉整個 NiTE 環境。
簡單的範例
上面算是概要性的講了一下 NiTE 2 要怎麼使用,接下來,則是一個極簡單的 NiTE 的範例。在這個範例程式裡面,基本上是直接去使用 NiTE2 的 UserTracker 來進行使用者的追蹤,完全不會直接使用到 OpenNI 的介面。
// STL Header #include <iostream> // 1. include NiTE Header #include <NiTE.h> // using namespace using namespace std; int main( int argc, char** argv ) { // 2. initialize NiTE nite::NiTE::initialize(); // 3. create user tracker nite::UserTracker mUserTracker; mUserTracker.create(); nite::UserTrackerFrameRef mUserFrame; for( int i = 0; i < 300; ++ i ) { // 4. get user frame mUserTracker.readFrame( &mUserFrame ); // 5. get users' data const nite::Array<nite::UserData>& aUsers = mUserFrame.getUsers(); for( int i = 0; i < aUsers.getSize(); ++ i ) { const nite::UserData& rUser = aUsers[i]; if( rUser.isNew() ) cout << "New User [" << rUser.getId() << "] found." << endl; if( rUser.isLost() ) cout << "User [" << rUser.getId() << "] lost." << endl; } } nite::NiTE::shutdown(); return 0; }
可以看到,整個流程基本上和 OpenNI 2 非常的相近,最大的差別,就在於 UserTracker 讀取到的資料,是 UserTrackerFrameRef 的形式,資料的存取方法和 OpenNI 的 VideoStream 不同而已。
像在個範例面,Heresy 就是先透過 UserTrackerFrameRef 的 getUsers() 這個函式,來取得使用者的列表;這邊的列表,會是一個陣列 nite::UserData 的陣列(和 OpenNI 2 一樣,NiTE 2 又自己定義一個 Array…他們完全沒有想過可以直接用 OpenNI 2 寫好的,或直接用 STL 現成的嗎? orz),裡面儲存的是目前偵測到的所有使用者。
不過由於這篇文章 Heresy 還不會仔細提 UserTrackerFrameRef 的各項功能細節,所以這邊就只有先使用他提供的 isNew() 和 isLost() 這兩個函式,來判斷這個使用者是剛被偵測到?還是不見了。包括骨架追蹤在內的細節,就等之後的文章再來說明了。
另外,如果要進行錯誤偵測的話,基本上也和 OpenNI 2 的方法(參考《OpenNI 2 的錯誤處理》)類似,只是回傳的狀態型別從 openni::Status 變成 nite::Status 而已。
這篇算是 NiTE2 的基本概論,大致上就先到這邊了。接下來應該是還會再花時間,針對 UserTracker 和 HandTracker 做進一步的說明(希望還有時間繼續寫下去…)。
[…] PrimeSense NiTE 呢?恩,不意外地,不能用。 這點,主要應該還是卡在 PrimeSense 的 NiTE […]
讚讚
請問一下, NITE2 計畫是不是已經停了? 現在還能找到 NITE2 for ARM 嗎? 謝謝
讚讚
請參考 https://kheresy.wordpress.com/2014/07/04/201407%e3%80%81openni-%e7%8f%be%e6%b3%81/
讚讚
How to display the tracking result?
讚讚
It depends on what GUI do you use.
讚讚