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();

下面是簡單的右手骨骼座標跟蹤,並顯示右手座標信息的程序代碼:

複製代碼
// YeNITE2SimpleUsingOpenCV_Skeleton.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;

                // 開始人體骨骼跟蹤
                mUserTracker.startSkeletonTracking( rUser.getId() );
            }
            
            // 獲取骨骼座標
            const nite::Skeleton& rSkeleton = rUser.getSkeleton();
            if( rSkeleton.getState() == nite::SKELETON_TRACKED )
            {
                // 得到右手座標
                const nite::SkeletonJoint& righthand
                    = rSkeleton.getJoint( nite::JOINT_RIGHT_HAND );
                const nite::Point3f& position = righthand.getPosition();
                cout << "右手座標: " << position.x << "/" << position.y << "/" << position.z << endl;
            }

        }
    }

    // 釋放     
    mUserFrame.release();      
    
    // 關閉跟蹤器
    mUserTracker.destroy();

    // 關閉NITE環境
    nite::NiTE::shutdown();

    return 0;
}
複製代碼

    程序執行結果如下: 

    但通過對上述程序代碼觀察發現,在對人體骨骼跟蹤的時候,未做出(“投降”和“雙手抱胸”)的動作,也可以獲取骨骼座標信息。難道在NITE2骨骼跟蹤的時候,人體姿勢檢測是多餘的嗎?這個我的理解是:似乎姿勢跟蹤將會變成雞肋(完全靠自己的想象。。。)。

    接着藉助於OpenCV等常用工具庫,看看骨骼座標在深度圖像下的定位和顯示效果,直接上代碼:

複製代碼
// 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();

    // 創建User跟蹤器
    nite::UserTracker* mUserTracker = new nite::UserTracker;
    mUserTracker->create();

    // 創建OpenCV圖像窗口
    namedWindow( "Skeleton Image",  CV_WINDOW_AUTOSIZE );

    // 循環讀取數據流信息並保存在HandFrameRef中
    nite::UserTrackerFrameRef mUserFrame;

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

        // 將深度數據轉換成OpenCV格式
        const cv::Mat mHandDepth( mUserFrame.getDepthFrame().getHeight(), mUserFrame.getDepthFrame().getWidth(), CV_16UC1, 
            (void*)mUserFrame.getDepthFrame().getData());

        // 爲了讓深度圖像顯示的更加明顯一些,將CV_16UC1 ==> CV_8U格式
        cv::Mat mScaledHandDepth, thresholdDepth;
        mHandDepth.convertTo( mScaledHandDepth, CV_8U, 255.0 / 10000 );

        // 二值化處理,爲了顯示效果明顯
        cv::threshold(mScaledHandDepth, thresholdDepth, 50, 255, 0);

        // 從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;

                // 開始人體骨骼跟蹤
                mUserTracker->startSkeletonTracking( rUser.getId() );
            }

            // 獲取骨骼座標
            const nite::Skeleton& rSkeleton = rUser.getSkeleton();
            if( rSkeleton.getState() == nite::SKELETON_TRACKED )
            {
                // 只得到前8個骨骼點座標
                for(int i = 0; i < 8; i++)
                {
                    // 得到骨骼座標
                    const nite::SkeletonJoint& skeletonJoint
                        = rSkeleton.getJoint((nite::JointType)i);
                    const nite::Point3f& position = skeletonJoint.getPosition();

                    float depth_x, depth_y;

                    // 將骨骼點座標映射到深度座標中
                    mUserTracker->convertJointCoordinatesToDepth(position.x, position.y, position.z, &depth_x, &depth_y);

                    cv::Point point((int)depth_x, (int)depth_y);

                    // 將獲取的深度圖像中相對應的座標點重新賦值爲255.即在深度圖像中顯示出各個骨骼點。
                    thresholdDepth.at<uchar>(point) = 255;
                }
                
                // 顯示圖像
                cv::imshow( "Skeleton Image", thresholdDepth );
            }

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

    // 關閉Frame
    mUserFrame.release();

    // 關閉跟蹤器
    mUserTracker->destroy();

    // 關閉NITE環境
    nite::NiTE::shutdown();

    return 0;        
}
複製代碼

上圖: 圖上“白點”就是骨骼點。

    需要了解具體的骨骼點信息(位置、方向,以及可靠性等),可以看官網提供的參考文獻。我覺得遺憾的是,目前提供的15個骨骼點座標不包括了手腕等其它骨骼點、而且只能得到全身的,不能單獨獲取上半身骨骼座標。

結合程序註釋和之前的博文內容,我想最後一個程序應該挺好理解的。根據自己的感覺走,感覺寫代碼,沒做封裝、優化、重構,完全是面向過程,而且肯定還存在細節的問題,會在後面進一步優化的。

    寫的粗糙,歡迎指正批評~~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章