開始之前,讓我們自己開始再熟練熟練NITE 2的基本使用,主要包括以下幾個步驟:
1. 初始化NITE環境: nite::NiTE::initialize();
2. 創建User跟蹤器: nite::UserTracker mUserTracker; mUserTracker.create();
3. 創建並讀取User Frame信息:nite::UserTrackerFrameRef mUserFrame; mUserTracker.readFrame( &mUserFrame );
4. 從User Frame信息中獲取User信息: const nite::Array<nite::UserData>& aUsers = mUserFrame.getUsers();然后根據User信息開始手勢識別或者人體骨骼跟蹤識別等我們自己需要的處理,開始我們自己的開發之旅。
5. 釋放Frame信息:mUserFrame.release();
6. 關閉跟蹤器:mUserTracker.destroy();
7. 最后關閉NITE環境:nite::NiTE::shutdown();
在這個是用NITE 2的整個過程中,我們沒用是用到OpenNI的任何頭文件和函數,使得程序看起來比較直觀,不容易和OpenNI混淆使用。但是我們還是需要配置OpenNI 2,因為NITE 2是在OpenNI的基礎上封裝而來的;同時需要OpenNI 2和NITE 2兩個函數庫的Redist文件夾,我的做法是將這兩個文件復制合並成新的Redist文件夾,然后將這個新的文件夾放在指定的任意路徑下,最后在vs2010中將工作路徑(Working Directory)指定到這個新的Redist文件夾路徑上。
具體代碼如下(環境配置在這里省去):
// YeNite2SimpleUsingOpenCV.cpp : 定義控制台應用程序的入口點。 // #include "stdafx.h" #include <iostream> // 載入NiTE.h頭文件 #include <NiTE.h> // using namespace using namespace std; int main( int argc, char** argv ) { // 初始化NITE nite::NiTE::initialize(); // 創建User跟蹤器 nite::UserTracker mUserTracker; mUserTracker.create(); nite::UserTrackerFrameRef mUserFrame; for( int i = 0; i < 1000; ++ i ) { // 讀取User Frame信息 mUserTracker.readFrame( &mUserFrame ); // 從User Frame信息中獲取User信息 const nite::Array<nite::UserData>& aUsers = mUserFrame.getUsers(); // Frame中User的個數 for( int i = 0; i < aUsers.getSize(); ++ i ) { const nite::UserData& rUser = aUsers[i]; // 當有User用戶出現在Kinect面前,則判斷並顯示 if( rUser.isNew() ) cout << "New User [" << rUser.getId() << "] found." << endl; } } // 釋放
mUserFrame.release();
// 關閉跟蹤器 mUserTracker.destroy(); // 關閉NITE環境 nite::NiTE::shutdown(); return 0; }
在這程序基礎上,我們添加“手勢識別”:具體說明直接見程序(具體說明可以參考之前的博文談談NITE 2的第一個程序HandViewer):
// YeNite2SimpleUsingOpenCV.cpp : 定義控制台應用程序的入口點。 // #include "stdafx.h" #include <iostream> // 載入NiTE.h頭文件 #include <NiTE.h> // using namespace using namespace std; int main( int argc, char** argv ) { // 初始化NITE nite::NiTE::initialize(); // 創建Hand跟蹤器 nite::HandTracker* mHandTracker = new nite::HandTracker; mHandTracker->create(); // 開始手勢探測 mHandTracker->startGestureDetection(nite::GESTURE_CLICK); mHandTracker->startGestureDetection(nite::GESTURE_WAVE); mHandTracker->startGestureDetection(nite::GESTURE_HAND_RAISE); nite::HandTrackerFrameRef mHandFrame; while(true) { // 讀取Frame信息 nite::Status rc = mHandTracker->readFrame(&mHandFrame); if (rc != nite::STATUS_OK) { cout << "GetNextData failed" << endl; return 0; } // 獲取定位的手的快照信息,讀取此時一共有多少個手勢 const nite::Array<nite::GestureData>& gestures = mHandFrame.getGestures(); for (int i = 0; i < gestures.getSize(); ++i) { // 當獲取的手勢是正確完成了 if (gestures[i].isComplete()) { // 就開始定位此時手勢的坐標 const nite::Point3f& position = gestures[i].getCurrentPosition(); cout << "Gesture " << gestures[i].getType() << " at" << position.x << "," << position.y <<"," << position.z; // nite::HandId newId ===>typedef short int HandId; nite::HandId newId; // 開始跟蹤該有效手勢的手心坐標,並確定該手的Id。 // 函數原型為:NITE_API NiteStatus niteStartHandTracking(NiteHandTrackerHandle, const NitePoint3f*, NiteHandId* pNewHandId); mHandTracker->startHandTracking(gestures[i].getCurrentPosition(), &newId); } } // 獲取定位手。 const nite::Array<nite::HandData>& hands= mHandFrame.getHands(); for (int i = 0; i < hands.getSize(); ++i) { const nite::HandData& user = hands[i]; if (!user.isTracking()) { cout << "Lost hand %d\n" << user.getId(); nite::HandId id = user.getId(); } else { if (user.isNew()) { cout << "Found hand %d\n" << user.getId(); } else { cout << "Hand ID:" << hands[i].getId() <<hands[i].getPosition().x << "," << hands[i].getPosition().y << "," << hands[i].getPosition().z << endl; } } } } mHandFrame.release(); // 關閉跟蹤器 mHandTracker->destroy(); // 關閉NITE環境 nite::NiTE::shutdown(); return 0; }
程序執行結果如下:
但是我們知道,單單有Hand的坐標還夠,我們需要定位在具體的圖像位置,利用手的坐標,來分割出手的輪廓等我們需要的信息,所以就要借助於OpenCV等常用工具庫,現在開始看看如何結合OpenCV和HandTracker,直接上代碼:
// YeNite2SimpleUsingOpenCV.cpp : 定義控制台應用程序的入口點。 // #include "stdafx.h" #include <iostream> // 載入NiTE.h頭文件 #include <NiTE.h> // 載入OpenCV頭文件 #include "opencv2/opencv.hpp" #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> using namespace std; using namespace cv; int main( int argc, char** argv ) { // 初始化NITE nite::NiTE::initialize(); // 創建Hand跟蹤器 nite::HandTracker* mHandTracker = new nite::HandTracker; mHandTracker->create(); // 創建OpenCV圖像窗口 namedWindow( "Hand Image", CV_WINDOW_AUTOSIZE ); // 循環讀取數據流信息並保存在HandFrameRef中 nite::HandTrackerFrameRef mHandFrame; // 開始手勢探測 mHandTracker->startGestureDetection(nite::GESTURE_CLICK); mHandTracker->startGestureDetection(nite::GESTURE_WAVE); mHandTracker->startGestureDetection(nite::GESTURE_HAND_RAISE); while( true ) { // 讀取Frame信息 nite::Status rc = mHandTracker->readFrame(&mHandFrame); if (rc != nite::STATUS_OK) { cout << "GetNextData failed" << endl; return 0; } // 獲取定位的手的快照信息,讀取此時一共有多少個手勢 const nite::Array<nite::GestureData>& gestures = mHandFrame.getGestures(); for (int i = 0; i < gestures.getSize(); ++i) { // 當獲取的手勢是正確完成了 if (gestures[i].isComplete()) { // 就開始定位此時手勢的坐標 const nite::Point3f& position = gestures[i].getCurrentPosition(); cout << "Gesture " << gestures[i].getType() << " at" << position.x << "," << position.y <<"," << position.z; // nite::HandId newId ===>typedef short int HandId; nite::HandId newId; // 開始跟蹤該有效手勢的手心坐標,並確定該手的Id。 // 函數原型為:NITE_API NiteStatus niteStartHandTracking(NiteHandTrackerHandle, const NitePoint3f*, NiteHandId* pNewHandId); mHandTracker->startHandTracking(gestures[i].getCurrentPosition(), &newId); } } // 獲取定位手。 const nite::Array<nite::HandData>& hands= mHandFrame.getHands(); for (int i = 0; i < hands.getSize(); ++i) { const nite::HandData& user = hands[i]; if (!user.isTracking()) { cout << "Lost hand %d\n" << user.getId(); nite::HandId id = user.getId(); } else { if (user.isNew()) { cout << "Found hand %d\n" << user.getId(); } else { cout << "Hand ID:" << hands[i].getId() <<hands[i].getPosition().x << "," << hands[i].getPosition().y << "," << hands[i].getPosition().z << endl; float x, y; // 將手心坐標轉換映射到深度坐標中 mHandTracker->convertHandCoordinatesToDepth(hands[i].getPosition().x, hands[i].getPosition().y, hands[i].getPosition().z, &x, &y); // 將深度數據轉換成OpenCV格式 const cv::Mat mHandDepth( mHandFrame.getDepthFrame().getHeight(), mHandFrame.getDepthFrame().getWidth(), CV_16UC1, (void*)mHandFrame.getDepthFrame().getData()); // 為了讓深度圖像顯示的更加明顯一些,將CV_16UC1 ==> CV_8U格式 cv::Mat mScaledHandDepth; mHandDepth.convertTo( mScaledHandDepth, CV_8U, 255.0 / 10000 ); // 提取以手心為中心,200*200大小的圖像 cv::Mat mHandsizeDepth = mScaledHandDepth(Rect(x - 100, y -100 , 200, 200)); // 顯示手的圖像 cv::imshow( "Hand Image", mHandsizeDepth ); } } } // 終止快捷鍵 if( cv::waitKey(1) == 'q') break; } // 關閉Frame mHandFrame.release(); // 關閉跟蹤器 mHandTracker->destroy(); // 關閉NITE環境 nite::NiTE::shutdown(); return 0; }
上圖
結合程序注釋和之前的博文內容,我想最后一個程序應該挺好理解的。根據自己的感覺走,感覺寫代碼,沒做封裝、優化、重構,完全是面向過程,而且肯定還存在細節的問題,會在后面進一步優化的。
寫的粗糙,歡迎指正批評~~~