2803 lines
111 KiB
C++
2803 lines
111 KiB
C++
//--------------------------------------------------------------------------------------
|
|
// GestureDetector.cpp
|
|
//
|
|
// Definitions for the gesture detector and gesture detector trainer, as well definitions
|
|
// for weak and strong classifiers. The gesture detector trainer uses the AdaBoost
|
|
// learning algorithm.
|
|
//
|
|
// Advanced Technology Group (ATG)
|
|
// Copyright (C) Microsoft Corporation. All rights reserved.
|
|
//--------------------------------------------------------------------------------------
|
|
#pragma once
|
|
#include "GestureDetector.h"
|
|
#include <float.h>
|
|
#include <algorithm>
|
|
#include <assert.h>
|
|
|
|
#ifdef GESTURE_EVALUATOR
|
|
#ifdef TARGET_PS4
|
|
using namespace PS4Depth;
|
|
using namespace PS4OpticalFlow;
|
|
#elif TARGET_DURANGO
|
|
#include <Windows.Kinect.h>
|
|
#endif
|
|
#endif
|
|
|
|
#define MAX_SENSIBLE_DELTATIME (0.2f) //seconds, 5fps
|
|
|
|
using namespace std;
|
|
|
|
|
|
namespace KinectGesture
|
|
{
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Static definitions
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
XMVECTOR GestureDetector::vUp = XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f );
|
|
XMVECTOR GestureDetector::vAverageNormalToGravity[ KINECT_GESTURE_MAX_SIMULTANEOUS_GESTURES ] = {
|
|
GestureDetector::vUp,
|
|
GestureDetector::vUp,
|
|
GestureDetector::vUp,
|
|
GestureDetector::vUp,
|
|
GestureDetector::vUp,
|
|
GestureDetector::vUp,
|
|
GestureDetector::vUp,
|
|
GestureDetector::vUp,
|
|
GestureDetector::vUp,
|
|
GestureDetector::vUp };
|
|
|
|
UINT64 GestureDetector::m_uPreviousTimeStamp = 0;
|
|
UINT GestureDetector::m_nNumInstances = 0;
|
|
#if defined(_XBOX) || defined(ITF_X360) ||\
|
|
defined(DURANGO) || defined(ITF_DURANGO) ||\
|
|
defined(__ORBIS__) || defined(ITF_ORBIS)||\
|
|
defined(WIN32) || defined(ITF_WIN32)
|
|
ClassifierData** GestureDetector::m_ClassifierData = NULL;
|
|
UINT GestureDetector::m_nNumClassifierData = 0;
|
|
#else
|
|
vector<ClassifierData*> GestureDetector::m_ClassifierData;
|
|
|
|
#endif
|
|
|
|
#if defined( _XBOX ) || defined( TARGET_X360 ) || defined( ITF_X360 )
|
|
|
|
static const CHAR g_szGestureFileIDX360[] = "GestureDetectorX360";
|
|
static const CHAR g_szGestureFileIDOld[] = "GestureDetectorFile";
|
|
static const CHAR* g_aszGestureFileIDs[] = {g_szGestureFileIDX360, g_szGestureFileIDOld};
|
|
static const CHAR g_szLabeledExampleFileID[] = "LabeledExampleX360";
|
|
|
|
#elif defined( DURANGO ) || defined( TARGET_DURANGO ) || defined( ITF_DURANGO )
|
|
|
|
static const CHAR g_szGestureFileIDOld[] = "GestureDetectorFile"; // Allow old gestures to be loaded on DURANGO
|
|
static const CHAR g_szGestureFileIDX360[] = "GestureDetectorX360"; // Allow X360 gestures to be loaded on DURANGO
|
|
static const CHAR g_szGestureFileIDDurango[] = "GestureDetectorDurango";
|
|
static const CHAR* g_aszGestureFileIDs[] = {g_szGestureFileIDDurango,g_szGestureFileIDX360,g_szGestureFileIDOld};
|
|
static const CHAR g_szLabeledExampleFileID[] = "LabeledExampleDurango";
|
|
|
|
#elif defined( __ORBIS__ ) || defined( TARGET_ORBIS ) || defined( ITF_ORBIS )
|
|
|
|
static const CHAR g_szGestureFileIDOld[] = "GestureDetectorFile"; // Allow old gestures to be loaded on ORBIS
|
|
static const CHAR g_szGestureFileIDX360[] = "GestureDetectorX360"; // Allow X360 gestures to be loaded on ORBIS
|
|
//static const CHAR g_szGestureFileIDDurango[] = "GestureDetectorDurango"; // Allow Durango gestures to be loaded on ORBIS
|
|
static const CHAR g_szGestureFileIDORBIS[] = "GestureDetectorORBIS";
|
|
static const CHAR* g_aszGestureFileIDs[] = {g_szGestureFileIDORBIS,g_szGestureFileIDX360,g_szGestureFileIDOld};
|
|
static const CHAR g_szLabeledExampleFileID[] = "LabeledExampleORBIS";
|
|
|
|
#elif defined( WIN32 ) || defined( TARGET_WIN32 ) || defined( ITF_WIN32 )
|
|
|
|
static const CHAR g_szGestureFileIDOld[] = "GestureDetectorFile"; // Allow old gestures to be loaded on DURANGO
|
|
static const CHAR g_szGestureFileIDX360[] = "GestureDetectorX360"; // Allow X360 gestures to be loaded on DURANGO
|
|
static const CHAR g_szGestureFileIDDurango[] = "GestureDetectorDurango";
|
|
static const CHAR* g_aszGestureFileIDs[] = {g_szGestureFileIDDurango,g_szGestureFileIDX360,g_szGestureFileIDOld};
|
|
static const CHAR g_szLabeledExampleFileID[] = "LabeledExampleDurango";
|
|
#else
|
|
#error unsuported platform
|
|
#endif
|
|
|
|
static const int g_numGestureFileIDs = sizeof( g_aszGestureFileIDs ) / sizeof( CHAR* );
|
|
|
|
const CHAR** getGestureFileIDs()
|
|
{
|
|
return g_aszGestureFileIDs;
|
|
}
|
|
|
|
const CHAR* getLabeledExampleFileID()
|
|
{
|
|
return g_szLabeledExampleFileID;
|
|
}
|
|
|
|
const int getLabeledExampleFileIDLen()
|
|
{
|
|
#if defined( __ORBIS__ )
|
|
return sizeof(g_szLabeledExampleFileID)/sizeof(*g_szLabeledExampleFileID) /
|
|
static_cast<size_t>(!(sizeof(g_szLabeledExampleFileID) % sizeof(*g_szLabeledExampleFileID)));
|
|
#else
|
|
return ARRAYSIZE(g_szLabeledExampleFileID);
|
|
#endif
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: DecisionStump()
|
|
// Desc: Constructor
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
DecisionStump::DecisionStump()
|
|
{
|
|
m_fThreshold = 0.0f;
|
|
|
|
#if !defined(_XBOX) && !defined(ITF_X360) &&\
|
|
!defined(DURANGO) && !defined(ITF_DURANGO) &&\
|
|
!defined(__ORBIS__) && !defined(ITF_ORBIS)&&\
|
|
!defined(WIN32) && !defined(ITF_WIN32)
|
|
m_iReverse = 1;
|
|
#endif
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Read
|
|
// Desc: Read data
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
HRESULT DecisionStump::Read( FILE* pFile )
|
|
{
|
|
fread( &m_fThreshold, sizeof( m_fThreshold ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
m_fThreshold = ByteSwap32BitRead( m_fThreshold );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Read
|
|
// Desc: Read data
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
HRESULT DecisionStump::Read( VOID* pBuffer )
|
|
{
|
|
mread( &m_fThreshold, sizeof( m_fThreshold ), 1, pBuffer );
|
|
m_fThreshold = ByteSwap32BitRead( m_fThreshold );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Write
|
|
// Desc: Write data
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
HRESULT DecisionStump::Write( FILE* pFile )
|
|
{
|
|
FLOAT fBigEndianValue = ByteSwap32BitWrite( m_fThreshold );
|
|
fwrite( &fBigEndianValue, sizeof( fBigEndianValue ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: WeakClassifier()
|
|
// Desc: Constructor
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
WeakClassifier::WeakClassifier() : DecisionStump()
|
|
{
|
|
m_fAlpha = 0.0f;
|
|
// m_pData = NULL;
|
|
m_uDataIndex = 0;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Read
|
|
// Desc: Read data
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
HRESULT WeakClassifier::Read( FILE* pFile, UINT* pDataID )
|
|
{
|
|
UINT uValue;
|
|
RETURN_ON_FAIL( DecisionStump::Read( pFile ) );
|
|
|
|
fread( &m_fAlpha, sizeof( m_fAlpha ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
|
|
fread( &uValue, sizeof( uValue ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
|
|
m_fAlpha = ByteSwap32BitRead( m_fAlpha );
|
|
uValue = ByteSwap32BitRead( uValue );
|
|
|
|
*pDataID = uValue;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Read
|
|
// Desc: Read data
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
HRESULT WeakClassifier::Read( VOID* pBuffer, UINT* pDataID )
|
|
{
|
|
UINT uValue;
|
|
RETURN_ON_FAIL( DecisionStump::Read( pBuffer ) );
|
|
|
|
mread( &m_fAlpha, sizeof( m_fAlpha ), 1, pBuffer );
|
|
mread( &uValue, sizeof( uValue ), 1, pBuffer );
|
|
|
|
m_fAlpha = ByteSwap32BitRead( m_fAlpha );
|
|
uValue = ByteSwap32BitRead( uValue );
|
|
|
|
*pDataID = uValue;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Write
|
|
// Desc: Write data
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
HRESULT WeakClassifier::Write( FILE* pFile )
|
|
{
|
|
RETURN_ON_FAIL( DecisionStump::Write( pFile ) );
|
|
|
|
FLOAT fBigEndianValue = ByteSwap32BitWrite( m_fAlpha );
|
|
fwrite( &fBigEndianValue, sizeof( fBigEndianValue ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
|
|
UINT uBigEndianValue;
|
|
// if ( m_pData )
|
|
{
|
|
// uBigEndianValue = ByteSwap32BitWrite( m_pData->GetID() );
|
|
uBigEndianValue = ByteSwap32BitWrite( m_uDataIndex );
|
|
fwrite( &uBigEndianValue, sizeof( uBigEndianValue ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
}
|
|
// else
|
|
// {
|
|
// uBigEndianValue = (UINT)-1;
|
|
// fwrite( &uBigEndianValue, sizeof( uBigEndianValue ), 1, pFile );
|
|
// RETURN_ON_FILE_ERROR( pFile );
|
|
// return E_FAIL;
|
|
// }
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: StrongClassifier()
|
|
// Desc: Constructor
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
StrongClassifier::StrongClassifier()
|
|
{
|
|
|
|
#if defined(_XBOX) || defined(ITF_X360) ||\
|
|
defined(DURANGO) || defined(ITF_DURANGO) ||\
|
|
defined(__ORBIS__) || defined(ITF_ORBIS)||\
|
|
defined(WIN32) || defined(ITF_WIN32)
|
|
m_WeakClassifiers = NULL;
|
|
m_nNumWeakClassifiers = 0;
|
|
#else
|
|
m_WeakClassifiers.clear();
|
|
#endif
|
|
|
|
m_fTotalAlpha = 0.0f;
|
|
m_fFilterPerFrameResultsThreshold = 0.001f;
|
|
m_nFilterPerFrameResultsNumFrames = 5;
|
|
|
|
for( UINT uiLabel = 0; uiLabel < eLabel_Count; ++uiLabel )
|
|
{
|
|
m_fRangeMax[uiLabel] = 1.0f;
|
|
m_fRangeMin[uiLabel] = 0.0f;
|
|
m_fMean[uiLabel] = 0.5f;
|
|
m_fStdDev[uiLabel] = 0.5f;
|
|
}
|
|
|
|
// initial values are of a similar order of magnitude to test data
|
|
// this is just so older files have a realistic fallback until they are updated
|
|
m_fEnergyMean = 100.0f;
|
|
m_fEnergyStdDev = 50.0f;
|
|
|
|
for ( UINT i = 0; i < KINECT_GESTURE_MAX_SIMULTANEOUS_GESTURES; i++ )
|
|
{
|
|
Reset( i );
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: ~StrongClassifier()
|
|
// Desc: Destructor
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
StrongClassifier::~StrongClassifier()
|
|
{
|
|
#if defined(_XBOX) || defined(ITF_X360) ||\
|
|
defined(DURANGO) || defined(ITF_DURANGO) ||\
|
|
defined(__ORBIS__) || defined(ITF_ORBIS)||\
|
|
defined(WIN32) || defined(ITF_WIN32)
|
|
if ( m_WeakClassifiers )
|
|
{
|
|
FreeAligned( m_WeakClassifiers );
|
|
}
|
|
m_WeakClassifiers = NULL;
|
|
m_nNumWeakClassifiers = 0;
|
|
#else
|
|
m_WeakClassifiers.clear();
|
|
#endif
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Initialize
|
|
// Desc: Initialize and allocate memory
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
HRESULT StrongClassifier::Initialize( const UINT nNumWeakClassifiers )
|
|
{
|
|
#if defined(_XBOX) || defined(ITF_X360) ||\
|
|
defined(DURANGO) || defined(ITF_DURANGO) ||\
|
|
defined(__ORBIS__) || defined(ITF_ORBIS)||\
|
|
defined(WIN32) || defined(ITF_WIN32)
|
|
if ( m_WeakClassifiers )
|
|
{
|
|
FreeAligned( m_WeakClassifiers );
|
|
}
|
|
|
|
void* pMem;
|
|
RETURN_ON_NULL( pMem = AllocateAligned( sizeof( WeakClassifier ) * nNumWeakClassifiers, 4 ) );
|
|
m_WeakClassifiers = new (pMem) WeakClassifier[ nNumWeakClassifiers ];
|
|
|
|
m_nNumWeakClassifiers = nNumWeakClassifiers;
|
|
#else
|
|
m_WeakClassifiers.reserve( nNumWeakClassifiers );
|
|
|
|
for ( UINT i = 0; i < nNumWeakClassifiers; i++ )
|
|
{
|
|
m_WeakClassifiers.push_back( WeakClassifier() );
|
|
}
|
|
#endif
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Read
|
|
// Desc: Read data
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
HRESULT StrongClassifier::Read( FILE* pFile, BOOL bUsesEnergyStatistics )
|
|
{
|
|
fread( &m_fTotalAlpha, sizeof( m_fTotalAlpha ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
|
|
fread( &m_fFilterPerFrameResultsThreshold, sizeof( m_fFilterPerFrameResultsThreshold ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
|
|
fread( &m_nFilterPerFrameResultsNumFrames, sizeof( m_nFilterPerFrameResultsNumFrames ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
|
|
for( UINT uiLabel = 0; uiLabel < eLabel_Count; ++uiLabel )
|
|
{
|
|
fread( &m_fRangeMin[uiLabel], sizeof( m_fRangeMin[uiLabel] ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
|
|
fread( &m_fRangeMax[uiLabel], sizeof( m_fRangeMax[uiLabel] ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
|
|
fread( &m_fMean[uiLabel], sizeof( m_fMean[uiLabel] ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
|
|
fread( &m_fStdDev[uiLabel], sizeof( m_fStdDev[uiLabel] ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
}
|
|
|
|
if( bUsesEnergyStatistics )
|
|
{
|
|
fread( &m_fEnergyMean, sizeof( m_fEnergyMean ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
|
|
fread( &m_fEnergyStdDev, sizeof( m_fEnergyStdDev ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
}
|
|
|
|
m_fTotalAlpha = ByteSwap32BitRead( m_fTotalAlpha );
|
|
m_fFilterPerFrameResultsThreshold = ByteSwap32BitRead( m_fFilterPerFrameResultsThreshold );
|
|
m_nFilterPerFrameResultsNumFrames = ByteSwap32BitRead( m_nFilterPerFrameResultsNumFrames );
|
|
|
|
for( UINT uiLabel = 0; uiLabel < eLabel_Count; ++uiLabel )
|
|
{
|
|
m_fRangeMin[uiLabel] = ByteSwap32BitRead( m_fRangeMin[uiLabel] );
|
|
m_fRangeMax[uiLabel] = ByteSwap32BitRead( m_fRangeMax[uiLabel] );
|
|
m_fMean[uiLabel] = ByteSwap32BitRead( m_fMean[uiLabel] );
|
|
m_fStdDev[uiLabel] = ByteSwap32BitRead( m_fStdDev[uiLabel] );
|
|
}
|
|
|
|
if( bUsesEnergyStatistics )
|
|
{
|
|
m_fEnergyMean = ByteSwap32BitRead( m_fEnergyMean );
|
|
m_fEnergyStdDev = ByteSwap32BitRead( m_fEnergyStdDev );
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Read
|
|
// Desc: Read data
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
HRESULT StrongClassifier::Read( VOID* pBuffer, BOOL bUsesEnergyStatistics )
|
|
{
|
|
mread( &m_fTotalAlpha, sizeof( m_fTotalAlpha ), 1, pBuffer );
|
|
mread( &m_fFilterPerFrameResultsThreshold, sizeof( m_fFilterPerFrameResultsThreshold ), 1, pBuffer );
|
|
mread( &m_nFilterPerFrameResultsNumFrames, sizeof( m_nFilterPerFrameResultsNumFrames ), 1, pBuffer );
|
|
|
|
for( UINT uiLabel = 0; uiLabel < eLabel_Count; ++uiLabel )
|
|
{
|
|
mread( &m_fRangeMin[uiLabel], sizeof( m_fRangeMin[uiLabel] ), 1, pBuffer );
|
|
mread( &m_fRangeMax[uiLabel], sizeof( m_fRangeMax[uiLabel] ), 1, pBuffer );
|
|
mread( &m_fMean[uiLabel], sizeof( m_fMean[uiLabel] ), 1, pBuffer );
|
|
mread( &m_fStdDev[uiLabel], sizeof( m_fStdDev[uiLabel] ), 1, pBuffer );
|
|
}
|
|
|
|
if( bUsesEnergyStatistics )
|
|
{
|
|
mread( &m_fEnergyMean, sizeof( m_fEnergyMean ), 1, pBuffer );
|
|
mread( &m_fEnergyStdDev, sizeof( m_fEnergyStdDev ), 1, pBuffer );
|
|
}
|
|
|
|
m_fTotalAlpha = ByteSwap32BitRead( m_fTotalAlpha );
|
|
m_fFilterPerFrameResultsThreshold = ByteSwap32BitRead( m_fFilterPerFrameResultsThreshold );
|
|
m_nFilterPerFrameResultsNumFrames = ByteSwap32BitRead( m_nFilterPerFrameResultsNumFrames );
|
|
|
|
for( UINT uiLabel = 0; uiLabel < eLabel_Count; ++uiLabel )
|
|
{
|
|
m_fRangeMin[uiLabel] = ByteSwap32BitRead( m_fRangeMin[uiLabel] );
|
|
m_fRangeMax[uiLabel] = ByteSwap32BitRead( m_fRangeMax[uiLabel] );
|
|
m_fMean[uiLabel] = ByteSwap32BitRead( m_fMean[uiLabel] );
|
|
m_fStdDev[uiLabel] = ByteSwap32BitRead( m_fStdDev[uiLabel] );
|
|
}
|
|
|
|
if( bUsesEnergyStatistics )
|
|
{
|
|
m_fEnergyMean = ByteSwap32BitRead( m_fEnergyMean );
|
|
m_fEnergyStdDev = ByteSwap32BitRead( m_fEnergyStdDev );
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Write
|
|
// Desc: Write data
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
HRESULT StrongClassifier::Write( FILE* pFile )
|
|
{
|
|
FLOAT fBigEndianValue = ByteSwap32BitWrite( m_fTotalAlpha );
|
|
fwrite( &fBigEndianValue, sizeof( fBigEndianValue ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
|
|
fBigEndianValue = ByteSwap32BitWrite( m_fFilterPerFrameResultsThreshold );
|
|
fwrite( &fBigEndianValue, sizeof( fBigEndianValue ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
|
|
UINT uBigEndianValue = ByteSwap32BitWrite( m_nFilterPerFrameResultsNumFrames );
|
|
fwrite( &uBigEndianValue, sizeof( uBigEndianValue ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
|
|
for( UINT uiLabel = 0; uiLabel < eLabel_Count; ++uiLabel )
|
|
{
|
|
fBigEndianValue = ByteSwap32BitWrite( m_fRangeMin[uiLabel] );
|
|
fwrite( &fBigEndianValue, sizeof( fBigEndianValue ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
|
|
fBigEndianValue = ByteSwap32BitWrite( m_fRangeMax[uiLabel] );
|
|
fwrite( &fBigEndianValue, sizeof( fBigEndianValue ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
|
|
fBigEndianValue = ByteSwap32BitWrite( m_fMean[uiLabel] );
|
|
fwrite( &fBigEndianValue, sizeof( fBigEndianValue ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
|
|
fBigEndianValue = ByteSwap32BitWrite( m_fStdDev[uiLabel] );
|
|
fwrite( &fBigEndianValue, sizeof( fBigEndianValue ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
}
|
|
|
|
fBigEndianValue = ByteSwap32BitWrite( m_fEnergyMean );
|
|
fwrite( &fBigEndianValue, sizeof( fBigEndianValue ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
|
|
fBigEndianValue = ByteSwap32BitWrite( m_fEnergyStdDev );
|
|
fwrite( &fBigEndianValue, sizeof( fBigEndianValue ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Read
|
|
// Desc: Read data for a weak classifier in the strong classifier
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
HRESULT StrongClassifier::Read( FILE* pFile, const UINT uWeakClassifierIndex, UINT* pDataID )
|
|
{
|
|
if ( uWeakClassifierIndex >= GetNumWeakClassifiers() )
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
return m_WeakClassifiers[ uWeakClassifierIndex ].Read( pFile, pDataID );
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Read
|
|
// Desc: Read data for a weak classifier in the strong classifier
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
HRESULT StrongClassifier::Read( VOID* pBuffer, const UINT uWeakClassifierIndex, UINT* pDataID )
|
|
{
|
|
if ( uWeakClassifierIndex >= GetNumWeakClassifiers() )
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
return m_WeakClassifiers[ uWeakClassifierIndex ].Read( pBuffer, pDataID );
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Write
|
|
// Desc: Write data for weak classifier in the strong classifier
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
HRESULT StrongClassifier::Write( FILE* pFile, const UINT uWeakClassifierIndex )
|
|
{
|
|
if ( uWeakClassifierIndex >= GetNumWeakClassifiers() )
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
return m_WeakClassifiers[ uWeakClassifierIndex ].Write( pFile );
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Classify
|
|
// Desc: The classifications for the strong classifier as a weighted sum of the weak classifiers
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
#if defined(_XBOX) || defined(ITF_X360) ||\
|
|
defined(DURANGO) || defined(ITF_DURANGO) ||\
|
|
defined(__ORBIS__) || defined(ITF_ORBIS)||\
|
|
defined(WIN32) || defined(ITF_WIN32)
|
|
INT StrongClassifier::Classify( const UINT uPlayerIdx, ClassifierData** __restrict classifierData, FLOAT* pConfidence )
|
|
{
|
|
FLOAT fSum = 0.0f;
|
|
|
|
const UINT nNumWeakClassifiers = m_nNumWeakClassifiers;
|
|
|
|
WeakClassifier* pWeakClassifier = &m_WeakClassifiers[ 0 ];
|
|
|
|
// UINT uClassifierDataIndex = pWeakClassifier->GetData()->GetID();
|
|
UINT uClassifierDataIndex = pWeakClassifier->GetDataIndex();
|
|
|
|
// Prefetch data
|
|
#ifdef _XBOX
|
|
__dcbt( 0, pWeakClassifier );
|
|
__dcbt( 128, pWeakClassifier );
|
|
#endif
|
|
|
|
for ( UINT i = 0; i < nNumWeakClassifiers; i++ )
|
|
{
|
|
// uClassifierDataIndex = pWeakClassifier->GetData()->GetID();
|
|
uClassifierDataIndex = pWeakClassifier->GetDataIndex();
|
|
|
|
FLOAT fAlpha = pWeakClassifier->GetAlpha();
|
|
FLOAT fValue = classifierData[ uClassifierDataIndex ]->GetValue( uPlayerIdx );
|
|
|
|
fSum += fAlpha * pWeakClassifier->Classify( fValue );
|
|
|
|
pWeakClassifier++;
|
|
#ifdef _XBOX
|
|
__dcbt( 128, pWeakClassifier );
|
|
#endif
|
|
}
|
|
|
|
// Normalize the confidence
|
|
assert( m_fTotalAlpha > 0.0f );
|
|
*pConfidence = fSum / m_fTotalAlpha;
|
|
|
|
if ( fSum > 0.0f )
|
|
{
|
|
#ifdef _XBOX
|
|
return (INT)g_fClassificationLabelCorrect;
|
|
#else
|
|
return g_iClassificationLabelCorrect;
|
|
#endif
|
|
}
|
|
#ifdef _XBOX
|
|
return (INT)g_fClassificationLabelIncorrect;
|
|
#else
|
|
return g_iClassificationLabelIncorrect;
|
|
#endif
|
|
}
|
|
#else
|
|
INT8 StrongClassifier::Classify( const UINT uPlayerIdx, const vector<ClassifierData*>& classifierData, FLOAT* pConfidence )
|
|
{
|
|
FLOAT fSum = 0.0f;
|
|
|
|
const UINT nNumWeakClassifiers = (UINT)m_WeakClassifiers.size();
|
|
|
|
for ( UINT i = 0; i < nNumWeakClassifiers; i++ )
|
|
{
|
|
WeakClassifier* pWeakClassifier = &m_WeakClassifiers[ i ];
|
|
// UINT uClassifierDataIndex = pWeakClassifier->GetData()->GetID();
|
|
UINT uClassifierDataIndex = pWeakClassifier->GetDataIndex();
|
|
|
|
FLOAT fValue = classifierData[ uClassifierDataIndex ]->GetValue( uPlayerIdx );
|
|
FLOAT fAlpha = pWeakClassifier->GetAlpha();
|
|
|
|
fSum += fAlpha * pWeakClassifier->Classify( fValue );
|
|
}
|
|
|
|
// Normalize the confidence value
|
|
assert( m_fTotalAlpha > 0.0f );
|
|
*pConfidence = fSum / m_fTotalAlpha;
|
|
|
|
if ( fSum > 0.0f )
|
|
{
|
|
return g_iClassificationLabelCorrect;
|
|
}
|
|
|
|
return g_iClassificationLabelIncorrect;
|
|
}
|
|
#endif
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Detect
|
|
// Desc: Calls Classify() and can optionally filter the per frame classifications
|
|
//--------------------------------------------------------------------------------------
|
|
#if defined(_XBOX) || defined(ITF_X360) ||\
|
|
defined(DURANGO) || defined(ITF_DURANGO) ||\
|
|
defined(__ORBIS__) || defined(ITF_ORBIS)||\
|
|
defined(WIN32) || defined(ITF_WIN32)
|
|
BOOL StrongClassifier::Detect( const UINT uPlayerIdx, ClassifierData** __restrict classifierData, Results* pResults, const BOOL bFilterResults )
|
|
#else
|
|
BOOL StrongClassifier::Detect( const UINT uPlayerIdx, const vector<ClassifierData*>& classifierData, Results* pResults, const BOOL bFilterResults )
|
|
#endif
|
|
{
|
|
FLOAT fConfidence;
|
|
|
|
// Do the classification.
|
|
Classify( uPlayerIdx, classifierData, &fConfidence );
|
|
|
|
// Filter the per frame results to per gesture results
|
|
if ( bFilterResults )
|
|
{
|
|
FilterDetectionResults( uPlayerIdx, fConfidence, pResults );
|
|
}
|
|
else
|
|
{
|
|
pResults->m_fConfidence = fConfidence;
|
|
pResults->m_bDetected = ( fConfidence > 0.0f );
|
|
pResults->m_bFirstFrameDetected = pResults->m_bDetected;
|
|
}
|
|
|
|
return pResults->m_bDetected;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: FilterDetectionResults
|
|
// Desc: Classification is done per frame, not per gesture. We can filter the results
|
|
// in anyway we want. This is a simple default filtering method provided that
|
|
// uses the sum of a sliding window of results. The size of the sliding window
|
|
// can be seen as a frequency and the threshold as an amplitude.
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
VOID StrongClassifier::FilterDetectionResults( const UINT uPlayerIdx, const FLOAT fConfidence, Results* pResults )
|
|
{
|
|
// Use a small sliding window of previous results to do the filtering
|
|
if ( m_fClassificationHistory[ uPlayerIdx ].size() >= m_nFilterPerFrameResultsNumFrames )
|
|
{
|
|
m_fClassificationHistory[ uPlayerIdx ].pop_front();
|
|
}
|
|
|
|
// Filter based on the sum of the clamped confidence values
|
|
FLOAT fSum = 0.0f;
|
|
for ( UINT i = 0; i < m_fClassificationHistory[ uPlayerIdx ].size(); i++ )
|
|
{
|
|
fSum += max( 0.0f, m_fClassificationHistory[ uPlayerIdx ][ i ] );
|
|
}
|
|
|
|
// Determine if any previous frames in the sliding window had positive classifications
|
|
BOOL bNoPreviousDetections = ( fSum < m_fFilterPerFrameResultsThreshold );
|
|
|
|
// Add the new confidence value to the sliding window
|
|
m_fClassificationHistory[ uPlayerIdx ].push_back( fConfidence );
|
|
fSum += max( 0.0f, fConfidence );
|
|
|
|
// Output the results
|
|
pResults->m_fConfidence = fSum;
|
|
pResults->m_bDetected = ( fSum > m_fFilterPerFrameResultsThreshold );
|
|
pResults->m_bFirstFrameDetected = ( bNoPreviousDetections && pResults->m_bDetected );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
//
|
|
//--------------------------------------------------------------------------------------
|
|
#if defined(_XBOX) || defined(ITF_X360) ||\
|
|
defined(DURANGO) || defined(ITF_DURANGO) ||\
|
|
defined(__ORBIS__) || defined(ITF_ORBIS)||\
|
|
defined(WIN32) || defined(ITF_WIN32)
|
|
VOID StrongClassifier::GetBoneWeights( ClassifierData** __restrict classifierData, FLOAT *weight, UINT numWeights )
|
|
#else
|
|
VOID StrongClassifier::GetBoneWeights( const vector<ClassifierData*>& classifierData, FLOAT *weight, UINT numWeights )
|
|
#endif
|
|
{
|
|
FLOAT fSum = 0.0f;
|
|
|
|
#if defined(_XBOX) || defined(ITF_X360) ||\
|
|
defined(DURANGO) || defined(ITF_DURANGO) ||\
|
|
defined(__ORBIS__) || defined(ITF_ORBIS)||\
|
|
defined(WIN32) || defined(ITF_WIN32)
|
|
const UINT nNumWeakClassifiers = m_nNumWeakClassifiers;
|
|
#else
|
|
const UINT nNumWeakClassifiers = (UINT)m_WeakClassifiers.size();
|
|
#endif
|
|
|
|
WeakClassifier* pWeakClassifier = &m_WeakClassifiers[ 0 ];
|
|
|
|
// UINT uClassifierDataIndex = pWeakClassifier->GetData()->GetID();
|
|
UINT uClassifierDataIndex = pWeakClassifier->GetDataIndex();
|
|
|
|
//float weight[NUI_SKELETON_POSITION_COUNT];
|
|
|
|
UINT32 buffer[3+3]; //3 for the header, 1..3 for the bone indices
|
|
|
|
for(UINT i=0; i<numWeights; i++) weight[i] = 0.0f;
|
|
|
|
for ( UINT i = 0; i < nNumWeakClassifiers; i++ )
|
|
{
|
|
// uClassifierDataIndex = pWeakClassifier->GetData()->GetID();
|
|
uClassifierDataIndex = pWeakClassifier->GetDataIndex();
|
|
|
|
FLOAT fAlpha = pWeakClassifier->GetAlpha();
|
|
|
|
for(int j=0; j<3+3; j++) buffer[j] = 0xffffffff;
|
|
|
|
mopen(&buffer);
|
|
classifierData[ uClassifierDataIndex ]->Write( &buffer );
|
|
mclose();
|
|
|
|
//ToDo: detect how many bones it returned (1,2,3),
|
|
// add alpha to boneweights
|
|
for(int j=3; j<3+3; j++)
|
|
{
|
|
if (buffer[j] < numWeights )//!= 0xffffffff)
|
|
weight[buffer[j]] += fabsf(fAlpha);
|
|
}
|
|
fSum += fabsf(fAlpha);
|
|
|
|
pWeakClassifier++;
|
|
}
|
|
|
|
for(UINT i=0; i<numWeights; i++) weight[i] /= fSum;
|
|
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
//
|
|
//--------------------------------------------------------------------------------------
|
|
#if defined(_XBOX) || defined(ITF_X360) ||\
|
|
defined(DURANGO) || defined(ITF_DURANGO) ||\
|
|
defined(__ORBIS__) || defined(ITF_ORBIS)||\
|
|
defined(WIN32) || defined(ITF_WIN32)
|
|
VOID StrongClassifier::CalculateUsedRanges( ClassifierData** __restrict classifierData, float *fRangeMin, float *fRangeMax, float *fSumAlpha )
|
|
#else
|
|
VOID StrongClassifier::CalculateUsedRanges( const vector<ClassifierData*>& classifierData, float *fRangeMin, float *fRangeMax, float *fSumAlpha )
|
|
#endif
|
|
{
|
|
//the arrays are expected to be initialised by the caller, so we can call this on multiple classifiers after one another
|
|
#if defined(_XBOX) || defined(ITF_X360) ||\
|
|
defined(DURANGO) || defined(ITF_DURANGO) ||\
|
|
defined(__ORBIS__) || defined(ITF_ORBIS)||\
|
|
defined(WIN32) || defined(ITF_WIN32)
|
|
const UINT nNumWeakClassifiers = m_nNumWeakClassifiers;
|
|
#else
|
|
const UINT nNumWeakClassifiers = (UINT)m_WeakClassifiers.size();
|
|
#endif
|
|
WeakClassifier* pWeakClassifier = &m_WeakClassifiers[ 0 ];
|
|
for ( UINT i = 0; i < nNumWeakClassifiers; i++ )
|
|
{
|
|
UINT uClassifierDataIndex = pWeakClassifier->GetDataIndex();
|
|
UINT uDataType = classifierData[ uClassifierDataIndex ]->GetType();
|
|
|
|
float f = pWeakClassifier->GetThreshold();
|
|
fRangeMin[uDataType] = min(fRangeMin[uDataType], f);
|
|
fRangeMax[uDataType] = max(fRangeMax[uDataType], f);
|
|
fSumAlpha[uDataType] += fabsf(pWeakClassifier->GetAlpha());
|
|
|
|
pWeakClassifier++;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Optimize
|
|
// Desc: Ten's of thousands of weak classifiers can be generated during learning, but
|
|
// we only neead about 1000. This allows us to reduce the number of weak classifiers
|
|
// that will be used at runtime
|
|
//--------------------------------------------------------------------------------------
|
|
#if !defined(_XBOX) && !defined(ITF_X360) &&\
|
|
!defined(DURANGO) && !defined(ITF_DURANGO) &&\
|
|
!defined(__ORBIS__) && !defined(ITF_ORBIS)&&\
|
|
!defined(WIN32) && !defined(ITF_WIN32)
|
|
VOID StrongClassifier::Optimize( const UINT nMaxNumClassifers, const BOOL bBakeReverseInAlpha )
|
|
{
|
|
// Optimize for runtime by reducing the number of weak classifiers that contribute
|
|
// to the strong classifier. We simply sort the weak classifiers based on the
|
|
// weights in the strong classifier and take the first N classifiers
|
|
|
|
// Sort using alpha
|
|
sort( m_WeakClassifiers.rbegin(), m_WeakClassifiers.rend() );
|
|
|
|
// Now throw away all classifiers with zero alpha
|
|
const UINT nNumWeakClassifiers = (UINT)m_WeakClassifiers.size();
|
|
UINT uLastValid = nNumWeakClassifiers;
|
|
for ( UINT i = 0; i < nNumWeakClassifiers; i++ )
|
|
{
|
|
if ( m_WeakClassifiers[ i ].GetAlpha() == 0.0f )
|
|
{
|
|
uLastValid = i;
|
|
break;
|
|
}
|
|
}
|
|
m_WeakClassifiers.resize( uLastValid );
|
|
|
|
// Now prune to the max number of classifers asked, prune the classifiers with the lowest alphas
|
|
if ( nMaxNumClassifers > 0 )
|
|
{
|
|
UINT nNumClassifiers = min( (UINT)m_WeakClassifiers.size(), nMaxNumClassifers );
|
|
m_WeakClassifiers.resize( nNumClassifiers );
|
|
}
|
|
|
|
// Normalize the weak classifier weights, so that we get a probability value in the range [-1..1]
|
|
// when detecting. -1 is 100% certain this is not the gesture, 0 don't know, 1 is 100% sure this is the gesture
|
|
FLOAT fSum = 0;
|
|
for ( UINT i = 0; i < m_WeakClassifiers.size(); i++ )
|
|
{
|
|
fSum += m_WeakClassifiers[ i ].GetAlpha();
|
|
}
|
|
SetTotalAlpha( fSum );
|
|
|
|
// Another runtime optimization is to bake fReverse into fAlpha. This reduces the data size
|
|
// per classifier and also removes one float multiply per classifier
|
|
if ( bBakeReverseInAlpha )
|
|
{
|
|
for ( UINT i = 0; i < m_WeakClassifiers.size(); i++ )
|
|
{
|
|
FLOAT fAlpha = m_WeakClassifiers[ i ].GetAlpha();
|
|
INT8 iReverse = m_WeakClassifiers[ i ].GetReverseValue();
|
|
|
|
// Bake the reverse value into fAlpha and reset the reverse value
|
|
fAlpha *= (FLOAT)iReverse;
|
|
m_WeakClassifiers[ i ].SetAlpha( fAlpha );
|
|
m_WeakClassifiers[ i ].SetIsReversed( FALSE );
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: GestureDetector()
|
|
// Desc: Constructor
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
GestureDetector::GestureDetector()
|
|
{
|
|
for ( UINT i = 0; i < KINECT_GESTURE_MAX_SIMULTANEOUS_GESTURES; i++ )
|
|
{
|
|
m_StrongClassifier.Reset( i );
|
|
}
|
|
m_nNumInstances++;
|
|
|
|
#ifdef GESTURE_EVALUATOR
|
|
setOpticalFlowCloseCellWeights();
|
|
#endif
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: ~GestureDetector()
|
|
// Desc: Destructor
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
GestureDetector::~GestureDetector()
|
|
{
|
|
m_nNumInstances--;
|
|
|
|
if ( m_nNumInstances == 0 )
|
|
{
|
|
FreeClassifierData();
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Initialize
|
|
// Desc: Initialize and allocate memory used by the classifier data
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
HRESULT GestureDetector::Initialize( const UINT nNumWeakClassifiers, UINT nNumClassifierData )
|
|
{
|
|
RETURN_ON_FAIL( m_StrongClassifier.Initialize( nNumWeakClassifiers ) );
|
|
|
|
//since we only allocate when this is called the first time, we have to make enough space for all data
|
|
// even if the first loaded gesture only contains a fraction of them.
|
|
nNumClassifierData = 0xC00; //this is enough for now and probably a bit more
|
|
|
|
#if defined(_XBOX) || defined(ITF_X360) ||\
|
|
defined(DURANGO) || defined(ITF_DURANGO) ||\
|
|
defined(__ORBIS__) || defined(ITF_ORBIS)||\
|
|
defined(WIN32) || defined(ITF_WIN32)
|
|
if ( !m_ClassifierData )
|
|
{
|
|
m_nNumClassifierData = nNumClassifierData;
|
|
|
|
void* pMem;
|
|
RETURN_ON_NULL( pMem = AllocateAligned( sizeof( ClassifierData* ) * nNumClassifierData, 4 ) );
|
|
m_ClassifierData = new (pMem) ClassifierData*[ nNumClassifierData ];
|
|
|
|
for ( UINT i = 0; i < m_nNumClassifierData; i++ )
|
|
{
|
|
m_ClassifierData[ i ] = NULL;
|
|
}
|
|
}
|
|
#else
|
|
if( m_ClassifierData.empty() )
|
|
{
|
|
m_ClassifierData.clear();
|
|
m_ClassifierData.reserve( nNumClassifierData );
|
|
for ( UINT i = 0; i < nNumClassifierData; i++ )
|
|
{
|
|
m_ClassifierData.push_back( NULL );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
m_uPreviousTimeStamp = 0;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: FreeClassifierData
|
|
// Desc: Free memory allocated for classifier data
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
VOID GestureDetector::FreeClassifierData()
|
|
{
|
|
#if defined(_XBOX) || defined(ITF_X360) ||\
|
|
defined(DURANGO) || defined(ITF_DURANGO) ||\
|
|
defined(__ORBIS__) || defined(ITF_ORBIS)||\
|
|
defined(WIN32) || defined(ITF_WIN32)
|
|
if ( m_ClassifierData )
|
|
{
|
|
for ( UINT i = 0; i < m_nNumClassifierData; i++ )
|
|
{
|
|
if ( m_ClassifierData[ i ] )
|
|
{
|
|
FreeAligned( m_ClassifierData[ i ] );
|
|
m_ClassifierData[ i ] = NULL;
|
|
}
|
|
}
|
|
FreeAligned( m_ClassifierData );
|
|
m_ClassifierData = NULL;
|
|
m_nNumClassifierData = 0;
|
|
}
|
|
#else
|
|
for ( UINT i = 0; i < m_ClassifierData.size(); i++ )
|
|
{
|
|
if ( m_ClassifierData[ i ] )
|
|
{
|
|
FreeAligned( m_ClassifierData[ i ] );
|
|
m_ClassifierData[ i ] = NULL;
|
|
}
|
|
}
|
|
m_ClassifierData.clear();
|
|
#endif
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: ValidateHeader
|
|
// Desc: Checks the file ID read from the gesture file is in the list of supported ones
|
|
//--------------------------------------------------------------------------------------
|
|
BOOL GestureDetector::ValidateHeader( CHAR* szHeader )
|
|
{
|
|
for( int i = 0; i < g_numGestureFileIDs; ++i )
|
|
{
|
|
if(!strcmp(szHeader,g_aszGestureFileIDs[i]))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef GESTURE_EVALUATOR
|
|
|
|
void GestureDetector::GaussianBlurKernel( float* weights, int kernelSize )
|
|
{
|
|
// ITF_ASSERT(Size&1 != 0); //must be an odd number
|
|
const unsigned int Half = (unsigned int)kernelSize >> 1;
|
|
weights[Half] = 1.0f;
|
|
|
|
for (unsigned int Weight = 1; Weight < Half + 1; ++Weight)
|
|
{
|
|
const float x = 3.0f * (float)Weight / (float)Half;
|
|
weights[Half - Weight] = exp(-x * x / 2.0f);
|
|
weights[Half + Weight] = weights[Half - Weight];
|
|
}
|
|
//normalise to make sum == 1
|
|
float k = 0.0f;
|
|
for (int Weight = 0; Weight < kernelSize; ++Weight)
|
|
{
|
|
k += weights[Weight];
|
|
}
|
|
for (int Weight = 0; Weight < kernelSize; ++Weight)
|
|
{
|
|
weights[Weight] /= k;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Load
|
|
// Desc: Load data
|
|
//--------------------------------------------------------------------------------------
|
|
// TODO: Refactor to:
|
|
// 1. Load whole file
|
|
// 2. Call LoadFromMemory
|
|
HRESULT GestureDetector::Load( const CHAR* szFileName )
|
|
{
|
|
FILE* pFile = NULL;
|
|
fopen_s( &pFile, szFileName, "rb" );
|
|
RETURN_ON_NULL( pFile );
|
|
|
|
// Check that this is indeed a gesture file
|
|
const int maxIDSize = 64;
|
|
CHAR szFileID[ maxIDSize ];
|
|
int IDSize = 0;
|
|
bool IDReadComplete = false;
|
|
while( ( IDSize != maxIDSize ) && !IDReadComplete )
|
|
{
|
|
fread( szFileID + IDSize, 1, 1, pFile );
|
|
IDReadComplete = ( szFileID[ IDSize ] == '\0' );
|
|
++IDSize;
|
|
}
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
|
|
UINT nNumClassifierData;
|
|
UINT nNumWeakClassifiers;
|
|
|
|
// Check the file header
|
|
if ( GestureDetector::ValidateHeader( szFileID ) )
|
|
{
|
|
// Read the version number. This is only added for backwards compatibility when we need to change file formats in the future
|
|
FLOAT fVersion;
|
|
fread( &fVersion, sizeof( fVersion ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
fVersion = ByteSwap32BitRead( fVersion );
|
|
|
|
BOOL bUsesEnergyStatistics = TRUE;
|
|
|
|
// Check that the file version is valid and can be loaded
|
|
if( fabsf( fVersion - g_fVersion_1_3 ) <= g_fVersionEpsilon )
|
|
{
|
|
bUsesEnergyStatistics = FALSE;
|
|
}
|
|
else if ( fabsf( fVersion - g_fCurrentVersion ) > g_fVersionEpsilon )
|
|
{
|
|
fclose( pFile );
|
|
printf( "\nError: File version %f != current version %f\n", fVersion, g_fCurrentVersion );
|
|
return E_FAIL;
|
|
}
|
|
|
|
fread( &nNumWeakClassifiers, sizeof( nNumWeakClassifiers ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
nNumWeakClassifiers = ByteSwap32BitRead( nNumWeakClassifiers );
|
|
|
|
fread( &nNumClassifierData, sizeof( nNumClassifierData ), 1, pFile );
|
|
RETURN_ON_FILE_ERROR( pFile );
|
|
nNumClassifierData = ByteSwap32BitRead( nNumClassifierData );
|
|
|
|
// Classifier data is shared between weak classifiers, there it's static. Therefore
|
|
// we only want to allocate the memory once
|
|
RETURN_ON_FAIL( Initialize( nNumWeakClassifiers, nNumClassifierData ) );
|
|
|
|
#if defined(_XBOX) || defined(ITF_X360) ||\
|
|
defined(DURANGO) || defined(ITF_DURANGO) ||\
|
|
defined(__ORBIS__) || defined(ITF_ORBIS)||\
|
|
defined(WIN32) || defined(ITF_WIN32)
|
|
UINT nClassifierDataArraySize = m_nNumClassifierData;
|
|
#else
|
|
UINT nClassifierDataArraySize = (UINT)m_ClassifierData.size();
|
|
#endif
|
|
//lookup table for where they ended up (or were found)
|
|
vector<UINT> vIndex;
|
|
vector<UINT> vUID;
|
|
vIndex.reserve(nNumClassifierData);
|
|
vUID.reserve(nNumClassifierData);
|
|
for ( UINT j = 0; j < nNumClassifierData; j++ )
|
|
{
|
|
vIndex.push_back(0xffffffff);
|
|
vUID.push_back(0);
|
|
}
|
|
|
|
for ( UINT i = 0; i < nNumClassifierData; i++ )
|
|
{
|
|
ClassifierData* pClassifierData = ClassifierData::CreatNewInstance( pFile );
|
|
RETURN_ON_NULL( pClassifierData );
|
|
RETURN_ON_FAIL( pClassifierData->Read( pFile ) );
|
|
pClassifierData->SetID( pClassifierData->MakeUID() );
|
|
vUID[ i ] = pClassifierData->GetID();
|
|
|
|
UINT uHashIndex = pClassifierData->GetID() % nClassifierDataArraySize; //temp. hash function
|
|
|
|
if ( uHashIndex < nClassifierDataArraySize )
|
|
{
|
|
if ( m_ClassifierData[ uHashIndex ] == NULL )
|
|
{//store
|
|
m_ClassifierData[ uHashIndex ] = pClassifierData;
|
|
vIndex[ i ] = uHashIndex;
|
|
continue;
|
|
} else
|
|
{//check if it's the same; if not, must relocate this one somewhere
|
|
if (m_ClassifierData[ uHashIndex ]->GetID() == pClassifierData->GetID())
|
|
{//already loaded
|
|
FreeAligned( pClassifierData );
|
|
vIndex[ i ] = uHashIndex;
|
|
continue;
|
|
}
|
|
//different
|
|
}
|
|
}
|
|
|
|
{//try to find it elsewhere, or a free slot to store it
|
|
for( UINT j=0; j<nClassifierDataArraySize; j++ )
|
|
{
|
|
if (++uHashIndex >= nClassifierDataArraySize) uHashIndex=0; //hash function step
|
|
|
|
if (m_ClassifierData[ uHashIndex ] == NULL)
|
|
{//free slot found; if it's not found yet, we're done.
|
|
m_ClassifierData[ uHashIndex ] = pClassifierData;
|
|
vIndex[ i ] = uHashIndex;
|
|
break;
|
|
} else
|
|
if (m_ClassifierData[ uHashIndex ]->GetID() == pClassifierData->GetID())
|
|
{//found
|
|
FreeAligned( pClassifierData );
|
|
vIndex[ i ] = uHashIndex;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (vIndex[ i ]==0xffffffff)
|
|
{//failed to store it; must dispose
|
|
FreeAligned( pClassifierData );
|
|
}
|
|
}
|
|
|
|
RETURN_ON_FAIL( m_StrongClassifier.Read( pFile, bUsesEnergyStatistics ) );
|
|
|
|
// Read classifiers
|
|
for ( UINT i = 0; i < nNumWeakClassifiers; i++ )
|
|
{
|
|
UINT uDataIndex;
|
|
RETURN_ON_FAIL( m_StrongClassifier.Read( pFile, i, &uDataIndex ) ); //the original UID it was saved with. might not match the new one.
|
|
|
|
// Setup data pointer in classifier
|
|
WeakClassifier* pWeakClassifier = m_StrongClassifier.GetWeakClassifierAt( i );
|
|
pWeakClassifier->SetDataIndex(0xffffffff);
|
|
|
|
if (uDataIndex < nNumClassifierData && vIndex[ uDataIndex ] != 0xffffffff)
|
|
{
|
|
ClassifierData *pData = m_ClassifierData[ vIndex[ uDataIndex ] ];
|
|
if ( pData!=NULL && pData->GetID() == vUID[ uDataIndex ])
|
|
{
|
|
pWeakClassifier->SetDataIndex( vIndex[ uDataIndex ] );
|
|
}
|
|
}
|
|
|
|
if (pWeakClassifier->GetDataIndex() == 0xffffffff)
|
|
{//WTF?
|
|
pWeakClassifier->SetDataIndex( 0 );
|
|
pWeakClassifier->SetAlpha( 0.0f ); //eliminate influence
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fclose( pFile );
|
|
return E_FAIL;
|
|
}
|
|
|
|
fclose( pFile );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: LoadFromMemory
|
|
// Desc: Load data from memory location
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
HRESULT GestureDetector::LoadFromMemory( VOID* pBuffer )
|
|
{
|
|
RETURN_ON_NULL( pBuffer );
|
|
|
|
// Check that this is indeed a gesture file
|
|
const int maxIDSize = 64;
|
|
CHAR szFileID[ maxIDSize ];
|
|
int IDSize = 0;
|
|
bool IDReadComplete = false;
|
|
while( ( IDSize != maxIDSize ) && !IDReadComplete )
|
|
{
|
|
mread( szFileID + IDSize, 1, 1, pBuffer );
|
|
IDReadComplete = ( szFileID[ IDSize ] == '\0' );
|
|
++IDSize;
|
|
}
|
|
|
|
UINT nNumClassifierData;
|
|
UINT nNumWeakClassifiers;
|
|
|
|
// Check the file header
|
|
if ( GestureDetector::ValidateHeader(szFileID) )
|
|
{
|
|
// Read the version number. This is only added for backwards compatibility when we need to change file formats in the future
|
|
FLOAT fVersion;
|
|
mread( &fVersion, sizeof( fVersion ), 1, pBuffer );
|
|
fVersion = ByteSwap32BitRead( fVersion );
|
|
|
|
BOOL bUsesEnergyStatistics = TRUE;
|
|
|
|
const float versionDiff_1_3 = fabsf( fVersion - g_fVersion_1_3 );
|
|
const float versionDiff = fabsf( fVersion - g_fCurrentVersion );
|
|
|
|
// Check that the file version is valid and can be loaded
|
|
if( versionDiff_1_3 <= g_fVersionEpsilon )
|
|
{
|
|
bUsesEnergyStatistics = FALSE;
|
|
}
|
|
else if ( versionDiff > g_fVersionEpsilon )
|
|
{
|
|
mclose();
|
|
char buffer[1024];
|
|
|
|
#if defined( __ORBIS__ ) || defined( ITF_ORBIS )
|
|
snprintf_s( buffer, 1024, "\nError: File version %f != current version %f\n", fVersion, g_fCurrentVersion );
|
|
#else
|
|
_snprintf_s( buffer, 1024, "\nError: File version %f != current version %f\n", fVersion, g_fCurrentVersion );
|
|
#endif
|
|
|
|
Assert( false, buffer );
|
|
return E_FAIL;
|
|
}
|
|
|
|
mread( &nNumWeakClassifiers, sizeof( nNumWeakClassifiers ), 1, pBuffer );
|
|
nNumWeakClassifiers = ByteSwap32BitRead( nNumWeakClassifiers );
|
|
|
|
mread( &nNumClassifierData, sizeof( nNumClassifierData ), 1, pBuffer );
|
|
nNumClassifierData = ByteSwap32BitRead( nNumClassifierData );
|
|
|
|
// Classifier data is shared between weak classifiers, there it's static. Therefoer
|
|
// we only want to allocate the memory once
|
|
RETURN_ON_FAIL( Initialize( nNumWeakClassifiers, nNumClassifierData + 0x100 ) );
|
|
|
|
#if defined(_XBOX) || defined(ITF_X360) ||\
|
|
defined(DURANGO) || defined(ITF_DURANGO) ||\
|
|
defined(__ORBIS__) || defined(ITF_ORBIS)||\
|
|
defined(WIN32) || defined(ITF_WIN32)
|
|
UINT nClassifierDataArraySize = m_nNumClassifierData;
|
|
#else
|
|
UINT nClassifierDataArraySize = (UINT)m_ClassifierData.size();
|
|
#endif
|
|
//lookup table for where they ended up (or were found)
|
|
vector<UINT> vIndex;
|
|
vector<UINT> vUID;
|
|
vIndex.reserve(nNumClassifierData);
|
|
vUID.reserve(nNumClassifierData);
|
|
for ( UINT j = 0; j < nNumClassifierData; j++ )
|
|
{
|
|
vIndex.push_back(0xffffffff);
|
|
vUID.push_back(0);
|
|
}
|
|
|
|
for ( UINT i = 0; i < nNumClassifierData; i++ )
|
|
{
|
|
ClassifierData* pClassifierData = ClassifierData::CreatNewInstance( pBuffer );
|
|
RETURN_ON_NULL( pClassifierData );
|
|
RETURN_ON_FAIL( pClassifierData->Read( pBuffer ) );
|
|
pClassifierData->SetID( pClassifierData->MakeUID() );
|
|
vUID[ i ] = pClassifierData->GetID();
|
|
|
|
UINT uHashIndex = pClassifierData->GetID() % nClassifierDataArraySize; //temp. hash function
|
|
|
|
if ( uHashIndex < nClassifierDataArraySize )
|
|
{
|
|
if ( m_ClassifierData[ uHashIndex ] == NULL )
|
|
{//store
|
|
m_ClassifierData[ uHashIndex ] = pClassifierData;
|
|
vIndex[ i ] = uHashIndex;
|
|
continue;
|
|
} else
|
|
{//check if it's the same; if not, must relocate this one somewhere
|
|
if (m_ClassifierData[ uHashIndex ]->GetID() == pClassifierData->GetID())
|
|
{//already loaded
|
|
FreeAligned( pClassifierData );
|
|
vIndex[ i ] = uHashIndex;
|
|
continue;
|
|
}
|
|
//different
|
|
}
|
|
}
|
|
|
|
{//try to find it elsewhere, or a free slot to store it
|
|
for( UINT j=0; j<nClassifierDataArraySize; j++ )
|
|
{
|
|
if (++uHashIndex >= nClassifierDataArraySize) uHashIndex=0; //hash function step
|
|
|
|
if (m_ClassifierData[ uHashIndex ] == NULL)
|
|
{//free slot found; if it's not found yet, we're done.
|
|
m_ClassifierData[ uHashIndex ] = pClassifierData;
|
|
vIndex[ i ] = uHashIndex;
|
|
break;
|
|
} else
|
|
if (m_ClassifierData[ uHashIndex ]->GetID() == pClassifierData->GetID())
|
|
{//found
|
|
FreeAligned( pClassifierData );
|
|
vIndex[ i ] = uHashIndex;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (vIndex[ i ]==0xffffffff)
|
|
{//failed to store it; must dispose
|
|
FreeAligned( pClassifierData );
|
|
}
|
|
}
|
|
|
|
RETURN_ON_FAIL( m_StrongClassifier.Read( pBuffer, bUsesEnergyStatistics ) );
|
|
|
|
// Read classifiers
|
|
for ( UINT i = 0; i < nNumWeakClassifiers; i++ )
|
|
{
|
|
UINT uDataIndex;
|
|
RETURN_ON_FAIL( m_StrongClassifier.Read( pBuffer, i, &uDataIndex ) ); //the original UID it was saved with. might not match the new one.
|
|
|
|
// Setup data pointer in classifier
|
|
WeakClassifier* pWeakClassifier = m_StrongClassifier.GetWeakClassifierAt( i );
|
|
pWeakClassifier->SetDataIndex(0xffffffff);
|
|
|
|
if (uDataIndex < nNumClassifierData && vIndex[ uDataIndex ] != 0xffffffff)
|
|
{
|
|
ClassifierData *pData = m_ClassifierData[ vIndex[ uDataIndex ] ];
|
|
if ( pData!=NULL && pData->GetID() == vUID[ uDataIndex ])
|
|
{
|
|
pWeakClassifier->SetDataIndex( vIndex[ uDataIndex ] );
|
|
}
|
|
}
|
|
|
|
if (pWeakClassifier->GetDataIndex() == 0xffffffff)
|
|
{//WTF?
|
|
pWeakClassifier->SetDataIndex( 0 );
|
|
pWeakClassifier->SetAlpha( 0.0f ); //eliminate influence
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mclose();
|
|
return E_FAIL;
|
|
}
|
|
|
|
mclose();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Detect
|
|
// Desc: Does the gesture detection
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
BOOL GestureDetector::Detect( const UINT uPlayerIdx, Results* pResults, const BOOL bFilterResults )
|
|
{
|
|
assert( uPlayerIdx < KINECT_GESTURE_MAX_SIMULTANEOUS_GESTURES );
|
|
assert( pResults );
|
|
|
|
//this function does NOT actually depend on the presence of any skeleton. the classisierData does, but that's a separate issue.
|
|
// in fact, if there's no skeleton we know it way above and don't even call this function.
|
|
// but with the introduction of optical flow based scoring, it's especially unnecessary.
|
|
|
|
// NUI_SKELETON_DATA* pSkeletonData = ClassifierData::GetCurrentSkeleton( uPlayerIdx );
|
|
// if ( pSkeletonData && pSkeletonData->eTrackingState == NUI_SKELETON_TRACKED )
|
|
// {
|
|
return m_StrongClassifier.Detect( uPlayerIdx, m_ClassifierData, pResults, bFilterResults );
|
|
// }
|
|
|
|
// #ifdef _XBOX
|
|
// pResults->m_fConfidence = bFilterResults ? 0.0f : (FLOAT)g_fClassificationLabelIncorrect;
|
|
// #else
|
|
// pResults->m_fConfidence = bFilterResults ? 0.0f : (FLOAT)g_iClassificationLabelIncorrect;
|
|
// #endif
|
|
// pResults->m_bDetected = FALSE;
|
|
// pResults->m_bFirstFrameDetected = FALSE;
|
|
//
|
|
// return FALSE;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: GetBoneWeights
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
VOID GestureDetector::GetBoneWeights( FLOAT *weights, UINT numWeights )
|
|
{
|
|
//#ifdef _XBOX
|
|
m_StrongClassifier.GetBoneWeights( m_ClassifierData, weights, numWeights );
|
|
//#endif
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: CalculateUsedRanges
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
VOID GestureDetector::CalculateUsedRanges( float *fRangeMin, float *fRangeMax, float *fSumAlpha )
|
|
{
|
|
m_StrongClassifier.CalculateUsedRanges( m_ClassifierData, fRangeMin, fRangeMax, fSumAlpha );
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Update
|
|
// Desc: Per frame update of classifier data that is shared between weak classifiers
|
|
//--------------------------------------------------------------------------------------
|
|
BOOL GestureDetector::Update( const UINT uPlayerIdx, const GESTURE_SKELETON_TYPE* pSkeletonData, const LARGE_INTEGER& liTimeStampFromNuiFrame, const XMVECTOR& vNormalToGravity )
|
|
{
|
|
BOOL bReset = FALSE;
|
|
#if defined( __ORBIS__ ) || defined( ITF_ORBIS )
|
|
UINT64 uTimeStamp = liTimeStampFromNuiFrame;
|
|
#else
|
|
UINT64 uTimeStamp = liTimeStampFromNuiFrame.QuadPart;
|
|
#endif
|
|
|
|
// Xed files record UINT64 time stamps which are different from LARGE_INTEGER time stamps from NUI_SKELETON_FRAME at runtime
|
|
#if defined( GESTURE_TRAINER )
|
|
FLOAT fDeltaTimeInSeconds = ( uTimeStamp - m_uPreviousTimeStamp ) * 0.000001f; // Example files for all platforms have timestamps in micro seconds
|
|
#elif defined( _XBOX ) || defined( TARGET_X360 ) || defined( ITF_X360 )
|
|
FLOAT fDeltaTimeInSeconds = ( uTimeStamp - m_uPreviousTimeStamp ) * 0.001f; // milliseconds
|
|
#elif defined( DURANGO ) || defined( TARGET_DURANGO ) || defined( ITF_DURANGO )
|
|
FLOAT fDeltaTimeInSeconds = ( uTimeStamp - m_uPreviousTimeStamp ) * 0.0000001f; // 100-microseconds
|
|
#elif defined( __ORBIS__ ) || defined( TARGET_ORBIS ) || defined( ITF_ORBIS )
|
|
FLOAT fDeltaTimeInSeconds = ( uTimeStamp - m_uPreviousTimeStamp ) * 0.000001f; // microseconds
|
|
#elif defined( WIN32 ) || defined( ITF_WIN32 )
|
|
FLOAT fDeltaTimeInSeconds = ( uTimeStamp - m_uPreviousTimeStamp ) * 0.0000001f; // 100-microseconds
|
|
#else
|
|
#error unsuported platform
|
|
#endif
|
|
|
|
//check for discontinuities in the training data (due to stitching multiple Examples together)
|
|
if (fDeltaTimeInSeconds <= 0 || fDeltaTimeInSeconds > MAX_SENSIBLE_DELTATIME )
|
|
{
|
|
fDeltaTimeInSeconds = 0.0f;
|
|
bReset = TRUE;
|
|
} else
|
|
// dont use a huge time delta on the first update
|
|
if ( m_uPreviousTimeStamp == 0 )
|
|
{
|
|
fDeltaTimeInSeconds = 0.0f;
|
|
bReset = TRUE;
|
|
}
|
|
m_uPreviousTimeStamp = uTimeStamp;
|
|
|
|
return Update( uPlayerIdx, pSkeletonData, fDeltaTimeInSeconds, vNormalToGravity, bReset, NULL );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Update
|
|
// Desc: Per frame update of classifier data that is shared between weak classifiers
|
|
//--------------------------------------------------------------------------------------
|
|
BOOL GestureDetector::Update( const UINT uPlayerIdx, const GESTURE_SKELETON_TYPE* pSkeletonData, float fDeltaTimeInSeconds, const XMVECTOR& vNormalToGravity, BOOL bReset, const VelocityGrid* opticalFlowGrid )
|
|
{
|
|
GESTURE_SKELETON_TYPE skeleton;
|
|
|
|
if ( pSkeletonData != NULL )
|
|
{
|
|
#ifdef _XBOX
|
|
XMemCpy( &skeleton, pSkeletonData, sizeof( GESTURE_SKELETON_TYPE ) );
|
|
#else
|
|
memcpy( &skeleton, pSkeletonData, sizeof( GESTURE_SKELETON_TYPE ) );
|
|
#endif
|
|
|
|
// Apply tilt correction on the data
|
|
ApplyTiltCorrection( uPlayerIdx, &skeleton, (GESTURE_SKELETON_TYPE*)pSkeletonData, vNormalToGravity, bReset );
|
|
}
|
|
else
|
|
{
|
|
#ifdef _XBOX
|
|
XMemSet( &skeleton, 0, sizeof( GESTURE_SKELETON_TYPE ) );
|
|
#else
|
|
memset( &skeleton, 0, sizeof( GESTURE_SKELETON_TYPE ) );
|
|
#endif
|
|
}
|
|
|
|
// Update the sliding window of skeleton data frames
|
|
ClassifierData::UpdateHistory( uPlayerIdx, &skeleton, fDeltaTimeInSeconds, &bReset, opticalFlowGrid );
|
|
|
|
if ( bReset )
|
|
{
|
|
// Make sure we have a valid delta time, so just use 33ms
|
|
fDeltaTimeInSeconds = 0.033f;
|
|
}
|
|
|
|
// Update the values in the classifier data
|
|
#if defined(_XBOX) || defined(ITF_X360) ||\
|
|
defined(DURANGO) || defined(ITF_DURANGO) ||\
|
|
defined(__ORBIS__) || defined(ITF_ORBIS)||\
|
|
defined(WIN32) || defined(ITF_WIN32)
|
|
const UINT nNumData = m_nNumClassifierData;
|
|
#else
|
|
const UINT nNumData = (UINT)m_ClassifierData.size();
|
|
#endif
|
|
|
|
#ifdef _XBOX
|
|
__dcbt( 0, m_ClassifierData );
|
|
__dcbt( 128, m_ClassifierData );
|
|
#endif
|
|
|
|
for ( UINT i = 0; i < nNumData; i++ )
|
|
{
|
|
if ( m_ClassifierData[ i ] )
|
|
{
|
|
m_ClassifierData[ i ]->Update( uPlayerIdx, &fDeltaTimeInSeconds );
|
|
}
|
|
|
|
#ifdef _XBOX
|
|
__dcbt( 128, m_ClassifierData[ i ] );
|
|
#endif
|
|
}
|
|
|
|
return bReset;
|
|
}
|
|
|
|
#ifdef GESTURE_EVALUATOR
|
|
|
|
bool GestureDetector::Update( const UINT uPlayerIdx, const GESTURE_SKELETON_TYPE* pSkeletonData, const LARGE_INTEGER& liTimeStampFromNuiFrame )
|
|
{
|
|
return Update(uPlayerIdx, pSkeletonData,liTimeStampFromNuiFrame,XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f ));
|
|
}
|
|
|
|
BOOL GestureDetector::Update( const UINT uPlayerIdx, const GESTURE_SKELETON_TYPE* pSkeletonData, const float fDeltaTimeInSeconds, BOOL bReset, const VelocityGrid* opticalFlowGrid )
|
|
{
|
|
return Update(uPlayerIdx,pSkeletonData,fDeltaTimeInSeconds,XMVectorSet(0.0f,1.0f,0.0f,0.0f),bReset,opticalFlowGrid);
|
|
}
|
|
|
|
#endif
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: ResetPlayer
|
|
// Desc: Called when a newly tracked player is detected
|
|
//--------------------------------------------------------------------------------------
|
|
VOID GestureDetector::ResetPlayer( const UINT uPlayerIdx, const GESTURE_SKELETON_TYPE* pSkeletonData, const XMVECTOR& vNormalToGravity )
|
|
{
|
|
vAverageNormalToGravity[ uPlayerIdx ] = vNormalToGravity;
|
|
ClassifierData::Reset( uPlayerIdx, pSkeletonData );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: ApplyTiltCorrection
|
|
// Desc: Applies tilt correction to the skeleton data. Source and destination can be the same
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
VOID GestureDetector::ApplyTiltCorrection( const UINT uPlayerIdx, GESTURE_SKELETON_TYPE* pDstSkeleton, const GESTURE_SKELETON_TYPE* pSrcSkeleton, const XMVECTOR& vNormalToGravity, BOOL bReset )
|
|
{
|
|
if ( !pDstSkeleton ||
|
|
!pSrcSkeleton )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( bReset )
|
|
{
|
|
vAverageNormalToGravity[ uPlayerIdx ] = vNormalToGravity;
|
|
}
|
|
|
|
// Get our Up vector from sensor.
|
|
XMVECTOR vNormToGrav = vNormalToGravity;
|
|
|
|
// Check for an invalid up vector
|
|
XMVECTOR vDot = XMVector3Dot( vNormToGrav, vNormToGrav );
|
|
if ( fabsf( XMVectorGetX( vDot ) ) < FLT_EPSILON )
|
|
{
|
|
vNormToGrav = vUp;
|
|
}
|
|
|
|
// Average this a lot so that it doesn't add jumpiness ot the scene.
|
|
vAverageNormalToGravity[ uPlayerIdx ] = XMVectorLerp( vAverageNormalToGravity[ uPlayerIdx ], vNormToGrav, 0.1f );
|
|
|
|
if ( GESTURE_GET_TRACKING( pSrcSkeleton ) != GESTURE_SKELETON_TRACKED )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Generate the leveling matrix and apply it
|
|
XMMATRIX matLevel;
|
|
|
|
// Normalize
|
|
vNormToGrav = XMVector4Normalize( vAverageNormalToGravity[ uPlayerIdx ] );
|
|
|
|
// Rotation axis
|
|
XMVECTOR vAxis = XMVector3Cross( vNormToGrav, vUp );
|
|
|
|
// if the rotation axis is zero, then Gravity == Camera
|
|
// therefore return the Identity Matrix.
|
|
if ( XMVector4Equal( vAxis, XMVectorZero() ) )
|
|
{
|
|
matLevel = XMMatrixIdentity();
|
|
}
|
|
else
|
|
{
|
|
// angle to rotate
|
|
XMVECTOR vAngle = XMVector4Dot( vUp, vNormToGrav );
|
|
FLOAT fAngle = acosf( XMVectorGetX( vAngle ) );
|
|
matLevel = XMMatrixRotationAxis( vAxis, fAngle );
|
|
}
|
|
|
|
for ( UINT i = 0; i < GESTURE_JOINT_COUNT; i++ )
|
|
{
|
|
GESTURE_GET_JOINT_POS( pDstSkeleton, i ) = XMVector3Transform( GESTURE_GET_JOINT_POS( pSrcSkeleton, i ), matLevel );
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: GetEnergyLevel
|
|
// Desc: Use muscle frame data to approximate energy expended
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
FLOAT GestureDetector::GetEnergyLevel( const UINT uPlayerIdx )
|
|
{
|
|
return ClassifierData::GetEnergyLevel( uPlayerIdx );
|
|
}
|
|
|
|
#ifdef GESTURE_EVALUATOR
|
|
|
|
void GestureDetector::opticalflowUpdateDepthMap( unsigned short* _pDepthMapData, int _pitch )
|
|
{
|
|
for (u8 y = 0; y < OPTICALFLOW_FULLSCREENGRID_Y; ++ y)
|
|
{
|
|
for (u8 x = 0; x < OPTICALFLOW_FULLSCREENGRID_X; ++ x)
|
|
{
|
|
OpticalFlowCell &cell = m_opticalFlowGrid[x][y];
|
|
cell.iprev = cell.i;
|
|
cell.i = 0.0f;
|
|
}
|
|
}
|
|
|
|
//now sampling only every 4th pixel for speed
|
|
|
|
for (u16 y = 0; y < OPTICALFLOW_DEPTH_HEIGHT; y += OPTICALFLOW_SAMPLING_Y)
|
|
{
|
|
unsigned short* line = _pDepthMapData + (_pitch / sizeof(unsigned char)) * y;
|
|
|
|
for (u16 x = 0; x < OPTICALFLOW_DEPTH_WIDTH; x += OPTICALFLOW_SAMPLING_X)
|
|
{
|
|
// normalize the depth value against a maximum depth
|
|
|
|
u16 depthFromDepthMapLine = opticalFlow_getDepthFromDepthMapLine(line, x);
|
|
|
|
f32 depthNormalized = (f32)depthFromDepthMapLine * OPTICALFLOW_NORMALIZE;
|
|
|
|
f32 depthMin = ( depthNormalized > 1.0f) ? 1.0f : depthNormalized;
|
|
|
|
f32 i = depthFromDepthMapLine == 0 ? 0.0f : 1.0f - depthMin;
|
|
|
|
i32 cx = (i32)((f32)x * OPTICALFLOW_WIDTH_TO_CELL);
|
|
i32 cy = (i32)((f32)y * OPTICALFLOW_HEIGHT_TO_CELL);
|
|
|
|
m_opticalFlowGrid[cx][cy].i += i * OPTICALFLOW_SAMPLE_PERCENT;
|
|
}
|
|
}
|
|
}
|
|
|
|
KinectGesture::u16 GestureDetector::opticalFlow_getDepthFromDepthMapLine( unsigned short* _line, u16 _x )
|
|
{
|
|
USHORT depth = (USHORT)_line[_x];
|
|
if (depth == INVALID_DEPTH)
|
|
depth = 0;
|
|
|
|
return (u16)depth;
|
|
}
|
|
|
|
vector2 mapCameraSpaceToDepthSpace(mathLib_vector4 cameraSpaceVector)
|
|
{
|
|
#if TARGET_DURANGO || TARGET_X360
|
|
const float fovOver2 = ( KINECT_FOV * MTH_DEGTORAD ) / 2.0f;
|
|
#elif TARGET_ORBIS
|
|
const float fovOver2 = ( HT_FOVX * MTH_DEGTORAD ) / 2.0f;
|
|
#endif
|
|
const float f = cos( fovOver2 ) / sin( fovOver2 ); // f = 1 / tg(fov/2)
|
|
|
|
#if TARGET_DURANGO
|
|
const float aspect = ( float )DurangoDepth::OutputWidth / ( float )DurangoDepth::OutputHeight;
|
|
#elif TARGET_ORBIS
|
|
const float aspect = ( float )PS4Depth::OutputWidth / ( float )PS4Depth::OutputHeight;
|
|
#elif TARGET_X360
|
|
const float aspect = ( float )X360Depth::OutputWidth / ( float )X360Depth::OutputHeight;
|
|
#endif
|
|
|
|
Matrix44 cameraToDepthProjectionMatrix(-f / aspect, 0.0f, 0.0f, 0.0f,
|
|
0.0f, f, 0.0f, 0.0f,
|
|
#ifdef TARGET_ORBIS
|
|
0.0f, 0.0f, -( ( HT_NEAR_CLIP + HT_FAR_CLIP ) / ( HT_NEAR_CLIP - HT_FAR_CLIP ) ), ( 2 * HT_FAR_CLIP * HT_NEAR_CLIP ) / ( HT_NEAR_CLIP - HT_FAR_CLIP ),
|
|
#elif TARGET_DURANGO || TARGET_X360
|
|
0.0f, 0.0f, -( ( KINECT_NEAR_Z + KINECT_FAR_Z) / ( KINECT_NEAR_Z- KINECT_FAR_Z) ), ( 2 * KINECT_FAR_Z* KINECT_NEAR_Z) / ( KINECT_NEAR_Z- KINECT_FAR_Z),
|
|
#endif
|
|
0.0f, 0.0f, 1.0f, 0.0f );
|
|
|
|
mathLib_vector4 depthSpaceVector = cameraToDepthProjectionMatrix.TransformPoint(cameraSpaceVector);
|
|
|
|
if(abs(depthSpaceVector.v[3] > FLT_EPSILON))
|
|
{
|
|
const float reciprocalW = 1.0f / depthSpaceVector.v[ 3 ];
|
|
depthSpaceVector.v[ 0 ] = ( depthSpaceVector.v[ 0 ] * reciprocalW ) + 0.5f;
|
|
depthSpaceVector.v[ 1 ] = ( depthSpaceVector.v[ 1 ] * reciprocalW ) + 0.5f;
|
|
depthSpaceVector.v[ 2 ] = ( depthSpaceVector.v[ 2 ] * reciprocalW ) + 0.5f;
|
|
depthSpaceVector.v[ 3 ] = 1.0f;
|
|
}
|
|
|
|
return vector2(depthSpaceVector.v[0],depthSpaceVector.v[1]);
|
|
}
|
|
|
|
|
|
void GestureDetector::opticalFlow_update( f32 timeStamp )
|
|
{
|
|
for (u8 y = 1; y < OPTICALFLOW_FULLSCREENGRID_Y - 1; ++ y)
|
|
{
|
|
for (u8 x = 1; x < OPTICALFLOW_FULLSCREENGRID_X - 1; ++ x)
|
|
{
|
|
OpticalFlowCell &cell = m_opticalFlowGrid[x][y];
|
|
|
|
cell.It = cell.i - cell.iprev;
|
|
cell.Ix = (m_opticalFlowGrid[x + 1][y].i - m_opticalFlowGrid[x - 1][y].i) * 0.5f; // detailed : cell.Ix = (grid[x][y].i - grid[x-1][y].i + grid[x+1][y].i - grid[x][y].i) * 0.5f;
|
|
cell.Iy = (m_opticalFlowGrid[x][y + 1].i - m_opticalFlowGrid[x][y - 1].i) * 0.5f; // detailed : cell.Iy = (grid[x][y].i - grid[x][y-1].i + grid[x][y+1].i - grid[x][y].i) * 0.5f;
|
|
|
|
cell.ua = 0.0f;
|
|
cell.va = 0.0f;
|
|
f32 sum = 0.0f;
|
|
|
|
for (i8 v0 = - 1; v0 <= 1; ++ v0)
|
|
{
|
|
for (i8 u0 = - 1; u0 <= 1; ++ u0)
|
|
{
|
|
u8 u = x + u0;
|
|
u8 v = y + v0;
|
|
OpticalFlowCell &nbr = m_opticalFlowGrid[u][v];
|
|
f32 w = opticalFlowCloseCellsWeight[u0 + 1][v0 + 1];
|
|
cell.ua += nbr.u * w;
|
|
cell.va += nbr.v * w;
|
|
sum += w;
|
|
}
|
|
|
|
cell.ua /= sum;
|
|
cell.va /= sum;
|
|
|
|
f32 f = (cell.Ix * cell.ua + cell.Iy * cell.va + cell.It) / (OPTICALFLOW_ALPHA_SQUARE + cell.Ix * cell.Ix + cell.Iy * cell.Iy);
|
|
f32 u2 = cell.ua - cell.Ix * f;
|
|
f32 v2 = cell.va - cell.Iy * f;
|
|
|
|
cell.u = u2 * OPTICALFLOW_DAMPING;
|
|
cell.v = v2 * OPTICALFLOW_DAMPING;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VectorMathLimited::vector2 GestureDetector::mapCameraSpaceToDepthSpace( mathLib_vector4 cameraSpaceVector )
|
|
{
|
|
#if TARGET_DURANGO || TARGET_X360
|
|
const float fovOver2 = ( KINECT_FOV * MTH_DEGTORAD ) / 2.0f;
|
|
#elif TARGET_ORBIS
|
|
const float fovOver2 = ( HT_FOVX * MTH_DEGTORAD ) / 2.0f;
|
|
#endif
|
|
|
|
const float f = cos( fovOver2 ) / sin( fovOver2 ); // f = 1 / tg(fov/2)
|
|
|
|
#if TARGET_DURANGO
|
|
const float aspect = ( float )DurangoDepth::OutputWidth / ( float )DurangoDepth::OutputHeight;
|
|
#elif TARGET_ORBIS
|
|
const float aspect = ( float )PS4Depth::OutputWidth / ( float )PS4Depth::OutputHeight;
|
|
#elif TARGET_X360
|
|
const float aspect = ( float )X360Depth::OutputWidth / ( float )X360Depth::OutputHeight;
|
|
#endif
|
|
|
|
Matrix44 cameraToDepthProjectionMatrix(-f / aspect, 0.0f, 0.0f, 0.0f,
|
|
0.0f, f, 0.0f, 0.0f,
|
|
#ifdef TARGET_ORBIS
|
|
0.0f, 0.0f, -( ( HT_NEAR_CLIP + HT_FAR_CLIP ) / ( HT_NEAR_CLIP - HT_FAR_CLIP ) ), ( 2 * HT_FAR_CLIP * HT_NEAR_CLIP ) / ( HT_NEAR_CLIP - HT_FAR_CLIP ),
|
|
#elif TARGET_DURANGO || TARGET_X360
|
|
0.0f, 0.0f, -( ( KINECT_NEAR_Z + KINECT_FAR_Z) / ( KINECT_NEAR_Z- KINECT_FAR_Z) ), ( 2 * KINECT_FAR_Z* KINECT_NEAR_Z) / ( KINECT_NEAR_Z- KINECT_FAR_Z),
|
|
#endif
|
|
0.0f, 0.0f, 1.0f, 0.0f );
|
|
|
|
mathLib_vector4 depthSpaceVector = cameraToDepthProjectionMatrix.TransformPoint(cameraSpaceVector);
|
|
|
|
if(abs(depthSpaceVector.v[3] > FLT_EPSILON))
|
|
{
|
|
const float reciprocalW = 1.0f / depthSpaceVector.v[ 3 ];
|
|
depthSpaceVector.v[ 0 ] = ( depthSpaceVector.v[ 0 ] * reciprocalW ) + 0.5f;
|
|
depthSpaceVector.v[ 1 ] = ( depthSpaceVector.v[ 1 ] * reciprocalW ) + 0.5f;
|
|
depthSpaceVector.v[ 2 ] = ( depthSpaceVector.v[ 2 ] * reciprocalW ) + 0.5f;
|
|
depthSpaceVector.v[ 3 ] = 1.0f;
|
|
}
|
|
|
|
return vector2(depthSpaceVector.v[0],depthSpaceVector.v[1]);
|
|
}
|
|
|
|
void GestureDetector::opticalFlow_getPositionVelocityGrid( mathLib_vector4 position, VelocityGrid* velocityGrid )
|
|
{
|
|
mathLib_vector4 v1 = position + mathLib_vector4((-KINECT_GESTURE_VELOCITY_GRID_WIDTH) * 0.5f, KINECT_GESTURE_VELOCITY_GRID_HEIGHT * 0.5f, 0.0f, 1.0f);
|
|
mathLib_vector4 v2 = position + mathLib_vector4(KINECT_GESTURE_VELOCITY_GRID_WIDTH * 0.5f, (- KINECT_GESTURE_VELOCITY_GRID_HEIGHT) * 0.5f, 0.0f, 1.0f);
|
|
|
|
vector2 screenSpacePos[2];
|
|
|
|
screenSpacePos[0] = mapCameraSpaceToDepthSpace(v1);
|
|
|
|
screenSpacePos[0].v[0] = clamp(screenSpacePos[0].v[0] * (float)(OPTICALFLOW_DEPTH_WIDTH), 0.0f, (float)OPTICALFLOW_DEPTH_WIDTH);
|
|
screenSpacePos[0].v[1] = clamp(screenSpacePos[0].v[1] * (float)(OPTICALFLOW_DEPTH_HEIGHT), 0.0f, (float)OPTICALFLOW_DEPTH_HEIGHT);
|
|
|
|
screenSpacePos[1] = mapCameraSpaceToDepthSpace(v2);
|
|
|
|
screenSpacePos[1].v[0] = clamp(screenSpacePos[1].v[0] * (float)(OPTICALFLOW_DEPTH_WIDTH), 0.0f, (float)OPTICALFLOW_DEPTH_WIDTH);
|
|
screenSpacePos[1].v[1] = clamp(screenSpacePos[1].v[1] * (float)(OPTICALFLOW_DEPTH_HEIGHT), 0.0f, (float)OPTICALFLOW_DEPTH_HEIGHT);
|
|
|
|
const float stepx = (screenSpacePos[1].v[0] - screenSpacePos[0].v[0]) / (float)KINECT_GESTURE_VELOCITY_GRID_X;
|
|
const float stepy = (screenSpacePos[1].v[1] - screenSpacePos[0].v[1]) / (float)KINECT_GESTURE_VELOCITY_GRID_Y;
|
|
|
|
float baseScale = 4.0f;
|
|
float scale = baseScale * (stepx / OPTICALFLOW_DEPTH_WIDTH);
|
|
float invertedSqrScale = 1.0f / (scale * scale);
|
|
|
|
for (int i = 0; i < KINECT_GESTURE_VELOCITY_GRID_X; ++ i)
|
|
{
|
|
for (int j = 0; j < KINECT_GESTURE_VELOCITY_GRID_Y; ++ j)
|
|
{
|
|
vector3 AABBmin(screenSpacePos[1].v[0] + stepx * i, screenSpacePos[1].v[1] + stepy * j, 0.0f);
|
|
vector3 AABBmax(screenSpacePos[1].v[0] + stepx * (i + 1), screenSpacePos[1].v[1] + stepy * (j + 1), 0.0f);
|
|
vector3 v = opticalFlow_getRegionVelocity(AABBmin, AABBmax);
|
|
|
|
//compensate for size (and contrast)
|
|
|
|
vector3 scaledV = v * invertedSqrScale;
|
|
|
|
velocityGrid->mVelocity[i][j][0] = scaledV.v[0];
|
|
velocityGrid->mVelocity[i][j][1] = scaledV.v[1];
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
float GestureDetector::clamp(float val, float min, float max)
|
|
{
|
|
if(val < min)
|
|
{
|
|
return min;
|
|
}
|
|
else if(val > max)
|
|
{
|
|
return max;
|
|
}
|
|
else return val;
|
|
}
|
|
|
|
vector3& GestureDetector::opticalFlow_getRegionVelocity( vector3 min, vector3 max )
|
|
{
|
|
//ToDo: calculate the average velocity for a given (screen) region using a weighted sum of the overlapping grid cells
|
|
|
|
int xmin = (int)clamp(min.v[0] * (float)OPTICALFLOW_FULLSCREENGRID_X / (float)OPTICALFLOW_DEPTH_WIDTH, 0.0f, (float)OPTICALFLOW_FULLSCREENGRID_X);
|
|
int ymin = (int)clamp(min.v[1] * (float)OPTICALFLOW_FULLSCREENGRID_Y / (float)OPTICALFLOW_DEPTH_HEIGHT, 0.0f, (float)OPTICALFLOW_FULLSCREENGRID_Y);
|
|
int xmax = (int)clamp(max.v[0] * (float)OPTICALFLOW_FULLSCREENGRID_X / (float)OPTICALFLOW_DEPTH_WIDTH, 0.0f, (float)OPTICALFLOW_FULLSCREENGRID_X);
|
|
int ymax = (int)clamp(max.v[1] * (float)OPTICALFLOW_FULLSCREENGRID_Y / (float)OPTICALFLOW_DEPTH_HEIGHT, 0.0f, (float)OPTICALFLOW_FULLSCREENGRID_Y);
|
|
|
|
float xsum = 0.0f;
|
|
float ysum = 0.0f;
|
|
float sum = 0.0f;
|
|
|
|
for (int y = ymin; y < ymax; ++ y)
|
|
{
|
|
for (int x = xmin; x < xmax; ++ x)
|
|
{
|
|
const OpticalFlowCell &cell = m_opticalFlowGrid[x][y];
|
|
xsum += cell.u;
|
|
ysum += cell.v;
|
|
sum += 1.0f;
|
|
}
|
|
}
|
|
|
|
if (sum > 0.0f)
|
|
return *new vector3(xsum / sum, ysum / sum, 0.0f);
|
|
|
|
return *new vector3(0,0,0);
|
|
}
|
|
|
|
void GestureDetector::setOpticalFlowCloseCellWeights()
|
|
{
|
|
float temp[3][3] = { {1.0f, 2.0f, 1.0f},
|
|
{2.0f, 5.0f, 2.0f},
|
|
{1.0f, 2.0f, 1.0f} };
|
|
|
|
for(int i=0;i<3;i++)
|
|
{
|
|
for(int j=0;j<3;j++)
|
|
{
|
|
opticalFlowCloseCellsWeight[i][j] = temp[i][j];
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Print
|
|
// Desc: Prints useful debug information
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
CHAR* DebugOutput::Print( const ClassifierData::EType type )
|
|
{
|
|
switch( type )
|
|
{
|
|
#ifdef ADD_TYPE_DIFF_POSITION_X
|
|
case ClassifierData::TYPE_DIFF_POSITION_X:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "DiffPositionX" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_DIFF_POSITION_Y
|
|
case ClassifierData::TYPE_DIFF_POSITION_Y:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "DiffPositionY" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_DIFF_POSITION_Z
|
|
case ClassifierData::TYPE_DIFF_POSITION_Z:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "DiffPositionZ" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_ANGLE
|
|
case ClassifierData::TYPE_ANGLE:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "Angles" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_TIME_SPACE_ANGLE
|
|
case ClassifierData::TYPE_TIME_SPACE_ANGLE:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "TimeSpaceAngles" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_POSITION_SPEED
|
|
case ClassifierData::TYPE_POSITION_SPEED:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "Speed" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_POSITION_ACCELERATION
|
|
case ClassifierData::TYPE_POSITION_ACCELERATION:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "Acceleration" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_POSITION_ACCELERATION_X
|
|
case ClassifierData::TYPE_POSITION_ACCELERATION_X:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "AccelerationX" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_POSITION_ACCELERATION_Y
|
|
case ClassifierData::TYPE_POSITION_ACCELERATION_Y:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "AccelerationY" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_POSITION_ACCELERATION_Z
|
|
case ClassifierData::TYPE_POSITION_ACCELERATION_Z:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "AccelerationZ" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_POSITION_SPEED_SQ
|
|
case ClassifierData::TYPE_POSITION_SPEED_SQ:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "Speed^2" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_POSITION_VELOCITY_X
|
|
case ClassifierData::TYPE_POSITION_VELOCITY_X:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "VelocityX" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_POSITION_VELOCITY_Y
|
|
case ClassifierData::TYPE_POSITION_VELOCITY_Y:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "VelocityY" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_POSITION_VELOCITY_Z
|
|
case ClassifierData::TYPE_POSITION_VELOCITY_Z:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "VelocityZ" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_POSITION_VELOCITYSQ_X
|
|
case ClassifierData::TYPE_POSITION_VELOCITYSQ_X:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "VelocityX^2" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_POSITION_VELOCITYSQ_Y
|
|
case ClassifierData::TYPE_POSITION_VELOCITYSQ_Y:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "VelocityY^2" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_POSITION_VELOCITYSQ_Z
|
|
case ClassifierData::TYPE_POSITION_VELOCITYSQ_Z:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "VelocityZ^2" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_ANGLE_VELOCITY
|
|
case ClassifierData::TYPE_ANGLE_VELOCITY:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "AngleVelocity" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_ANGLE_ACCELERATION
|
|
case ClassifierData::TYPE_ANGLE_ACCELERATION:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "AngleAcceleration" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_MUSCLE_POWER
|
|
case ClassifierData::TYPE_MUSCLE_POWER:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "MusclePower" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_MUSCLE_FORCES
|
|
case ClassifierData::TYPE_MUSCLE_FORCE_X:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "MuscleForceX" );
|
|
break;
|
|
|
|
case ClassifierData::TYPE_MUSCLE_FORCE_Y:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "MuscleForceY" );
|
|
break;
|
|
|
|
case ClassifierData::TYPE_MUSCLE_FORCE_Z:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "MuscleForceZ" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_MUSCLE_TORQUES
|
|
case ClassifierData::TYPE_MUSCLE_TORQUE_X:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "MuscleTorqueX" );
|
|
break;
|
|
|
|
case ClassifierData::TYPE_MUSCLE_TORQUE_Y:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "MuscleTorqueY" );
|
|
break;
|
|
|
|
case ClassifierData::TYPE_MUSCLE_TORQUE_Z:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "MuscleTorqueZ" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_DIFF_MUSCLE_FORCE_X
|
|
case ClassifierData::TYPE_DIFF_MUSCLE_FORCE_X:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "DiffMuscleForceX" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_DIFF_MUSCLE_FORCE_Y
|
|
case ClassifierData::TYPE_DIFF_MUSCLE_FORCE_Y:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "DiffMuscleForceY" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_DIFF_MUSCLE_FORCE_Z
|
|
case ClassifierData::TYPE_DIFF_MUSCLE_FORCE_Z:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "DiffMuscleForceZ" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_BONE_LENGTH_CHANGES
|
|
case ClassifierData::TYPE_BONE_LENGTH_CHANGES:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "BoneLengthChanges" );
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_OPTICAL_FLOW
|
|
case ClassifierData::TYPE_OPTICAL_FLOW_X:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "OpticalFlowX" );
|
|
break;
|
|
case ClassifierData::TYPE_OPTICAL_FLOW_Y:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "OpticalFlowY" );
|
|
break;
|
|
case ClassifierData::TYPE_OPTICAL_FLOW_LENGTH_SQ:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "OpticalFlowLengthSQ" );
|
|
break;
|
|
case ClassifierData::TYPE_OPTICAL_FLOW_TANGENT:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "OpticalFlowTangent" );
|
|
break;
|
|
case ClassifierData::TYPE_OPTICAL_FLOW_X_DIFF:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "OpticalFlowXDiff" );
|
|
break;
|
|
case ClassifierData::TYPE_OPTICAL_FLOW_Y_DIFF:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "OpticalFlowYDiff" );
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "" );
|
|
}
|
|
return m_szBuffer;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Print
|
|
// Desc: Prints useful debug information
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
CHAR* DebugOutput::Print( const GESTURE_JOINT_INDEX joint )
|
|
{
|
|
switch ( joint )
|
|
{
|
|
case GESTURE_JOINT_SPINE_BASE:
|
|
#if defined( DURANGO ) || defined( TARGET_DURANGO )
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "SpineBase" );
|
|
#else
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "HipCenter" );
|
|
#endif
|
|
break;
|
|
|
|
case GESTURE_JOINT_SPINE_MID:
|
|
#if defined( DURANGO ) || defined( TARGET_DURANGO )
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "SpineMid" );
|
|
#else
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "Spine" );
|
|
#endif
|
|
break;
|
|
|
|
case GESTURE_JOINT_SPINE_SHOULDER:
|
|
#if defined( DURANGO ) || defined( TARGET_DURANGO )
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "SpineShoulder" );
|
|
#else
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "ShoulderCenter" );
|
|
#endif
|
|
break;
|
|
|
|
#if defined( DURANGO ) || defined( TARGET_DURANGO )
|
|
case GESTURE_JOINT_NECK:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "Neck" );
|
|
break;
|
|
#endif
|
|
|
|
case GESTURE_JOINT_HEAD:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "Head" );
|
|
break;
|
|
|
|
case GESTURE_JOINT_SHOULDER_LEFT:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "ShoulderLeft" );
|
|
break;
|
|
|
|
case GESTURE_JOINT_ELBOW_LEFT:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "ElbowLeft" );
|
|
break;
|
|
|
|
case GESTURE_JOINT_WRIST_LEFT:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "WristLeft" );
|
|
break;
|
|
|
|
case GESTURE_JOINT_HAND_LEFT:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "HandLeft" );
|
|
break;
|
|
|
|
case GESTURE_JOINT_SHOULDER_RIGHT:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "ShoulderRight" );
|
|
break;
|
|
|
|
case GESTURE_JOINT_ELBOW_RIGHT:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "ElbowRight" );
|
|
break;
|
|
|
|
case GESTURE_JOINT_WRIST_RIGHT:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "WristRight" );
|
|
break;
|
|
|
|
case GESTURE_JOINT_HAND_RIGHT:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "HandRight" );
|
|
break;
|
|
|
|
case GESTURE_JOINT_HIP_LEFT:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "HipLeft" );
|
|
break;
|
|
|
|
case GESTURE_JOINT_KNEE_LEFT:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "KneeLeft" );
|
|
break;
|
|
|
|
case GESTURE_JOINT_ANKLE_LEFT:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "AnkleLeft" );
|
|
break;
|
|
|
|
case GESTURE_JOINT_FOOT_LEFT:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "FootLeft" );
|
|
break;
|
|
|
|
case GESTURE_JOINT_HIP_RIGHT:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "HipRight" );
|
|
break;
|
|
|
|
case GESTURE_JOINT_KNEE_RIGHT:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "KneeRight" );
|
|
break;
|
|
|
|
case GESTURE_JOINT_ANKLE_RIGHT:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "AnkleRight" );
|
|
break;
|
|
|
|
case GESTURE_JOINT_FOOT_RIGHT:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "FootRight" );
|
|
break;
|
|
|
|
#if defined( DURANGO ) || defined( TARGET_DURANGO )
|
|
case GESTURE_JOINT_THUMB_LEFT:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "ThumbLeft" );
|
|
break;
|
|
|
|
case GESTURE_JOINT_THUMB_RIGHT:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "ThumbRight" );
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
// sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "" );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%d", (int)joint );
|
|
}
|
|
|
|
return m_szBuffer;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Print
|
|
// Desc: Prints useful debug information
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
CHAR* DebugOutput::Print( const ClassifierData* pClassifierData )
|
|
{
|
|
assert( pClassifierData );
|
|
|
|
CHAR szType[ MAX_PATH ];
|
|
CHAR szJoint0[ MAX_PATH ];
|
|
CHAR szJoint1[ MAX_PATH ];
|
|
CHAR szJoint2[ MAX_PATH ];
|
|
|
|
switch( pClassifierData->m_Type )
|
|
{
|
|
#ifdef ADD_TYPE_DIFF_POSITION_X
|
|
case ClassifierData::TYPE_DIFF_POSITION_X:
|
|
{
|
|
ClassifierDataUsingDiffPositionX* pData = (ClassifierDataUsingDiffPositionX*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndices[ 0 ] ) );
|
|
strcpy_s( szJoint1, sizeof( szType ), Print( pData->m_jointIndices[ 1 ] ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s, %s ) %s inferred joints", szType, szJoint0, szJoint1, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_DIFF_POSITION_Y
|
|
case ClassifierData::TYPE_DIFF_POSITION_Y:
|
|
{
|
|
ClassifierDataUsingDiffPositionY* pData = (ClassifierDataUsingDiffPositionY*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndices[ 0 ] ) );
|
|
strcpy_s( szJoint1, sizeof( szType ), Print( pData->m_jointIndices[ 1 ] ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s, %s ) %s inferred joints", szType, szJoint0, szJoint1, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_DIFF_POSITION_Z
|
|
case ClassifierData::TYPE_DIFF_POSITION_Z:
|
|
{
|
|
ClassifierDataUsingDiffPositionZ* pData = (ClassifierDataUsingDiffPositionZ*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndices[ 0 ] ) );
|
|
strcpy_s( szJoint1, sizeof( szType ), Print( pData->m_jointIndices[ 1 ] ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s, %s ) %s inferred joints", szType, szJoint0, szJoint1, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_ANGLE
|
|
case ClassifierData::TYPE_ANGLE:
|
|
{
|
|
ClassifierDataUsingAngles* pData = (ClassifierDataUsingAngles*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndices[ 0 ] ) );
|
|
strcpy_s( szJoint1, sizeof( szType ), Print( pData->m_jointIndices[ 1 ] ) );
|
|
strcpy_s( szJoint2, sizeof( szType ), Print( pData->m_jointIndices[ 2 ] ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s, %s, %s ) %s inferred joints", szType, szJoint0, szJoint1, szJoint2, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_TIME_SPACE_ANGLE
|
|
case ClassifierData::TYPE_TIME_SPACE_ANGLE:
|
|
{
|
|
ClassifierDataUsingTimeSpaceAngles* pData = (ClassifierDataUsingTimeSpaceAngles*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndex ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s ) %s inferred joints", szType, szJoint0, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_POSITION_SPEED
|
|
case ClassifierData::TYPE_POSITION_SPEED:
|
|
{
|
|
ClassifierDataUsingPositionSpeed* pData = (ClassifierDataUsingPositionSpeed*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndex ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s ) %s inferred joints", szType, szJoint0, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_POSITION_ACCELERATION
|
|
case ClassifierData::TYPE_POSITION_ACCELERATION:
|
|
{
|
|
ClassifierDataUsingPositionAcceleration* pData = (ClassifierDataUsingPositionAcceleration*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndex ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s ) %s inferred joints", szType, szJoint0, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_POSITION_ACCELERATION_X
|
|
case ClassifierData::TYPE_POSITION_ACCELERATION_X:
|
|
{
|
|
ClassifierDataUsingPositionAccelerationX* pData = (ClassifierDataUsingPositionAccelerationX*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndex ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s ) %s inferred joints", szType, szJoint0, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_POSITION_ACCELERATION_Y
|
|
case ClassifierData::TYPE_POSITION_ACCELERATION_Y:
|
|
{
|
|
ClassifierDataUsingPositionAccelerationY* pData = (ClassifierDataUsingPositionAccelerationY*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndex ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s ) %s inferred joints", szType, szJoint0, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_POSITION_ACCELERATION_Z
|
|
case ClassifierData::TYPE_POSITION_ACCELERATION_Z:
|
|
{
|
|
ClassifierDataUsingPositionAccelerationZ* pData = (ClassifierDataUsingPositionAccelerationZ*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndex ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s ) %s inferred joints", szType, szJoint0, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_POSITION_SPEED_SQ
|
|
case ClassifierData::TYPE_POSITION_SPEED_SQ:
|
|
{
|
|
ClassifierDataUsingPositionSpeedSQ* pData = (ClassifierDataUsingPositionSpeedSQ*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndex ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s ) %s inferred joints", szType, szJoint0, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_POSITION_VELOCITY_X
|
|
case ClassifierData::TYPE_POSITION_VELOCITY_X:
|
|
{
|
|
ClassifierDataUsingPositionVelocityX* pData = (ClassifierDataUsingPositionVelocityX*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndex ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s ) %s inferred joints", szType, szJoint0, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_POSITION_VELOCITY_Y
|
|
case ClassifierData::TYPE_POSITION_VELOCITY_Y:
|
|
{
|
|
ClassifierDataUsingPositionVelocityY* pData = (ClassifierDataUsingPositionVelocityY*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndex ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s ) %s inferred joints", szType, szJoint0, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_POSITION_VELOCITY_Z
|
|
case ClassifierData::TYPE_POSITION_VELOCITY_Z:
|
|
{
|
|
ClassifierDataUsingPositionVelocityZ* pData = (ClassifierDataUsingPositionVelocityZ*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndex ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s ) %s inferred joints", szType, szJoint0, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_POSITION_VELOCITYSQ_X
|
|
case ClassifierData::TYPE_POSITION_VELOCITYSQ_X:
|
|
{
|
|
ClassifierDataUsingPositionVelocitySQX* pData = (ClassifierDataUsingPositionVelocitySQX*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndex ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s ) %s inferred joints", szType, szJoint0, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_POSITION_VELOCITYSQ_Y
|
|
case ClassifierData::TYPE_POSITION_VELOCITYSQ_Y:
|
|
{
|
|
ClassifierDataUsingPositionVelocitySQY* pData = (ClassifierDataUsingPositionVelocitySQY*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndex ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s ) %s inferred joints", szType, szJoint0, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_POSITION_VELOCITYSQ_Z
|
|
case ClassifierData::TYPE_POSITION_VELOCITYSQ_Z:
|
|
{
|
|
ClassifierDataUsingPositionVelocityZ* pData = (ClassifierDataUsingPositionVelocityZ*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndex ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s ) %s inferred joints", szType, szJoint0, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_ANGLE_VELOCITY
|
|
case ClassifierData::TYPE_ANGLE_VELOCITY:
|
|
{
|
|
ClassifierDataUsingAngleVelocities* pData = (ClassifierDataUsingAngleVelocities*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndices[ 0 ] ) );
|
|
strcpy_s( szJoint1, sizeof( szType ), Print( pData->m_jointIndices[ 1 ] ) );
|
|
strcpy_s( szJoint2, sizeof( szType ), Print( pData->m_jointIndices[ 2 ] ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s, %s, %s ) %s inferred joints", szType, szJoint0, szJoint1, szJoint2, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_ANGLE_ACCELERATION
|
|
case ClassifierData::TYPE_ANGLE_ACCELERATION:
|
|
{
|
|
ClassifierDataUsingAngleAcceleration* pData = (ClassifierDataUsingAngleAcceleration*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndices[ 0 ] ) );
|
|
strcpy_s( szJoint1, sizeof( szType ), Print( pData->m_jointIndices[ 1 ] ) );
|
|
strcpy_s( szJoint2, sizeof( szType ), Print( pData->m_jointIndices[ 2 ] ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s, %s, %s ) %s inferred joints", szType, szJoint0, szJoint1, szJoint2, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_MUSCLE_POWER
|
|
case ClassifierData::TYPE_MUSCLE_POWER:
|
|
{
|
|
ClassifierDataUsingMusclePower* pData = (ClassifierDataUsingMusclePower*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndex ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s ) %s inferred joints", szType, szJoint0, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_MUSCLE_FORCES
|
|
case ClassifierData::TYPE_MUSCLE_FORCE_X:
|
|
{
|
|
ClassifierDataUsingMuscleForceX* pData = (ClassifierDataUsingMuscleForceX*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndex ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s ) %s inferred joints", szType, szJoint0, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
|
|
case ClassifierData::TYPE_MUSCLE_FORCE_Y:
|
|
{
|
|
ClassifierDataUsingMuscleForceY* pData = (ClassifierDataUsingMuscleForceY*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndex ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s ) %s inferred joints", szType, szJoint0, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
|
|
case ClassifierData::TYPE_MUSCLE_FORCE_Z:
|
|
{
|
|
ClassifierDataUsingMuscleForceZ* pData = (ClassifierDataUsingMuscleForceZ*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndex ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s ) %s inferred joints", szType, szJoint0, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_MUSCLE_TORQUES
|
|
case ClassifierData::TYPE_MUSCLE_TORQUE_X:
|
|
{
|
|
ClassifierDataUsingMuscleTorqueX* pData = (ClassifierDataUsingMuscleTorqueX*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndex ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s ) %s inferred joints", szType, szJoint0, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
|
|
case ClassifierData::TYPE_MUSCLE_TORQUE_Y:
|
|
{
|
|
ClassifierDataUsingMuscleTorqueY* pData = (ClassifierDataUsingMuscleTorqueY*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndex ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s ) %s inferred joints", szType, szJoint0, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
|
|
case ClassifierData::TYPE_MUSCLE_TORQUE_Z:
|
|
{
|
|
ClassifierDataUsingMuscleTorqueZ* pData = (ClassifierDataUsingMuscleTorqueZ*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndex ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s ) %s inferred joints", szType, szJoint0, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_DIFF_MUSCLE_FORCE_X
|
|
case ClassifierData::TYPE_DIFF_MUSCLE_FORCE_X:
|
|
{
|
|
ClassifierDataUsingDiffMuscleForceX* pData = (ClassifierDataUsingDiffMuscleForceX*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndices[ 0 ] ) );
|
|
strcpy_s( szJoint1, sizeof( szType ), Print( pData->m_jointIndices[ 1 ] ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s, %s ) %s inferred joints", szType, szJoint0, szJoint1, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_DIFF_MUSCLE_FORCE_Y
|
|
case ClassifierData::TYPE_DIFF_MUSCLE_FORCE_Y:
|
|
{
|
|
ClassifierDataUsingDiffMuscleForceY* pData = (ClassifierDataUsingDiffMuscleForceY*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndices[ 0 ] ) );
|
|
strcpy_s( szJoint1, sizeof( szType ), Print( pData->m_jointIndices[ 1 ] ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s, %s ) %s inferred joints", szType, szJoint0, szJoint1, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_DIFF_MUSCLE_FORCE_Z
|
|
case ClassifierData::TYPE_DIFF_MUSCLE_FORCE_Z:
|
|
{
|
|
ClassifierDataUsingDiffMuscleForceZ* pData = (ClassifierDataUsingDiffMuscleForceZ*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndices[ 0 ] ) );
|
|
strcpy_s( szJoint1, sizeof( szType ), Print( pData->m_jointIndices[ 1 ] ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s, %s ) %s inferred joints", szType, szJoint0, szJoint1, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_BONE_LENGTH_CHANGES
|
|
case ClassifierData::TYPE_BONE_LENGTH_CHANGES:
|
|
{
|
|
ClassifierDataUsingBoneLengthChanges* pData = (ClassifierDataUsingBoneLengthChanges*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
strcpy_s( szJoint0, sizeof( szType ), Print( pData->m_jointIndices[ 0 ] ) );
|
|
strcpy_s( szJoint1, sizeof( szType ), Print( pData->m_jointIndices[ 1 ] ) );
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %s, %s ) %s inferred joints", szType, szJoint0, szJoint1, pData->m_bRejectInferred ? "rejecting" : "using" );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef ADD_TYPE_OPTICAL_FLOW
|
|
case ClassifierData::TYPE_OPTICAL_FLOW_X:
|
|
case ClassifierData::TYPE_OPTICAL_FLOW_Y:
|
|
case ClassifierData::TYPE_OPTICAL_FLOW_LENGTH_SQ:
|
|
case ClassifierData::TYPE_OPTICAL_FLOW_TANGENT:
|
|
{
|
|
ClassifierDataBaseClassForOneJoint* pData = (ClassifierDataBaseClassForOneJoint*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
int i = (pData->m_jointIndex - GESTURE_JOINT_COUNT) % 3;
|
|
int j = (pData->m_jointIndex - GESTURE_JOINT_COUNT) / 3;
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %d, %d )", szType, i, j );
|
|
}
|
|
break;
|
|
case ClassifierData::TYPE_OPTICAL_FLOW_X_DIFF:
|
|
case ClassifierData::TYPE_OPTICAL_FLOW_Y_DIFF:
|
|
{
|
|
ClassifierDataBaseClassForTwoJoints* pData = (ClassifierDataBaseClassForTwoJoints*)pClassifierData;
|
|
strcpy_s( szType, sizeof( szType ), Print( pData->m_Type ) );
|
|
int a = (pData->m_jointIndices[ 0 ] - GESTURE_JOINT_COUNT);
|
|
int b = (pData->m_jointIndices[ 1 ] - GESTURE_JOINT_COUNT);
|
|
int i0 = a % 3;
|
|
int j0 = a / 3;
|
|
int i1 = b % 3;
|
|
int j1 = b / 3;
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s( %d, %d - %d, %d )", szType, i0, j0, i1, j1 );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "" );
|
|
}
|
|
|
|
return m_szBuffer;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Print
|
|
// Desc: Prints useful debug information
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
CHAR* DebugOutput::Print( const WeakClassifier* pWeakClassifier, const ClassifierData* pData )
|
|
{
|
|
assert( pWeakClassifier );
|
|
assert( pData );
|
|
|
|
CHAR szBuffer[ MAX_PATH ];
|
|
|
|
#if defined(_XBOX) || defined(ITF_X360) ||\
|
|
defined(DURANGO) || defined(ITF_DURANGO) ||\
|
|
defined(__ORBIS__) || defined(ITF_ORBIS)||\
|
|
defined(WIN32) || defined(ITF_WIN32)
|
|
BOOL bReverse = ( pWeakClassifier->m_fAlpha < 0.0f );
|
|
#else
|
|
BOOL bReverse = FALSE;
|
|
if ( pWeakClassifier->m_iReverse < 0 ||
|
|
pWeakClassifier->m_fAlpha < 0.0f )
|
|
{
|
|
bReverse = TRUE;
|
|
}
|
|
#endif
|
|
sprintf_s( szBuffer, sizeof( szBuffer ), "%s, %s %f, alpha = %f"
|
|
// , Print( pWeakClassifier->m_pData ),
|
|
, Print( pData ),
|
|
bReverse ? "fValue <" : "fValue >=", pWeakClassifier->m_fThreshold, fabs( pWeakClassifier->m_fAlpha ) );
|
|
|
|
sprintf_s( m_szBuffer, sizeof( m_szBuffer ), "%s", szBuffer );
|
|
|
|
return m_szBuffer;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: Print
|
|
// Desc: Prints useful debug information
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
CHAR* DebugOutput::Print( GestureDetector* pGestureDetector, const UINT uWeakClassiferIndex )
|
|
{
|
|
assert( pGestureDetector );
|
|
|
|
if ( uWeakClassiferIndex < pGestureDetector->m_StrongClassifier.GetNumWeakClassifiers() )
|
|
{
|
|
WeakClassifier* pWeakClassifier = pGestureDetector->GetWeakClassifierAt( uWeakClassiferIndex );
|
|
ClassifierData* pData = pGestureDetector->m_ClassifierData[ pWeakClassifier->GetDataIndex() ];
|
|
Print( pWeakClassifier, pData );
|
|
}
|
|
else
|
|
{
|
|
m_szBuffer[ 0 ] = '\0';
|
|
}
|
|
|
|
return m_szBuffer;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: mopen
|
|
// Desc: Opens memory pointer for reading from memory
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
static size_t g_uOffset = 0;
|
|
|
|
HRESULT mopen( VOID* pSource )
|
|
{
|
|
g_uOffset = 0;
|
|
return pSource ? S_OK : E_FAIL;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: mread
|
|
// Desc: Reads from memory
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
size_t mread( VOID* pDest, size_t elementSize, size_t count, VOID* pSource )
|
|
{
|
|
size_t uOffset = (size_t)(elementSize * count);
|
|
memcpy( pDest, (VOID*)((size_t)pSource + g_uOffset), uOffset );
|
|
g_uOffset += uOffset;
|
|
return g_uOffset;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: mwrite
|
|
// Desc: Writes to memory
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
size_t mwrite( VOID* pSrc, size_t elementSize, size_t count, VOID* pSource )
|
|
{
|
|
size_t uOffset = (size_t)(elementSize * count);
|
|
memcpy( (VOID*)((size_t)pSource + g_uOffset), pSrc, uOffset );
|
|
g_uOffset += uOffset;
|
|
return g_uOffset;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: mclose
|
|
// Desc: Close memory reads
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
VOID mclose()
|
|
{
|
|
g_uOffset = 0;
|
|
}
|
|
|
|
}
|