利用OpenNI进行手势识别 (小斤)

小斤将介绍如何使用OpenNI让Kinect识别出手势,并显示输出。目前版本的OpenNI支持四种手势:RaiseHand, Wave, Click和MovingHand,分别代表手的“举起”,“挥动”,“前推”和“移动”四种动作。值得一提的是,当前微软官方的Kinect SDK还不支持手势识别,也可以说是使用OpenNI的好处之一吧。
        有了手势识别后,其实可以利用Kinect做一些实际的东东,比如利用鼠标键盘控制一些应用程序等等。

        闲话不说了,速度搞起。代码一部分参考了Heresy童鞋的文章,手势部分写得很完备了,在巨人的肩膀上,小斤使用OpenCV进行显示,红色点表示手的位置轨迹,蓝色实心圈表示前推的位置,黄色直线表示手挥动的轨迹(从起点到终点)。

[cpp] view plain copy
  1. #include <stdlib.h>  
  2. #include <iostream>  
  3. #include "opencv/cv.h"  
  4. #include "opencv/highgui.h"  
  5. #include <XnCppWrapper.h>  
  6.   
  7. using namespace std;  
  8. using namespace cv;  
  9.   
  10. // output for XnPoint3D  
  11. ostream& operator<<( ostream& out, const XnPoint3D& rPoint )  
  12. {  
  13.     out << "(" << rPoint.X << "," << rPoint.Y << "," << rPoint.Z << ")";  
  14.     return out;  
  15. }  
  16.   
  17. //【4】  
  18. // callback function for gesture recognized  
  19. void XN_CALLBACK_TYPE gestureRecog( xn::GestureGenerator &generator,  
  20.                                    const XnChar *strGesture,  
  21.                                    const XnPoint3D *pIDPosition,  
  22.                                    const XnPoint3D *pEndPosition,  
  23.                                    void *pCookie )  
  24. {  
  25.     cout << strGesture<<" from "<<*pIDPosition<<" to "<<*pEndPosition << endl;  
  26.   
  27.     int imgStartX=0;  
  28.     int imgStartY=0;  
  29.     int imgEndX=0;  
  30.     int imgEndY=0;  
  31.     char locationinfo[100];  
  32.   
  33.     imgStartX=(int)(640/2-(pIDPosition->X));  
  34.     imgStartY=(int)(480/2-(pIDPosition->Y));  
  35.     imgEndX=(int)(640/2-(pEndPosition->X));  
  36.     imgEndY=(int)(480/2-(pEndPosition->Y));  
  37.   
  38.     IplImage* refimage=(IplImage*)pCookie;  
  39.     if(strcmp(strGesture,"RaiseHand")==0)  
  40.     {  
  41.         cvCircle(refimage,cvPoint(imgStartX,imgStartY),1,CV_RGB(255,0,0),2);  
  42.     }  
  43.     else if(strcmp(strGesture,"Wave")==0)  
  44.     {  
  45.         cvLine(refimage,cvPoint(imgStartX,imgStartY),cvPoint(imgEndX,imgEndY),CV_RGB(255,255,0),6);  
  46.     }  
  47.     else if(strcmp(strGesture,"Click")==0)  
  48.     {  
  49.         cvCircle(refimage,cvPoint(imgStartX,imgStartY),6,CV_RGB(0,0,255),12);  
  50.     }  
  51.   
  52.     cvSetImageROI(refimage,cvRect(40,450,640,30));  
  53.     CvFont font;  
  54.     cvInitFont( &font, CV_FONT_VECTOR0,1, 1, 0, 3, 5);  
  55.     cvSet(refimage, cvScalar(255,255,255));  
  56.     sprintf(locationinfo,"From: %d,%d to %d,%d",(int)pIDPosition->X,(int)pIDPosition->Y,(int)(pEndPosition->X),(int)(pEndPosition->Y));  
  57.     cvPutText(refimage, locationinfo ,cvPoint(30, 30), &font, CV_RGB(0,0,0));  
  58.     cvResetImageROI(refimage);  
  59. }  
  60.   
  61. void clearImg(IplImage* inputimg)  
  62. {  
  63.     CvFont font;  
  64.     cvInitFont( &font, CV_FONT_VECTOR0,1, 1, 0, 3, 5);  
  65.     memset(inputimg->imageData,255,640*480*3);  
  66.     cvPutText(inputimg, "Hand Raise!" ,cvPoint(20, 20), &font, CV_RGB(255,0,0));  
  67.     cvPutText(inputimg, "Hand Wave!" , cvPoint(20, 50), &font, CV_RGB(255,255,0));  
  68.     cvPutText(inputimg, "Hand Push!" , cvPoint(20, 80), &font, CV_RGB(0,0,255));  
  69. }  
  70.   
  71. //【5】  
  72. // callback function for gesture progress  
  73. void XN_CALLBACK_TYPE gestureProgress( xn::GestureGenerator &generator,  
  74.                                       const XnChar *strGesture,  
  75.                                       const XnPoint3D *pPosition,  
  76.                                       XnFloat fProgress,  
  77.                                       void *pCookie )  
  78. {  
  79.     cout << strGesture << ":" << fProgress << " at " << *pPosition << endl;  
  80. }  
  81.   
  82.   
  83. int main( int argc, char** argv )  
  84. {  
  85.     IplImage* drawPadImg=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3);  
  86.     IplImage* cameraImg=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3);  
  87.   
  88.     cvNamedWindow("Gesture",1);  
  89.     cvNamedWindow("Camera",1);  
  90.   
  91.     clearImg(drawPadImg);  
  92.   
  93.     XnStatus res;  
  94.     char key=0;  
  95.   
  96.     // context  
  97.     xn::Context context;  
  98.     res = context.Init();  
  99.     xn::ImageMetaData imgMD;  
  100.   
  101.     // create generator   
  102.     xn::ImageGenerator imageGenerator;  
  103.     res = imageGenerator.Create( context );   
  104.     //【1】  
  105.     xn::GestureGenerator gestureGenerator;  
  106.     res = gestureGenerator.Create( context );  
  107.   
  108.   //【2】  
  109.     // Add gesture  
  110.     //gestureGenerator.AddGesture( "MovingHand", NULL );  
  111.     gestureGenerator.AddGesture( "Wave", NULL );  
  112.     gestureGenerator.AddGesture( "Click", NULL );  
  113.     gestureGenerator.AddGesture( "RaiseHand", NULL );  
  114.     //gestureGenerator.AddGesture("MovingHand",NULL);  
  115.   
  116.   /【3】  
  117.     // 6. Register callback functions of gesture generator  
  118.     XnCallbackHandle handle;  
  119.     gestureGenerator.RegisterGestureCallbacks( gestureRecog, gestureProgress, (void*)drawPadImg, handle );  
  120.   
  121.     //start generate data  
  122.     context.StartGeneratingAll();  
  123.     res = context.WaitAndUpdateAll();    
  124.   
  125.     while( (key!=27) && !(res = context.WaitAndUpdateAll())  )   
  126.     {    
  127.         if(key=='c')  
  128.         {  
  129.             clearImg(drawPadImg);  
  130.         }  
  131.   
  132.         imageGenerator.GetMetaData(imgMD);  
  133.         memcpy(cameraImg->imageData,imgMD.Data(),640*480*3);  
  134.         cvCvtColor(cameraImg,cameraImg,CV_RGB2BGR);  
  135.   
  136.         cvShowImage("Gesture",drawPadImg);  
  137.         cvShowImage("Camera",cameraImg);  
  138.   
  139.         key=cvWaitKey(20);  
  140.   
  141.     }  
  142.     cvDestroyWindow("Gesture");  
  143.     cvDestroyWindow("Camera");  
  144.     cvReleaseImage(&drawPadImg);  
  145.     cvReleaseImage(&cameraImg);  
  146.     context.StopGeneratingAll();  
  147.     context.Shutdown();  
  148.   
  149.     return 0;  
  150. }  
        【1】看过上一篇文章的童鞋应该已经了解了生成器的用法(传送门),对于手势识别,小斤使用了GestureGenerator这个生成器,它的创建方式与ImageGenerator一致,使用Context作为参数调用Create方法。
        【2】GestureGenerator创建后,只有我们告诉它,要识别什么手势它才干活。这边使用AddGesture方法,把手势加入。

        【3】这一步小斤注册了GestureGenerator相关的回调函数:

