在ubuntu14.04下openni+opencv+kinectV1的学习四:人体骨架

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_36355662/article/details/76869159

1、学习了解

在OpenNI中,要对人进行骨架追踪,需要人先摆出PSI的姿势,然后系统根据该姿势进行骨骼校正,待校正完成后才进行骨骼的跟踪,其流程图可以参考下面的图:
这里写图片描述
由图可以看出,其完成骨骼跟踪主要分为3个部分,首先需检测到人体,然后需要固定的PSI姿势来对人体的姿势进行校正,待姿势校正完成后,才能进行人体骨骼的追踪。参见博客文章Kinect+OpenNI学习笔记之6(获取人体骨架并在Qt中显示)
而PSI的姿势见下图:
这里写图片描述
参见博客文章Kinect开发教程五:OpenNI获取人体骨架
在OpenNI库的enum XnSkeletonJoint中,定义了24个人体的关节,所以设计了一个二维的连接表矩阵,矩阵的大小为14*2,即有14条边,每条边有2个顶点,矩阵中对应位置的值表示的是对应边的节点骨架的标号,在OpenNI中人体的骨架节点共分为15个,手脚共12个,头部2个,躯干1个。如下:
XN_SKEL_HEAD = 1, XN_SKEL_NECK = 2,
XN_SKEL_TORSO = 3, XN_SKEL_WAIST = 4,
XN_SKEL_LEFT_COLLAR = 5, XN_SKEL_LEFT_SHOULDER = 6,
XN_SKEL_LEFT_ELBOW = 7, XN_SKEL_LEFT_WRIST = 8,
XN_SKEL_LEFT_HAND = 9, XN_SKEL_LEFT_FINGERTIP =10,
XN_SKEL_RIGHT_COLLAR =11, XN_SKEL_RIGHT_SHOULDER =12,
XN_SKEL_RIGHT_ELBOW =13, XN_SKEL_RIGHT_WRIST =14,
XN_SKEL_RIGHT_HAND =15, XN_SKEL_RIGHT_FINGERTIP =16,
XN_SKEL_LEFT_HIP =17, XN_SKEL_LEFT_KNEE =18,
XN_SKEL_LEFT_ANKLE =19, XN_SKEL_LEFT_FOOT =20,
XN_SKEL_RIGHT_HIP =21, XN_SKEL_RIGHT_KNEE =22,
XN_SKEL_RIGHT_ANKLE =23, XN_SKEL_RIGHT_FOOT =24
程序中对这15个点编了序号,头部为0, 颈部为1, 躯干为2, 左肩膀为3, 左手肘为4, 左手腕5,右肩膀为6,右手肘为7,右手腕为8,左臀为9,左膝盖为10,左脚跟为11,右臀为12,右膝盖为13,右脚跟为14。目前可使用的有14个关节,如下图:
这里写图片描述
参见博客文章透過 OpenNI / NITE 分析人體骨架(上)

2、编写代码

代码摘自小斤博客,见Kinect开发教程五:OpenNI获取人体骨架 ,此处就不罗列了。

3、代码解释

习惯从main主函数开始看起,下面对一些重点代码进行解释:
【1】程序创建了三个生成器depthGenerator,imageGenerator和userGenerator。最后一个没有见过,他是具有检测新的User(以下称为人物)出现或者离开,获取画面中的人物数,人物位置信息,与上一教程介绍的GestureGenerator类似,通过注册回调函数的方式,一旦其检测到了动静(如人物出现),那么相应的回调函数就会被调用。但是也有些不同,在进行骨架的判断和姿态检测是需要用到OpenNI延伸的功能,与这种延伸功能相关的类可以称作为Capability。在进行人体骨骼分析时,user generator需要有支援Skeleton和Pose Detection这2个的capability。

【2】到了第一个注册函数,他有四个参数:
userGenerator.RegisterUserCallbacks( NewUser, LostUser, NULL, userCBHandle );
UserHandler NewUserCB:检测到新使用者的 callback function
UserHandler LostUserCB:使用者消失的 callback function
void* pCookie:额外传送给 callback function 使用的数据
XnCallbackHandle& hCallback:管理 callback function 用的 handle 值,当要取消 callback function 时就会触发这个函数。
其中要注意的是,在 callback function 的部分,NewUser() 和 LostUser() 在被调用的时候,都会取得三个变量的值:xn::UserGenerator& generator、XnUserID user、void* pCookie。

