首页 > 代码库 > Kinect 手势识别之挥手(C++实现)

Kinect 手势识别之挥手(C++实现)

以下代码是根据这里改编成C++版的,实现思路上相同,但是细节有差异,不影响理解

#include<iostream>
#include<Windows.h>
#include<NuiApi.h>
#include<ctime>
#include<cassert>
#include<process.h>

#pragma comment(lib,"kinect10.lib")
using namespace std;

HANDLE DetectionEvent = CreateEvent( NULL, false, false, NULL );

// 记录手势当前位置
enum GesturePos{
	NonePos = 0,
	Left,
	Right,
	Neutral
};

// 判断识别状态
enum DetectionState{
	NoneState = 0,
	Success,
	Failed,
	InProgress
};

// 判断手势需要的数据
struct DataState{
	GesturePos Pos;		// 每个人的左右手
	DetectionState State;
	int times;
	time_t timestamp;
	void Reset()
	{
		Pos = GesturePos::NonePos;
		State = DetectionState::NoneState;
		times = 0;
		timestamp = 0;
	}
};

// 完成手势判断逻辑功能
class GestureDetection{
public:
	GestureDetection( float neutral_threshold, int times, double difftimes )
		: neutral_threshold( neutral_threshold )
		, times(times)
		, difftimes(difftimes)
		, left_hand(0)
		, right_hand(1)
	{
		for( int i = 0; i < NUI_SKELETON_COUNT; i++ )
		{
			wave_datas[i][left_hand].Reset();
			wave_datas[i][right_hand].Reset();
		}
	}
	// 功能:循环接收骨骼数据,如果识别出为挥手动作则输出:success,
	// 识别失败输出:failed,
	void Update( const NUI_SKELETON_FRAME* frame )
	{
		if( NULL == frame )
			return ;
		for( int i = 0; i < NUI_SKELETON_COUNT; i++ )
		{
			JudgeState( frame->SkeletonData[i], wave_datas[i][left_hand], true );
			JudgeState( frame->SkeletonData[i], wave_datas[i][right_hand], false );
		}
	}
private:
	DataState wave_datas[NUI_SKELETON_COUNT][2];		// 记录每个人,每只手的状态
	const int left_hand;							// 左手 ID
	const int right_hand;							// 右手 ID
	// 中间位置阀值:在该范围内的都认为手在中间位置(相对于肘部的 x 坐标)
	const float neutral_threshold;		
	// 挥手次数阀值,达到该次数认为是挥手
	const int times;
	// 时间限制,如果超过该时间差依然识别不出挥手动作则认为识别失败
	const double difftimes;

	// 判断当前的状态成功输出:success,并生成事件:DetectionEvent 
	// 失败输出 failed,供 UpDate 函数调用
	void JudgeState( const NUI_SKELETON_DATA& skeletonData, DataState& data, bool isLeft = true )
	{
		int elbow = (isLeft)? NUI_SKELETON_POSITION_ELBOW_LEFT:
			NUI_SKELETON_POSITION_ELBOW_RIGHT; 

		int hand = (isLeft)? NUI_SKELETON_POSITION_HAND_LEFT:
			NUI_SKELETON_POSITION_HAND_RIGHT; 

		if( !IsSkeletonTrackedWell( skeletonData, isLeft ) )
		{
			if( data.State == InProgress )
			{
#ifdef _DEBUG
				cout << "not a well skeleton, detection failed!\n";
#endif
				data.Reset();
				return ;
			}
		}

		float curpos = skeletonData.SkeletonPositions[hand].x ;
		float center = skeletonData.SkeletonPositions[elbow].x;

		if( !IsNeutral( curpos, center ) )
		{
			if( data.Pos == NonePos )
			{
#ifdef _DEBUG
				cout << "found!\n";
#endif

				data.times++;
				data.Pos = IsLeftSide(curpos,center)?Left:Right;
				data.State = InProgress;
				data.timestamp = time(NULL);
			}
			else if( ( (data.Pos == Left ) && IsRightSide( curpos, center ))
				|| ( (data.Pos == Right) && IsLeftSide( curpos, center ) ) 
				)
			{
				assert( data.State == InProgress );
				data.times++;
				data.Pos = (data.Pos == Left)?Right:Left;
#ifdef _DEBUG
				cout << "times:" << data.times<< endl;
				if( data.Pos == Left )
				{
					cout << "left !\n";
				}
				else if( data.Pos == Right )
				{
					cout << "right!\n";
				}
				else
					cout << "you can't see me!\n";
#endif
				if( data.times >= times )
				{
#ifdef _DEBUG
					cout << "success!\n";
#endif
					SetEvent( DetectionEvent );
					data.Reset();
				}
				else if( difftime( time(NULL), data.timestamp ) > difftimes )
				{
#ifdef _DEBUG
						cout << "time out, detection failed!\n";
						cout << "data.times : " << data.times << endl;
#endif
						data.Reset();
				}
			}
		}

	}

