summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorStéphane Lenclud <github@lenclud.com>2019-01-31 10:32:17 +0100
committerStéphane Lenclud <github@lenclud.com>2019-02-07 13:24:13 +0100
commita774441fde00f0ce6c8387339c786e9e2f42f088 (patch)
tree0877ab55801aee6b4de748dcbd1190a4922da41b
parentb9d34a26d35d05f793161e54001329edb5122a81 (diff)
Kinect: First draft.
-rw-r--r--tracker-kinect-face/CMakeLists.txt6
-rw-r--r--tracker-kinect-face/lang/nl_NL.ts22
-rw-r--r--tracker-kinect-face/lang/ru_RU.ts22
-rw-r--r--tracker-kinect-face/lang/stub.ts22
-rw-r--r--tracker-kinect-face/lang/zh_CN.ts22
-rw-r--r--tracker-kinect-face/test.cpp45
-rw-r--r--tracker-kinect-face/test.h28
-rw-r--r--tracker-kinect-face/test.ui65
-rw-r--r--tracker-kinect-face/test_dialog.cpp3
-rw-r--r--tracker-kinect-face/tracker.cpp451
-rw-r--r--tracker-kinect-face/tracker.h90
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 &quot;Abort&quot; 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 &quot;Abort&quot; 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 &quot;Abort&quot; 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 &quot;Abort&quot; 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 &quot;Abort&quot; 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;
+
+};