[cpp] view plain copy
  1. XnStatus xn::GestureGenerator::RegisterGestureCallbacks  (GestureRecognized  RecognizedCB,  GestureProgress  ProgressCB,  void *  pCookie, XnCallbackHandle &  hCallback )  

        其中定义RecognizedCB是手势识别后的回调函数,ProgressCB是手势进行中的回调函数。

        pCookie是传给回调函数的指针,可以放一些用户数据,代码中,小斤把程序的画板图像指针传给了回调函数,这样便可以在回调函数中直接绘图了。
        phCallback是一个回调函数的handle,可用来注销回调函数,注销的方法是调用UnregisterGestureCallbacks  ( XnCallbackHandle  hCallback   ) 。
        【4】这个gestureRecog()便是RecognizedCB回调函数的真身了,它有五个参数:

        generator指定发现手势的生成器。

        strGesture告诉我们哪种手势已被识别。

        pIDPosition和pEndPosition代表了该手势的起始位置和终止位置。对于RaiseHand的手势,两个值是一样的,但对于Click和Wave手势,由于手的位置是发生过变化的,值是不同的。
         pCookie是传入的用户数据。
         在【4】中,小斤针对不同的手势进行了绘图,此外,还显示了手的当前pIDPosition和pEndPosition。
        【5】gestureProgress()回调函数与gestureRecog()类似,因为是在手势进行中被调用的,它只有一个position,表示当前手的位置,此外,还有一个fProgress表示当前的进度。
        程序编译执行后,效果如下:
利用OpenNI进行手势识别 (小斤)_第1张图片


        小斤这边测试结果是,RaiseHand最容易触发,基本上都在RecognizedCB中出现,而MovingHand没有被触发过。Click和Wave手势在RecognizedCB和ProgressCB都可能触发,虽然Wave手势的轨迹不一定准确,但Wave轨迹的用处不大。

        如果组合使用RaiseHand,Click和Wave,通过一些位置判定和调整,实现应用程序的部分控制是没有多大问题的,大家自由发挥想象吧,有好点子欢迎讨论!

你可能感兴趣的:(vector,测试,null,callback,generator,微软)