首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 其他教程 > 开源软件 >

NUI Kinect OpenNI Nite 追踪 玩家 骨骼 流程

2012-07-20 
【原创】 NUI Kinect OpenNI Nite 追踪 玩家 骨骼 流程http://nuihq.com/wordpress/?p47搬家中, 清关注 nui

【原创】 NUI Kinect OpenNI Nite 追踪 玩家 骨骼 流程

http://nuihq.com/wordpress/?p=47

搬家中, 清关注 nuihq.com

?

nuihq.com qt nui ni ui kinect primesense WAVI Xtion 体感

用语约定:

玩家:程序开发的终端用户, 在设备前玩游戏的家伙

对玩家骨骼的追踪是Nite中的重要功能之一, 下面我就为大家简单分析以下Nite自带的 “Players” 这个例子的主要流程

简化代码

要分析代码要现精简代码, 将主要的流程提取出来, 进行分析, 例子的核心文件只有三个, main.cpp, ScreenDrawer.h, ScreenDrawer.cpp, 因为我们只是分析追踪的流程, 因此画图部分可以略去, 首先分析 main.cpp

首先我们要剥离的是有关画图显示的代码, 这样, 关于gl的代码我们就可以跳过然后关于这个例子中有一个玩家会话控制的功能, 即玩家短暂的离开后再次回来可以让程序很快地恢复校准, 同时为上层的游戏提供一个“中间层”, 将先后加入或更换的玩家对应到游戏中的角色,会话控制的代码是写例子的人自己的实现, 也可以不看(说实话我真的没看懂), 就是 AssignPlayer , FindPlayer, LostPlayer, 这几个函数接着可以略去的代码是有关深度图录像的函数, StopCapture 和 StartCapture 从入口函数入手首先是从xml创建环境, 然后初始化并检查生产节点, 接着检查生产节点的能力, 这部分使用了上边定义的宏, 用来检查错误并退出main函数。接着开始绑定回调函数, 将玩家事件, 校准事件绑定到对应的处理函数, 程序的流程

OpenNI 的数据获取和处理都是在他的更新函数 g_Context.WaitOneUpdateAll(g_DepthGenerator); 中进行的, 你绑定的回调函数也是在这个函数中被调用的, OpenNI有多种更新函数, 具体差别看文档~在更新函数中, OpenNI 等待并搜集所有的数据, 和之前缓存的数据一同进行处理, 如果符合某个事件的条件, 则调取你设定的回调。 这个函数一般要循环调用以持续对数据进行更新, 在官方的例子中, 他是使用opengl的显示循环进行循环调用的。接下来我们针对例子进行分析:

当一个玩家出现在kinect的“视野”中后, openni会对其进行识别, 如果发现这“东西”像是个人, 则调用例子中的 NewUser 函数, 例子中 NewUser 会使用 g_UserGenerator.GetPoseDetectionCap().StartPoseDetection(“Psi”, user); 函数开启这个玩家的姿势检测, 检测的姿势叫做 “Psi”, 这个姿势就是我们说的 “投降” 姿势, 双手举过头顶~如果此时玩家做出了 “投降” 姿势, 则 PoseDetected 这个回调会被调用, 在这个函数中, 例子使用g_UserGenerator.GetSkeletonCap().RequestCalibration(user, TRUE); 请求对玩家进行 “校准”, 就是对其尝试进行骨骼模式匹配, 同时例子使用 g_UserGenerator.GetPoseDetectionCap().StopPoseDetection(user); 停止玩家的姿势检测, 我实验了一下这个地方如果不停止姿势检测的话也是没有问题的, 但是如果不需要再检测玩家姿势的话就停掉吧, 毕竟可以省点资源嘛之后openni会用一段时间对玩家进行骨骼模式匹配, 匹配不一定能够成功, 和玩家出现在kinect设备视野中的完整程度有关, 有过完整出现的话基本是可以顺利识别的。 当匹配完成后, 无论成功失败, 都会调用 CalibrationCompleted(xn::SkeletonCapability& skeleton, XnUserID user, XnCalibrationStatus eStatus, void* cxt) 这个函数, 我们可以看到这个函数中有对校准结果的检测, XN_CALIBRATION_STATUS_OK 代表成功, 成功的话, 例子调用了 g_UserGenerator.GetSkeletonCap().StartTracking(user) 开始对此用户进行骨骼追踪至此, 就可以获取用户的骨骼数据了, 在 ScreenDrawer.cpp 中的 DrawLimb(XnUserID player, XnSkeletonJoint eJoint1, XnSkeletonJoint eJoint2) 函数中, g_UserGenerator.GetSkeletonCap().GetSkeletonJointPosition(player, eJoint1, joint1), eJoint1 这个参数是从下边的DrawDepthMap函数的后边传过来的, 分别代表身体关节, 可以的值有

XN_SKEL_HEAD,
XN_SKEL_NECK,
XN_SKEL_LEFT_SHOULDER, XN_SKEL_RIGHT_SHOULDER,
XN_SKEL_LEFT_ELBOW, XN_SKEL_RIGHT_ELBOW,
XN_SKEL_LEFT_HAND, XN_SKEL_RIGHT_HAND,
XN_SKEL_TORSO,
XN_SKEL_LEFT_HIP, XN_SKEL_RIGHT_HIP,
XN_SKEL_LEFT_KNEE, XN_SKEL_RIGHT_KNEE,
XN_SKEL_LEFT_FOOT, XN_SKEL_RIGHT_FOOT

注意和改进

写到这里, 基本的流程就分析完了, 程序中有些问题

这里要注意一下, 由于openni是用C写的, 所以如果你要使用C++进行开发的话你的回调函数要使用静态成员函数, 或使用非成员函数, 这样的话才可以在编译时确定函数的地址, 进行绑定例子程序有个缺点, 就是一个玩家校准失败后, 就不能再进行校准, 因此可以将 main.cpp CalibrationCompleted 函数修改如下
if (eStatus == XN_CALIBRATION_STATUS_OK){...... // 原有代码}else{       printf("请求重新校准 %d 号玩家!\n", int(user));       g_UserGenerator.GetSkeletonCap().RequestCalibration(user, false); // 重新请求校准}
还有个小问题, 就是 main.cpp CalibrationEnded 这个函数是没有用的~, 下边回调中并没有绑定, 整个程序中也没有用到

热点排行