Kinect+OpenNI学习笔记之5(使用OpenNI自带的类进行简单手势识别)

  

  前言

  因为OpenNI可以获取到kinect的深度信息,而深度信息在手势识别中有很大用处,因此本文就来使用OpenNI自带的类来做简单的手势识别。识别的动作为4种,挥手,手移动,举手,往前推手。通过后面的实验可以发现,其实提供的类的效果非常不好。

  开发环境:QtCreator2.5.1+OpenNI1.5.4.0+Qt4.8.2+OpenCV2.4.2

 

  实验说明

  跟手势相关的是GestureGenerator这个类,它的初始化过程和depth_metadata,image_metadata都一样,因此首先在上2篇文章的COpenNI类中增加一个public类对象GestureGenerator gesture_generator;为什么不放在private里呢?因为我们的COpenNI对象需要调用这个变量来设置手势获取的一些属性,比如手势识别的种类等,总之就是这个变量外部需要能够访问得到,因此这里我将其放在public里面。另外在COpenNI类的Init()函数中需要加入下面的代码:

status = gesture_generator.Create(context);

        if(CheckError("Create gesture generator error!")) {

            return false;

        }

        /*添加手势识别的种类*/

        gesture_generator.AddGesture("Wave", NULL);

        gesture_generator.AddGesture("click", NULL);

        gesture_generator.AddGesture("RaiseHand", NULL);

        gesture_generator.AddGesture("MovingHand", NULL);

  OpenNI进行手势识别的方式是采用函数回调,即如果一个手势发生了或者正在发生时可以触发相应的回调函数,从而去执行回调函数,这有点类似于Qt中的信号与槽的关系。在OpenNI中设置回调函数的原型为:

  XnStatus RegisterGestureCallbacks(GestureRecognized RecognizedCB, GestureProgress ProgressCB, void* pCookie, XnCallbackHandle& hCallback);

  其中前2个参数为回调函数,第一个回调函数表示手部某个动作已经执行完毕,第二个参数表示收部某个动作正在执行;参数三为一个空指针,即可以指向任何数据类型的指针,其作用为给回调函数当额外的参数使用;参数四为回调函数的处理函数,用来记录和管理回调函数的。参数三在本实验中设置为NULL,参数四实际上本实验中也没有用到。

上面2个回调函数的名称可以自定义,但是这2个函数参数的个数和类型不能改变,这2个回调函数的参数个数都为5,但是其类型有些不同,具体的可以参考后面提供的代码。

  由于在程序中添加了4种动作的捕捉,所以打算在检测到某个手势动作时,在窗口显示栏的图片上添加相应的手势动作文字提示。很明显,只有当手势检测到时才能在图片上添加文字,该部分在回调函数中实现。但是如果我们单独在回调函数中给图片添加相应的文字,然后在主程序中显示图片,则因为回调函数一结束完就回到了主函数的while循环中,而这时图片的内容已经更新了(即有文字的图片被重新覆盖了),因此人眼一瞬间看不到有文字提示的图片。最后个人的解决方法是用一个标志来表示检测到了某个手势动作,如果检测到了则显示存储下来的有文字的图片,反正,显示正常的图片。本程序提供的图片为深度图。

 

 

  实验结果

  举手的显示结果如下:

  Kinect+OpenNI学习笔记之5(使用OpenNI自带的类进行简单手势识别)_第1张图片

   其实从本人的实验过程来看,大部分的手势动作都被检测为举手RaiseHand,少部分为挥手Wave,其它的基本上没出现过。说明OpenNI自带的手势识别类的功能不是很强。

 

 

  实验主要部分代码及注释(附录有实验工程code下载链接):

copenni.cpp:

#include <XnCppWrapper.h>
#include <QtGui/QtGui>
#include <iostream>

using namespace xn;
using namespace std;

class COpenNI
{
public:
    ~COpenNI() {
        context.Release();//释放空间
    }
    bool Initial() {
        //初始化
        status = context.Init();
        if(CheckError("Context initial failed!")) {
            return false;
        }
        context.SetGlobalMirror(true);//设置镜像
        //产生图片node
        status = image_generator.Create(context);
        if(CheckError("Create image generator  error!")) {
            return false;
        }
        //产生深度node
        status = depth_generator.Create(context);
        if(CheckError("Create depth generator  error!")) {
            return false;
        }
        //视角校正
        status = depth_generator.GetAlternativeViewPointCap().SetViewPoint(image_generator);
        if(CheckError("Can't set the alternative view point on depth generator!")) {
            return false;
        }
        status = gesture_generator.Create(context);
        if(CheckError("Create gesture generator error!")) {
            return false;
        }
        /*添加手势识别的种类*/
        gesture_generator.AddGesture("Wave", NULL);
        gesture_generator.AddGesture("click", NULL);
        gesture_generator.AddGesture("RaiseHand", NULL);
        gesture_generator.AddGesture("MovingHand", NULL);
        return true;

    }