【3】到了第二个注册函数,这里出现了一个新的Capability,SkeletonCapability。它可以理解为生成器的一种能力,比如SkeletonCapability就可以理解UserGenerator获取人物骨架信息的能力。
而要使用 skeleton capability 前,还要使用 SetSkeletonProfile() 函数来设定skeleton profile,决定要使用哪些关节。在 OpenNI 是定义了五种不同的 profile:
XN_SKEL_PROFILE_NONE:没有任何关节。
XN_SKEL_PROFILE_ALL:代表所有关节,以目前的 NITE 来说就是 15 个。
XN_SKEL_PROFILE_UPPER:含躯干(torso)的上半身, 以目前的 NITE 来说就是九个关节。
XN_SKEL_PROFILE_LOWER:含躯干(torso)的下半身,以目前的 NITE 来说理论上有七个,但是Heresy 实验后只有列出不含 torso 的六个关节。
XN_SKEL_PROFILE_HEAD_HANDS:只有头和双手三个关节。
在获取人物骨架前,首先要进行标定的工作,因此SkeletonCapability需要注册两个回调函数CalibrationStart和CalibrationEnd,分别在人物标定开始与结束时调用,他们包含以下参数:
CalibrationStart CalibrationStartCB:开始进行骨架校正的回调函数;
CalibrationEnd CalibrationEndCB:骨架校正完的回调函数;
void* pCookie:额外传送给回调函数的数据信息;
XnCallbackHandle& hCallback:管理回调函数用的 handle 值。

重点关注CalibrationEnd这个回调函数,看看他的参数
xn::SkeletonCapability& skeleton:即是skeletonCap。
XnUserID user:目前进行骨架校正的使用者。
XnBool bSuccess:人体骨架校正校正是否成功。
void* pCookie:额外传送的数据,此处就是userGenerator,便于在回调函数里面操作。
CalibrationEnd()函数的作用就是通过 bSuccess 标志来判断是否有正确骨架,如果成功的话就调用skeleton.StartTracking( user )的skeleton capability开始对这个使用者骨架进行跟踪;而如果失败就要求 pose detectioncapability 重新检测PSI姿势,等到下一尺再分析骨架。
在要重新检测PSI姿势时,要对pose detection capability做操作。而CalibrationEnd() 取得的只有 skeleton capability,并沒有 pose detection capability。因此,要通过外部传来的 pCookie 来获得,而在这里由于得到的是 mUserGenerator 指针,但是类型已经是 void* 了,所以要使用的话,要通过强制转换成 xn::UserGenerator* 才行。如下所示:
((xn::UserGenerator*)pCookie)->GetPoseDetectionCap().StartPoseDetection( “Psi”, user );
实际上这个程序里CalibrationEnd() 只有用到 skeleton capability 和 pose detection capability,而他本身就可以直接取得 skeleton capability,拿不到的只有 pose detection capability,所以可以不用把 user generator 传进来,只要 pose detection capability 也是可以的。
【4】到了这个函数,作用和注册回调函数相同。
userGenerator.GetPoseDetectionCap().RegisterToPoseDetected( PoseDetected,&userGenerator, poseCBHandle ),其中
PoseDetection PoseDetected:检测到姿势调用的回调函数;
void* userGenerator:额外传送的数据;
poseCBHandle:管理回调函数用的 handle 值。

看看这个PoseDetected() 函数,他总供有四个参数:
xn::PoseDetectionCapability& poseDetection:pose detection capability 他等同于主函数中mUserGenerator.GetPoseDetectionCap() 取得的 pose detection capability参数;
const XnChar* strPose:检测到姿势,这里就只有PSI一种;
XnUserID user:检测到该姿势的使用者;
void* pCookie:额外传送的数据,这里就是user generator,也即是 mUserGenerator 。

【5】最后进入main函数主循环,首先通过收集器获得彩色图像数据和使用者信息数据,再通过GetSkeletonJoint()方法,可以得到对应关节的XnSkeletonJointTransformation,这个结构体包含position和orientation,position中又包含一个position和fConfidence,分别代表关节的位置和可信度,orientation同样如此,包含关节的运动方向和可信度。这些步骤得到的position信息,是一个真实场景的3D坐标,需要通过投影转换到屏幕坐标,转换过程通过ConvertRealWorldToProjective()方法实现。
为了更直观地输出显示,可以各个关节通过直线连接起来,形成一个人体的骨架。这里定义了startSkelPoints和endSkelPoints数组,两个数组的值一一对应,代表一组起点终点的关节对,将每组起点和终点通过直线连接,比如HEAD与NECT与TORSO等。

4、参考文章

【1】Kinect+OpenNI学习笔记之6(获取人体骨架并在Qt中显示)
【2】Kinect开发教程五:OpenNI获取人体骨架
【3】透過 OpenNI / NITE 分析人體骨架(上)
【4】透過 OpenNI / NITE 分析人體骨架(下)

猜你喜欢

转载自blog.csdn.net/qq_36355662/article/details/76869159