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循環中,而這時圖片的內容已經更新了(即有文字的圖片被重新覆蓋了),因此人眼一瞬間看不到有文字提示的圖片。最后個人的解決方法是用一個標志來表示檢測到了某個手勢動作,如果檢測到了則顯示存儲下來的有文字的圖片,反正,顯示正常的圖片。本程序提供的圖片為深度圖。

 

 

  實驗結果

  舉手的顯示結果如下:

  

   其實從本人的實驗過程來看,大部分的手勢動作都被檢測為舉手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中出現如下錯誤:

  

  則表示是imshow函數需要還來不及顯示完成就被其它的函數給中斷了,這可能在回調函數中出現這種情況。

 

  

  實驗總結

  通過本次實驗對OpenNI自帶的手勢識別類的使用有了初步的了解。

 

 

  參考資料:

    OpenNI 的手勢偵測

 

 

  附錄:實驗工程code下載

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM