562 lines
26 KiB
C++
562 lines
26 KiB
C++
//--------------------------------------------------------------------------------------
|
|
// GestureDetector.h
|
|
//
|
|
// 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 alogorith.
|
|
//
|
|
// Advanced Technology Group (ATG)
|
|
// Copyright (C) Microsoft Corporation. All rights reserved.
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
#pragma once
|
|
|
|
#if defined( GESTURE_TRAINER )
|
|
|
|
#ifdef GESTURE_EVALUATOR
|
|
#include "MacrosAndTypes.h"
|
|
#include "OpticalFlowTracker.h"
|
|
#endif
|
|
|
|
#if defined( TARGET_X360 )
|
|
|
|
#include <windows.h>
|
|
|
|
#elif defined( TARGET_DURANGO )
|
|
|
|
#include <windows.h>
|
|
|
|
#elif defined( TARGET_ORBIS )
|
|
|
|
#include "SK_iisu/PS4/include/HumanTrackingCTypes.h"
|
|
#include "Kinect/PS4/commontypes.h"
|
|
|
|
#else
|
|
#error unsuported platform
|
|
#endif
|
|
|
|
#else // defined( GESTURE_TRAINER )
|
|
|
|
#if defined( _XBOX ) || defined( ITF_X360 )
|
|
|
|
#include <xtl.h>
|
|
|
|
#elif defined( DURANGO ) || defined( ITF_DURANGO )
|
|
|
|
#include <windows.h>
|
|
|
|
#elif defined( __ORBIS__ ) || defined( ITF_ORBIS )
|
|
|
|
#include "SK_iisu/PS4/include/HumanTrackingCTypes.h"
|
|
#include "Kinect/PS4/commontypes.h"
|
|
|
|
#elif defined ( WIN32 ) ||defined( ITF_WIN32)
|
|
|
|
#include <windows.h>
|
|
|
|
#else
|
|
#error unsuported platform
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#include <vector>
|
|
#include <deque>
|
|
#include <stdio.h>
|
|
|
|
#include "Common.h"
|
|
#include "ClassifierData.h"
|
|
#include "NuiTypes.h"
|
|
|
|
#ifdef WIN64
|
|
#undef WIN32
|
|
#endif
|
|
|
|
namespace KinectGesture
|
|
{
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Defines and constants
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
// For optimization reasons we use _fsel() on XBox, which uses double's, but on PC we
|
|
// want to make sure there are no floating point errors when comparing 1 and -1, so we
|
|
// simply use int
|
|
#if defined(_XBOX)
|
|
static const DOUBLE g_fClassificationLabelCorrect = 1.0f;
|
|
static const DOUBLE g_fClassificationLabelIncorrect = -1.0f;
|
|
#else
|
|
static const INT8 g_iClassificationLabelCorrect = 1;
|
|
static const INT8 g_iClassificationLabelIncorrect = -1;
|
|
#endif
|
|
|
|
// Version history
|
|
//static const FLOAT g_fCurrentVersion = 1.0f; // First version
|
|
//static const FLOAT g_fCurrentVersion = 1.1f; // Added LoadFromMemory() method, and alloc skeleton data as 16 byte aligned
|
|
//static const FLOAT g_fCurrentVersion = 1.2f; // Minor bug fixes, adding namespace and improvements to automatically finding filtering parameters
|
|
static const FLOAT g_fVersion_1_3 = 1.3f; // Added statistics in strong classifier
|
|
static const FLOAT g_fCurrentVersion = 1.4f; // Added energy statistics in strong classifier
|
|
|
|
static const FLOAT g_fVersionEpsilon = 0.01f; // I have no idea why we're using floats for version numbers.
|
|
|
|
const CHAR** getGestureFileIDs();
|
|
const CHAR* getLabeledExampleFileID();
|
|
const int getLabeledExampleFileIDLen();
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// External functions
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
extern void Assert( bool expression, const char* message );
|
|
extern void* AllocateAligned( unsigned int size, unsigned int alignment );
|
|
extern void FreeAligned( void* memory );
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: DecisionStump
|
|
// Desc: A simple decision stump from which weak classifiers are derived
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
class DecisionStump
|
|
{
|
|
public:
|
|
DecisionStump();
|
|
inline VOID SetThreshold( const FLOAT fThreshold ) { m_fThreshold = fThreshold; }
|
|
inline FLOAT GetThreshold() const { return m_fThreshold; }
|
|
|
|
#if !defined(_XBOX) && !defined(ITF_X360) &&\
|
|
!defined(DURANGO) && !defined(ITF_DURANGO) &&\
|
|
!defined(__ORBIS__) && !defined(ITF_ORBIS)&&\
|
|
!defined ( WIN32 ) && !defined( ITF_WIN32)
|
|
inline VOID SetIsReversed( const BOOL bReverse ) { m_iReverse = bReverse ? -1 : 1; }
|
|
inline BOOL GetIsReversed() const { return ( m_iReverse == -1 ); }
|
|
inline INT8 GetReverseValue() const { return m_iReverse; }
|
|
#endif
|
|
|
|
// For optimization reason, we return a float value which can be multiplied by the floating point weight value. If we
|
|
// used integer as on PC, we introduce a LHS to for the integeter to float conversion. Also, on Xbox we bake m_iReverse
|
|
// into m_fAlpha, and we can use __fsel intrinsic
|
|
#if defined(_XBOX) || defined(ITF_X360)
|
|
__forceinline FLOAT Classify( const FLOAT fValue ) const
|
|
{
|
|
return (FLOAT)__fsel( fValue - m_fThreshold, g_fClassificationLabelCorrect, g_fClassificationLabelIncorrect );
|
|
}
|
|
#elif defined(DURANGO) || defined(ITF_DURANGO) || defined(__ORBIS__) || defined(ITF_ORBIS)||defined ( WIN32 ) ||defined( ITF_WIN32)
|
|
// on durango & orbis we still bake in m_iReverse
|
|
__forceinline INT8 Classify( const FLOAT fValue ) const
|
|
{
|
|
return ( ( ( fValue - m_fThreshold ) >= 0.0f ) ? g_iClassificationLabelCorrect : g_iClassificationLabelIncorrect );
|
|
}
|
|
#else
|
|
__forceinline INT8 Classify( const FLOAT fValue ) const
|
|
{
|
|
return ( ( ( fValue - m_fThreshold ) >= 0.0f ) ? g_iClassificationLabelCorrect : g_iClassificationLabelIncorrect ) * m_iReverse;
|
|
}
|
|
#endif
|
|
|
|
HRESULT Read( FILE* pFile );
|
|
HRESULT Read( VOID* pBuffer );
|
|
HRESULT Write( FILE* pFile );
|
|
|
|
protected:
|
|
FLOAT m_fThreshold; // The classifier threshold value
|
|
|
|
#if !defined(_XBOX) && !defined(ITF_X360) &&\
|
|
!defined(DURANGO) && !defined(ITF_DURANGO) &&\
|
|
!defined(__ORBIS__) && !defined(ITF_ORBIS)&&\
|
|
!defined ( WIN32 ) && !defined( ITF_WIN32)
|
|
INT8 m_iReverse; // To reverse the classification we can multiply the result by -1
|
|
#endif
|
|
};
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: WeakClassifier
|
|
// Desc: A weak classifier implemented as h(x), with x as example input data, and
|
|
// h(x) returning either +1 or -1
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
class ClassifierData;
|
|
class WeakClassifier : public DecisionStump
|
|
{
|
|
public:
|
|
WeakClassifier();
|
|
|
|
HRESULT Read( FILE* pFile, UINT* pDataID );
|
|
HRESULT Read( VOID* pBuffer, UINT* pDataID );
|
|
HRESULT Write( FILE* pFile );
|
|
|
|
inline VOID SetAlpha( const FLOAT fAlpha ) { m_fAlpha = fAlpha; }
|
|
inline FLOAT GetAlpha() const { return m_fAlpha; }
|
|
|
|
// inline VOID SetData( ClassifierData* pData ) { m_pData = pData; }
|
|
// inline ClassifierData* GetData() const { return m_pData; }
|
|
inline VOID SetDataIndex( UINT index ) { m_uDataIndex = index; }
|
|
inline UINT GetDataIndex() const { return m_uDataIndex; }
|
|
|
|
// Sort based on fAlpha, the confidence of the classifier
|
|
inline bool operator < (const WeakClassifier& weakClassifier ) { return ( m_fAlpha < weakClassifier.m_fAlpha ); }
|
|
|
|
WeakClassifier& operator = ( const WeakClassifier& source )
|
|
{
|
|
SetThreshold( source.GetThreshold() );
|
|
|
|
#if !defined(_XBOX) && !defined(ITF_X360) &&\
|
|
!defined(DURANGO) && !defined(ITF_DURANGO) &&\
|
|
!defined(__ORBIS__) && !defined(ITF_ORBIS)&&\
|
|
!defined ( WIN32 ) &&!defined( ITF_WIN32)
|
|
SetIsReversed( source.GetIsReversed() );
|
|
#endif
|
|
|
|
SetAlpha( source.GetAlpha() );
|
|
// SetData( source.GetData() );
|
|
SetDataIndex( source.GetDataIndex() );
|
|
return *this;
|
|
}
|
|
|
|
protected:
|
|
FLOAT m_fAlpha; // Confidence of weak classifier
|
|
// ClassifierData* m_pData; // Pointer to value that will be compared to threshold (why not directly data index?)
|
|
UINT m_uDataIndex;
|
|
|
|
friend class DebugOutput;
|
|
};
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: StrongClassifier
|
|
// Desc: A strong classifier implemented as H(x) as the weighted sum of the weak
|
|
// classifiers h(x)
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
class StrongClassifier
|
|
{
|
|
public:
|
|
enum Label
|
|
{
|
|
eLabel_Correct = 0,
|
|
eLabel_Incorrect = 1,
|
|
|
|
eLabel_Count,
|
|
};
|
|
|
|
struct Results
|
|
{
|
|
#ifdef GESTURE_EVALUATOR
|
|
public:
|
|
#endif
|
|
FLOAT m_fConfidence; // The confidence calculated from the normalized weighted sum
|
|
BOOL m_bDetected; // Detected a gesture
|
|
BOOL m_bFirstFrameDetected; // Detection can occur over several frames. If it's the first frame, this value is true
|
|
};
|
|
|
|
public:
|
|
StrongClassifier();
|
|
~StrongClassifier();
|
|
|
|
HRESULT Initialize( const UINT nNumWeakClassifiers );
|
|
|
|
inline VOID Reset( const UINT uPlayerIdx ) { m_fClassificationHistory[ uPlayerIdx ].clear(); }
|
|
|
|
HRESULT Read( FILE* pFile, BOOL bUsesEnergyStatistics );
|
|
HRESULT Read( VOID* pBuffer, BOOL bUsesEnergyStatistics );
|
|
HRESULT Write( FILE* pFile );
|
|
|
|
HRESULT Read( FILE* pFile, const UINT uWeakClassifierIndex, UINT* pDataID );
|
|
HRESULT Read( VOID* pBuffer, const UINT uWeakClassifierIndex, UINT* pDataID );
|
|
HRESULT Write( FILE* pFile, const UINT uWeakClassifierIndex );
|
|
|
|
#if defined(_XBOX) || defined(ITF_X360) ||\
|
|
defined(DURANGO) || defined(ITF_DURANGO) ||\
|
|
defined(__ORBIS__) || defined(ITF_ORBIS)||\
|
|
defined ( WIN32 ) ||defined( ITF_WIN32)
|
|
inline UINT GetNumWeakClassifiers() const { return m_nNumWeakClassifiers; }
|
|
BOOL Detect( const UINT uPlayerIdx, ClassifierData** __restrict classifierData, Results* pResults, const BOOL bFilterResults );
|
|
#else
|
|
inline UINT GetNumWeakClassifiers() const { return (UINT)m_WeakClassifiers.size(); }
|
|
BOOL Detect( const UINT uPlayerIdx, const std::vector<ClassifierData*>& classifierData, Results* pResults, const BOOL bFilterResults );
|
|
inline VOID Add( const WeakClassifier weakClassifier ) { m_WeakClassifiers.push_back( weakClassifier ); }
|
|
#endif
|
|
|
|
VOID FilterDetectionResults( const UINT uPlayerIdx, const FLOAT fConfidence, Results* pResults );
|
|
|
|
#if defined(_XBOX) || defined(ITF_X360) ||\
|
|
defined(DURANGO) || defined(ITF_DURANGO) ||\
|
|
defined(__ORBIS__) || defined(ITF_ORBIS)||\
|
|
defined ( WIN32 ) ||defined( ITF_WIN32)
|
|
VOID GetBoneWeights( ClassifierData** __restrict classifierData, FLOAT *weights, UINT numWeights );
|
|
VOID CalculateUsedRanges( ClassifierData** __restrict classifierData, float *fRangeMin, float *fRangeMax, float *fSumAlpha );
|
|
#else
|
|
VOID GetBoneWeights( const std::vector<ClassifierData*>& classifierData, FLOAT *weights, UINT numWeights );
|
|
VOID CalculateUsedRanges( const std::vector<ClassifierData*>& classifierData, float *fRangeMin, float *fRangeMax, float *fSumAlpha );
|
|
#endif
|
|
|
|
inline WeakClassifier* GetWeakClassifierAt( const UINT uWeakClassifierIndex ) { return &m_WeakClassifiers[ uWeakClassifierIndex ]; }
|
|
inline VOID SetTotalAlpha( const FLOAT fTotalAlpha ) { m_fTotalAlpha = fTotalAlpha; }
|
|
|
|
inline VOID SetDetectionThreshold( const FLOAT fThreshold ) { m_fFilterPerFrameResultsThreshold = fThreshold; }
|
|
inline FLOAT GetDetectionThreshold() const { return m_fFilterPerFrameResultsThreshold; }
|
|
|
|
inline VOID SetNumFramesToFilter( const UINT nNumFramesToFilter ) { m_nFilterPerFrameResultsNumFrames = nNumFramesToFilter; }
|
|
inline UINT GetNumFramesToFilter() const { return m_nFilterPerFrameResultsNumFrames; }
|
|
|
|
inline VOID SetRangeMax( const FLOAT fRangeMax, const Label eLabel ) { m_fRangeMax[eLabel] = fRangeMax; }
|
|
inline FLOAT GetRangeMax( const Label eLabel ) const { return m_fRangeMax[eLabel]; }
|
|
|
|
inline VOID SetRangeMin( const FLOAT fRangeMin, const Label eLabel ) { m_fRangeMin[eLabel] = fRangeMin; }
|
|
inline FLOAT GetRangeMin( const Label eLabel ) const { return m_fRangeMin[eLabel]; }
|
|
|
|
inline VOID SetMean( const FLOAT fMean, const Label eLabel ) { m_fMean[eLabel] = fMean; }
|
|
inline FLOAT GetMean( const Label eLabel ) const { return m_fMean[eLabel]; }
|
|
|
|
inline VOID SetStdDev( const FLOAT fStdDev, const Label eLabel ) { m_fStdDev[eLabel] = fStdDev; }
|
|
inline FLOAT GetStdDev( const Label eLabel ) const { return m_fStdDev[eLabel]; }
|
|
|
|
// Energy-related stuff
|
|
inline VOID SetEnergyMean( const FLOAT fMean ) { m_fEnergyMean = fMean; }
|
|
inline FLOAT GetEnergyMean() const { return m_fEnergyMean; }
|
|
|
|
inline VOID SetEnergyStdDev( const FLOAT fStdDev ) { m_fEnergyStdDev = fStdDev; }
|
|
inline FLOAT GetEnergyStdDev() const { return m_fEnergyStdDev; }
|
|
|
|
#if !defined(_XBOX) && !defined(ITF_X360) &&\
|
|
!defined(DURANGO) && !defined(ITF_DURANGO) &&\
|
|
!defined(__ORBIS__) && !defined(ITF_ORBIS)&&\
|
|
!defined ( WIN32 ) && !defined( ITF_WIN32)
|
|
VOID Optimize( const UINT nMaxNumClassifers, const BOOL bBakeReverseInAlpha = TRUE );
|
|
#endif
|
|
|
|
protected:
|
|
|
|
#if defined(_XBOX) || defined(ITF_X360) ||\
|
|
defined(DURANGO) || defined(ITF_DURANGO) ||\
|
|
defined(__ORBIS__) || defined(ITF_ORBIS)||\
|
|
defined ( WIN32 ) ||defined( ITF_WIN32)
|
|
WeakClassifier* m_WeakClassifiers;
|
|
UINT m_nNumWeakClassifiers;
|
|
#else
|
|
std::vector<WeakClassifier> m_WeakClassifiers;
|
|
#endif
|
|
std::deque<FLOAT> m_fClassificationHistory[ KINECT_GESTURE_MAX_SIMULTANEOUS_GESTURES ];
|
|
FLOAT m_fTotalAlpha; // Sum of all alpha values of all valid weak classifiers
|
|
FLOAT m_fFilterPerFrameResultsThreshold; // Per frame filtering detection threshold
|
|
UINT m_nFilterPerFrameResultsNumFrames; // Size of the sliding window when filtering per frame results
|
|
|
|
// scale values so we can score an accuracy range
|
|
FLOAT m_fRangeMax[eLabel_Count];
|
|
FLOAT m_fRangeMin[eLabel_Count];
|
|
FLOAT m_fMean[eLabel_Count];
|
|
FLOAT m_fStdDev[eLabel_Count];
|
|
|
|
// energy values
|
|
FLOAT m_fEnergyMean;
|
|
FLOAT m_fEnergyStdDev;
|
|
|
|
#if defined(_XBOX) || defined(ITF_X360) ||\
|
|
defined(DURANGO) || defined(ITF_DURANGO) ||\
|
|
defined(__ORBIS__) || defined(ITF_ORBIS)||\
|
|
defined ( WIN32 ) ||defined( ITF_WIN32)
|
|
INT Classify( const UINT uPlayerIdx, ClassifierData** __restrict classifierData, FLOAT* pConfidence );
|
|
#else
|
|
INT8 Classify( const UINT uPlayerIdx, const std::vector<ClassifierData*>& classifierData, FLOAT* pConfidence );
|
|
#endif
|
|
|
|
friend class DebugOutput;
|
|
};
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: GestureDetector
|
|
// Desc: The gesture detector that can be run on the PC or Xbox
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
class GestureDetector
|
|
{
|
|
public:
|
|
typedef StrongClassifier::Results Results;
|
|
VelocityGrid* m_velocityGrid;
|
|
|
|
public:
|
|
GestureDetector();
|
|
~GestureDetector();
|
|
|
|
HRESULT Load( const CHAR* szFileName );
|
|
HRESULT LoadFromMemory( VOID* pBuffer );
|
|
|
|
#ifdef GESTURE_EVALUATOR
|
|
OpticalFlowCell m_opticalFlowGrid[OPTICALFLOW_FULLSCREENGRID_X][OPTICALFLOW_FULLSCREENGRID_Y];
|
|
bool Update( const UINT uPlayerIdx, const GESTURE_SKELETON_TYPE* pSkeletonData, const LARGE_INTEGER& liTimeStampFromNuiFrame);
|
|
static BOOL Update( const UINT uPlayerIdx, const GESTURE_SKELETON_TYPE* pSkeletonData, const float fDeltaTimeInSeconds, BOOL bReset, const VelocityGrid* opticalFlowGrid );
|
|
#endif
|
|
static BOOL Update( const UINT uPlayerIdx, const GESTURE_SKELETON_TYPE* pSkeletonData, const LARGE_INTEGER& liTimeStampFromNuiFrame, const XMVECTOR& vNormalToGravity );
|
|
static BOOL Update( const UINT uPlayerIdx, const GESTURE_SKELETON_TYPE* pSkeletonData, const float fDeltaTimeInSeconds, const XMVECTOR& vNormalToGravity, BOOL bReset, const VelocityGrid* opticalFlowGrid );
|
|
static VOID ResetPlayer( const UINT uPlayerIdx, const GESTURE_SKELETON_TYPE* pSkeletonData, const XMVECTOR& vNormalToGravity );
|
|
BOOL Detect( const UINT uPlayerIdx, Results* pResults, const BOOL bFilterResults );
|
|
|
|
static FLOAT GetEnergyLevel( const UINT uPlayerIdx );
|
|
|
|
VOID GetBoneWeights( FLOAT *weights, UINT numWeights );
|
|
VOID CalculateUsedRanges( float *fRangeMin, float *fRangeMax, float *fSumAlpha );
|
|
|
|
inline VOID SetDetectionThreshold( const FLOAT fThreshold ) { m_StrongClassifier.SetDetectionThreshold( fThreshold ); }
|
|
inline FLOAT GetDetectionThreshold() const { return m_StrongClassifier.GetDetectionThreshold(); }
|
|
inline VOID SetNumFramesToFilter( const UINT nNumFramesToFilter ) { m_StrongClassifier.SetNumFramesToFilter( nNumFramesToFilter ); }
|
|
inline UINT GetNumFramesToFilter() const { return m_StrongClassifier.GetNumFramesToFilter(); }
|
|
inline VOID SetRangeMax( const FLOAT fRangeMax, const StrongClassifier::Label eLabel ) { m_StrongClassifier.SetRangeMax( fRangeMax, eLabel ); }
|
|
inline FLOAT GetRangeMax( const StrongClassifier::Label eLabel ) const { return m_StrongClassifier.GetRangeMax( eLabel ); }
|
|
inline VOID SetRangeMin( const FLOAT fRangeMin, const StrongClassifier::Label eLabel ) { m_StrongClassifier.SetRangeMin( fRangeMin, eLabel ); }
|
|
inline FLOAT GetRangeMin( const StrongClassifier::Label eLabel ) const { return m_StrongClassifier.GetRangeMin( eLabel ); }
|
|
inline VOID SetMean( const FLOAT fMean, const StrongClassifier::Label eLabel ) { m_StrongClassifier.SetMean( fMean, eLabel ); }
|
|
inline FLOAT GetMean( const StrongClassifier::Label eLabel ) const { return m_StrongClassifier.GetMean( eLabel ); }
|
|
inline VOID SetStdDev( const FLOAT fStdDev, const StrongClassifier::Label eLabel ) { m_StrongClassifier.SetStdDev( fStdDev, eLabel ); }
|
|
inline FLOAT GetStdDev( const StrongClassifier::Label eLabel ) const { return m_StrongClassifier.GetStdDev( eLabel ); }
|
|
|
|
// Energy-related stuff
|
|
inline VOID SetEnergyMean( const FLOAT fMean ) { m_StrongClassifier.SetEnergyMean( fMean ); }
|
|
inline FLOAT GetEnergyMean() const { return m_StrongClassifier.GetEnergyMean(); }
|
|
inline VOID SetEnergyStdDev( const FLOAT fStdDev ) { m_StrongClassifier.SetEnergyStdDev( fStdDev ); }
|
|
inline FLOAT GetEnergyStdDev() const { return m_StrongClassifier.GetEnergyStdDev(); }
|
|
|
|
HRESULT Initialize( const UINT nNumWeakClassifiers, UINT nNumClassifierData );
|
|
static VOID FreeClassifierData();
|
|
|
|
#ifdef GESTURE_EVALUATOR
|
|
static float clamp(float val, float min, float max);
|
|
static void GaussianBlurKernel( float* weights, int kernelSize );
|
|
vector2 mapCameraSpaceToDepthSpace(mathLib_vector4 cameraSpaceVector);
|
|
|
|
//Optical Flow Specifics
|
|
f32 opticalFlowCloseCellsWeight[3][3];
|
|
void opticalFlow_getPositionVelocityGrid(mathLib_vector4 pos, VelocityGrid* velocityGrid);
|
|
vector3& opticalFlow_getRegionVelocity(vector3 min, vector3 max);
|
|
void setOpticalFlowCloseCellWeights();
|
|
void opticalflowUpdateDepthMap(unsigned short* _pDepthMapData, int _pitch);
|
|
u16 opticalFlow_getDepthFromDepthMapLine(unsigned short* _line, u16 _x);
|
|
void opticalFlow_update(f32 timeStamp);
|
|
|
|
#endif
|
|
|
|
protected:
|
|
StrongClassifier m_StrongClassifier;
|
|
|
|
static XMVECTOR vUp;
|
|
static XMVECTOR vAverageNormalToGravity[ KINECT_GESTURE_MAX_SIMULTANEOUS_GESTURES ];
|
|
|
|
#if defined(_XBOX) || defined(ITF_X360) ||\
|
|
defined(DURANGO) || defined(ITF_DURANGO) ||\
|
|
defined(__ORBIS__) || defined(ITF_ORBIS)||\
|
|
defined ( WIN32 ) ||defined( ITF_WIN32)
|
|
static ClassifierData** m_ClassifierData;
|
|
static UINT m_nNumClassifierData;
|
|
#else
|
|
static std::vector<ClassifierData*> m_ClassifierData;
|
|
#endif
|
|
static UINT m_nNumInstances;
|
|
static UINT64 m_uPreviousTimeStamp;
|
|
|
|
WeakClassifier* GetWeakClassifierAt( const UINT uWeakClassiferIndex ) { return m_StrongClassifier.GetWeakClassifierAt( uWeakClassiferIndex ); }
|
|
|
|
static VOID ApplyTiltCorrection( const UINT uPlayerIdx, GESTURE_SKELETON_TYPE* pDstSkeleton, const GESTURE_SKELETON_TYPE* pSrcSkeleton, const XMVECTOR& vNormalToGravity, BOOL bReset );
|
|
|
|
static BOOL ValidateHeader( CHAR* szHeader );
|
|
|
|
friend class DebugOutput;
|
|
};
|
|
|
|
|
|
#if defined( GESTURE_TRAINER )
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: GestureDetectorTrainer
|
|
// Desc: The gesture detector trainer that uses the AdaBoost machine learning algorithm
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
class GestureDetectorTrainer : public GestureDetector
|
|
{
|
|
public:
|
|
struct LabeledExamples
|
|
{
|
|
std::vector<GESTURE_SKELETON_TYPE*> m_pExamples; // Each tracked skeleton is an example
|
|
std::vector<INT8> m_iLabels; // 1 for true, -1 for false
|
|
std::vector<UINT64> m_uTimeStamps; // Event time stamp from the example file
|
|
};
|
|
|
|
public:
|
|
GestureDetectorTrainer();
|
|
~GestureDetectorTrainer();
|
|
|
|
VOID Reset();
|
|
|
|
HRESULT Save( const CHAR* szFileName );
|
|
HRESULT Load( const CHAR* szFileName ) { return GestureDetector::Load( szFileName ); }
|
|
HRESULT LoadFromMemory( VOID* pBuffer ) { return GestureDetector::LoadFromMemory( pBuffer ); }
|
|
|
|
HRESULT Train( CHAR* const* szLeFiles, const UINT uiLeFileCount, const DOUBLE fAccuracyLevel, const UINT nNumWeakClassifiersAtRuntime
|
|
, const FLOAT fWeightOfFalsePositivesWhenFiltering, const BOOL bOnlyRetrainDetectionParameters
|
|
, const UINT uCPUAvailableForTraining, BOOL useSkeleton, BOOL useOpticalFlow, UINT framesToSkip );
|
|
HRESULT Test( const CHAR* szPath, const BOOL bTestOnTrainingData );
|
|
HRESULT GenerateLabeledExamples( const CHAR* szGestureName, CHAR* const* szExampleFiles, const UINT uiExampleFileCount, const CHAR* szDestPath, const BOOL bUseRawSkeletonData, const INT iSkeletonIndexToProcess, INT8 iLabels[] = NULL, CHAR* const* szOutputFiles = NULL );
|
|
UINT GetNumWeakClassifiers() { return m_StrongClassifier.GetNumWeakClassifiers(); }
|
|
UINT GetNumExamples() { return (UINT)m_LabeledExamples.m_pExamples.size(); }
|
|
UINT GetNumPositiveExamples();
|
|
UINT GetNumNegativeExamples() { return GetNumExamples() - GetNumPositiveExamples(); }
|
|
UINT GetNumGestures() const { return m_nTotalNumGestures; }
|
|
UINT GetNumTrainingGestures() const { return m_nNumTrainingGestures; }
|
|
INT8 GetLabel( const UINT uIndex ) const { return m_LabeledExamples.m_iLabels[ uIndex ]; }
|
|
|
|
BOOL m_bUseSkeleton;
|
|
BOOL m_bUseOpticalFlow;
|
|
UINT m_nFramesToSkip;
|
|
|
|
protected:
|
|
LabeledExamples m_LabeledExamples; // Labeled examples used as ground truth during training and testing
|
|
UINT m_nTotalNumGestures; // Total number of gestures
|
|
UINT m_nNumTrainingGestures; // Total number of gestures which we're training on
|
|
UINT m_nNumThreadsForTraining; // Total number of threads the user allows for training
|
|
UINT m_nMaxNumThreads; // Total threads available on PC
|
|
UINT m_nNumWeakClassifiersAtRuntime; // Total number of weak classifiers at runtime
|
|
DOUBLE m_fErrorThreshold; // Error threshold in AdaBoost, with max value 0.5, since weak classifiers need to be better that 50/50 chance
|
|
|
|
HRESULT GenerateLabeledExamplesFromRawSkeleton( const CHAR* szGestureName, const CHAR* szExampleFileName, INT iSkeletonIndexToProcess, INT8 iExampleLabel = 0 );
|
|
|
|
HRESULT SaveLabeledExamples( const CHAR* szFileName );
|
|
HRESULT LoadLabeledExamples( const CHAR* szFileName );
|
|
|
|
|
|
HRESULT GenerateWeakClassifiers( ClassifierData::EType classifierDataType = ClassifierData::NUM_FEATURES );
|
|
HRESULT GenerateWeakClassifiers( ClassifierData* pClassifierData, const FLOAT fMin, const FLOAT fMax, const FLOAT fStep, const BOOL bUseReject = TRUE );
|
|
VOID CombineWeakClassifiers( GestureDetectorTrainer* pGestureDetectorTrainers, const UINT nNumTrainers );
|
|
|
|
HRESULT TrainWeakClassifiers();
|
|
HRESULT TrainWeakClassifiers( ClassifierData::EType classifierDataType, LabeledExamples& labeledExamples, const DOUBLE fErrorThreshold );
|
|
HRESULT TrainStrongClassifier( const BOOL bFinalPass );
|
|
|
|
VOID Optimize( const UINT nMaxNumClassifers, const BOOL bBakeReverseInAlpha = TRUE ) { m_StrongClassifier.Optimize( nMaxNumClassifers, bBakeReverseInAlpha ); }
|
|
VOID OptimizeDetectionParameters( const FLOAT fWeightOfFalsePositivesWhenFiltering );
|
|
VOID Test( FLOAT* pTruePositives, FLOAT* pFalsePositives, std::vector<FLOAT>& fRawClassificationResults );
|
|
|
|
//VOID ByteSwapSkeletonDataRead( GESTURE_SKELETON_TYPE* pSkeletonData );
|
|
VOID MirrorSkeletonData( GESTURE_SKELETON_TYPE* pSkeletonData );
|
|
};
|
|
#endif
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Name: DebugOutput
|
|
// Desc: Simple class that outputs text information for debuggin purposes and
|
|
// knowledge extraction puroposes
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
class DebugOutput
|
|
{
|
|
public:
|
|
CHAR* Print( const ClassifierData::EType type );
|
|
CHAR* Print( const GESTURE_JOINT_INDEX joint );
|
|
CHAR* Print( const ClassifierData* pClassifierData );
|
|
CHAR* Print( const WeakClassifier* pWeakClassifier, const ClassifierData* pClassifierData );
|
|
CHAR* Print( GestureDetector* pGestureDetector, const UINT uWeakClassiferIndex );
|
|
|
|
protected:
|
|
CHAR m_szBuffer[ MAX_PATH ];
|
|
};
|
|
|
|
}
|