zoukankan      html  css  js  c++  java
  • [转载]Kinect开发教程五:OpenNI获取人体骨架

     http://blog.csdn.net/chenxin_130/article/details/6950480

    临近毕业,小斤最近一直忙活着相关事宜,教程这边也搁浅了一阵。前几篇教程介绍了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个关节,如下图:

        先上代码:

    1. #include <stdlib.h>  
    2. #include <iostream>  
    3. #include <vector>  
    4.   
    5. #include <XnCppWrapper.h>  
    6. #include <XnModuleCppInterface.h>   
    7. #include "cv.h"  
    8. #include "highgui.h"  
    9.   
    10. using namespace std;  
    11. using namespace cv;  
    12.   
    13. //#pragma comment (lib,"cv210")  
    14. //#pragma comment (lib,"cxcore210")  
    15. //#pragma comment (lib,"highgui210")  
    16. //#pragma comment (lib,"OpenNI")  
    17.   
    18. //【1】  
    19. xn::UserGenerator userGenerator;  
    20. xn::DepthGenerator depthGenerator;  
    21. xn::ImageGenerator imageGenerator;  
    22.   
    23. /* 
    24.     XN_SKEL_HEAD          = 1,    XN_SKEL_NECK            = 2, 
    25.   XN_SKEL_TORSO         = 3,    XN_SKEL_WAIST           = 4, 
    26.     XN_SKEL_LEFT_COLLAR        = 5,    XN_SKEL_LEFT_SHOULDER        = 6, 
    27.   XN_SKEL_LEFT_ELBOW        = 7,  XN_SKEL_LEFT_WRIST          = 8, 
    28.   XN_SKEL_LEFT_HAND          = 9,    XN_SKEL_LEFT_FINGERTIP    =10, 
    29.     XN_SKEL_RIGHT_COLLAR    =11,    XN_SKEL_RIGHT_SHOULDER    =12, 
    30.   XN_SKEL_RIGHT_ELBOW        =13,  XN_SKEL_RIGHT_WRIST          =14, 
    31.   XN_SKEL_RIGHT_HAND      =15,    XN_SKEL_RIGHT_FINGERTIP    =16, 
    32.     XN_SKEL_LEFT_HIP          =17,    XN_SKEL_LEFT_KNEE            =18, 
    33.   XN_SKEL_LEFT_ANKLE        =19,  XN_SKEL_LEFT_FOOT            =20, 
    34.   XN_SKEL_RIGHT_HIP          =21,    XN_SKEL_RIGHT_KNEE          =22, 
    35.     XN_SKEL_RIGHT_ANKLE        =23,    XN_SKEL_RIGHT_FOOT          =24     
    36. */  
    37. //a line will be drawn between start point and corresponding end point  
    38. int startSkelPoints[14]={1,2,6,6,12,17,6,7,12,13,17,18,21,22};  
    39. int endSkelPoints[14]={2,3,12,21,17,21,7,9,13,15,18,20,22,24};  
    40.   
    41. // callback function of user generator: new user  
    42. void XN_CALLBACK_TYPE NewUser( xn::UserGenerator& generator, XnUserID user,void* pCookie )  
    43. {  
    44.     cout << "New user identified: " << user << endl;  
    45.     //userGenerator.GetSkeletonCap().LoadCalibrationDataFromFile( user, "UserCalibration.txt" );  
    46.     generator.GetPoseDetectionCap().StartPoseDetection("Psi", user);  
    47. }  
    48.   
    49. // callback function of user generator: lost user  
    50. void XN_CALLBACK_TYPE LostUser( xn::UserGenerator& generator, XnUserID user,void* pCookie )  
    51. {  
    52.     cout << "User " << user << " lost" << endl;  
    53. }  
    54.   
    55. // callback function of skeleton: calibration start  
    56. void XN_CALLBACK_TYPE CalibrationStart( xn::SkeletonCapability& skeleton,XnUserID user,void* pCookie )  
    57. {  
    58.     cout << "Calibration start for user " <<  user << endl;  
    59. }  
    60.   
    61. // callback function of skeleton: calibration end   
    62. void XN_CALLBACK_TYPE CalibrationEnd( xn::SkeletonCapability& skeleton,XnUserID user,XnCalibrationStatus calibrationError,void* pCookie )  
    63. {  
    64.     cout << "Calibration complete for user " <<  user << ", ";  
    65.     if( calibrationError==XN_CALIBRATION_STATUS_OK )  
    66.     {  
    67.         cout << "Success" << endl;  
    68.         skeleton.StartTracking( user );  
    69.         //userGenerator.GetSkeletonCap().SaveCalibrationDataToFile(user, "UserCalibration.txt" );  
    70.     }  
    71.     else  
    72.     {  
    73.         cout << "Failure" << endl;  
    74.         //For the current version of OpenNI, only Psi pose is available  
    75.         ((xn::UserGenerator*)pCookie)->GetPoseDetectionCap().StartPoseDetection( "Psi", user );  
    76.     }  
    77. }  
    78.   
    79. // callback function of pose detection: pose start  
    80. void XN_CALLBACK_TYPE PoseDetected( xn::PoseDetectionCapability& poseDetection,const XnChar* strPose,XnUserID user,void* pCookie)  
    81. {  
    82.     cout << "Pose " << strPose << " detected for user " <<  user << endl;  
    83.     ((xn::UserGenerator*)pCookie)->GetSkeletonCap().RequestCalibration( user, FALSE );  
    84.     poseDetection.StopPoseDetection( user );  
    85. }  
    86.   
    87. void clearImg(IplImage* inputimg)  
    88. {  
    89.     CvFont font;  
    90.     cvInitFont( &font, CV_FONT_VECTOR0,1, 1, 0, 3, 5);  
    91.     memset(inputimg->imageData,255,640*480*3);  
    92. }  
    93.   
    94.   
    95. int main( int argc, char** argv )  
    96. {  
    97.     char key=0;  
    98.     int imgPosX=0;  
    99.     int imgPosY=0;  
    100.   
    101.     // initial context  
    102.     xn::Context context;  
    103.     context.Init();  
    104.     xn::ImageMetaData imageMD;  
    105.   
    106.     IplImage* cameraImg=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3);  
    107.     cvNamedWindow("Camera",1);  
    108.   
    109.     // map output mode  
    110.     XnMapOutputMode mapMode;  
    111.     mapMode.nXRes = 640;  
    112.     mapMode.nYRes = 480;  
    113.     mapMode.nFPS = 30;  
    114.   
    115.     // create generator  
    116.     depthGenerator.Create( context );  
    117.     depthGenerator.SetMapOutputMode( mapMode );  
    118.     imageGenerator.Create( context );  
    119.     userGenerator.Create( context );  
    120.   
    121.   //【2】  
    122.     // Register callback functions of user generator  
    123.     XnCallbackHandle userCBHandle;  
    124.     userGenerator.RegisterUserCallbacks( NewUser, LostUser, NULL, userCBHandle );  
    125.   
    126.   //【3】  
    127.     // Register callback functions of skeleton capability  
    128.     xn::SkeletonCapability skeletonCap = userGenerator.GetSkeletonCap();  
    129.     skeletonCap.SetSkeletonProfile( XN_SKEL_PROFILE_ALL );  
    130.     XnCallbackHandle calibCBHandle;  
    131.     skeletonCap.RegisterToCalibrationStart( CalibrationStart,&userGenerator, calibCBHandle );  
    132.     skeletonCap.RegisterToCalibrationComplete( CalibrationEnd,&userGenerator, calibCBHandle );  
    133.   
    134.   //【4】  
    135.     // Register callback functions of Pose Detection capability  
    136.     XnCallbackHandle poseCBHandle;  
    137.     userGenerator.GetPoseDetectionCap().RegisterToPoseDetected( PoseDetected,&userGenerator, poseCBHandle );  
    138.   
    139.   
    140.     // start generate data  
    141.     context.StartGeneratingAll();  
    142.     while( key!=27 )  
    143.     {  
    144.         context.WaitAndUpdateAll();  
    145.   
    146.         imageGenerator.GetMetaData(imageMD);  
    147.         memcpy(cameraImg->imageData,imageMD.Data(),640*480*3);  
    148.         cvCvtColor(cameraImg,cameraImg,CV_RGB2BGR);  
    149.         // get users  
    150.         XnUInt16 userCounts = userGenerator.GetNumberOfUsers();  
    151.         if( userCounts > 0 )  
    152.         {  
    153.             XnUserID* userID = new XnUserID[userCounts];  
    154.             userGenerator.GetUsers( userID, userCounts );  
    155.             forint i = 0; i < userCounts; ++i )  
    156.             {  
    157.                 //【5】  
    158.                 // if is tracking skeleton  
    159.                 if( skeletonCap.IsTracking( userID[i] ) )  
    160.                 {  
    161.                     XnPoint3D skelPointsIn[24],skelPointsOut[24];  
    162.                     XnSkeletonJointTransformation mJointTran;  
    163.                     for(int iter=0;iter<24;iter++)  
    164.                     {  
    165.                         //XnSkeletonJoint from 1 to 24            
    166.                                                 skeletonCap.GetSkeletonJoint( userID[i],XnSkeletonJoint(iter+1), mJointTran );  
    167.                         skelPointsIn[iter]=mJointTran.position.position;  
    168.                     }  
    169.                     depthGenerator.ConvertRealWorldToProjective(24,skelPointsIn,skelPointsOut);  
    170.   
    171.                     //【6】  
    172.                     for(int d=0;d<14;d++)  
    173.                     {  
    174.                         CvPoint startpoint = cvPoint(skelPointsOut[startSkelPoints[d]-1].X,skelPointsOut[startSkelPoints[d]-1].Y);  
    175.                         CvPoint endpoint = cvPoint(skelPointsOut[endSkelPoints[d]-1].X,skelPointsOut[endSkelPoints[d]-1].Y);  
    176.               
    177.                         cvCircle(cameraImg,startpoint,3,CV_#0000ff,12);  
    178.                         cvCircle(cameraImg,endpoint,3,CV_#0000ff,12);  
    179.                         cvLine(cameraImg,startpoint,endpoint,CV_#0000ff,4);  
    180.                     }  
    181.                 }  
    182.             }  
    183.             delete [] userID;  
    184.         }  
    185.         cvShowImage("Camera",cameraImg);  
    186.   
    187.         key=cvWaitKey(20);  
    188.   
    189.   
    190.     }  
    191.     // stop and shutdown  
    192.     cvDestroyWindow("Camera");  
    193.     cvReleaseImage(&cameraImg);  
    194.     context.StopGeneratingAll();  
    195.     context.Shutdown();  
    196.   
    197.     return 0;  
    198. }  

        【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姿势,如图:


        小斤并为其注册了回调函数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姿势数秒,标定成功后,骨架就会正确显示出来了。祝大家玩得愉快。


  • 相关阅读:
    NSCharacterSet 最经常使用的使用方法
    IOS
    hdu 3117 Fibonacci Numbers
    Hibernate5配置与使用具体解释
    SDNU 1206.蚂蚁感冒 【代码如此简单,思维练习】【7月29】
    2048游戏分析、讨论与扩展
    hash_set和hash_map
    实现邮箱找回的思路分析
    学习OpenCV——粒子滤波(网上两篇文章总结)
    学习OpenCV——配置CUDA环境
  • 原文地址:https://www.cnblogs.com/fx2008/p/2323331.html
Copyright © 2011-2022 走看看