溫故而知新——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();
下面是簡單的右手骨骼坐標跟蹤,並顯示右手坐標信息的程序代碼:
// YeNITE2SimpleUsingOpenCV_Skeleton.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; // 開始人體骨骼跟蹤 mUserTracker.startSkeletonTracking( rUser.getId() ); } // 獲取骨骼坐標 const nite::Skeleton& rSkeleton = rUser.getSkeleton(); if( rSkeleton.getState() == nite::SKELETON_TRACKED ) { // 得到右手坐標 const nite::SkeletonJoint& righthand = rSkeleton.getJoint( nite::JOINT_RIGHT_HAND ); const nite::Point3f& position = righthand.getPosition(); cout << "右手坐標: " << position.x << "/" << position.y << "/" << position.z << endl; } } } // 釋放 mUserFrame.release(); // 關閉跟蹤器 mUserTracker.destroy(); // 關閉NITE環境 nite::NiTE::shutdown(); return 0; }
程序執行結果如下:
但通過對上述程序代碼觀察發現,在對人體骨骼跟蹤的時候,未做出(“投降”和“雙手抱胸”)的動作,也可以獲取骨骼坐標信息。難道在NITE2骨骼跟蹤的時候,人體姿勢檢測是多余的嗎?這個我的理解是:似乎姿勢跟蹤將會變成雞肋(完全靠自己的想象。。。)。
接着借助於OpenCV等常用工具庫,看看骨骼坐標在深度圖像下的定位和顯示效果,直接上代碼:
// 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(); // 創建User跟蹤器 nite::UserTracker* mUserTracker = new nite::UserTracker; mUserTracker->create(); // 創建OpenCV圖像窗口 namedWindow( "Skeleton Image", CV_WINDOW_AUTOSIZE ); // 循環讀取數據流信息並保存在HandFrameRef中 nite::UserTrackerFrameRef mUserFrame; while( true ) { // 讀取Frame信息 nite::Status rc = mUserTracker->readFrame(&mUserFrame); if (rc != nite::STATUS_OK) { cout << "GetNextData failed" << endl; return 0; } // 將深度數據轉換成OpenCV格式 const cv::Mat mHandDepth( mUserFrame.getDepthFrame().getHeight(), mUserFrame.getDepthFrame().getWidth(), CV_16UC1, (void*)mUserFrame.getDepthFrame().getData()); // 為了讓深度圖像顯示的更加明顯一些,將CV_16UC1 ==> CV_8U格式 cv::Mat mScaledHandDepth, thresholdDepth; mHandDepth.convertTo( mScaledHandDepth, CV_8U, 255.0 / 10000 ); // 二值化處理,為了顯示效果明顯 cv::threshold(mScaledHandDepth, thresholdDepth, 50, 255, 0); // 從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; // 開始人體骨骼跟蹤 mUserTracker->startSkeletonTracking( rUser.getId() ); } // 獲取骨骼坐標 const nite::Skeleton& rSkeleton = rUser.getSkeleton(); if( rSkeleton.getState() == nite::SKELETON_TRACKED ) { // 只得到前8個骨骼點坐標 for(int i = 0; i < 8; i++) { // 得到骨骼坐標 const nite::SkeletonJoint& skeletonJoint = rSkeleton.getJoint((nite::JointType)i); const nite::Point3f& position = skeletonJoint.getPosition(); float depth_x, depth_y; // 將骨骼點坐標映射到深度坐標中 mUserTracker->convertJointCoordinatesToDepth(position.x, position.y, position.z, &depth_x, &depth_y); cv::Point point((int)depth_x, (int)depth_y); // 將獲取的深度圖像中相對應的坐標點重新賦值為255.即在深度圖像中顯示出各個骨骼點。 thresholdDepth.at<uchar>(point) = 255; } // 顯示圖像 cv::imshow( "Skeleton Image", thresholdDepth ); } } // 終止快捷鍵 if( cv::waitKey(1) == 'q') break; } // 關閉Frame mUserFrame.release(); // 關閉跟蹤器 mUserTracker->destroy(); // 關閉NITE環境 nite::NiTE::shutdown(); return 0; }
上圖: 圖上“白點”就是骨骼點。
需要了解具體的骨骼點信息(位置、方向,以及可靠性等),可以看官網提供的參考文獻。我覺得遺憾的是,目前提供的15個骨骼點坐標不包括了手腕等其它骨骼點、而且只能得到全身的,不能單獨獲取上半身骨骼坐標。
結合程序注釋和之前的博文內容,我想最后一個程序應該挺好理解的。根據自己的感覺走,感覺寫代碼,沒做封裝、優化、重構,完全是面向過程,而且肯定還存在細節的問題,會在后面進一步優化的。
寫的粗糙,歡迎指正批評~~~