代码摘自小斤的博客: Kinect开发教程三:利用OpenNI进行手势识别
#include <stdlib.h> #include <iostream> #include "opencv/cv.h" #include "opencv/highgui.h" #include <XnCppWrapper.h> using namespace std; using namespace cv; // output for XnPoint3D ostream& operator<<( ostream& out, const XnPoint3D& rPoint ) { out << "(" << rPoint.X << "," << rPoint.Y << "," << rPoint.Z << ")"; return out; } //【4】 // callback function for gesture recognized void XN_CALLBACK_TYPE gestureRecog( xn::GestureGenerator &generator, const XnChar *strGesture, const XnPoint3D *pIDPosition, const XnPoint3D *pEndPosition, void *pCookie ) { cout << strGesture<<" from "<<*pIDPosition<<" to "<<*pEndPosition << endl; int imgStartX=0; int imgStartY=0; int imgEndX=0; int imgEndY=0; char locationinfo[100]; imgStartX=(int)(640/2-(pIDPosition->X)); imgStartY=(int)(480/2-(pIDPosition->Y)); imgEndX=(int)(640/2-(pEndPosition->X)); imgEndY=(int)(480/2-(pEndPosition->Y));//设置点绘制坐标 IplImage* refimage=(IplImage*)pCookie;//获取画布首地址 if(strcmp(strGesture,"RaiseHand")==0) {//发现举起手势就绘制小红点 cvCircle(refimage,cvPoint(imgStartX,imgStartY),1,CV_RGB(255,0,0),2); } else if(strcmp(strGesture,"Wave")==0) {//发现挥动手势就绘制小黄点 cvLine(refimage,cvPoint(imgStartX,imgStartY),cvPoint(imgEndX,imgEndY),CV_RGB(255,255,0),6); } else if(strcmp(strGesture,"Click")==0) {//发现前推手势就绘制大蓝点 cvCircle(refimage,cvPoint(imgStartX,imgStartY),6,CV_RGB(0,0,255),12); } cvSetImageROI(refimage,cvRect(40,450,640,30));//设置图像的感兴趣区域(矩形),即设置图像操作区域 CvFont font; cvInitFont( &font, CV_FONT_VECTOR0,1, 1, 0, 3, 5);//字体结构初始化 cvSet(refimage, cvScalar(255,255,255));//设置画布为白色 sprintf(locationinfo,"From: %d,%d to %d,%d",(int)pIDPosition->X,(int)pIDPosition->Y,(int)(pEndPosition->X),(int)(pEndPosition->Y)); cvPutText(refimage, locationinfo ,cvPoint(30, 30), &font, CV_RGB(0,0,0));//在画布上写字符串 cvResetImageROI(refimage);//取消感兴趣区域的设置 } void clearImg(IplImage* inputimg) { CvFont font; cvInitFont( &font, CV_FONT_VECTOR0,1, 1, 0, 3, 5);//初始化字体结构体 memset(inputimg->imageData,255,640*480*3);//图像RGB值初始化 cvPutText(inputimg, "Hand Raise!" ,cvPoint(20, 20), &font, CV_RGB(255,0,0));//红色 cvPutText(inputimg, "Hand Wave!" , cvPoint(20, 50), &font, CV_RGB(255,255,0));//黄色 cvPutText(inputimg, "Hand Push!" , cvPoint(20, 80), &font, CV_RGB(0,0,255));//蓝色 } //【5】 // callback function for gesture progress void XN_CALLBACK_TYPE gestureProgress( xn::GestureGenerator &generator, const XnChar *strGesture, const XnPoint3D *pPosition, XnFloat fProgress, void *pCookie ) { cout << strGesture << ":" << fProgress << " at " << *pPosition << endl; } int main( int argc, char** argv ) { IplImage* drawPadImg=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3); IplImage* cameraImg=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3); cvNamedWindow("Gesture",1); cvNamedWindow("Camera",1); clearImg(drawPadImg);//清空手势绘制屏幕 XnStatus res; char key=0; // context xn::Context context; res = context.Init();//摄像头初始化 xn::ImageMetaData imgMD; // create generator xn::ImageGenerator imageGenerator; res = imageGenerator.Create( context ); //创建图像生成器 //【1】 xn::GestureGenerator gestureGenerator; res = gestureGenerator.Create( context );//创建手势生成器 //【2】 // Add gesture //gestureGenerator.AddGesture( "MovingHand", NULL ); gestureGenerator.AddGesture( "Wave", NULL ); gestureGenerator.AddGesture( "Click", NULL ); gestureGenerator.AddGesture( "RaiseHand", NULL );//加入要识别的手势 //gestureGenerator.AddGesture("MovingHand",NULL); //【3】 // 6. Register callback functions of gesture generator XnCallbackHandle handle; //注册识别手势相关的回调函数 gestureGenerator.RegisterGestureCallbacks( gestureRecog, gestureProgress, (void*)drawPadImg, handle ); //start generate data context.StartGeneratingAll();//开始收集图像数据 res = context.WaitAndUpdateAll(); while( (key!=27) && !(res = context.WaitAndUpdateAll()) ) { if(key=='c') {//c键按下就清除画布 clearImg(drawPadImg); } imageGenerator.GetMetaData(imgMD);//获取图像数据 memcpy(cameraImg->imageData,imgMD.Data(),640*480*3);//拷贝图像数据 cvCvtColor(cameraImg,cameraImg,CV_RGB2BGR);//图像数据格式转换 cvShowImage("Gesture",drawPadImg);//显示画布 cvShowImage("Camera",cameraImg);//显示彩色图像 key=cvWaitKey(20);//等待按键按下 } cvDestroyWindow("Gesture"); cvDestroyWindow("Camera");//关闭窗口 cvReleaseImage(&drawPadImg); cvReleaseImage(&cameraImg);//释放内存空间 context.StopGeneratingAll();//关闭数据收集开关 context.Shutdown();//关闭所有驱动并且正确地清除所 return 0; }编译运行之后,有如下图所示的实验效果。实验中,举手动作最容易被触发,正常坐着就能识别,而另外两个就不是了,识别效果较差。在站起来,有一些距离下识别效果好,最好的识别效果在某一段距离内,三个手势都能够准确识别。
【1】clearImg(drawPadImg)函数清除了手势绘制窗口,并显示了红色的Hand Raise,黄色的Hand Wave和蓝色的Hand Push。 void cvInitFont( CvFont* font, int font_face, double hscale,double vscale, double shear=0, int thickness=1, int line_type=8 )函数的作用是初始化字体结构体。其中: font 被初始化的字体结构体。 font_face 字体名称标识符。只是Hershey 字体集( http://sources.isc.org/utils/misc/hershey-font.txt )的一个子集得到支持。 CV_FONT_HERSHEY_SIMPLEX - 正常大小无衬线字体。 CV_FONT_HERSHEY_PLAIN - 小号无衬线字体。 CV_FONT_HERSHEY_DUPLEX - 正常大小无衬线字体。( 比CV_FONT_HERSHEY_SIMPLEX更复杂) CV_FONT_HERSHEY_COMPLEX - 正常大小有衬线字体。 CV_FONT_HERSHEY_TRIPLEX - 正常大小有衬线字体 ( 比CV_FONT_HERSHEY_COMPLEX更复杂) CV_FONT_HERSHEY_COMPLEX_SMALL - CV_FONT_HERSHEY_COMPLEX 的小译本。 CV_FONT_HERSHEY_SCRIPT_SIMPLEX - 手写风格字体。 CV_FONT_HERSHEY_SCRIPT_COMPLEX - 比CV_FONT_HERSHEY_SCRIPT_SIMPLEX更复杂。 这个参数能够由一个值和可选择的CV_FONT_ITALIC字体标记合成,就是斜体字。 hscale 字体宽度。如果等于1.0f,字符的宽度是最初的字体宽度。如果等于0.5f,字符的宽度是最初的字体宽度的一半。 vscale 字体高度。如果等于1.0f,字符的高度是最初的字体高度。如果等于0.5f,字符的高度是最初的字体高度的一半。 shear 字体的斜度。当值为0时 ,字符不倾斜;当值为1.0f时,字体倾斜≈45度,等等。厚度让字母着重显示。函数cvLine用于绘制字母。 thickness 字体笔划的粗细程度。 line_type 字体笔划的类型,参见cvLine。 函数cvInitFont初始化字体结构体,字体结构体可以被传递到文字显示函数中。摘自博客文章cvInitFont 。 memset(inputimg->imageData,255,640*480*3)作用是将已开辟内存空间的inputimg->imageData的首640*480*3个字节设置为255,感觉是在对图像的RGB值进行初始化。 cvPutText(CvArr* img, const char* text, CvPoint origin, const CvFont* font, CvScalar color);函数中各个参数的含义如下所示: img—图片指针(需要说明的是,CvArr* 等价于void*,一般我们在这里传递一个IplImage*); text—显然是需要打印到图片上的字符串的内容; origin—字符串在图片上打印的原点(即,字符串的左下角在图片中的位置) font—描述字体属性的变量; color—字体的颜色; 摘自博客文章opencv 中的 cvPutText() 函数的使用 。
【2】XnStatus xn::GestureGenerator::RegisterGestureCallbacks (GestureRecognized RecognizedCB, GestureProgress ProgressCB, void * pCookie, XnCallbackHandle & phCallback ) RecognizedCB是手势识别后的回调函数; ProgressCB是手势进行中的回调函数; pCookie是传给回调函数的指针,可以放一些用户数据; phCallback是一个回调函数的handle,可用来注销回调函数,注销的方法是调用UnregisterGestureCallbacks ( XnCallbackHandle hCallback ) 。 OpenNI支持四种手势:RaiseHand, Wave, Click和MovingHand,分别代表手的“举起”,“挥动”,“前推”和“移动”四种动作。
【3】void cvCircle( CvArr* img, CvPoint center, int radius, CvScalar color, int thickness=1, int line_type=8, int shift=0 )函数的功能是指绘制圆形。其中参数有: img 图像,即画布首地址 center 圆心坐标 radius 圆形的半径 color 线条的颜色 thickness 如果是正数,表示组成圆的线条的粗细程度。否则,表示圆是否被填充 line_type 线条的类型。见 cvLine 的描述 shift 圆心坐标点和半径值的小数点位数 摘自百度百科。
【4】cvSetImageROI(img1,cvRect(100,100,356,156)),(100,100)表示ROI区域的左上角坐标,356,156分别表示ROI区域的长宽,函数作用是设置改矩形区域为图像操作的感兴趣区域。具体解释可以查看博客文章。
【5】int sprintf( char *buffer, const char *format [, argument] … ); 功能:把格式化的数据写入buffer容器中(字符串),返回buffer的长度。这里s为一个buffer,类型为字符数组名或者字符指针(需要初始化)。
