主要内容:
一、姿势探测识别
1.1 内容介绍
NITE 2的姿势探测识别功能和人体骨骼跟踪一样,是由UserTracker提供的,在NiTE 2.0版本中,提供了两种姿势:“POSE_PSI”(我称它为“投降姿势”)和“POSE_CROSS_HAND”(称之为“双手抱胸”),除此之外,我们没办法提供自己设定的特定姿势的探测和识别。
在之前的版本中,由于“POSE_PSI”是用来做骨架跟踪校正的标志姿势使用的,但后来NITE提供了不用校正姿势的情况下就可以进行骨架跟踪了,所以在没有特殊使用的情况下,本人(谈谈NITE 2与OpenCV结合的第二个程序(提取人体骨骼坐标))觉得在骨骼跟踪上,姿势的探测已然成为了多余的了;但对于使用姿势识别有关方面的研究的,我想这个姿势探测识别应该还是重要的,没准在NITE后面的版本中提供”自制的特定姿势“跟踪识别了。
由于在NITE中主要提供的是人体跟踪和手的跟踪,而人体姿势探测属于前者,所以姿势探测识别和人体骨骼跟踪一样,都是使用UserTracker。首先通过获得新的使用者信息;然后根据指定使用者,利用UserTracker开始进行姿势的探测;最后根据每次探测到最新的姿势资料,进行判定识别,以及开始我们自己需要的处理工作。
1.2 代码介绍
同样的,直接上代码说明如何利用UserTracker进行姿势探测。
#include "stdafx.h" #include <iostream> // 载入NiTE头文件 #include <NiTE.h> // using namespace using namespace std; using namespace nite; int main( int argc, char** argv ) { // 初始化NiTE环境 NiTE::initialize(); // 创建UserTracker跟踪器 UserTracker mUserTracker; mUserTracker.create(); while(true) { // 读取帧信息 UserTrackerFrameRef mUserFrame; mUserTracker.readFrame( &mUserFrame); // 通过帧信息,获得用户数据UserData const Array<UserData>& aUsers = mUserFrame.getUsers(); for( int i = 0; i < aUsers.getSize(); ++ i ) { const UserData& rUser = aUsers[i]; const UserId& uID = rUser.getId(); if( rUser.isNew() ) { cout << "User " << uID << " found." << endl; // 为每一个新用户进行姿势探测 cout << "Start pose detection " << uID<< endl; // 探测uID的两种姿势 mUserTracker.startPoseDetection( uID, POSE_PSI ); mUserTracker.startPoseDetection( uID, POSE_CROSSED_HANDS ); } else if( rUser.isLost() ) { cout << "User " << uID << " lost." << endl; } else { // 读取用户的“POSI_PSI”的姿势状态 const PoseData& rPosePSI = rUser.getPose( POSE_PSI ); // 当做了“POSI_PSI”时: if( rPosePSI.isEntered() ) cout << " 开始---投降姿势(PSI pose)" << endl; if( rPosePSI.isHeld() ) cout << " 保持---投降姿势(PSI pose)" << endl; // 当做完了“POSI_PSI”后,双手放下时: if( rPosePSI.isExited() ) cout << "停止---投降姿势(PSI pose)" << endl; // 同样的读取“POSE_CROSSED_HANDS”的姿势状态 const PoseData& rPCH = rUser.getPose( POSE_CROSSED_HANDS ); if( rPCH.isEntered() ) cout << " 开始---双手抱胸(Cross Hand pose)" << endl; if( rPCH.isHeld() ) cout << " 保持---双手抱胸(Cross Hand pose)" << endl; if( rPCH.isExited() ) cout << " 停止---双手抱胸(Cross Hand pose)" << endl; } } } // 关闭UserTracker跟踪 mUserTracker.destroy(); // 关闭NITE环境 NiTE::shutdown(); return 0; }
上图:
当开始姿势探测时,只要双手举起,PSI就会触发,使rPosePSI.isEntered()值为true;当保持着这个姿势一段时间,就会使rPosePSI.isHeld()值为true,表示目前的状态为保持着投降姿势;当双手放下时,rPosePSI.isExited()值为true,表示不再保持着投降姿势。同样的道理,当你做出”双手抱胸“的姿势(POSE_CROSSED_HANDS)时,也同样提供isEntered()、isHeld()和isExited()三个函数来表示当前探测的姿势的状态情况。
注:当我无论如何做出”双手抱胸“的姿势(POSE_CROSSED_HANDS)时,都无法触发这一姿势的跟踪,所以我的结论是我还不知道怎么去”双手抱胸“~~~
1.3 总结
对于姿势探测识别,主要包括以下几个步骤:
- 初始化NiTE环境:NiTE::initialize();
- 创建UserTracker跟踪器:UserTracker::create();
- 读取跟踪器帧信息:UserTracker::readFrame( &UserTrackerFrameRef);
- 通过帧信息,获得用户数据UserData:mUserFrame::getUsers();
- 对特定用户开始姿势探测(包括”投降姿势“和”双手抱胸姿势“的探测):UserTracker::startPoseDetection(UserId user, PoseType type);
- 读取用户的指定姿势的状态信息:PoseData& getPose(PoseType type);
- 主要有isEntered()、isHeld()、isExited()三个函数来表示当前探测的姿势的状态情况;
- 停止UserTracker跟踪器:UserTracker::destroy();
- 最后停止NiTE环境:NiTE::shutdown();
二、手势探测识别
2.1 内容介绍
在NITE中,手势探测识别主要是由HandTracker类提供的,和UserTracker一样,HandTracker还提供的手部位置的跟踪(谈谈NITE 2的第一个程序HandViewer和谈谈NITE 2与OpenCV结合的第一个程序,以及谈谈NITE 2与OpenCV结合提取指尖坐标等处都做了介绍了)。根据目前的NITE提供的手势跟踪和之前的版本个人感觉差不多。首先都是不针对指定用户的手势识别(这点和姿势探测识别不一样),而是针对整个界面帧信息进行分析,找到符合的手势;其次探测识别的手势只有三个:”GESTURE_WAVE“(挥手)、”GESTURE_CLICK“(手掌前推在缩回来)和”GESTURE_HAND_RAISE“(手举起)。最后目前都没有提供自制的手势探测(这点和姿势探测一样)。
2.2 代码说明
#include "stdafx.h" #include <iostream> // 载入NiTE头文件 #include <NiTE.h> // using namespace using namespace std; using namespace nite; int main( int argc, char** argv ) { // 初始化NiTE环境 NiTE::initialize(); // 创建HandTracker跟踪器 HandTracker mHandTracker; mHandTracker.create(); // 设定手势探测(GESTURE_WAVE、GESTURE_CLICK和GESTURE_HAND_RAISE) mHandTracker.startGestureDetection( GESTURE_WAVE ); mHandTracker.startGestureDetection( GESTURE_CLICK ); mHandTracker.startGestureDetection( GESTURE_HAND_RAISE ); while(true) { // 读取帧信息 HandTrackerFrameRef mHandFrame; mHandTracker.readFrame( &mHandFrame ); // 整个界面帧信息进行分析,找到符合的手势 const Array<GestureData>& aGestures = mHandFrame.getGestures(); for( int i = 0; i < aGestures.getSize(); ++ i ) { const GestureData& rGesture = aGestures[i]; // 对找到的手势进行类型判断,并输出类型 cout << "Detect gesture "; switch( rGesture.getType() ) { case GESTURE_WAVE: cout << "摇手手势---“wave”:"; break; case GESTURE_CLICK: cout << "前推并收回手势---“click”"; break; case GESTURE_HAND_RAISE: cout << "举起手势---“hand raise”"; break; } // 得到的手势信息中还包含了当前手势的坐标位置 const Point3f& rPos = rGesture.getCurrentPosition(); cout << " 手势位置为: (" << rPos.x << ", " << rPos.y << ", " << rPos.z << ")" << endl; // 以及手势状态,完成状态和进行状态 if( rGesture.isComplete() ) cout << " 手势完成"; if( rGesture.isInProgress() ) cout << " 手势正在进行"; cout << endl; } } // 关闭HandTracker跟踪 mHandTracker.destroy(); // 关闭NITE环境 NiTE::shutdown(); return 0; }
上图:
注:三个手势中”举起“手势太好识别了,直接掩盖了其它两个手势,所以在实际的手势识别中,我建议不用检测”举起“这个手势~~~。我把上面代码中mHandTracker.startGestureDetection( GESTURE_HAND_RAISE );给注释了,再看运行结果:
注:排除了”举起“手势的捣乱之后,发现”前推并收回“手势(click),动作最好掌控,识别过程和效果也是最好(个人觉得);同时我怎么”摇手“(wave),好像都出不了检测结果,具体什么原因现在我也不知道(估计又是我手势做的不对),有知道的烦请教教我,谢谢~~~
2.3 总结
具体手势探测识别流程和1.3的相似,看了上面的代码肯定知道,所以这里就不写了。最后我的建议是:如果要进行手势探测识别的话,我提议是探测”click“手势~~~
写的粗糙~~~