开始之前,让我们自己开始再熟练熟练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; }
上图
结合程序注释和之前的博文内容,我想最后一个程序应该挺好理解的。根据自己的感觉走,感觉写代码,没做封装、优化、重构,完全是面向过程,而且肯定还存在细节的问题,会在后面进一步优化的。
写的粗糙,欢迎指正批评~~~