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

Kinect开发课程五:OpenNI获取人体骨架

2012-07-02 
Kinect开发教程五:OpenNI获取人体骨架??? 临近毕业,小斤最近一直忙活着相关事宜,教程这边也搁浅了一阵。前

Kinect开发教程五:OpenNI获取人体骨架

??? 临近毕业,小斤最近一直忙活着相关事宜,教程这边也搁浅了一阵。前几篇教程介绍了OpenNI的一些基本范例以及手势应用,但如果光用Kinect识别一些手势,总有点杀鸡用牛刀的感觉。在大部分体感应用中,获取骨架的步骤都不可缺少,这也是小斤一直想写的专题。

??? 好了,废话不多说了,让我们进入正题吧!

??? 在OpenNI库的enum XnSkeletonJoint中,定义了24个人体的关节,如下:

? 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???

??? 小斤试下来,目前可使用的有14个关节,如下图:

Kinect开发课程五:OpenNI获取人体骨架

?

??? 先上代码:

#include <stdlib.h>#include <iostream>#include <vector>#include <XnCppWrapper.h>#include <XnModuleCppInterface.h> #include "cv.h"#include "highgui.h"using namespace std;using namespace cv;//#pragma comment (lib,"cv210")//#pragma comment (lib,"cxcore210")//#pragma comment (lib,"highgui210")//#pragma comment (lib,"OpenNI")//【1】xn::UserGenerator userGenerator;xn::DepthGenerator depthGenerator;xn::ImageGenerator imageGenerator;/*    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    *///a line will be drawn between start point and corresponding end pointint startSkelPoints[14]={1,2,6,6,12,17,6,7,12,13,17,18,21,22};int endSkelPoints[14]={2,3,12,21,17,21,7,9,13,15,18,20,22,24};// callback function of user generator: new uservoid XN_CALLBACK_TYPE NewUser( xn::UserGenerator& generator, XnUserID user,void* pCookie ){cout << "New user identified: " << user << endl;//userGenerator.GetSkeletonCap().LoadCalibrationDataFromFile( user, "UserCalibration.txt" );generator.GetPoseDetectionCap().StartPoseDetection("Psi", user);}// callback function of user generator: lost uservoid XN_CALLBACK_TYPE LostUser( xn::UserGenerator& generator, XnUserID user,void* pCookie ){cout << "User " << user << " lost" << endl;}// callback function of skeleton: calibration startvoid XN_CALLBACK_TYPE CalibrationStart( xn::SkeletonCapability& skeleton,XnUserID user,void* pCookie ){cout << "Calibration start for user " <<  user << endl;}// callback function of skeleton: calibration end void XN_CALLBACK_TYPE CalibrationEnd( xn::SkeletonCapability& skeleton,XnUserID user,XnCalibrationStatus calibrationError,void* pCookie ){cout << "Calibration complete for user " <<  user << ", ";if( calibrationError==XN_CALIBRATION_STATUS_OK ){cout << "Success" << endl;skeleton.StartTracking( user );//userGenerator.GetSkeletonCap().SaveCalibrationDataToFile(user, "UserCalibration.txt" );}else{cout << "Failure" << endl;//For the current version of OpenNI, only Psi pose is available((xn::UserGenerator*)pCookie)->GetPoseDetectionCap().StartPoseDetection( "Psi", user );}}// callback function of pose detection: pose startvoid XN_CALLBACK_TYPE PoseDetected( xn::PoseDetectionCapability& poseDetection,const XnChar* strPose,XnUserID user,void* pCookie){cout << "Pose " << strPose << " detected for user " <<  user << endl;((xn::UserGenerator*)pCookie)->GetSkeletonCap().RequestCalibration( user, FALSE );poseDetection.StopPoseDetection( user );}void clearImg(IplImage* inputimg){CvFont font;cvInitFont( &font, CV_FONT_VECTOR0,1, 1, 0, 3, 5);memset(inputimg->imageData,255,640*480*3);}int main( int argc, char** argv ){char key=0;int imgPosX=0;int imgPosY=0;// initial contextxn::Context context;context.Init();xn::ImageMetaData imageMD;IplImage* cameraImg=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3);cvNamedWindow("Camera",1);// map output modeXnMapOutputMode mapMode;mapMode.nXRes = 640;mapMode.nYRes = 480;mapMode.nFPS = 30;// create generatordepthGenerator.Create( context );depthGenerator.SetMapOutputMode( mapMode );imageGenerator.Create( context );userGenerator.Create( context );  //【2】// Register callback functions of user generatorXnCallbackHandle userCBHandle;userGenerator.RegisterUserCallbacks( NewUser, LostUser, NULL, userCBHandle );  //【3】// Register callback functions of skeleton capabilityxn::SkeletonCapability skeletonCap = userGenerator.GetSkeletonCap();skeletonCap.SetSkeletonProfile( XN_SKEL_PROFILE_ALL );XnCallbackHandle calibCBHandle;skeletonCap.RegisterToCalibrationStart( CalibrationStart,&userGenerator, calibCBHandle );skeletonCap.RegisterToCalibrationComplete( CalibrationEnd,&userGenerator, calibCBHandle );  //【4】// Register callback functions of Pose Detection capabilityXnCallbackHandle poseCBHandle;userGenerator.GetPoseDetectionCap().RegisterToPoseDetected( PoseDetected,&userGenerator, poseCBHandle );// start generate datacontext.StartGeneratingAll();while( key!=27 ){context.WaitAndUpdateAll();imageGenerator.GetMetaData(imageMD);memcpy(cameraImg->imageData,imageMD.Data(),640*480*3);cvCvtColor(cameraImg,cameraImg,CV_RGB2BGR);// get usersXnUInt16 userCounts = userGenerator.GetNumberOfUsers();if( userCounts > 0 ){XnUserID* userID = new XnUserID[userCounts];userGenerator.GetUsers( userID, userCounts );for( int i = 0; i < userCounts; ++i ){//【5】// if is tracking skeletonif( skeletonCap.IsTracking( userID[i] ) ){XnPoint3D skelPointsIn[24],skelPointsOut[24];XnSkeletonJointTransformation mJointTran;for(int iter=0;iter<24;iter++){//XnSkeletonJoint from 1 to 24                                                skeletonCap.GetSkeletonJoint( userID[i],XnSkeletonJoint(iter+1), mJointTran );skelPointsIn[iter]=mJointTran.position.position;}depthGenerator.ConvertRealWorldToProjective(24,skelPointsIn,skelPointsOut);//【6】for(int d=0;d<14;d++){CvPoint startpoint = cvPoint(skelPointsOut[startSkelPoints[d]-1].X,skelPointsOut[startSkelPoints[d]-1].Y);CvPoint endpoint = cvPoint(skelPointsOut[endSkelPoints[d]-1].X,skelPointsOut[endSkelPoints[d]-1].Y);cvCircle(cameraImg,startpoint,3,CV_RGB(0,0,255),12);cvCircle(cameraImg,endpoint,3,CV_RGB(0,0,255),12);cvLine(cameraImg,startpoint,endpoint,CV_RGB(0,0,255),4);}}}delete [] userID;}cvShowImage("Camera",cameraImg);key=cvWaitKey(20);}// stop and shutdowncvDestroyWindow("Camera");cvReleaseImage(&cameraImg);context.StopGeneratingAll();context.Shutdown();return 0;}
?

