JD2022-TU1/main/extern/KinectGesture/Src/GestureDetector.cpp

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;
}
}