//-------------------------------------------------------------------------------------- // 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 #include #include #ifdef GESTURE_EVALUATOR #ifdef TARGET_PS4 using namespace PS4Depth; using namespace PS4OpticalFlow; #elif TARGET_DURANGO #include #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 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(!(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, 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, 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, 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; iGetData()->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& 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 vIndex; vector 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) 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 vIndex; vector 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) 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; } }