谈谈NITE 2与OpenCV结合的第一个程序

    开始之前,让我们自己开始再熟练熟练NITE 2的基本使用,主要包括以下几个步骤:

    1. 初始化NITE环境: nite::NiTE::initialize();

    2. 创建User跟踪器: nite::UserTracker mUserTracker; mUserTracker.create();

    3. 创建并读取User Frame信息:nite::UserTrackerFrameRef mUserFrame; mUserTracker.readFrame( &mUserFrame );

    4. 从User Frame信息中获取User信息:  const nite::Array<nite::UserData>& aUsers = mUserFrame.getUsers();然后根据User信息开始手势识别或者人体骨骼跟踪识别等我们自己需要的处理,开始我们自己的开发之旅。

    5. 释放Frame信息:mUserFrame.release();

    6. 关闭跟踪器:mUserTracker.destroy();

    7. 最后关闭NITE环境:nite::NiTE::shutdown();

    在这个是用NITE 2的整个过程中,我们没用是用到OpenNI的任何头文件和函数,使得程序看起来比较直观,不容易和OpenNI混淆使用。但是我们还是需要配置OpenNI 2,因为NITE 2是在OpenNI的基础上封装而来的;同时需要OpenNI 2和NITE 2两个函数库的Redist文件夹,我的做法是将这两个文件复制合并成新的Redist文件夹,然后将这个新的文件夹放在指定的任意路径下,最后在vs2010中将工作路径(Working Directory)指定到这个新的Redist文件夹路径上。

具体代码如下(环境配置在这里省去):

// YeNite2SimpleUsingOpenCV.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>

// 载入NiTE.h头文件
#include <NiTE.h>

// using namespace
using namespace std;