??? 【1】? 对于人体骨架的获取,小斤声明了UserGenerator这个生成器,UserGenerator具有检测新的User(以下称为人物)出现或者离开,获取画面中的人物数,人物位置信息,与上一教程介绍的GestureGenerator类似,通过注册回调函数的方式,一旦其检测到了动静(如人物出现),那么相应的回调函数就会被调用。

??? 【2】? 小斤为UserGenerator注册了NewUser和LostUser两个回调函数,对应人物出现和人物消失。

??? 【3】? 这里出现了一个新的Capability,SkeletonCapability。小斤为了避免混淆,常常将Capability理解为生成器的一种能力,比如SkeletonCapability就可以理解UserGenerator获取人物骨架信息的能力。

??? 在获取人物骨架前,首先要进行标定的工作,因此SkeletonCapability需要注册两个回调函数CalibrationStart和CalibrationEnd,分别在人物标定开始与结束时调用。(在较早版本的OpenNI中,接口名可能有所变化)

??? 【4】? 与【3】类似,userGenerator.GetPoseDetectionCap()获取了一个PoseDetectionCapability,这个Capability可以检测人物的特定姿势,目前来说,只支持Psi姿势,如图:

Kinect开发课程五:OpenNI获取人体骨架

??? 小斤并为其注册了回调函数PoseDetected,在检测到人物的Psi姿势时,会调用该函数。

??? 将【2】【3】【4】的回调函数串联起来看,(1)人物出现会触发NewUser(),开始Pose检测;(2)检测到Pose会触发PoseDetected(),请求标定;(3)标定开始触发CalibrationStart();(4)标定结束触发CalibrationEnd(),如果标定成功,那么调用SkeletonCapability的StartTracking()开始跟踪对应的人物。

??? 【5】? 通过GetSkeletonJoint()方法,可以得到对应关节的XnSkeletonJointTransformation,这个结构体包含position和orientation,position中又包含一个position和fConfidence,分别代表关节的位置和可信度,orientation同样如此,包含关节的运动方向和可信度。这里小斤对24个关节都进行了操作,但能得到位置信息的只有14个。

这些步骤得到的position信息,是一个真实场景的3D坐标,需要通过投影转换到屏幕坐标,转换过程通过ConvertRealWorldToProjective()方法实现。

??? 【6】? 为了更直观地输出显示,可以各个关节通过直线连接起来,形成一个人体的骨架。小斤定义了startSkelPoints和endSkelPoints数组,两个数组的值一一对应,代表一组起点终点的关节对,将每组起点和终点通过直线连接,比如HEAD与NECT与TORSO等。

?

??? 整个程序启动后,先将身体正对摄像头(至少露出头部和上半身),控制台会显示“New user identified”,然后做出Psi姿势,在Pose Psi detected后,程序开始标定工作,此时维持Psi姿势数秒,标定成功后,骨架就会正确显示出来了。祝大家玩得愉快。


Kinect开发课程五:OpenNI获取人体骨架

?

?

----------------------------------

作者:小斤(陈忻)

本文属于原创文章,如需转载引用请注明原文作者和链接,谢谢。

热点排行