diff options
| -rw-r--r-- | tracker-kinect-face/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | tracker-kinect-face/lang/nl_NL.ts | 22 | ||||
| -rw-r--r-- | tracker-kinect-face/lang/ru_RU.ts | 22 | ||||
| -rw-r--r-- | tracker-kinect-face/lang/stub.ts | 22 | ||||
| -rw-r--r-- | tracker-kinect-face/lang/zh_CN.ts | 22 | ||||
| -rw-r--r-- | tracker-kinect-face/test.cpp | 45 | ||||
| -rw-r--r-- | tracker-kinect-face/test.h | 28 | ||||
| -rw-r--r-- | tracker-kinect-face/test.ui | 65 | ||||
| -rw-r--r-- | tracker-kinect-face/test_dialog.cpp | 3 | ||||
| -rw-r--r-- | tracker-kinect-face/tracker.cpp | 451 | ||||
| -rw-r--r-- | tracker-kinect-face/tracker.h | 90 | 
11 files changed, 776 insertions, 0 deletions
| diff --git a/tracker-kinect-face/CMakeLists.txt b/tracker-kinect-face/CMakeLists.txt new file mode 100644 index 00000000..d139b445 --- /dev/null +++ b/tracker-kinect-face/CMakeLists.txt @@ -0,0 +1,6 @@ + +if(DEFINED ENV{KINECTSDK20_DIR}) +    otr_module(tracker-kinect-face) +	target_include_directories(opentrack-tracker-kinect-face SYSTEM PUBLIC $ENV{KINECTSDK20_DIR}inc) +	target_link_libraries(opentrack-tracker-kinect-face $ENV{KINECTSDK20_DIR}lib/x86/kinect20.lib $ENV{KINECTSDK20_DIR}lib/x86/kinect20.face.lib) +endif() diff --git a/tracker-kinect-face/lang/nl_NL.ts b/tracker-kinect-face/lang/nl_NL.ts new file mode 100644 index 00000000..b6139882 --- /dev/null +++ b/tracker-kinect-face/lang/nl_NL.ts @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="nl_NL"> +<context> +    <name>test_metadata</name> +    <message> +        <source>Kinect Face</source> +        <translation type="unfinished"></translation> +    </message> +</context> +<context> +    <name>test_ui</name> +    <message> +        <source>Sine wave</source> +        <translation type="unfinished"></translation> +    </message> +    <message> +        <source>Pressing "Abort" will immediately crash the application.</source> +        <translation type="unfinished"></translation> +    </message> +</context> +</TS> diff --git a/tracker-kinect-face/lang/ru_RU.ts b/tracker-kinect-face/lang/ru_RU.ts new file mode 100644 index 00000000..52aab9f3 --- /dev/null +++ b/tracker-kinect-face/lang/ru_RU.ts @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="ru_RU"> +<context> +    <name>test_metadata</name> +    <message> +        <source>Kinect Face</source> +        <translation type="unfinished"></translation> +    </message> +</context> +<context> +    <name>test_ui</name> +    <message> +        <source>Sine wave</source> +        <translation type="unfinished"></translation> +    </message> +    <message> +        <source>Pressing "Abort" will immediately crash the application.</source> +        <translation type="unfinished"></translation> +    </message> +</context> +</TS> diff --git a/tracker-kinect-face/lang/stub.ts b/tracker-kinect-face/lang/stub.ts new file mode 100644 index 00000000..84a3c6af --- /dev/null +++ b/tracker-kinect-face/lang/stub.ts @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1"> +<context> +    <name>test_metadata</name> +    <message> +        <source>Kinect Face</source> +        <translation type="unfinished"></translation> +    </message> +</context> +<context> +    <name>test_ui</name> +    <message> +        <source>Sine wave</source> +        <translation type="unfinished"></translation> +    </message> +    <message> +        <source>Pressing "Abort" will immediately crash the application.</source> +        <translation type="unfinished"></translation> +    </message> +</context> +</TS> diff --git a/tracker-kinect-face/lang/zh_CN.ts b/tracker-kinect-face/lang/zh_CN.ts new file mode 100644 index 00000000..84a3c6af --- /dev/null +++ b/tracker-kinect-face/lang/zh_CN.ts @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1"> +<context> +    <name>test_metadata</name> +    <message> +        <source>Kinect Face</source> +        <translation type="unfinished"></translation> +    </message> +</context> +<context> +    <name>test_ui</name> +    <message> +        <source>Sine wave</source> +        <translation type="unfinished"></translation> +    </message> +    <message> +        <source>Pressing "Abort" will immediately crash the application.</source> +        <translation type="unfinished"></translation> +    </message> +</context> +</TS> diff --git a/tracker-kinect-face/test.cpp b/tracker-kinect-face/test.cpp new file mode 100644 index 00000000..c1ada177 --- /dev/null +++ b/tracker-kinect-face/test.cpp @@ -0,0 +1,45 @@ +/* Copyright (c) 2014, Stanislaw Halik <sthalik@misaki.pl> + + * Permission to use, copy, modify, and/or distribute this + * software for any purpose with or without fee is hereby granted, + * provided that the above copyright notice and this permission + * notice appear in all copies. + */ + +#include "test.h" +#include "tracker.h" +#include "api/plugin-api.hpp" +#include "compat/math-imports.hpp" + +#include <QPushButton> + +#include <cmath> +#include <QDebug> + + +test_dialog::test_dialog() +{ +    ui.setupUi(this); + +    connect(ui.buttonBox, &QDialogButtonBox::clicked, [this](QAbstractButton* btn) { +        if (btn == ui.buttonBox->button(QDialogButtonBox::Abort)) +            // NOLINTNEXTLINE +            *(volatile int*)nullptr = 0; +    }); + +    connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK())); +    connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel())); +} + +void test_dialog::doOK() +{ +    //s.b->save(); +    close(); +} + +void test_dialog::doCancel() +{ +    close(); +} + +OPENTRACK_DECLARE_TRACKER(KinectFaceTracker, test_dialog, test_metadata) diff --git a/tracker-kinect-face/test.h b/tracker-kinect-face/test.h new file mode 100644 index 00000000..2e105ae3 --- /dev/null +++ b/tracker-kinect-face/test.h @@ -0,0 +1,28 @@ +#pragma once +#include "ui_test.h" +#include "compat/macros.hpp" +#include "api/plugin-api.hpp" + + +class test_dialog : public ITrackerDialog +{ +    Q_OBJECT + +    Ui::test_ui ui; +public: +    test_dialog(); +    void register_tracker(ITracker *) override {} +    void unregister_tracker() override {} +private slots: +    void doOK(); +    void doCancel(); +}; + +class test_metadata : public Metadata +{ +    Q_OBJECT + +    QString name() { return tr("Kinect Face"); } +    QIcon icon() { return QIcon(":/images/opentrack.png"); } +}; + diff --git a/tracker-kinect-face/test.ui b/tracker-kinect-face/test.ui new file mode 100644 index 00000000..509eb819 --- /dev/null +++ b/tracker-kinect-face/test.ui @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>test_ui</class> + <widget class="QWidget" name="test_ui"> +  <property name="windowModality"> +   <enum>Qt::NonModal</enum> +  </property> +  <property name="geometry"> +   <rect> +    <x>0</x> +    <y>0</y> +    <width>278</width> +    <height>58</height> +   </rect> +  </property> +  <property name="windowTitle"> +   <string>Sine wave</string> +  </property> +  <property name="windowIcon"> +   <iconset> +    <normaloff>../gui/images/opentrack.png</normaloff>../gui/images/opentrack.png</iconset> +  </property> +  <property name="layoutDirection"> +   <enum>Qt::LeftToRight</enum> +  </property> +  <property name="autoFillBackground"> +   <bool>false</bool> +  </property> +  <layout class="QVBoxLayout" name="verticalLayout"> +   <item> +    <widget class="QLabel" name="label"> +     <property name="sizePolicy"> +      <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> +       <horstretch>0</horstretch> +       <verstretch>0</verstretch> +      </sizepolicy> +     </property> +     <property name="text"> +      <string>Pressing "Abort" will immediately crash the application.</string> +     </property> +    </widget> +   </item> +   <item> +    <widget class="QDialogButtonBox" name="buttonBox"> +     <property name="sizePolicy"> +      <sizepolicy hsizetype="Minimum" vsizetype="Maximum"> +       <horstretch>0</horstretch> +       <verstretch>0</verstretch> +      </sizepolicy> +     </property> +     <property name="standardButtons"> +      <set>QDialogButtonBox::Abort|QDialogButtonBox::Close</set> +     </property> +    </widget> +   </item> +  </layout> + </widget> + <resources/> + <connections/> + <slots> +  <slot>startEngineClicked()</slot> +  <slot>stopEngineClicked()</slot> +  <slot>cameraSettingsClicked()</slot> + </slots> +</ui> diff --git a/tracker-kinect-face/test_dialog.cpp b/tracker-kinect-face/test_dialog.cpp new file mode 100644 index 00000000..5d33555b --- /dev/null +++ b/tracker-kinect-face/test_dialog.cpp @@ -0,0 +1,3 @@ +#include "test.h" +#include "api/plugin-api.hpp" + diff --git a/tracker-kinect-face/tracker.cpp b/tracker-kinect-face/tracker.cpp new file mode 100644 index 00000000..3fe3b8cc --- /dev/null +++ b/tracker-kinect-face/tracker.cpp @@ -0,0 +1,451 @@ + + +#include "tracker.h" + + +const double KinectFaceTracker::incr[6] = +{ +	50, 40, 80, +	70, 5, 3 +}; + +KinectFaceTracker::KinectFaceTracker(): +	m_pKinectSensor(nullptr), +	m_pCoordinateMapper(nullptr), +	m_pColorFrameReader(nullptr), +	m_pColorRGBX(nullptr), +	m_pBodyFrameReader(nullptr) + +{ +	for (int i = 0; i < BODY_COUNT; i++) +	{ +		m_pFaceFrameSources[i] = nullptr; +		m_pFaceFrameReaders[i] = nullptr; +	} + +	// create heap storage for color pixel data in RGBX format +	m_pColorRGBX = new RGBQUAD[cColorWidth * cColorHeight]; +} + +KinectFaceTracker::~KinectFaceTracker() +{ +	if (m_pColorRGBX) +	{ +		delete[] m_pColorRGBX; +		m_pColorRGBX = nullptr; +	} + +	// clean up Direct2D +	//SafeRelease(m_pD2DFactory); + +	// done with face sources and readers +	for (int i = 0; i < BODY_COUNT; i++) +	{ +		SafeRelease(m_pFaceFrameSources[i]); +		SafeRelease(m_pFaceFrameReaders[i]); +	} + +	// done with body frame reader +	SafeRelease(m_pBodyFrameReader); + +	// done with color frame reader +	SafeRelease(m_pColorFrameReader); + +	// done with coordinate mapper +	SafeRelease(m_pCoordinateMapper); + +	// close the Kinect Sensor +	if (m_pKinectSensor) +	{ +		m_pKinectSensor->Close(); +	} + +	SafeRelease(m_pKinectSensor); +} + +module_status KinectFaceTracker::start_tracker(QFrame*) +{ +	t.start(); + +	if (SUCCEEDED(InitializeDefaultSensor())) +	{ +		return status_ok(); +	} + +	return error("Kinect init failed!"); +} + +#ifdef EMIT_NAN +#   include <cstdlib> +#endif + +void KinectFaceTracker::data(double *data) +{ +	const double dt = t.elapsed_seconds(); +	t.start(); + +#ifdef EMIT_NAN +	if ((rand() % 4) == 0) +	{ +		for (int i = 0; i < 6; i++) +			data[i] = 0. / 0.; +	} +	else +#endif +		for (int i = 0; i < 6; i++) +		{ +			double x = last_x[i] + incr[i] * dt; +			if (x > 180) +				x = -360 + x; +			else if (x < -180) +				x = 360 + x; +			x = copysign(fmod(fabs(x), 360), x); +			last_x[i] = x; + +			if (i >= 3) +			{ +				data[i] = x; +			} +			else +			{ +				data[i] = x * 100 / 180.; +			} +		} + + +	Update(); +	//TODO: check if data is valid +	data[0] = 0; +	data[1] = 0; +	data[2] = 0; +	ExtractFaceRotationInDegrees(&faceRotation,&data[3], &data[4], &data[5]); + +} + + +/// <summary> +/// Converts rotation quaternion to Euler angles  +/// And then maps them to a specified range of values to control the refresh rate +/// </summary> +/// <param name="pQuaternion">face rotation quaternion</param> +/// <param name="pPitch">rotation about the X-axis</param> +/// <param name="pYaw">rotation about the Y-axis</param> +/// <param name="pRoll">rotation about the Z-axis</param> +void KinectFaceTracker::ExtractFaceRotationInDegrees(const Vector4* pQuaternion, double* pYaw, double* pPitch, double* pRoll) +{ +	double x = pQuaternion->x; +	double y = pQuaternion->y; +	double z = pQuaternion->z; +	double w = pQuaternion->w; + +	// convert face rotation quaternion to Euler angles in degrees		 +	double dPitch, dYaw, dRoll; +	dPitch = atan2(2 * (y * z + w * x), w * w - x * x - y * y + z * z) / M_PI * 180.0; +	dYaw = asin(2 * (w * y - x * z)) / M_PI * 180.0; +	dRoll = atan2(2 * (x * y + w * z), w * w + x * x - y * y - z * z) / M_PI * 180.0; + +	// clamp rotation values in degrees to a specified range of values to control the refresh rate +	/* +	double increment = c_FaceRotationIncrementInDegrees; +	*pPitch = static_cast<int>(floor((dPitch + increment/2.0 * (dPitch > 0 ? 1.0 : -1.0)) / increment) * increment); +	*pYaw = static_cast<int>(floor((dYaw + increment/2.0 * (dYaw > 0 ? 1.0 : -1.0)) / increment) * increment); +	*pRoll = static_cast<int>(floor((dRoll + increment/2.0 * (dRoll > 0 ? 1.0 : -1.0)) / increment) * increment); +	*/ + +	*pPitch = dPitch; +	*pYaw = dYaw; +	*pRoll = dRoll; + + +} + + +/// <summary> +/// Initializes the default Kinect sensor +/// </summary> +/// <returns>S_OK on success else the failure code</returns> +HRESULT KinectFaceTracker::InitializeDefaultSensor() +{ +	HRESULT hr; + +	hr = GetDefaultKinectSensor(&m_pKinectSensor); +	if (FAILED(hr)) +	{ +		return hr; +	} + +	if (m_pKinectSensor) +	{ +		// Initialize Kinect and get color, body and face readers +		IColorFrameSource* pColorFrameSource = nullptr; +		IBodyFrameSource* pBodyFrameSource = nullptr; + +		hr = m_pKinectSensor->Open(); + +		if (SUCCEEDED(hr)) +		{ +			hr = m_pKinectSensor->get_CoordinateMapper(&m_pCoordinateMapper); +		} + +		if (SUCCEEDED(hr)) +		{ +			hr = m_pKinectSensor->get_ColorFrameSource(&pColorFrameSource); +		} + +		if (SUCCEEDED(hr)) +		{ +			hr = pColorFrameSource->OpenReader(&m_pColorFrameReader); +		} + +		if (SUCCEEDED(hr)) +		{ +			hr = m_pKinectSensor->get_BodyFrameSource(&pBodyFrameSource); +		} + +		if (SUCCEEDED(hr)) +		{ +			hr = pBodyFrameSource->OpenReader(&m_pBodyFrameReader); +		} + +		if (SUCCEEDED(hr)) +		{ +			// create a face frame source + reader to track each body in the fov +			for (int i = 0; i < BODY_COUNT; i++) +			{ +				if (SUCCEEDED(hr)) +				{ +					// create the face frame source by specifying the required face frame features +					hr = CreateFaceFrameSource(m_pKinectSensor, 0, c_FaceFrameFeatures, &m_pFaceFrameSources[i]); +				} +				if (SUCCEEDED(hr)) +				{ +					// open the corresponding reader +					hr = m_pFaceFrameSources[i]->OpenReader(&m_pFaceFrameReaders[i]); +				} +			} +		} + +		SafeRelease(pColorFrameSource); +		SafeRelease(pBodyFrameSource); +	} + +	if (!m_pKinectSensor || FAILED(hr)) +	{ +		//SetStatusMessage(L"No ready Kinect found!", 10000, true); +		return E_FAIL; +	} + +	return hr; +} + +/// <summary> +/// Main processing function +/// </summary> +void KinectFaceTracker::Update() +{ +	if (!m_pColorFrameReader || !m_pBodyFrameReader) +	{ +		return; +	} + +	IColorFrame* pColorFrame = nullptr; +	HRESULT hr = m_pColorFrameReader->AcquireLatestFrame(&pColorFrame); + +	if (SUCCEEDED(hr)) +	{ +		INT64 nTime = 0; +		IFrameDescription* pFrameDescription = nullptr; +		int nWidth = 0; +		int nHeight = 0; +		ColorImageFormat imageFormat = ColorImageFormat_None; +		UINT nBufferSize = 0; +		RGBQUAD *pBuffer = nullptr; + +		hr = pColorFrame->get_RelativeTime(&nTime); + +		if (SUCCEEDED(hr)) +		{ +			hr = pColorFrame->get_FrameDescription(&pFrameDescription); +		} + +		if (SUCCEEDED(hr)) +		{ +			hr = pFrameDescription->get_Width(&nWidth); +		} + +		if (SUCCEEDED(hr)) +		{ +			hr = pFrameDescription->get_Height(&nHeight); +		} + +		if (SUCCEEDED(hr)) +		{ +			hr = pColorFrame->get_RawColorImageFormat(&imageFormat); +		} + +		if (SUCCEEDED(hr)) +		{ +			if (imageFormat == ColorImageFormat_Bgra) +			{ +				hr = pColorFrame->AccessRawUnderlyingBuffer(&nBufferSize, reinterpret_cast<BYTE**>(&pBuffer)); +			} +			else if (m_pColorRGBX) +			{ +				pBuffer = m_pColorRGBX; +				nBufferSize = cColorWidth * cColorHeight * sizeof(RGBQUAD); +				hr = pColorFrame->CopyConvertedFrameDataToArray(nBufferSize, reinterpret_cast<BYTE*>(pBuffer), ColorImageFormat_Bgra); +			} +			else +			{ +				hr = E_FAIL; +			} +		} + +		if (SUCCEEDED(hr)) +		{ +			//DrawStreams(nTime, pBuffer, nWidth, nHeight); +			ProcessFaces(); +		} + +		SafeRelease(pFrameDescription); +	} + +	SafeRelease(pColorFrame); +} + + +/// <summary> +/// Updates body data +/// </summary> +/// <param name="ppBodies">pointer to the body data storage</param> +/// <returns>indicates success or failure</returns> +HRESULT KinectFaceTracker::UpdateBodyData(IBody** ppBodies) +{ +	HRESULT hr = E_FAIL; + +	if (m_pBodyFrameReader != nullptr) +	{ +		IBodyFrame* pBodyFrame = nullptr; +		hr = m_pBodyFrameReader->AcquireLatestFrame(&pBodyFrame); +		if (SUCCEEDED(hr)) +		{ +			hr = pBodyFrame->GetAndRefreshBodyData(BODY_COUNT, ppBodies); +		} +		SafeRelease(pBodyFrame); +	} + +	return hr; +} + + + +/// <summary> +/// Processes new face frames +/// </summary> +void KinectFaceTracker::ProcessFaces() +{ +	HRESULT hr; +	IBody* ppBodies[BODY_COUNT] = { 0 }; +	bool bHaveBodyData = SUCCEEDED(UpdateBodyData(ppBodies)); + +	// iterate through each face reader +	for (int iFace = 0; iFace < BODY_COUNT; ++iFace) +	{ +		// retrieve the latest face frame from this reader +		IFaceFrame* pFaceFrame = nullptr; +		hr = m_pFaceFrameReaders[iFace]->AcquireLatestFrame(&pFaceFrame); + +		BOOLEAN bFaceTracked = false; +		if (SUCCEEDED(hr) && nullptr != pFaceFrame) +		{ +			// check if a valid face is tracked in this face frame +			hr = pFaceFrame->get_IsTrackingIdValid(&bFaceTracked); +		} + +		if (SUCCEEDED(hr)) +		{ +			if (bFaceTracked) +			{ +				IFaceFrameResult* pFaceFrameResult = nullptr; +				RectI faceBox = { 0 }; +				PointF facePoints[FacePointType::FacePointType_Count];				 +				DetectionResult faceProperties[FaceProperty::FaceProperty_Count]; +				//D2D1_POINT_2F faceTextLayout; + +				hr = pFaceFrame->get_FaceFrameResult(&pFaceFrameResult); + +				// need to verify if pFaceFrameResult contains data before trying to access it +				if (SUCCEEDED(hr) && pFaceFrameResult != nullptr) +				{ +					hr = pFaceFrameResult->get_FaceBoundingBoxInColorSpace(&faceBox); + +					if (SUCCEEDED(hr)) +					{ +						hr = pFaceFrameResult->GetFacePointsInColorSpace(FacePointType::FacePointType_Count, facePoints); +					} + +					if (SUCCEEDED(hr)) +					{ +						hr = pFaceFrameResult->get_FaceRotationQuaternion(&faceRotation); +					} + +					if (SUCCEEDED(hr)) +					{ +						hr = pFaceFrameResult->GetFaceProperties(FaceProperty::FaceProperty_Count, faceProperties); +					} + +					if (SUCCEEDED(hr)) +					{ +						//hr = GetFaceTextPositionInColorSpace(ppBodies[iFace], &faceTextLayout); +					} + +					if (SUCCEEDED(hr)) +					{ +						// draw face frame results +						//m_pDrawDataStreams->DrawFaceFrameResults(iFace, &faceBox, facePoints, &faceRotation, faceProperties, &faceTextLayout); +					} +				} + +				SafeRelease(pFaceFrameResult); +			} +			else +			{ +				// face tracking is not valid - attempt to fix the issue +				// a valid body is required to perform this step +				if (bHaveBodyData) +				{ +					// check if the corresponding body is tracked  +					// if this is true then update the face frame source to track this body +					IBody* pBody = ppBodies[iFace]; +					if (pBody != nullptr) +					{ +						BOOLEAN bTracked = false; +						hr = pBody->get_IsTracked(&bTracked); + +						UINT64 bodyTId; +						if (SUCCEEDED(hr) && bTracked) +						{ +							// get the tracking ID of this body +							hr = pBody->get_TrackingId(&bodyTId); +							if (SUCCEEDED(hr)) +							{ +								// update the face frame source with the tracking ID +								m_pFaceFrameSources[iFace]->put_TrackingId(bodyTId); +							} +						} +					} +				} +			} +		} + +		SafeRelease(pFaceFrame); +	} + +	if (bHaveBodyData) +	{ +		for (int i = 0; i < _countof(ppBodies); ++i) +		{ +			SafeRelease(ppBodies[i]); +		} +	} +} diff --git a/tracker-kinect-face/tracker.h b/tracker-kinect-face/tracker.h new file mode 100644 index 00000000..88243b1f --- /dev/null +++ b/tracker-kinect-face/tracker.h @@ -0,0 +1,90 @@ + + + +#include <cmath> + +#include "api/plugin-api.hpp" +#include "compat/timer.hpp" +#include "compat/macros.hpp" + +// Kinect Header files +#include <Kinect.h> +#include <Kinect.Face.h> + +#pragma once + +// Safe release for interfaces +template<class Interface> +inline void SafeRelease(Interface *& pInterfaceToRelease) +{ +	if (pInterfaceToRelease != nullptr) +	{ +		pInterfaceToRelease->Release(); +		pInterfaceToRelease = nullptr; +	} +} + + +class KinectFaceTracker : public ITracker +{ +public: +	KinectFaceTracker(); +	~KinectFaceTracker() override; +	module_status start_tracker(QFrame *) override; +	void data(double *data) override; + +private: +	static const double incr[6]; +	double last_x[6]{}; +	Timer t; + +	// Kinect stuff + +	// define the face frame features required to be computed by this application +	static const DWORD c_FaceFrameFeatures = +		FaceFrameFeatures::FaceFrameFeatures_BoundingBoxInColorSpace +		| FaceFrameFeatures::FaceFrameFeatures_PointsInColorSpace +		| FaceFrameFeatures::FaceFrameFeatures_RotationOrientation +		| FaceFrameFeatures::FaceFrameFeatures_Happy +		| FaceFrameFeatures::FaceFrameFeatures_RightEyeClosed +		| FaceFrameFeatures::FaceFrameFeatures_LeftEyeClosed +		| FaceFrameFeatures::FaceFrameFeatures_MouthOpen +		| FaceFrameFeatures::FaceFrameFeatures_MouthMoved +		| FaceFrameFeatures::FaceFrameFeatures_LookingAway +		| FaceFrameFeatures::FaceFrameFeatures_Glasses +		| FaceFrameFeatures::FaceFrameFeatures_FaceEngagement; + +	static const int       cColorWidth = 1920; +	static const int       cColorHeight = 1080; + + +	void Update(); +	HRESULT InitializeDefaultSensor(); +	void ProcessFaces(); +	HRESULT UpdateBodyData(IBody** ppBodies); +	void ExtractFaceRotationInDegrees(const Vector4* pQuaternion, double* pPitch, double* pYaw, double* pRoll); + +	// Current Kinect +	IKinectSensor*         m_pKinectSensor; + +	// Coordinate mapper +	ICoordinateMapper*     m_pCoordinateMapper; + +	// Color reader +	IColorFrameReader*     m_pColorFrameReader; + +	// Body reader +	IBodyFrameReader*      m_pBodyFrameReader; + +	// Face sources +	IFaceFrameSource*	   m_pFaceFrameSources[BODY_COUNT]; + +	// Face readers +	IFaceFrameReader*	   m_pFaceFrameReaders[BODY_COUNT]; + +	// +	RGBQUAD*               m_pColorRGBX; + +	Vector4 faceRotation; + +}; | 