int main( int argc, char** argv )
{
    // 初始化NITE
    nite::NiTE::initialize();

    // 创建User跟踪器
    nite::UserTracker mUserTracker;
    mUserTracker.create();

    nite::UserTrackerFrameRef mUserFrame;
    for( int i = 0; i < 1000; ++ i )
    {
        // 读取User Frame信息
        mUserTracker.readFrame( &mUserFrame );

        // 从User Frame信息中获取User信息
        const nite::Array<nite::UserData>& aUsers = mUserFrame.getUsers();

        // Frame中User的个数
        for( int i = 0; i < aUsers.getSize(); ++ i )
        {
            const nite::UserData& rUser = aUsers[i];

            // 当有User用户出现在Kinect面前,则判断并显示
            if( rUser.isNew() )
                cout << "New User [" << rUser.getId() << "] found." << endl;
        }
    }

    // 释放
mUserFrame.release();

// 关闭跟踪器 mUserTracker.destroy(); // 关闭NITE环境 nite::NiTE::shutdown(); return 0; }

    在这程序基础上,我们添加“手势识别”:具体说明直接见程序(具体说明可以参考之前的博文谈谈NITE 2的第一个程序HandViewer):

// YeNite2SimpleUsingOpenCV.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>

// 载入NiTE.h头文件
#include <NiTE.h>

// using namespace
using namespace std;

int main( int argc, char** argv )
{
    // 初始化NITE
    nite::NiTE::initialize();

    // 创建Hand跟踪器
    nite::HandTracker* mHandTracker = new nite::HandTracker;
    mHandTracker->create();

    // 开始手势探测
    mHandTracker->startGestureDetection(nite::GESTURE_CLICK);
    mHandTracker->startGestureDetection(nite::GESTURE_WAVE);
    mHandTracker->startGestureDetection(nite::GESTURE_HAND_RAISE);
    
    nite::HandTrackerFrameRef mHandFrame;
    while(true)
    {
        // 读取Frame信息
        nite::Status rc = mHandTracker->readFrame(&mHandFrame);
        if (rc != nite::STATUS_OK)
        {
            cout << "GetNextData failed" << endl;
            return 0;
        }

        // 获取定位的手的快照信息,读取此时一共有多少个手势
        const nite::Array<nite::GestureData>& gestures = mHandFrame.getGestures();
        for (int i = 0; i < gestures.getSize(); ++i)
        {
            // 当获取的手势是正确完成了
            if (gestures[i].isComplete())
            {
                // 就开始定位此时手势的坐标
                const nite::Point3f& position = gestures[i].getCurrentPosition();
                cout << "Gesture " << gestures[i].getType() << " at" << position.x << "," << position.y <<"," << position.z;


                // nite::HandId newId ===>typedef short int HandId;
                nite::HandId newId;
                // 开始跟踪该有效手势的手心坐标,并确定该手的Id。
                // 函数原型为:NITE_API NiteStatus niteStartHandTracking(NiteHandTrackerHandle, const NitePoint3f*, NiteHandId* pNewHandId);
                mHandTracker->startHandTracking(gestures[i].getCurrentPosition(), &newId);
            }
        }

        // 获取定位手。
        const nite::Array<nite::HandData>& hands= mHandFrame.getHands();
        for (int i = 0; i < hands.getSize(); ++i)
        {
            const nite::HandData& user = hands[i];

            if (!user.isTracking())
            {
                cout << "Lost hand %d\n" << user.getId();
                nite::HandId id = user.getId();
            }
            else
            {
                if (user.isNew())
                {
                    cout << "Found hand %d\n" << user.getId();
                }
                else
                {
                    cout << "Hand ID:" << hands[i].getId()
                        <<hands[i].getPosition().x << "," 
                        << hands[i].getPosition().y << ","
                        << hands[i].getPosition().z << endl;
                }
            }
        }
    }

    mHandFrame.release();
    // 关闭跟踪器
    mHandTracker->destroy();

    // 关闭NITE环境
    nite::NiTE::shutdown();

    return 0;
}

程序执行结果如下:

    但是我们知道,单单有Hand的坐标还够,我们需要定位在具体的图像位置,利用手的坐标,来分割出手的轮廓等我们需要的信息,所以就要借助于OpenCV等常用工具库,现在开始看看如何结合OpenCV和HandTracker,直接上代码:

// YeNite2SimpleUsingOpenCV.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>

// 载入NiTE.h头文件
#include <NiTE.h>

// 载入OpenCV头文件
#include "opencv2/opencv.hpp"
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace std;
using namespace cv;

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

    // 初始化NITE
    nite::NiTE::initialize();

    // 创建Hand跟踪器
    nite::HandTracker* mHandTracker = new nite::HandTracker;
    mHandTracker->create();

    // 创建OpenCV图像窗口
    namedWindow( "Hand Image",  CV_WINDOW_AUTOSIZE );

    // 循环读取数据流信息并保存在HandFrameRef中
    nite::HandTrackerFrameRef mHandFrame;

    // 开始手势探测
    mHandTracker->startGestureDetection(nite::GESTURE_CLICK);
    mHandTracker->startGestureDetection(nite::GESTURE_WAVE);
    mHandTracker->startGestureDetection(nite::GESTURE_HAND_RAISE);

    while( true )
    {
        // 读取Frame信息
        nite::Status rc = mHandTracker->readFrame(&mHandFrame);
        if (rc != nite::STATUS_OK)
        {
            cout << "GetNextData failed" << endl;
            return 0;
        }

        // 获取定位的手的快照信息,读取此时一共有多少个手势
        const nite::Array<nite::GestureData>& gestures = mHandFrame.getGestures();
        for (int i = 0; i < gestures.getSize(); ++i)
        {
            // 当获取的手势是正确完成了
            if (gestures[i].isComplete())
            {
                // 就开始定位此时手势的坐标
                const nite::Point3f& position = gestures[i].getCurrentPosition();
                cout << "Gesture " << gestures[i].getType() << " at" << position.x << "," << position.y <<"," << position.z;

                // nite::HandId newId ===>typedef short int HandId;
                nite::HandId newId;
                // 开始跟踪该有效手势的手心坐标,并确定该手的Id。
                // 函数原型为:NITE_API NiteStatus niteStartHandTracking(NiteHandTrackerHandle, const NitePoint3f*, NiteHandId* pNewHandId);
                mHandTracker->startHandTracking(gestures[i].getCurrentPosition(), &newId);
            }
        }

        // 获取定位手。
        const nite::Array<nite::HandData>& hands= mHandFrame.getHands();
        for (int i = 0; i < hands.getSize(); ++i)
        {
            const nite::HandData& user = hands[i];

            if (!user.isTracking())
            {
                cout << "Lost hand %d\n" << user.getId();
                nite::HandId id = user.getId();
            }
            else
            {
                if (user.isNew())
                {
                    cout << "Found hand %d\n" << user.getId();
                }
                else
                {
                    cout << "Hand ID:" << hands[i].getId()
                        <<hands[i].getPosition().x << "," 
                        << hands[i].getPosition().y << ","
                        << hands[i].getPosition().z << endl;
                    float x, y;

                    // 将手心坐标转换映射到深度坐标中
                    mHandTracker->convertHandCoordinatesToDepth(hands[i].getPosition().x, hands[i].getPosition().y,
                        hands[i].getPosition().z, &x, &y);

                    // 将深度数据转换成OpenCV格式
                    const cv::Mat mHandDepth( mHandFrame.getDepthFrame().getHeight(), mHandFrame.getDepthFrame().getWidth(), CV_16UC1, 
                        (void*)mHandFrame.getDepthFrame().getData());

                    // 为了让深度图像显示的更加明显一些,将CV_16UC1 ==> CV_8U格式
                    cv::Mat mScaledHandDepth;
                    mHandDepth.convertTo( mScaledHandDepth, CV_8U, 255.0 / 10000 );

                    // 提取以手心为中心,200*200大小的图像
                    cv::Mat mHandsizeDepth = mScaledHandDepth(Rect(x - 100, y -100 , 200, 200));

                    // 显示手的图像
                    cv::imshow( "Hand Image", mHandsizeDepth );
                }
            }

        }
        // 终止快捷键
        if( cv::waitKey(1) == 'q')
            break;
    }

    // 关闭Frame
    mHandFrame.release();

    // 关闭跟踪器
    mHandTracker->destroy();

    // 关闭NITE环境
    nite::NiTE::shutdown();

    return 0;        
}

上图

结合程序注释和之前的博文内容,我想最后一个程序应该挺好理解的。根据自己的感觉走,感觉写代码,没做封装、优化、重构,完全是面向过程,而且肯定还存在细节的问题,会在后面进一步优化的。

    写的粗糙,欢迎指正批评~~~

 

posted @ 2013-01-12 13:44  叶梅树  阅读(3810)  评论(8编辑  收藏  举报