在ubuntu14.04下openni+opencv+kinectV1的学习三:手势识别

1、编写代码

代码摘自小斤的博客: 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;
}

编译运行之后,有如下图所示的实验效果。实验中,举手动作最容易被触发,正常坐着就能识别,而另外两个就不是了,识别效果较差。在站起来,有一些距离下识别效果好,最好的识别效果在某一段距离内,三个手势都能够准确识别。

这里写图片描述

2、代码解释

【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,类型为字符数组名或者字符指针(需要初始化)。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值