    bool Start() {
        status = context.StartGeneratingAll();
        if(CheckError("Start generating error!")) {
            return false;
        }
        return true;
    }

    bool UpdateData() {
        status = context.WaitNoneUpdateAll();
        if(CheckError("Update date error!")) {
            return false;
        }
        //获取数据
        image_generator.GetMetaData(image_metadata);
        depth_generator.GetMetaData(depth_metadata);

        return true;
    }

public:
    DepthMetaData depth_metadata;
    ImageMetaData image_metadata;
    GestureGenerator gesture_generator;//外部要对其进行回调函数的设置,因此将它设为public类型

private:
    //该函数返回真代表出现了错误,返回假代表正确
    bool CheckError(const char* error) {
        if(status != XN_STATUS_OK ) {
            QMessageBox::critical(NULL, error, xnGetStatusString(status));
            cerr << error << ": " << xnGetStatusString( status ) << endl;
            return true;
        }
        return false;
    }

private:
    XnStatus    status;
    Context     context;
    DepthGenerator  depth_generator;
    ImageGenerator  image_generator;
};

 

main.cpp:

#include <QCoreApplication>

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <opencv2/core/core.hpp>
#include "copenni.cpp"

#include <iostream>

using namespace cv;
using namespace xn;

Mat depth_image;
Mat depth_image_result;//深度结果图,且在该图上显示手势动作的类型
COpenNI openni;
bool test_flag = false;

// callback function for gesture recognized
//回调函数,该函数的函数名字可以随便取,但是其参数的格式必须不能改变
//这里该函数的作用是表示上面4种手势发生完成后调用
void  XN_CALLBACK_TYPE  GRecognized ( xn::GestureGenerator &generator,
                                    const XnChar *strGesture,
                                    const  XnPoint3D *pIDPosition,
                                    const  XnPoint3D *pEndPosition,
                                    void *pCookie )
{
    depth_image_result = depth_image.clone();
    putText(depth_image_result, strGesture, Point(50, 150), 3, 0.8, Scalar(255, 0, 0), 2 );
    test_flag = true;
}

// callback function for gesture progress
//该函数表示上面4种手势某一种正在发生时调用
void  XN_CALLBACK_TYPE  GProgress ( xn::GestureGenerator &generator,
                                  const XnChar *strGesture,
                                  const  XnPoint3D *pPosition,
                                 XnFloat fProgress,
                                 void *pCookie )
{
    ;
}

int main (int argc, char **argv)
{

    if(!openni.Initial())
        return 1;
    XnCallbackHandle handle;
    openni.gesture_generator.RegisterGestureCallbacks(GRecognized, GProgress, NULL, handle);
    if(!openni.Start())
        return 1;
    namedWindow("depth image", CV_WINDOW_AUTOSIZE);
    putText(depth_image, "YES!", Point(50, 150), 3, 0.8, Scalar(255, 0, 0), 2 );
    while(1) {
        if(!openni.UpdateData()) {
            return 1;
        }
        /*获取并显示深度图像,且这2句代码不能放在回调函数中调用,否则后面的imshow函数会因为执行时找不到图片(因为此时回调函数不一定执行了)而报错*/
        Mat depth_image_src(openni.depth_metadata.YRes(), openni.depth_metadata.XRes(),
                            CV_16UC1, (char *)openni.depth_metadata.Data());//因为kinect获取到的深度图像实际上是无符号的16位数据
        depth_image_src.convertTo(depth_image, CV_8U, 255.0/8000);

        if(!test_flag)
            imshow("depth image", depth_image);
        else
            imshow("depth image", depth_image_result);
        waitKey(30);
        test_flag = false;
    }

}

 

 

  错误总结

  如果用Qt的控制台建立程序,运行程序时出现下面的错误提示:

  

  这是因为控制台程序不能使用Qt的界面(本程序中使用了QMessageBox),因此需要在工程pro的代码中把QT –  gui给去掉,否则会报类似的这种错误。

  如果是在OpenCV中出现如下错误:

  Kinect+OpenNI学习笔记之5(使用OpenNI自带的类进行简单手势识别)_第2张图片

  则表示是imshow函数需要还来不及显示完成就被其它的函数给中断了,这可能在回调函数中出现这种情况。

 

  

  实验总结

  通过本次实验对OpenNI自带的手势识别类的使用有了初步的了解。

 

 

  参考资料:

    OpenNI 的手势侦测

 

 

  附录:实验工程code下载

 

 

 

你可能感兴趣的:(kinect)