	bool IsLeftSide( float curpos, float center )
	{
		return curpos < (center - neutral_threshold) ;
	}
	bool IsRightSide( float curpos, float center )
	{
		return curpos > (center + neutral_threshold) ;
	}
	bool IsNeutral( float curpos, float center )
	{
		return !IsLeftSide(curpos,center)&&!IsRightSide(curpos,center);
	}

	// 判断骨骼追踪情况:包括骨骼追踪完好且手部位置在肘上面
	bool IsSkeletonTrackedWell( const NUI_SKELETON_DATA& skeletonData, 
		bool isLeft = true )
	{
		int elbow = (isLeft)? NUI_SKELETON_POSITION_ELBOW_LEFT:
			NUI_SKELETON_POSITION_ELBOW_RIGHT; 

		int hand = (isLeft)? NUI_SKELETON_POSITION_HAND_LEFT:
			NUI_SKELETON_POSITION_HAND_RIGHT; 

		if( skeletonData.eTrackingState != NUI_SKELETON_NOT_TRACKED )
		{
			if( (skeletonData.eSkeletonPositionTrackingState[hand]
			!= NUI_SKELETON_POSITION_NOT_TRACKED )
				&& (skeletonData.eSkeletonPositionTrackingState[elbow]
			!= NUI_SKELETON_POSITION_NOT_TRACKED) )
			{
				if( skeletonData.SkeletonPositions[hand].y > skeletonData.SkeletonPositions[elbow].y )
					return true;		
			}
		}
		return false;
	}
};

#define CHECK_FAILED( hr, msg ) if(FAILED(hr)) { cout << msg << endl; return -1; }
unsigned int __stdcall doWave( void* p )
{
	while( WaitForSingleObject( DetectionEvent,INFINITE ) == WAIT_OBJECT_0 )
	{
		cout << "do something when detecting wave hand\n";
		cout << "hello what can i do for you?\n";
	}
	return 0;
}
int main()
{
	int count;
	INuiSensor *kinect = NULL;
	HRESULT hr;
	NuiGetSensorCount(&count);
	if( count < 1 )
	{
		cout << "检测不到 Kinect\n";
		return -1;
	}

	hr = NuiCreateSensorByIndex(0, &kinect );
	if( FAILED(hr) || kinect == NULL)
	{
		cout << "创建 sensor 失败\n";
		return -1;
	}
	if( kinect->NuiStatus() != S_OK )
	{
		cout << "Kinect sensor 没准备好\n";
		return -1;
	}

	hr = kinect->NuiInitialize( NUI_INITIALIZE_FLAG_USES_SKELETON );
	CHECK_FAILED( hr, "初始化失败" );

	HANDLE next_frame_event = CreateEvent( NULL, true, false, NULL );

	hr = kinect->NuiSkeletonTrackingEnable( next_frame_event, NUI_SKELETON_TRACKING_FLAG_ENABLE_IN_NEAR_RANGE );
	CHECK_FAILED( hr, "打开骨骼追踪失败" );

	uintptr_t thread = _beginthreadex( NULL, 0, doWave, NULL, 0, NULL );
	if( thread <= 0 )
	{
		cout << "thread create failed!\n";
		return -1;
	}

	NUI_SKELETON_FRAME skeleton_frame;
	GestureDetection gesture_detection( 0.05, 3, 4 );
	cout << "开始检测\n";
	while(true)
	{
		if( WaitForSingleObject( next_frame_event, INFINITE ) == WAIT_OBJECT_0 )
		{
			kinect->NuiSkeletonGetNextFrame( 0, &skeleton_frame );
			NuiTransformSmooth( &skeleton_frame, NULL );
			gesture_detection.Update( &skeleton_frame );
		}
	}
	kinect->NuiShutdown();
	system("pause");
	return 0;
}

Kinect 手势识别之挥手(C++实现)