summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorWim Vriend <facetracknoir@gmail.com>2011-03-21 21:32:13 +0000
committerWim Vriend <facetracknoir@gmail.com>2011-03-21 21:32:13 +0000
commit7eeb8dfaede7bb54b37b8ea538135914a43ab011 (patch)
tree890f04508697583a3d3e1b849181122278096701
parent09b4c95b8036b9466ca89acbe0b1f3d810499863 (diff)
New effort to embrace faceAPI 3.2.6
git-svn-id: svn+ssh://svn.code.sf.net/p/facetracknoir/code@54 19e81ba0-9b1a-49c3-bd6c-561e1906d5fb
-rw-r--r--FTNoIR_Tracker_Base/ftnoir_tracker_base.h5
-rw-r--r--FTNoIR_Tracker_SM/FTNoIR_Tracker_SM.h11
-rw-r--r--FTNoIR_Tracker_SM/FTNoIR_Tracker_SM.vcproj99
-rw-r--r--FTNoIR_Tracker_SM/ftnoir_tracker_faceapi.cpp42
-rw-r--r--FTNoIR_Tracker_UDP/FTNoIR_Tracker_UDP.h4
-rw-r--r--FTNoIR_Tracker_UDP/ftnoir_tracker_udp.cpp4
-rw-r--r--FaceTrackNoIR.sln6
-rw-r--r--FaceTrackNoIR.suobin343552 -> 349184 bytes
-rw-r--r--FaceTrackNoIR/AutoClosePtr.h89
-rw-r--r--FaceTrackNoIR/FaceTrackNoIR.cpp34
-rw-r--r--FaceTrackNoIR/FaceTrackNoIR.h10
-rw-r--r--FaceTrackNoIR/FaceTrackNoIR.vcproj2
-rw-r--r--FaceTrackNoIR/main.cpp3
-rw-r--r--FaceTrackNoIR/tracker.cpp117
-rw-r--r--FaceTrackNoIR/tracker.h11
-rw-r--r--List of compatible games.xlsbin28672 -> 29184 bytes
-rw-r--r--bin/FaceTrackNoIR.exebin626688 -> 626688 bytes
-rw-r--r--bin/NPClient.dllbin90624 -> 90112 bytes
-rw-r--r--bin/Settings/Wings of Prey.ini20
-rw-r--r--faceAPI/build_options.h8
-rw-r--r--faceAPI/lock.h35
-rw-r--r--faceAPI/lockfree.h65
-rw-r--r--faceAPI/main.cpp460
-rw-r--r--faceAPI/mutex.h44
-rw-r--r--faceAPI/stdafx.cpp8
-rw-r--r--faceAPI/stdafx.h21
-rw-r--r--faceAPI/utils.h346
27 files changed, 1310 insertions, 134 deletions
diff --git a/FTNoIR_Tracker_Base/ftnoir_tracker_base.h b/FTNoIR_Tracker_Base/ftnoir_tracker_base.h
index 49a79b97..1965d3d2 100644
--- a/FTNoIR_Tracker_Base/ftnoir_tracker_base.h
+++ b/FTNoIR_Tracker_Base/ftnoir_tracker_base.h
@@ -3,6 +3,7 @@
#include "ftnoir_tracker_base_global.h"
#include <QtGui/QWidget>
+#include <QtGui/QFrame>
//
// x,y,z position in centimetres, yaw, pitch and roll in degrees...
@@ -21,8 +22,8 @@ struct THeadPoseData {
struct ITracker
{
virtual void Release() = 0; // Member required to enable Auto-remove
- virtual void Initialize() = 0;
- virtual void StartTracker() = 0;
+ virtual void Initialize( QFrame *videoframe ) = 0;
+ virtual void StartTracker( HWND parent_window ) = 0;
virtual void StopTracker() = 0;
virtual bool GiveHeadPoseData(THeadPoseData *data) = 0;
};
diff --git a/FTNoIR_Tracker_SM/FTNoIR_Tracker_SM.h b/FTNoIR_Tracker_SM/FTNoIR_Tracker_SM.h
index 12bfbe80..9af25800 100644
--- a/FTNoIR_Tracker_SM/FTNoIR_Tracker_SM.h
+++ b/FTNoIR_Tracker_SM/FTNoIR_Tracker_SM.h
@@ -1,5 +1,6 @@
#include "..\ftnoir_tracker_base\ftnoir_tracker_base.h"
#include "ui_FTNoIR_SMClientcontrols.h"
+#include "mainwindow.h"
#include "sm_api_qt.h"
#include <QMessageBox>
@@ -11,6 +12,7 @@ using namespace std;
//using namespace sm::faceapi::samplecode;
using namespace sm::faceapi;
using namespace sm::faceapi::qt;
+using namespace sm::faceapi::samplecode;
class FTNoIR_Tracker_SM : public ITracker
{
@@ -19,8 +21,8 @@ public:
~FTNoIR_Tracker_SM();
void Release();
- void Initialize();
- void StartTracker();
+ void Initialize( QFrame *videoframe );
+ void StartTracker( HWND parent_window );
void StopTracker();
bool GiveHeadPoseData(THeadPoseData *data); // Returns true if confidence is good
void loadSettings();
@@ -31,8 +33,9 @@ private:
/** face api variables **/
APIScope *faceapi_scope;
QSharedPointer<EngineBase> _engine;
- smEngineHandle _engine_handle;
-
+ VideoDisplayWidget *_display;
+ QVBoxLayout *l;
+ MainWindow *main_window;
//parameter list for the filter-function(s)
enum
{
diff --git a/FTNoIR_Tracker_SM/FTNoIR_Tracker_SM.vcproj b/FTNoIR_Tracker_SM/FTNoIR_Tracker_SM.vcproj
index cf0cd64f..b15c259b 100644
--- a/FTNoIR_Tracker_SM/FTNoIR_Tracker_SM.vcproj
+++ b/FTNoIR_Tracker_SM/FTNoIR_Tracker_SM.vcproj
@@ -38,7 +38,7 @@
/>
<Tool
Name="VCCLCompilerTool"
- AdditionalIncludeDirectories=".\GeneratedFiles;&quot;$(QTDIR)\include&quot;;&quot;.\GeneratedFiles\$(ConfigurationName)&quot;;&quot;$(QTDIR)\include\qtmain&quot;;&quot;$(QTDIR)\include\QtCore&quot;;&quot;$(QTDIR)\include\QtNetwork&quot;;&quot;$(QTDIR)\include\QtGui&quot;;.\;&quot;$(SM_API_PATH)\include&quot;;&quot;$(SM_API_CPP_WRAPPERS)\include&quot;;&quot;$(SM_API_WIDGETS)\include&quot;;&quot;$(SM_API_QTLIB)\include&quot;"
+ AdditionalIncludeDirectories=".\GeneratedFiles;&quot;$(QTDIR)\include&quot;;&quot;.\GeneratedFiles\$(ConfigurationName)&quot;;&quot;$(QTDIR)\include\qtmain&quot;;&quot;$(QTDIR)\include\QtCore&quot;;&quot;$(QTDIR)\include\QtNetwork&quot;;&quot;$(QTDIR)\include\QtGui&quot;;.\;&quot;$(SM_API_PATH)\include&quot;;&quot;$(SM_API_CPP_WRAPPERS)\include&quot;;&quot;$(SM_API_WIDGETS)\include&quot;;&quot;$(SM_API_QTDIR)\include&quot;"
PreprocessorDefinitions="UNICODE;WIN32;QT_LARGEFILE_SUPPORT;QT_THREAD_SUPPORT;QT_NO_DEBUG;NDEBUG;QT_CORE_LIB;FTNOIR_TRACKER_BASE_LIB"
RuntimeLibrary="2"
TreatWChar_tAsBuiltInType="false"
@@ -57,7 +57,7 @@
Name="VCLinkerTool"
AdditionalDependencies="qtmain.lib QtCore4.lib QtGui4.lib QtNetwork4.lib smft32.lib smftcpp.lib smftqt.lib"
OutputFile="$(OutDir)\$(ProjectName).dll"
- AdditionalLibraryDirectories="$(QTDIR)\lib"
+ AdditionalLibraryDirectories="&quot;$(QTDIR)\lib&quot;;&quot;$(SM_API_PATH)\bin&quot;;&quot;$(SM_API_CPP_WRAPPERS)\lib&quot;;&quot;$(SM_API_WIDGETS)\lib&quot;;&quot;$(SM_API_QTDIR)\lib&quot;"
GenerateDebugInformation="false"
SubSystem="2"
/>
@@ -171,6 +171,10 @@
RelativePath=".\ftnoir_tracker_faceapi.cpp"
>
</File>
+ <File
+ RelativePath=".\mainwindow.cpp"
+ >
+ </File>
</Filter>
<Filter
Name="Header Files"
@@ -203,12 +207,64 @@
/>
</FileConfiguration>
</File>
+ <File
+ RelativePath=".\mainwindow.h"
+ >
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ Description="Moc&apos;ing $(InputFileName)..."
+ CommandLine="&quot;$(QTDIR)\bin\moc.exe&quot; &quot;$(InputPath)&quot; -o &quot;.\GeneratedFiles\$(ConfigurationName)\moc_$(InputName).cpp&quot; -D_WINDLL -DFTNOIR_TRACKER_BASE_LIB -DNDEBUG -DQT_CORE_LIB -DQT_LARGEFILE_SUPPORT -DQT_NO_DEBUG -DQT_THREAD_SUPPORT -DUNICODE -DWIN32 -I&quot;$(QTDIR)\include\.&quot; -I&quot;$(QTDIR)\include\QtCore\.&quot; -I&quot;$(QTDIR)\include\QtGui\.&quot; -I&quot;$(QTDIR)\include\qtmain\.&quot; -I&quot;$(QTDIR)\include\QtNetwork\.&quot; -I&quot;$(SM_API_CPP_WRAPPERS)\include\.&quot; -I&quot;$(SM_API_PATH)\include\.&quot; -I&quot;$(SM_API_QTDIR)\include\.&quot; -I&quot;$(SM_API_WIDGETS)\include\.&quot; -I&quot;.\.&quot; -I&quot;.\GeneratedFiles\$(ConfigurationName)\.&quot; -I&quot;.\GeneratedFiles\.&quot;&#x0D;&#x0A;"
+ AdditionalDependencies="&quot;$(QTDIR)\bin\moc.exe&quot;;$(InputPath)"
+ Outputs="&quot;.\GeneratedFiles\$(ConfigurationName)\moc_$(InputName).cpp&quot;"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ Description="Moc&apos;ing $(InputFileName)..."
+ CommandLine="&quot;$(QTDIR)\bin\moc.exe&quot; &quot;$(InputPath)&quot; -o &quot;.\GeneratedFiles\$(ConfigurationName)\moc_$(InputName).cpp&quot; -D_WINDLL -DFTNOIR_TRACKER_SM_LIB -DQT_CORE_LIB -DQT_LARGEFILE_SUPPORT -DQT_THREAD_SUPPORT -DUNICODE -DWIN32 -I&quot;$(QTDIR)\include\.&quot; -I&quot;$(QTDIR)\include\QtCore\.&quot; -I&quot;$(QTDIR)\include\qtmain\.&quot; -I&quot;.\.&quot; -I&quot;.\GeneratedFiles\$(ConfigurationName)\.&quot; -I&quot;.\GeneratedFiles\.&quot;&#x0D;&#x0A;"
+ AdditionalDependencies="&quot;$(QTDIR)\bin\moc.exe&quot;;$(InputPath)"
+ Outputs="&quot;.\GeneratedFiles\$(ConfigurationName)\moc_$(InputName).cpp&quot;"
+ />
+ </FileConfiguration>
+ </File>
</Filter>
<Filter
Name="Form Files"
Filter="ui"
UniqueIdentifier="{99349809-55BA-4b9d-BF79-8FDBB0286EB3}"
>
+ <File
+ RelativePath=".\mainwindow.ui"
+ >
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ Description="Uic&apos;ing $(InputFileName)..."
+ CommandLine="&quot;$(QTDIR)\bin\uic.exe&quot; -o &quot;.\GeneratedFiles\ui_$(InputName).h&quot; &quot;$(InputPath)&quot;&#x0D;&#x0A;"
+ AdditionalDependencies="$(QTDIR)\bin\uic.exe"
+ Outputs="&quot;.\GeneratedFiles\ui_$(InputName).h&quot;"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCustomBuildTool"
+ Description="Uic&apos;ing $(InputFileName)..."
+ CommandLine="&quot;$(QTDIR)\bin\uic.exe&quot; -o &quot;.\GeneratedFiles\ui_$(InputName).h&quot; &quot;$(InputPath)&quot;&#x0D;&#x0A;"
+ AdditionalDependencies="$(QTDIR)\bin\uic.exe"
+ Outputs="&quot;.\GeneratedFiles\ui_$(InputName).h&quot;"
+ />
+ </FileConfiguration>
+ </File>
</Filter>
<Filter
Name="Resource Files"
@@ -223,17 +279,56 @@
UniqueIdentifier="{71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11}"
SourceControlFiles="false"
>
+ <File
+ RelativePath=".\GeneratedFiles\ui_mainwindow.h"
+ >
+ </File>
<Filter
Name="Release"
Filter="cpp;moc"
SourceControlFiles="false"
>
+ <File
+ RelativePath=".\GeneratedFiles\release\moc_mainwindow.cpp"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ </File>
</Filter>
<Filter
Name="Debug"
Filter="cpp;moc"
SourceControlFiles="false"
>
+ <File
+ RelativePath=".\GeneratedFiles\Debug\moc_mainwindow.cpp"
+ >
+ <FileConfiguration
+ Name="Release|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)1.obj"
+ XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)1.obj"
+ XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
+ />
+ </FileConfiguration>
+ </File>
</Filter>
</Filter>
</Files>
diff --git a/FTNoIR_Tracker_SM/ftnoir_tracker_faceapi.cpp b/FTNoIR_Tracker_SM/ftnoir_tracker_faceapi.cpp
index ee1b049d..f2f18877 100644
--- a/FTNoIR_Tracker_SM/ftnoir_tracker_faceapi.cpp
+++ b/FTNoIR_Tracker_SM/ftnoir_tracker_faceapi.cpp
@@ -1,6 +1,9 @@
#include "ftnoir_tracker_sm.h"
+#include "mainwindow.h"
using namespace sm::faceapi;
+using namespace sm::faceapi::qt;
+using namespace sm::faceapi::samplecode;
//using namespace sm::faceapi::qt;
FTNoIR_Tracker_SM::FTNoIR_Tracker_SM()
@@ -17,16 +20,29 @@ FTNoIR_Tracker_SM::FTNoIR_Tracker_SM()
FTNoIR_Tracker_SM::~FTNoIR_Tracker_SM()
{
- _engine->stop();
- smAPIQuit();
+ qDebug() << "stopTracker says: terminating";
+ //if ( _display ) {
+ // _display->disconnect();
+ // qDebug() << "stopTracker says: display disconnected";
+ // delete _display;
+ // qDebug() << "stopTracker says: display deleted";
+ // _display = 0;
+ // delete l;
+ // l = 0;
+ // qDebug() << "stopTracker says: l deleted";
+ //}
+
+ _engine->stop();
+ smAPIQuit();
}
void FTNoIR_Tracker_SM::Release()
{
+ qDebug() << "FTNoIR_Tracker_SM::Release says: Starting ";
delete this;
}
-void FTNoIR_Tracker_SM::Initialize()
+void FTNoIR_Tracker_SM::Initialize( QFrame *videoframe )
{
qDebug() << "FTNoIR_Tracker_SM::Initialize says: Starting ";
loadSettings();
@@ -54,10 +70,27 @@ void FTNoIR_Tracker_SM::Initialize()
QMessageBox::warning(0,"faceAPI Error",e.what(),QMessageBox::Ok,QMessageBox::NoButton);
}
+
+
+ // Show the video widget
+ qDebug() << "FTNoIR_Tracker_SM::Initialize says: videoframe = " << videoframe;
+
+ // QMainWindow derived class. See mainwindow.h
+ QSharedPointer<CameraBase> camera;
+ main_window = new MainWindow(camera,_engine,0);
+ main_window->show();
+
+ //videoframe->show();
+ //_display = new VideoDisplayWidget( _engine, videoframe, 0 );
+ //l = new QVBoxLayout(videoframe);
+ //l->setMargin(0);
+ //l->setSpacing(0);
+ //l->addWidget(_display);
+
return;
}
-void FTNoIR_Tracker_SM::StartTracker()
+void FTNoIR_Tracker_SM::StartTracker( HWND parent_window )
{
// starts the faceapi engine
@@ -75,6 +108,7 @@ void FTNoIR_Tracker_SM::StartTracker()
void FTNoIR_Tracker_SM::StopTracker()
{
+ qDebug() << "FTNoIR_Tracker_SM::StopTracker says: Starting ";
// stops the faceapi engine
_engine->stop();
return;
diff --git a/FTNoIR_Tracker_UDP/FTNoIR_Tracker_UDP.h b/FTNoIR_Tracker_UDP/FTNoIR_Tracker_UDP.h
index ab9c0ed5..506eb157 100644
--- a/FTNoIR_Tracker_UDP/FTNoIR_Tracker_UDP.h
+++ b/FTNoIR_Tracker_UDP/FTNoIR_Tracker_UDP.h
@@ -14,8 +14,8 @@ public:
~FTNoIR_Tracker_UDP();
void Release();
- void Initialize();
- void StartTracker();
+ void Initialize( QFrame *videoframe );
+ void StartTracker( HWND parent_window );
void StopTracker();
bool GiveHeadPoseData(THeadPoseData *data);
void loadSettings();
diff --git a/FTNoIR_Tracker_UDP/ftnoir_tracker_udp.cpp b/FTNoIR_Tracker_UDP/ftnoir_tracker_udp.cpp
index afeae10d..1905a3bd 100644
--- a/FTNoIR_Tracker_UDP/ftnoir_tracker_udp.cpp
+++ b/FTNoIR_Tracker_UDP/ftnoir_tracker_udp.cpp
@@ -97,7 +97,7 @@ void FTNoIR_Tracker_UDP::Release()
delete this;
}
-void FTNoIR_Tracker_UDP::Initialize()
+void FTNoIR_Tracker_UDP::Initialize( QFrame *videoframe )
{
qDebug() << "FTNoIR_Tracker_UDP::Initialize says: Starting ";
loadSettings();
@@ -121,7 +121,7 @@ void FTNoIR_Tracker_UDP::Initialize()
return;
}
-void FTNoIR_Tracker_UDP::StartTracker()
+void FTNoIR_Tracker_UDP::StartTracker( HWND parent_window )
{
start( QThread::TimeCriticalPriority );
return;
diff --git a/FaceTrackNoIR.sln b/FaceTrackNoIR.sln
index 31b3fa30..64bab306 100644
--- a/FaceTrackNoIR.sln
+++ b/FaceTrackNoIR.sln
@@ -13,6 +13,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FTNoIR_Filter_EWMA2", "FTNo
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FTNoIR_Tracker_SM", "FTNoIR_Tracker_SM\FTNoIR_Tracker_SM.vcproj", "{26346BCF-92A5-47A7-B4CF-73C402F7C3AD}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FTNoIR_FaceAPI_EXE", "faceAPI\FaceAPI2FSX.vcproj", "{EF743D32-0980-44D9-BA9E-B9D2456251AC}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@@ -40,6 +42,10 @@ Global
{26346BCF-92A5-47A7-B4CF-73C402F7C3AD}.Debug|Win32.Build.0 = Debug|Win32
{26346BCF-92A5-47A7-B4CF-73C402F7C3AD}.Release|Win32.ActiveCfg = Release|Win32
{26346BCF-92A5-47A7-B4CF-73C402F7C3AD}.Release|Win32.Build.0 = Release|Win32
+ {EF743D32-0980-44D9-BA9E-B9D2456251AC}.Debug|Win32.ActiveCfg = Debug|Win32
+ {EF743D32-0980-44D9-BA9E-B9D2456251AC}.Debug|Win32.Build.0 = Debug|Win32
+ {EF743D32-0980-44D9-BA9E-B9D2456251AC}.Release|Win32.ActiveCfg = Release|Win32
+ {EF743D32-0980-44D9-BA9E-B9D2456251AC}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/FaceTrackNoIR.suo b/FaceTrackNoIR.suo
index d41bcf62..a0fb59e4 100644
--- a/FaceTrackNoIR.suo
+++ b/FaceTrackNoIR.suo
Binary files differ
diff --git a/FaceTrackNoIR/AutoClosePtr.h b/FaceTrackNoIR/AutoClosePtr.h
new file mode 100644
index 00000000..690d2c62
--- /dev/null
+++ b/FaceTrackNoIR/AutoClosePtr.h
@@ -0,0 +1,89 @@
+////////////////////////////////////////////////////////////////////////////////
+// Auto pointer template.
+
+#if !defined(AUTOCLOSEPTR_H)
+#define AUTOCLOSEPTR_H
+
+//////////////////////////////////////////////////////////////////////////////
+// T auto pointer (not suitable for containers).
+// This auto pointer uses T's member function to perform clean up, rather
+// than `operator Closeete'.
+//
+template<typename T, typename R, R (T::*Close)()>
+class AutoClosePtr
+{
+public:
+ explicit AutoClosePtr(T* p = NULL)
+ : m_p(p)
+ { // construct from object pointer
+ }
+
+ AutoClosePtr(AutoClosePtr& Other)
+ : m_p(Other.Release())
+ { // construct from Other AutoClosePtr
+ }
+
+ AutoClosePtr& operator=(AutoClosePtr& Other)
+ { // assign Other
+ Reset(Other.Release());
+ return (*this);
+ }
+
+ ~AutoClosePtr()
+ { // close and destroy
+ if(m_p)
+ {
+ (m_p->*Close)();
+ m_p = NULL;
+ }
+ }
+
+ T& operator*() const
+ { // return T
+ return (*m_p);
+ }
+
+ T** operator&()
+ { // return the address of the wrapped pointer
+ Reset();
+ return &m_p;
+ }
+
+ T* operator->() const
+ { // return wrapped pointer
+ return Get();
+ }
+
+ operator bool() const
+ { // check wrapped pointer for NULL
+ return m_p != NULL;
+ }
+
+ T* Get() const
+ { // return wrapped pointer
+ return m_p;
+ }
+
+ T* Release()
+ { // return wrapped pointer and give up ownership
+ T* pTmp = m_p;
+ m_p = NULL;
+ return pTmp;
+ }
+
+ void Reset(T* p = NULL)
+ { // destroy the object and store new pointer
+ if(p != m_p)
+ {
+ if(m_p)
+ (m_p->*Close)();
+ }
+
+ m_p = p;
+ }
+
+private:
+ T* m_p; // the wrapped raw pointer
+};
+
+#endif // AUTOCLOSEPTR_H \ No newline at end of file
diff --git a/FaceTrackNoIR/FaceTrackNoIR.cpp b/FaceTrackNoIR/FaceTrackNoIR.cpp
index 2acf8ec0..029ed5b7 100644
--- a/FaceTrackNoIR/FaceTrackNoIR.cpp
+++ b/FaceTrackNoIR/FaceTrackNoIR.cpp
@@ -34,9 +34,6 @@
#include "FGServer.h"
#include "FTNServer.h"
-using namespace sm::faceapi;
-using namespace sm::faceapi::qt;
-
//
// Setup the Main Dialog
//
@@ -56,7 +53,7 @@ QMainWindow(parent, flags)
_curve_config = 0;
tracker = 0;
- _display = 0;
+// _display = 0;
l = 0;
trayIcon = 0;
@@ -204,6 +201,13 @@ void FaceTrackNoIR::updateSettings() {
}
}
+//
+// Get a pointer to the video-widget, to use in the DLL
+//
+QFrame *FaceTrackNoIR::getVideoWidget() {
+ return ui.video_frame;
+}
+
/** read the name of the first video-capturing device at start up **/
/** FaceAPI can only use this first one... **/
void FaceTrackNoIR::GetCameraNameDX() {
@@ -488,21 +492,13 @@ void FaceTrackNoIR::startTracker( ) {
//
// Create the Tracker and setup
//
- tracker = new Tracker ( ui.iconcomboBox->currentIndex(), ui.iconcomboTrackerSource->currentIndex() );
-
- // Show the video widget
- //ui.video_frame->show();
- //_display = new VideoDisplayWidget( tracker->getEngine(), ui.video_frame, 0 );
- //l = new QVBoxLayout(ui.video_frame);
- //l->setMargin(0);
- //l->setSpacing(0);
- //l->addWidget(_display);
+ tracker = new Tracker ( ui.iconcomboBox->currentIndex(), ui.iconcomboTrackerSource->currentIndex(), this );
//
// Setup the Tracker and send the settings.
// This is necessary, because the events are only triggered 'on change'
//
- tracker->setup( ui.headPoseWidget , this);
+ tracker->setup();
tracker->setSmoothing ( ui.slideSmoothing->value() );
tracker->setUseFilter (ui.chkUseEWMA->isChecked() );
@@ -574,15 +570,7 @@ void FaceTrackNoIR::stopTracker( ) {
//
// Delete the video-display.
//
- if ( _display ) {
- _display->disconnect();
- delete _display;
- _display = 0;
- delete l;
- l = 0;
- qDebug() << "stopTracker says: display deleted";
- }
- ui.video_frame->hide();
+// ui.video_frame->hide();
if ( tracker ) {
qDebug() << "stopTracker says: Deleting tracker!";
diff --git a/FaceTrackNoIR/FaceTrackNoIR.h b/FaceTrackNoIR/FaceTrackNoIR.h
index 7518d896..7f73b5af 100644
--- a/FaceTrackNoIR/FaceTrackNoIR.h
+++ b/FaceTrackNoIR/FaceTrackNoIR.h
@@ -50,11 +50,11 @@ typedef AutoClosePtr<ITrackerDialog, void, &ITrackerDialog::Release> ITrackerDia
typedef ITrackerDialog *(WINAPI *importGetTrackerDialog)(void);
-#include <sm_api_qt.h>
+//#include <sm_api_qt.h>
#include <Dshow.h>
-using namespace sm::faceapi;
-using namespace sm::faceapi::qt;
+//using namespace sm::faceapi;
+//using namespace sm::faceapi::qt;
class Tracker; // pre-define class to avoid circular includes
@@ -72,6 +72,8 @@ public:
void getGameProgramName(); // Get the ProgramName from the game and display it.
void updateSettings(); // Update the settings (let Tracker read INI-file).
+ QFrame *getVideoWidget(); // Get a pointer to the video-widget, to use in the DLL
+
private:
Ui::FaceTrackNoIRClass ui;
Tracker *tracker;
@@ -82,7 +84,7 @@ private:
ITrackerDialogPtr pTrackerDialog; // Pointer to Tracker dialog instance (in DLL)
/** face api variables **/
- VideoDisplayWidget *_display;
+// VideoDisplayWidget *_display;
QVBoxLayout *l;
QWidget *_engine_controls;
QWidget *_server_controls;
diff --git a/FaceTrackNoIR/FaceTrackNoIR.vcproj b/FaceTrackNoIR/FaceTrackNoIR.vcproj
index 16a3d715..10245e4b 100644
--- a/FaceTrackNoIR/FaceTrackNoIR.vcproj
+++ b/FaceTrackNoIR/FaceTrackNoIR.vcproj
@@ -70,7 +70,7 @@
/>
<Tool
Name="VCLinkerTool"
- AdditionalDependencies="QtMain.lib QtCore4.lib QtGui4.lib QtNetwork4.lib smft32.lib smftcpp.lib smftqt.lib Strmiids.lib Ws2_32.lib QBezierConfigurator.lib FSUIPC_User.lib"
+ AdditionalDependencies="QtMain.lib QtCore4.lib QtGui4.lib QtNetwork4.lib Strmiids.lib Ws2_32.lib QBezierConfigurator.lib FSUIPC_User.lib"
OutputFile="$(OutDir)\$(ProjectName).exe"
AdditionalLibraryDirectories=""
GenerateManifest="true"
diff --git a/FaceTrackNoIR/main.cpp b/FaceTrackNoIR/main.cpp
index 60a2f71b..954bf592 100644
--- a/FaceTrackNoIR/main.cpp
+++ b/FaceTrackNoIR/main.cpp
@@ -34,9 +34,6 @@
#include <QDebug>
#include <QList>
-using namespace sm::faceapi;
-using namespace sm::faceapi::qt;
-
int main(int argc, char *argv[])
{
//// QApplication a(argc, argv);
diff --git a/FaceTrackNoIR/tracker.cpp b/FaceTrackNoIR/tracker.cpp
index 18b9c0b8..39bc8dce 100644
--- a/FaceTrackNoIR/tracker.cpp
+++ b/FaceTrackNoIR/tracker.cpp
@@ -23,6 +23,7 @@
*********************************************************************************/
/*
Modifications (last one on top):
+ 20110313 - WVR: Removed 'set_initial'. Less is more.
20110109 - WVR: Added setZero option to define behaviour after STOP tracking via shortkey.
20110104 - WVR: Removed a few nasty bugs (it was impossible to stop tracker without crash).
20101224 - WVR: Removed the QThread inheritance of the Base Class for the protocol-servers.
@@ -57,7 +58,6 @@
// Flags
bool Tracker::confid = false;
-bool Tracker::set_initial = false;
bool Tracker::do_tracking = true;
bool Tracker::do_center = false;
bool Tracker::do_inhibit = false;
@@ -66,7 +66,6 @@ bool Tracker::setZero = true;
bool Tracker::setEngineStop = true;
HANDLE Tracker::hTrackMutex = 0;
-long Tracker::prevHeadPoseTime = 0;
THeadPoseDOF Tracker::Pitch; // One structure for each of 6DOF's
THeadPoseDOF Tracker::Yaw;
THeadPoseDOF Tracker::Roll;
@@ -83,12 +82,15 @@ IFilterPtr Tracker::pFilter; // Pointer to Filter instance (in DLL)
/** constructor **/
-Tracker::Tracker( int clientID, int facetrackerID ) {
+Tracker::Tracker( int clientID, int facetrackerID, FaceTrackNoIR *parent ) {
importGetTracker getIT;
QLibrary *trackerLib;
importGetFilter getFilter;
QLibrary *filterLib;
+QFrame *video_frame;
+ // Retieve the pointer to the parent
+ mainApp = parent;
// Remember the selected client, from the ListBox
// If the Tracker runs, this can NOT be changed...
@@ -112,6 +114,13 @@ QLibrary *filterLib;
Tracker::Z.initHeadPoseData();
//
+ // Locate the video-frame, for the DLL
+ //
+ video_frame = 0;
+ video_frame = mainApp->getVideoWidget();
+ qDebug() << "Tracker::setup VideoFrame = " << video_frame;
+
+ //
// Start the selected Tracker-engine
//
switch (selectedTracker) {
@@ -125,7 +134,7 @@ QLibrary *filterLib;
if (ptrXyz)
{
pTracker = ptrXyz;
- pTracker->Initialize();
+ pTracker->Initialize( video_frame );
qDebug() << "Tracker::setup Function Resolved!";
}
}
@@ -144,7 +153,7 @@ QLibrary *filterLib;
if (ptrXyz)
{
pTracker = ptrXyz;
- pTracker->Initialize();
+ pTracker->Initialize( video_frame );
qDebug() << "Tracker::setup Function Resolved!";
}
}
@@ -255,12 +264,11 @@ Tracker::~Tracker() {
}
/** setting up the tracker engine **/
-void Tracker::setup(QWidget *head, FaceTrackNoIR *parent) {
+void Tracker::setup() {
bool DLL_Ok;
// retrieve pointers to the User Interface and the main Application
- mainApp = parent;
- pTracker->StartTracker();
+ pTracker->StartTracker( mainApp->winId() );
//
// Check if the Protocol-server files were installed OK.
@@ -296,14 +304,12 @@ void Tracker::run() {
bool lastStartStopKey = false;
bool lastInhibitKey = false;
- SYSTEMTIME now;
- long newHeadPoseTime;
- float dT;
-
THeadPoseData current_camera_position; // Used for filtering
THeadPoseData target_camera_position;
THeadPoseData new_camera_position;
+ Tracker::do_center = true; // Center initially
+
current_camera_position.x = 0.0f;
current_camera_position.y = 0.0f;
current_camera_position.z = 0.0f;
@@ -406,7 +412,6 @@ void Tracker::run() {
if (Tracker::do_tracking) {
Tracker::do_center = true;
- Tracker::set_initial = false;
Tracker::confid = false;
Pitch.rawList.clear();
@@ -429,7 +434,7 @@ void Tracker::run() {
current_camera_position.pitch = 0.0f;
current_camera_position.roll = 0.0f;
- pTracker->StartTracker();
+ pTracker->StartTracker( mainApp->winId() );
}
else {
if (setEngineStop) { // Only stop engine when option is checked
@@ -471,49 +476,21 @@ void Tracker::run() {
THeadPoseData newpose;
Tracker::confid = pTracker->GiveHeadPoseData(&newpose);
- addHeadPose(newpose);
-
- //
- // Get the System-time and substract the time from the previous call.
- // dT will be used for the EWMA-filter.
- //
- GetSystemTime ( &now );
- newHeadPoseTime = (((now.wHour * 3600) + (now.wMinute * 60) + now.wSecond) * 1000) + now.wMilliseconds;
- dT = (newHeadPoseTime - Tracker::prevHeadPoseTime) / 1000.0f;
-
- // Remember time for next call
- Tracker::prevHeadPoseTime = newHeadPoseTime;
-
- //if the confidence is good enough the headpose will be updated **/
- if (Tracker::confid) {
-
- //
- // Most games need an offset to the initial position and NOT the
- // absolute distance to the camera: so remember the initial distance
- // to substract that later...
- //
- if(Tracker::set_initial == false) {
- Tracker::Pitch.initial_headPos = Tracker::Pitch.headPos;
- Tracker::Yaw.initial_headPos = Tracker::Yaw.headPos;
- Tracker::Roll.initial_headPos = Tracker::Roll.headPos;
- Tracker::X.initial_headPos = Tracker::X.headPos;
- Tracker::Y.initial_headPos = Tracker::Y.headPos;
- Tracker::Z.initial_headPos = Tracker::Z.headPos;
- MessageBeep (MB_ICONASTERISK);
- Tracker::set_initial = true;
- }
+ if ( Tracker::confid ) {
+ addHeadPose(newpose);
}
//
// If Center is pressed, copy the current values to the offsets.
//
- if (Tracker::do_center && Tracker::set_initial) {
- Pitch.offset_headPos = getSmoothFromList( &Pitch.rawList )- Tracker::Pitch.initial_headPos;
- Yaw.offset_headPos = getSmoothFromList( &Yaw.rawList ) - Tracker::Yaw.initial_headPos;
- Roll.offset_headPos = getSmoothFromList( &Roll.rawList ) - Tracker::Roll.initial_headPos;
- X.offset_headPos = getSmoothFromList( &X.rawList ) - Tracker::X.initial_headPos;
- Y.offset_headPos = getSmoothFromList( &Y.rawList ) - Tracker::Y.initial_headPos;
- Z.offset_headPos = getSmoothFromList( &Z.rawList ) - Tracker::Z.initial_headPos;
+ if (Tracker::confid && Tracker::do_center) {
+ Pitch.offset_headPos = getSmoothFromList( &Pitch.rawList );
+ Yaw.offset_headPos = getSmoothFromList( &Yaw.rawList );
+ Roll.offset_headPos = getSmoothFromList( &Roll.rawList );
+ X.offset_headPos = getSmoothFromList( &X.rawList );
+ Y.offset_headPos = getSmoothFromList( &Y.rawList );
+ Z.offset_headPos = getSmoothFromList( &Z.rawList );
+ MessageBeep (MB_ICONASTERISK);
Tracker::do_center = false;
}
@@ -521,23 +498,23 @@ void Tracker::run() {
if (Tracker::do_tracking && Tracker::confid) {
// Pitch
- target_camera_position.x = getSmoothFromList( &X.rawList ) - X.offset_headPos - X.initial_headPos;
- target_camera_position.y = getSmoothFromList( &Y.rawList ) - Y.offset_headPos - Y.initial_headPos;
- target_camera_position.z = getSmoothFromList( &Z.rawList ) - Z.offset_headPos - Z.initial_headPos;
- target_camera_position.pitch = getSmoothFromList( &Pitch.rawList ) - Pitch.offset_headPos - Pitch.initial_headPos;
- target_camera_position.yaw = getSmoothFromList( &Yaw.rawList ) - Yaw.offset_headPos - Yaw.initial_headPos;
- target_camera_position.roll = getSmoothFromList( &Roll.rawList ) - Roll.offset_headPos - Roll.initial_headPos;
+ target_camera_position.x = getSmoothFromList( &X.rawList ) - X.offset_headPos;
+ target_camera_position.y = getSmoothFromList( &Y.rawList ) - Y.offset_headPos;
+ target_camera_position.z = getSmoothFromList( &Z.rawList ) - Z.offset_headPos;
+ target_camera_position.pitch = getSmoothFromList( &Pitch.rawList ) - Pitch.offset_headPos;
+ target_camera_position.yaw = getSmoothFromList( &Yaw.rawList ) - Yaw.offset_headPos;
+ target_camera_position.roll = getSmoothFromList( &Roll.rawList ) - Roll.offset_headPos;
if (Tracker::useFilter && pFilter) {
pFilter->FilterHeadPoseData(&current_camera_position, &target_camera_position, &new_camera_position, Tracker::Pitch.newSample);
}
else {
- new_camera_position.x = getSmoothFromList( &X.rawList ) - X.offset_headPos - X.initial_headPos;
- new_camera_position.y = getSmoothFromList( &Y.rawList ) - Y.offset_headPos - Y.initial_headPos;
- new_camera_position.z = getSmoothFromList( &Z.rawList ) - Z.offset_headPos - Z.initial_headPos;
- new_camera_position.pitch = getSmoothFromList( &Pitch.rawList ) - Pitch.offset_headPos - Pitch.initial_headPos;
- new_camera_position.yaw = getSmoothFromList( &Yaw.rawList ) - Yaw.offset_headPos - Yaw.initial_headPos;
- new_camera_position.roll = getSmoothFromList( &Roll.rawList ) - Roll.offset_headPos - Roll.initial_headPos;
+ new_camera_position.x = getSmoothFromList( &X.rawList ) - X.offset_headPos;
+ new_camera_position.y = getSmoothFromList( &Y.rawList ) - Y.offset_headPos;
+ new_camera_position.z = getSmoothFromList( &Z.rawList ) - Z.offset_headPos;
+ new_camera_position.pitch = getSmoothFromList( &Pitch.rawList ) - Pitch.offset_headPos;
+ new_camera_position.yaw = getSmoothFromList( &Yaw.rawList ) - Yaw.offset_headPos;
+ new_camera_position.roll = getSmoothFromList( &Roll.rawList ) - Roll.offset_headPos;
}
new_camera_position.x = X.invert * getOutputFromCurve(&X.curve, new_camera_position.x, X.NeutralZone, X.MaxInput);
new_camera_position.y = Y.invert * getOutputFromCurve(&Y.curve, new_camera_position.y, Y.NeutralZone, Y.MaxInput);
@@ -757,13 +734,13 @@ void Tracker::setPowCurve( int x ) {
// Set the filter-value from the GUI.
//
void Tracker::getHeadPose( THeadPoseData *data ) {
- data->x = Tracker::X.headPos - Tracker::X.initial_headPos; // centimeters
- data->y = Tracker::Y.headPos - Tracker::Y.initial_headPos;
- data->z = Tracker::Z.headPos - Tracker::Z.initial_headPos;
+ data->x = Tracker::X.headPos - Tracker::X.offset_headPos; // centimeters
+ data->y = Tracker::Y.headPos - Tracker::Y.offset_headPos;
+ data->z = Tracker::Z.headPos - Tracker::Z.offset_headPos;
- data->pitch = Tracker::Pitch.headPos- Tracker::Pitch.initial_headPos; // degrees
- data->yaw = Tracker::Yaw.headPos- Tracker::Yaw.initial_headPos;
- data->roll = Tracker::Roll.headPos - Tracker::Roll.initial_headPos;
+ data->pitch = Tracker::Pitch.headPos- Tracker::Pitch.offset_headPos; // degrees
+ data->yaw = Tracker::Yaw.headPos- Tracker::Yaw.offset_headPos;
+ data->roll = Tracker::Roll.headPos - Tracker::Roll.offset_headPos;
}
//
diff --git a/FaceTrackNoIR/tracker.h b/FaceTrackNoIR/tracker.h
index 2ee26bba..bd29bf9f 100644
--- a/FaceTrackNoIR/tracker.h
+++ b/FaceTrackNoIR/tracker.h
@@ -94,7 +94,7 @@ class THeadPoseDOF {
public:
void initHeadPoseData(){
headPos = 0.0f;
- initial_headPos = 0.0f;
+// initial_headPos = 0.0f;
offset_headPos = 0.0f;
invert = 0.0f;
red = 0.0f;
@@ -109,7 +109,7 @@ public:
newSample = FALSE;
}
float headPos; // Current position (from faceTracker, radials or meters)
- float initial_headPos; // Position on startup (first valid value)
+// float initial_headPos; // Position on startup (first valid value)
float offset_headPos; // Offset for centering
float invert; // Invert measured value (= 1.0f or -1.0f)
float red; // Reduction factor (used for EWMA-filtering, between 0.0f and 1.0f)
@@ -174,18 +174,15 @@ private:
// Flags to start/stop/reset tracking
static bool confid; // Tracker data is OK
- static bool set_initial; // initial headpose is set
static bool do_tracking; // Start/stop tracking, using the shortkey
static bool do_center; // Center head-position, using the shortkey
static bool do_inhibit; // Inhibit DOF-axis, using the shortkey
static HANDLE hTrackMutex; // Prevent reading/writing the headpose simultaneously
-
static bool useFilter; // Use EWMA-filtering
static bool setZero; // Set to zero's, when OFF (one-shot)
static bool setEngineStop; // Stop tracker->engine, when OFF
- static long prevHeadPoseTime; // Time from previous sample
FaceTrackNoIR *mainApp;
@@ -197,10 +194,10 @@ protected:
void run();
public:
- Tracker( int clientID, int facetrackerID );
+ Tracker( int clientID, int facetrackerID, FaceTrackNoIR *parent );
~Tracker();
- void setup(QWidget *head, FaceTrackNoIR *parent);
+ void setup();
// void registerHeadPoseCallback();
bool handleGameCommand ( int command );
diff --git a/List of compatible games.xls b/List of compatible games.xls
index 27f99e67..c314351e 100644
--- a/List of compatible games.xls
+++ b/List of compatible games.xls
Binary files differ
diff --git a/bin/FaceTrackNoIR.exe b/bin/FaceTrackNoIR.exe
index 092b7d83..2830d3be 100644
--- a/bin/FaceTrackNoIR.exe
+++ b/bin/FaceTrackNoIR.exe
Binary files differ
diff --git a/bin/NPClient.dll b/bin/NPClient.dll
index b979550f..8da41135 100644
--- a/bin/NPClient.dll
+++ b/bin/NPClient.dll
Binary files differ
diff --git a/bin/Settings/Wings of Prey.ini b/bin/Settings/Wings of Prey.ini
index 6e34cb00..022b058a 100644
--- a/bin/Settings/Wings of Prey.ini
+++ b/bin/Settings/Wings of Prey.ini
@@ -1,5 +1,5 @@
[Tracking]
-Smooth=7
+Smooth=10
NeutralZone=5
sensYaw=200
sensPitch=200
@@ -20,8 +20,8 @@ redRoll=60
redX=50
redY=50
redZ=50
-minSmooth=20
-powCurve=10
+minSmooth=30
+powCurve=5
maxSmooth=70
[GameProtocol]
@@ -49,15 +49,15 @@ Inhibit_Z=false
SetZero=false
[Curves]
-Yaw_point1=@Variant(\0\0\0\x1a@\x14\0\0\0\0\0\0@\x14\0\0\0\0\0\0)
-Yaw_point2=@Variant(\0\0\0\x1a@`\xd0\0\0\0\0\0@A@\0\0\0\0\0)
-Yaw_point3=@Variant(\0\0\0\x1a@f\0\0\0\0\0\0@9\x80\0\0\0\0\0)
+Yaw_point1=@Variant(\0\0\0\x1a@\x10\0\0\0\0\0\0@\x14\0\0\0\0\0\0)
+Yaw_point2=@Variant(\0\0\0\x1a@`\xe0\0\0\0\0\0@A\x80\0\0\0\0\0)
+Yaw_point3=@Variant(\0\0\0\x1a@f\0\0\0\0\0\0@:\0\0\0\0\0\0)
Yaw_point4=@Variant(\0\0\0\x1a@f\x80\0\0\0\0\0@I\0\0\0\0\0\0)
-Pitch_point1=@Variant(\0\0\0\x1a@\b\0\0\0\0\0\0@\b\0\0\0\0\0\0)
-Pitch_point2=@Variant(\0\0\0\x1a@X\x80\0\0\0\0\0@9\x80\0\0\0\0\0)
+Pitch_point1=@Variant(\0\0\0\x1a@\x16\0\0\0\0\0\0@\x16\0\0\0\0\0\0)
+Pitch_point2=@Variant(\0\0\0\x1a@X\x80\0\0\0\0\0@:\0\0\0\0\0\0)
Pitch_point3=@Variant(\0\0\0\x1a@e\x80\0\0\0\0\0@3\0\0\0\0\0\0)
Pitch_point4=@Variant(\0\0\0\x1a@f\x80\0\0\0\0\0@I\0\0\0\0\0\0)
-Roll_point1=@Variant(\0\0\0\x1a@\x10\0\0\0\0\0\0@\x10\0\0\0\0\0\0)
+Roll_point1=@Variant(\0\0\0\x1a@\x10\0\0\0\0\0\0@\x14\0\0\0\0\0\0)
Roll_point2=@Variant(\0\0\0\x1a@^\xc0\0\0\0\0\0@?\0\0\0\0\0\0)
Roll_point3=@Variant(\0\0\0\x1a@d@\0\0\0\0\0@B\0\0\0\0\0\0)
Roll_point4=@Variant(\0\0\0\x1a@d@\0\0\0\0\0@I\0\0\0\0\0\0)
@@ -75,7 +75,7 @@ Z_point3=@Variant(\0\0\0\x1a@L\x80\0\0\0\0\0@A\0\0\0\0\0\0)
Z_point4=@Variant(\0\0\0\x1a@V\0\0\0\0\0\0@I\0\0\0\0\0\0)
[FTIR]
-useTIRViews=false
+useTIRViews=true
[TrackerSource]
Selection=0
diff --git a/faceAPI/build_options.h b/faceAPI/build_options.h
new file mode 100644
index 00000000..6bc6a44c
--- /dev/null
+++ b/faceAPI/build_options.h
@@ -0,0 +1,8 @@
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Build Options
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+//controls whether or not FaceAPI should use the callback or poll
+#define USE_HEADPOSE_CALLBACK 1
diff --git a/faceAPI/lock.h b/faceAPI/lock.h
new file mode 100644
index 00000000..efe38605
--- /dev/null
+++ b/faceAPI/lock.h
@@ -0,0 +1,35 @@
+#ifndef SM_API_TESTAPPCONSOLE_LOCK_H
+#define SM_API_TESTAPPCONSOLE_LOCK_H
+
+#include "mutex.h"
+
+namespace sm
+{
+ namespace faceapi
+ {
+ namespace samplecode
+ {
+ // A very simple scoped-lock class for sample code purposes.
+ // It is recommended that you use the boost threads library.
+ class Lock
+ {
+ public:
+ Lock(const Mutex &mutex): _mutex(mutex)
+ {
+ _mutex.lock();
+ }
+ ~Lock()
+ {
+ _mutex.unlock();
+ }
+ private:
+ // Noncopyable
+ Lock(const Lock &);
+ Lock &operator=(const Lock &);
+ private:
+ const Mutex &_mutex;
+ };
+ }
+ }
+}
+#endif
diff --git a/faceAPI/lockfree.h b/faceAPI/lockfree.h
new file mode 100644
index 00000000..ce7d2a64
--- /dev/null
+++ b/faceAPI/lockfree.h
@@ -0,0 +1,65 @@
+//lock free queue template class by Herb Sutter
+//Dr Dobbs Journal article http://www.drdobbs.com/cpp/210604448;jsessionid=OQGQPSMNL4X4XQE1GHPSKH4ATMY32JVN?pgno=1
+
+template <typename T> class LockFreeQueue
+{
+private:
+ struct Node
+ {
+ Node( T val ) : value(val), next(nullptr) { }
+ T value;
+ Node* next;
+ };
+
+ Node* first; // for producer only
+ Node* divider, last; // shared
+
+ //not working in VC2008
+ //atomic<Node*> divider, last; // shared
+
+public:
+ LockFreeQueue()
+ {
+ // add dummy separator
+ first = divider = last = new Node( T() );
+ }
+
+ ~LockFreeQueue()
+ {
+ while( first != nullptr )
+ {
+ // release the list
+ Node* tmp = first;
+ first = tmp->next;
+ delete tmp;
+ }
+ }
+
+ void Produce( const T& t )
+ {
+ last->next = new Node(t); // add the new item
+ last = last->next; // publish it
+
+ while( first != divider )
+ {
+ // trim unused nodes
+ Node* tmp = first;
+ first = first->next;
+ delete tmp;
+ }
+ }
+
+ bool Consume( T& result )
+ {
+ if( divider != last )
+ {
+ // if queue is nonempty
+ result = divider->next->value; // copy it back
+ divider = divider->next; // publish that we took it
+ return true; // and report success
+ }
+
+ return false; // else report empty
+ }
+};
+
diff --git a/faceAPI/main.cpp b/faceAPI/main.cpp
new file mode 100644
index 00000000..0aeb9bca
--- /dev/null
+++ b/faceAPI/main.cpp
@@ -0,0 +1,460 @@
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// FaceAPI2FSX program implementation
+// Merges the old CockpitCamera.cpp and TestAppConsole.cpp into a single file
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+//Precompiled header
+#include "stdafx.h"
+
+//FaceAPI headers
+#include "sm_api.h"
+#include "utils.h"
+
+//local headers
+#include "build_options.h"
+
+//namespaces
+using namespace std;
+using namespace sm::faceapi::samplecode;
+
+//
+// Definitions for the Shared Memory to send the data to FaceTrackNoIR
+//
+static const char* SM_MM_DATA = "SM_SharedMem";
+static const char* SM_FACEAPI = "SM_FaceAPI";
+static const char* SM_MUTEX = "SM_Mutex";
+
+struct TFaceData {
+ int DataID;
+ smEngineHeadPoseData new_pose;
+};
+typedef TFaceData * PFaceData;
+
+struct SMMemMap {
+ int command; // Command from FaceTrackNoIR
+ int status; // Status from faceAPI
+ TFaceData data;
+ HANDLE handle;
+};
+typedef SMMemMap * PSMMemMap;
+
+//
+// global variables
+//
+HANDLE hSMMemMap = NULL;
+SMMemMap *pMemData;
+HANDLE hSMMutex;
+smEngineHeadPoseData new_head_pose;
+
+//enums
+enum GROUP_ID
+{
+ GROUP0=0,
+};
+
+enum EVENT_ID
+{
+ EVENT_PING=0,
+ EVENT_INIT,
+};
+
+enum INPUT_ID
+{
+ INPUT0=0,
+};
+
+//function definitions
+void updateHeadPose(smEngineHeadPoseData* temp_head_pose);
+bool SMCreateMapping();
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+//FaceAPI function implementations
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void STDCALL receiveLogMessage(void *, const char *buf, int /*buf_len*/)
+{
+ Lock lock(g_mutex); // serialize logging calls from different threads to avoid garbled output.
+ //cout << string(buf);
+}
+
+// Callback function for face-data
+void STDCALL receiveFaceData(void *, smEngineFaceData face_data, smCameraVideoFrame video_frame)
+{
+ Lock lock(g_mutex);
+
+ // Get info including data pointer to original image from camera
+ smImageInfo video_frame_image_info;
+ THROW_ON_ERROR(smImageGetInfo(video_frame.image_handle, &video_frame_image_info)); // reentrant, so ok
+
+ // video_frame_image_info.plane_addr[*] now point to the image memory planes.
+ // The memory is only valid until the end of this routine unless you call smImageAddRef(video_frame.image_handle).
+ // So you can deep copy the image data here, or use smImageAddRef() and just copy the pointer.
+ // If you use smImageAddRef() you are responsible for calling smImageDestroy() to avoid a memory leak later.
+
+ // In this callback you will typically want to copy the smEngineFaceData data into your own data-structure.
+ // Since the smEngineFaceData contains multiple pod types copying it is not atomic and
+ // a mutex is required to avoid the race-condition with any thread simultaneously
+ // reading from your data-structure.
+ // Such a race condition will not crash your code but will create weird noise in the tracking data.
+
+ if (g_do_face_data_printing)
+ {
+ //cout << video_frame << " " << face_data;
+
+ // Save any face texture to a PNG file
+ if (face_data.texture)
+ {
+ // Create a unique filename
+ std::stringstream filename;
+ filename << "face_" << video_frame.frame_num << ".png";
+ // Try saving to a file
+ if (saveToPNGFile(filename.str(), face_data.texture->image_info) == SM_API_OK)
+ {
+ cout << "Saved face-texture to " << filename.str() << std::endl;
+ }
+ else
+ {
+ cout << "Error saving face-texture to " << filename.str() << std::endl;
+ }
+ }
+ }
+}
+
+// Callback function for head-pose
+void STDCALL receiveHeadPose(void *,smEngineHeadPoseData head_pose, smCameraVideoFrame video_frame)
+{
+ Lock lock(g_mutex);
+
+ // Get info including data pointer to original image from camera
+ smImageInfo video_frame_image_info;
+ THROW_ON_ERROR(smImageGetInfo(video_frame.image_handle, &video_frame_image_info)); // reentrant, so ok
+
+ // video_frame_image_info.plane_addr[*] now point to the image memory planes.
+ // The memory is only valid until the end of this routine unless you call smImageAddRef(video_frame.image_handle).
+ // So you can deep copy the image data here, or use smImageAddRef() and just copy the pointer.
+ // If you use smImageAddRef() you are responsible for calling smImageDestroy() to avoid a memory leak later.
+
+ // In this callback you will typically want to copy the smEngineFaceData data into your own data-structure.
+ // Since the smEngineFaceData contains multiple pod types copying it is not atomic and
+ // a mutex is required to avoid the race-condition with any thread simultaneously
+ // reading from your data-structure.
+ // Such a race condition will not crash your code but will create weird noise in the tracking data.
+
+ if (g_do_head_pose_printing)
+ {
+ //cout << video_frame << " " << head_pose << std::endl;
+ }
+
+ //make a copy of the new head pose data and send it to simconnect
+ //when we get a simmconnect frame event the new offset will be applied to the camera
+ updateHeadPose(&head_pose);
+}
+
+// Create the first available camera detected on the system, and return its handle
+smCameraHandle createFirstCamera()
+{
+ // Detect cameras
+ smCameraInfoList info_list;
+ THROW_ON_ERROR(smCameraCreateInfoList(&info_list));
+
+ if (info_list.num_cameras == 0)
+ {
+ throw runtime_error("No cameras were detected");
+ }
+ else
+ {
+ cout << "The followings cameras were detected: " << endl;
+ for (int i=0; i<info_list.num_cameras; ++i)
+ {
+ char buf[1024];
+ cout << " " << i << ". Type: " << info_list.info[i].type;
+ THROW_ON_ERROR(smStringWriteBuffer(info_list.info[i].model,buf,1024));
+ cout << " Model: " << string(buf);
+ cout << " Instance: " << info_list.info[i].instance_index << endl;
+ // Print all the possible formats for the camera
+ for (int j=0; j<info_list.info[i].num_formats; j++)
+ {
+ smCameraVideoFormat video_format = info_list.info[i].formats[j];
+ cout << " - Format: ";
+ cout << " res (" << video_format.res.w << "," << video_format.res.h << ")";
+ cout << " image code " << video_format.format;
+ cout << " framerate " << video_format.framerate << "(hz)";
+ cout << " upside-down? " << (video_format.is_upside_down ? "y":"n") << endl;
+ }
+ }
+ }
+
+ // Create the first camera detected on the system
+ smCameraHandle camera_handle = 0;
+ THROW_ON_ERROR(smCameraCreate(&info_list.info[0], // Use first camera
+ 0, // Use default settings for lens
+ &camera_handle));
+
+ // Destroy the info list
+ smCameraDestroyInfoList(&info_list);
+
+ return camera_handle;
+}
+
+// The main function: setup a tracking engine and show a video window, then loop on the keyboard.
+void run()
+{
+ // Capture control-C
+// signal(SIGINT, CtrlCHandler);
+
+ // Make the console window a bit bigger (see utils.h)
+ initConsole();
+
+ #ifdef _DEBUG
+ // Log API debugging information to a file
+ THROW_ON_ERROR(smLoggingSetFileOutputEnable(SM_API_TRUE));
+
+ // Hook up log message callback
+ THROW_ON_ERROR(smLoggingRegisterCallback(0,receiveLogMessage));
+ #endif
+
+ // Get the version
+ int major, minor, maint;
+ THROW_ON_ERROR(smAPIVersion(&major, &minor, &maint));
+ cout << endl << "API VERSION: " << major << "." << minor << "." << maint << "." << endl << endl;
+ // Print detailed license info
+ char *buff;
+ int size;
+ THROW_ON_ERROR(smAPILicenseInfoString(0,&size,SM_API_TRUE));
+ buff = new char[size];
+ THROW_ON_ERROR(smAPILicenseInfoString(buff,&size,SM_API_TRUE));
+ cout << "LICENSE: " << buff << endl << endl;
+ // Determine if non-commercial restrictions apply
+ const bool non_commercial_license = smAPINonCommercialLicense() == SM_API_TRUE;
+
+ // Initialize the API
+ THROW_ON_ERROR(smAPIInit());
+
+ #ifdef _DEBUG
+ // Get the path to the logfile
+ smStringHandle logfile_path_handle = 0;
+ THROW_ON_ERROR(smStringCreate(&logfile_path_handle));
+ THROW_ON_ERROR(smLoggingGetPath(logfile_path_handle));
+ int buf_len = 0;
+ unsigned short *buf = 0;
+ THROW_ON_ERROR(smStringGetBufferW(logfile_path_handle,(wchar_t **)&buf,&buf_len));
+ wcout << "Writing log to file: " << wstring((wchar_t *)buf) << endl;
+ THROW_ON_ERROR(smStringDestroy(&logfile_path_handle));
+ #endif
+
+ // Register the WDM category of cameras
+ THROW_ON_ERROR(smCameraRegisterType(SM_API_CAMERA_TYPE_WDM));
+
+ smEngineHandle engine_handle = 0;
+ smCameraHandle camera_handle = 0;
+ if (non_commercial_license)
+ {
+ // Create a new Head-Tracker engine that uses the camera
+ THROW_ON_ERROR(smEngineCreate(SM_API_ENGINE_LATEST_HEAD_TRACKER,&engine_handle));
+ }
+ else
+ {
+ // Print out a list of connected cameras, and choose the first camera on the system
+ camera_handle = createFirstCamera();
+ // Create a new Head-Tracker engine that uses the camera
+ THROW_ON_ERROR(smEngineCreateWithCamera(SM_API_ENGINE_LATEST_HEAD_TRACKER,camera_handle,&engine_handle));
+ }
+
+ // Check license for particular engine version (always ok for non-commercial license)
+ const bool engine_licensed = smEngineIsLicensed(engine_handle) == SM_API_OK;
+
+ cout << "-----------------------------------------------------" << endl;
+ cout << "Press 'r' to restart tracking" << endl;
+ cout << "Press 'a' to toggle auto-restart mode" << endl;
+ if (!non_commercial_license)
+ {
+ cout << "Press 'l' to toggle lip-tracking" << endl;
+ cout << "Press 'e' to toggle eyebrow-tracking" << endl;
+ }
+ if (engine_licensed)
+ {
+ cout << "Press 'h' to toggle printing of head-pose data" << endl;
+ cout << "Press 'f' to toggle printing of face-landmark data" << endl;
+ }
+ cout << "Press '1' to toggle face coordinate frame axes" << endl;
+ cout << "Press '2' to toggle performance info" << endl;
+ cout << "Press '3' to toggle face mask" << endl;
+ cout << "Press '4' to toggle face landmarks" << endl;
+ cout << "Press CTRL-C or 'q' to quit" << endl;
+ cout << "-----------------------------------------------------" << endl;
+
+ // Hook up callbacks to receive output data from engine.
+ // These functions will return errors if the engine is not licensed.
+ if (engine_licensed)
+ {
+ #if (USE_HEADPOSE_CALLBACK==1)
+ #pragma message("Using Headpose Callback")
+ THROW_ON_ERROR(smHTRegisterHeadPoseCallback(engine_handle,0,receiveHeadPose));
+ #endif
+ if (!non_commercial_license)
+ {
+ THROW_ON_ERROR(smHTRegisterFaceDataCallback(engine_handle,0,receiveFaceData));
+ }
+ }
+ else
+ {
+ cout << "Engine is not licensed, cannot obtain any output data." << endl;
+ }
+
+ if (!non_commercial_license)
+ {
+ // Enable lip and eyebrow tracking
+ THROW_ON_ERROR(smHTSetLipTrackingEnabled(engine_handle,SM_API_TRUE));
+ THROW_ON_ERROR(smHTSetEyebrowTrackingEnabled(engine_handle,SM_API_TRUE));
+ }
+
+ // Create and show a video-display window
+ smVideoDisplayHandle video_display_handle = 0;
+ THROW_ON_ERROR(smVideoDisplayCreate(engine_handle,&video_display_handle,0,TRUE));
+
+ // Setup the VideoDisplay
+ THROW_ON_ERROR(smVideoDisplaySetFlags(video_display_handle,g_overlay_flags));
+
+ // Get the handle to the window and change the title to "Hello World"
+ smWindowHandle win_handle = 0;
+ THROW_ON_ERROR(smVideoDisplayGetWindowHandle(video_display_handle,&win_handle));
+ SetWindowText(win_handle, _T("faceAPI Video-widget"));
+
+ // Start tracking
+ THROW_ON_ERROR(smEngineStart(engine_handle));
+
+ // Loop on the keyboard
+ while (processKeyPress(engine_handle, video_display_handle))
+ {
+ // Read and print the current head-pose (if not using the callback mechanism)
+ #if (USE_HEADPOSE_CALLBACK==0)
+ #pragma message("Polling Headpose Manually")
+ if (engine_licensed)
+ {
+ smEngineHeadPoseData head_pose;
+ Lock lock(g_mutex);
+
+ THROW_ON_ERROR(smHTCurrentHeadPose(engine_handle,&head_pose));
+ if (g_do_head_pose_printing)
+ {
+ std::cout << head_pose << std::endl;
+ }
+
+ }
+ #endif
+
+ // NOTE: If you have a windows event loop in your program you
+ // will not need to call smAPIProcessEvents(). This manually redraws the video window.
+ THROW_ON_ERROR(smAPIProcessEvents());
+
+ // Prevent CPU overload in our simple loop.
+ const int frame_period_ms = 10;
+ Sleep(frame_period_ms);
+ }
+
+ // Destroy engine
+ THROW_ON_ERROR(smEngineDestroy(&engine_handle));
+ // Destroy video display
+ THROW_ON_ERROR(smVideoDisplayDestroy(&video_display_handle));
+} // run()
+
+// Application entry point
+int _tmain(int /*argc*/, _TCHAR** /*argv*/)
+{
+ OutputDebugString(_T("_tmain() says: Starting Function\n"));
+
+ try
+ {
+ if (SMCreateMapping()) {
+ run();
+ }
+ }
+ catch (exception &e)
+ {
+ cerr << e.what() << endl;
+ }
+
+ return smAPIQuit();
+}
+
+//
+// This is called exactly once for each FaceAPI callback and must be within an exclusive lock
+//
+void updateHeadPose(smEngineHeadPoseData* temp_head_pose)
+{
+ //
+ // Check if the pointer is OK and wait for the Mutex.
+ //
+ if ( (pMemData != NULL) && (WaitForSingleObject(hSMMutex, 100) == WAIT_OBJECT_0) ) {
+
+ //
+ // Copy the Raw measurements directly to the client.
+ //
+ if (temp_head_pose->confidence > 0.0f)
+ {
+ memcpy(&pMemData->data,temp_head_pose,sizeof(smEngineHeadPoseData));
+ }
+ ReleaseMutex(hSMMutex);
+ }
+
+};
+
+//
+// Create a memory-mapping to the faceAPI data.
+// It contains the tracking data, a command-code from FaceTrackNoIR
+//
+//
+bool SMCreateMapping()
+{
+ OutputDebugString(_T("FTCreateMapping says: Starting Function\n"));
+
+ //
+ // A FileMapping is used to create 'shared memory' between the faceAPI and FaceTrackNoIR.
+ //
+ // Try to create a FileMapping to the Shared Memory.
+ // If one already exists: close it.
+ //
+ hSMMemMap = CreateFileMappingA( INVALID_HANDLE_VALUE , 00 , PAGE_READWRITE , 0 ,
+ sizeof( TFaceData ) + sizeof( HANDLE ) + 100,
+ (LPCSTR) SM_MM_DATA );
+
+ if ( hSMMemMap != 0 ) {
+ OutputDebugString(_T("FTCreateMapping says: FileMapping Created!\n"));
+ }
+
+ if ( ( hSMMemMap != 0 ) && ( (long) GetLastError == ERROR_ALREADY_EXISTS ) ) {
+ CloseHandle( hSMMemMap );
+ hSMMemMap = 0;
+ }
+
+ //
+ // Create a new FileMapping, Read/Write access
+ //
+ hSMMemMap = OpenFileMappingA( FILE_MAP_ALL_ACCESS , false , (LPCSTR) SM_MM_DATA );
+ if ( ( hSMMemMap != 0 ) ) {
+ OutputDebugString(_T("FTCreateMapping says: FileMapping Created again...\n"));
+ pMemData = (SMMemMap *) MapViewOfFile(hSMMemMap, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(TFaceData));
+ if (pMemData != NULL) {
+ OutputDebugString(_T("FTCreateMapping says: MapViewOfFile OK.\n"));
+// pMemData->handle = handle; // The game uses the handle, to send a message that the Program-Name was set!
+ }
+ hSMMutex = CreateMutexA(NULL, false, SM_MUTEX);
+ }
+ else {
+ OutputDebugString(_T("Error creating Shared Memory for faceAPI\n"));
+ cerr << "Error creating Shared Memory for faceAPI" << endl;
+ return false;
+ }
+
+ //if (pMemData != NULL) {
+ // pMemData->data.DataID = 1;
+ // pMemData->data.CamWidth = 100;
+ // pMemData->data.CamHeight = 250;
+ //}
+
+ return true;
+}
diff --git a/faceAPI/mutex.h b/faceAPI/mutex.h
new file mode 100644
index 00000000..11aabafc
--- /dev/null
+++ b/faceAPI/mutex.h
@@ -0,0 +1,44 @@
+#ifndef SM_API_TESTAPPCONSOLE_MUTEX_H
+#define SM_API_TESTAPPCONSOLE_MUTEX_H
+
+namespace sm
+{
+ namespace faceapi
+ {
+ namespace samplecode
+ {
+ // A very simple mutex class for sample code purposes.
+ // It is recommended that you use the boost threads library.
+ class Mutex
+ {
+ public:
+ Mutex()
+ {
+ if (!InitializeCriticalSectionAndSpinCount(&_cs,0x80000400))
+ {
+ throw std::runtime_error("Failed to initialize Mutex");
+ }
+ }
+ ~Mutex()
+ {
+ DeleteCriticalSection(&_cs);
+ }
+ void lock() const
+ {
+ EnterCriticalSection(&_cs);
+ }
+ void unlock() const
+ {
+ LeaveCriticalSection(&_cs);
+ }
+ private:
+ // Noncopyable
+ Mutex(const Mutex &);
+ Mutex &operator=(const Mutex &);
+ private:
+ mutable CRITICAL_SECTION _cs;
+ };
+ }
+ }
+}
+#endif
diff --git a/faceAPI/stdafx.cpp b/faceAPI/stdafx.cpp
new file mode 100644
index 00000000..b4263f59
--- /dev/null
+++ b/faceAPI/stdafx.cpp
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// TestAppConsole.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/faceAPI/stdafx.h b/faceAPI/stdafx.h
new file mode 100644
index 00000000..4ac15bbc
--- /dev/null
+++ b/faceAPI/stdafx.h
@@ -0,0 +1,21 @@
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later.
+#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows.
+#endif
+
+#include <stdio.h>
+#include <tchar.h>
+
+// TODO: reference additional headers your program requires here
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <cassert>
+#include <conio.h>
+#include "sm_api.h"
diff --git a/faceAPI/utils.h b/faceAPI/utils.h
new file mode 100644
index 00000000..9c67665e
--- /dev/null
+++ b/faceAPI/utils.h
@@ -0,0 +1,346 @@
+#ifndef SM_API_TESTAPPCONSOLE_UTILS_H
+#define SM_API_TESTAPPCONSOLE_UTILS_H
+
+#include "lock.h"
+
+#define THROW_ON_ERROR(x) \
+{ \
+ smReturnCode result = (x); \
+ if (result < 0) \
+ { \
+ std::stringstream s; \
+ s << "API error code: " << result; \
+ throw std::runtime_error(s.str()); \
+ } \
+}
+
+namespace sm
+{
+ namespace faceapi
+ {
+ namespace samplecode
+ {
+ // Global variables
+ Mutex g_mutex;
+ bool g_ctrl_c_detected(false);
+ bool g_do_head_pose_printing(false);
+ bool g_do_face_data_printing(false);
+ unsigned short g_overlay_flags(SM_API_VIDEO_DISPLAY_HEAD_MESH);
+
+ // CTRL-C handler function
+ void __cdecl CtrlCHandler(int)
+ {
+ Lock lock(g_mutex);
+ std::cout << "Ctrl-C detected, stopping..." << std::endl;
+ g_ctrl_c_detected = true;
+ }
+
+ // Radians to degrees conversion
+ float rad2deg(float rad)
+ {
+ return rad*57.2957795f;
+ }
+
+ void toggleFlag(unsigned short &val, unsigned short flag)
+ {
+ if (val & flag)
+ {
+ val = val & ~flag;
+ }
+ else
+ {
+ val = val | flag;
+ }
+ }
+
+ // Save an image to PNG file
+ smReturnCode saveToPNGFile(const std::string& filepath, smImageInfo image_info)
+ {
+ smBool ok;
+ smReturnCode error;
+
+ // Create an API string
+ smStringHandle filepath_handle = 0;
+ ok = (error = smStringCreate(&filepath_handle)) == SM_API_OK;
+ ok = ok && (error = smStringReadBuffer(filepath_handle,filepath.c_str(),filepath.size())) == SM_API_OK;
+
+ // Create an API image
+ smImageHandle image_handle = 0;
+ smImageMemoryCopyMode copy_mode = SM_API_IMAGE_MEMORYCOPYMODE_AUTO;
+ ok = ok && (error = smImageCreateFromInfo(&image_info,&copy_mode,&image_handle)) == SM_API_OK;
+
+ // Save the image as PNG
+ ok = ok && (error = smImageSaveToPNG(image_handle,filepath_handle)) == SM_API_OK;
+
+ // Destroy the image and string
+ smStringDestroy(&filepath_handle);
+ smImageDestroy(&image_handle);
+ return error;
+ }
+
+ // Stream operators for printing
+
+ std::ostream &operator<<(std::ostream & os, const smSize2i &s)
+ {
+ return os << "[" << s.h << "," << s.h << "]";
+ }
+
+ std::ostream &operator<<(std::ostream & os, const smCoord3f &pos)
+ {
+ return os << "(" << pos.x << "," << pos.y << "," << pos.z << ")";
+ }
+
+ std::ostream &operator<<(std::ostream & os, const smRotEuler &rot)
+ {
+ return os << "(" << rad2deg(rot.x_rads) << "," << rad2deg(rot.y_rads) << "," << rad2deg(rot.z_rads) << ")";
+ }
+
+ std::ostream &operator<<(std::ostream & os, const smPixel &p)
+ {
+ return os << "[" << static_cast<int>(p.x) << "," << static_cast<int>(p.y) << "]";
+ }
+
+ std::ostream &operator<<(std::ostream & os, const smFaceTexCoord &ftc)
+ {
+ return os << "{" << ftc.u << "," << ftc.v << "}";
+ }
+
+ std::ostream &operator<<(std::ostream & os, const smFaceLandmark &lm)
+ {
+ return os << "id "<< lm.id << " fc" << lm.fc << " ftc" << lm.ftc << " pc" << lm.pc << " wc" << lm.wc;
+ }
+
+ std::ostream &operator<<(std::ostream & os, const smImageInfo &im)
+ {
+ os << "format ";
+ switch (im.format)
+ {
+ case SM_API_IMAGECODE_GRAY_8U:
+ os << "GRAY_8U";
+ break;
+ case SM_API_IMAGECODE_GRAY_16U:
+ os << "GRAY_16U";
+ break;
+ case SM_API_IMAGECODE_YUY2:
+ os << "YUY2";
+ break;
+ case SM_API_IMAGECODE_I420:
+ os << "I420";
+ break;
+ case SM_API_IMAGECODE_BGRA_32U:
+ os << "BGRA_32U";
+ break;
+ case SM_API_IMAGECODE_ARGB_32U:
+ os << "ARGB_32U";
+ break;
+ case SM_API_IMAGECODE_BGR_24U:
+ os << "BGR_24U";
+ break;
+ case SM_API_IMAGECODE_RGB_24U:
+ os << "RGB_24U";
+ break;
+ default:
+ os << "unknown";
+ break;
+ }
+ os << " res" << im.res;
+ os << " plane_addr(" << static_cast<void *>(im.plane_addr[0]) << ","
+ << static_cast<void *>(im.plane_addr[1]) << ","
+ << static_cast<void *>(im.plane_addr[2]) << ","
+ << static_cast<void *>(im.plane_addr[3]) << ")";
+ os << " step_bytes(" << im.step_bytes[0] << "," << im.step_bytes[1] << "," << im.step_bytes[2] << "," << im.step_bytes[3] << ")";
+ os << " user_data " << im.user_data;
+ return os;
+ }
+
+ std::ostream &operator<<(std::ostream & os, const smFaceTexture &t)
+ {
+ os << "type ";
+ switch (t.type)
+ {
+ case SM_ORTHOGRAPHIC_PROJECTION:
+ os << "orthographic";
+ break;
+ default:
+ os << "unknown";
+ break;
+ }
+ os << " origin" << t.origin << " scale" << t.scale << std::endl;
+ os << " image_info " << t.image_info << std::endl;
+ os << " num_mask_landmarks " << t.num_mask_landmarks << std::endl;
+ for (int i=0; i<t.num_mask_landmarks; i++)
+ {
+ os << " " << t.mask_landmarks[i] << std::endl;
+ }
+ return os;
+ }
+
+ // Stream operator for printing face landmarks
+ std::ostream &operator<<(std::ostream &os, const smEngineFaceData &face_data)
+ {
+ fixed(os);
+ showpos(os);
+ os.precision(2);
+ os << "Face Data: "
+ << "origin_wc" << face_data.origin_wc << " "
+ << "num_landmarks " << face_data.num_landmarks
+ << std::endl;
+ for (int i=0; i<face_data.num_landmarks; i++)
+ {
+ os << " " << face_data.landmarks[i] << std::endl;
+ }
+ // Print any face texture info
+ if (face_data.texture)
+ {
+ os << "Face Texture: " << *face_data.texture;
+ }
+ return os;
+ }
+
+ // Stream operator for printing head-pose data
+ std::ostream &operator<<(std::ostream & os, const smEngineHeadPoseData &head_pose)
+ {
+ fixed(os);
+ showpos(os);
+ os.precision(2);
+ return os << "Head Pose: "
+ << "head_pos" << head_pose.head_pos << " "
+ << "head_rot" << head_pose.head_rot << " "
+ << "left_eye_pos" << head_pose.left_eye_pos << " "
+ << "right_eye_pos" << head_pose.right_eye_pos << " "
+ << "confidence " << head_pose.confidence;
+ }
+
+ std::ostream &operator<<(std::ostream & os, const smCameraVideoFrame &vf)
+ {
+ fixed(os);
+ showpos(os);
+ os.precision(2);
+ return os << "Framenum: " << vf.frame_num;
+ }
+
+ // Handles keyboard events: return false if quit.
+ bool processKeyPress(smEngineHandle engine_handle, smVideoDisplayHandle video_display_handle)
+ {
+ Lock lock(g_mutex);
+ if (g_ctrl_c_detected)
+ {
+ return false;
+ }
+ if (!_kbhit())
+ {
+ return true;
+ }
+ int key = _getch();
+ switch (key)
+ {
+ case 'q':
+ return false;
+ case 'r':
+ {
+ // Manually restart the tracking
+ THROW_ON_ERROR(smEngineStart(engine_handle));
+ std::cout << "Restarting tracking" << std::endl;
+ }
+ return true;
+ case 'a':
+ {
+ // Toggle auto-restart mode
+ int on;
+ THROW_ON_ERROR(smHTGetAutoRestartMode(engine_handle,&on));
+ THROW_ON_ERROR(smHTSetAutoRestartMode(engine_handle,!on));
+ std::cout << "Autorestart-mode is " << (on?"on":"off") << std::endl;
+ }
+ return true;
+ case '1':
+ toggleFlag(g_overlay_flags,SM_API_VIDEO_DISPLAY_REFERENCE_FRAME);
+ THROW_ON_ERROR(smVideoDisplaySetFlags(video_display_handle,g_overlay_flags));
+ return true;
+ case '2':
+ toggleFlag(g_overlay_flags,SM_API_VIDEO_DISPLAY_PERFORMANCE);
+ THROW_ON_ERROR(smVideoDisplaySetFlags(video_display_handle,g_overlay_flags));
+ return true;
+ case '3':
+ toggleFlag(g_overlay_flags,SM_API_VIDEO_DISPLAY_HEAD_MESH);
+ THROW_ON_ERROR(smVideoDisplaySetFlags(video_display_handle,g_overlay_flags));
+ return true;
+ case '4':
+ toggleFlag(g_overlay_flags,SM_API_VIDEO_DISPLAY_LANDMARKS);
+ THROW_ON_ERROR(smVideoDisplaySetFlags(video_display_handle,g_overlay_flags));
+ return true;
+ case 'l':
+ if (smAPINonCommercialLicense() == SM_API_TRUE)
+ {
+ return false;
+ }
+ else
+ {
+ int on;
+ THROW_ON_ERROR(smHTGetLipTrackingEnabled(engine_handle,&on));
+ THROW_ON_ERROR(smHTSetLipTrackingEnabled(engine_handle,!on));
+ }
+ return true;
+ case 'e':
+ if (smAPINonCommercialLicense() == SM_API_TRUE)
+ {
+ return false;
+ }
+ else
+ {
+ int on;
+ THROW_ON_ERROR(smHTGetEyebrowTrackingEnabled(engine_handle,&on));
+ THROW_ON_ERROR(smHTSetEyebrowTrackingEnabled(engine_handle,!on));
+ }
+ return true;
+ case 'h':
+ if (smEngineIsLicensed(engine_handle) != SM_API_OK)
+ {
+ return false;
+ }
+ else
+ {
+ g_do_head_pose_printing = !g_do_head_pose_printing;
+ std::cout << "HeadPose printing is " << (g_do_head_pose_printing?"on":"off") << std::endl;
+ }
+ return true;
+ case 'f':
+ if (smEngineIsLicensed(engine_handle) != SM_API_OK)
+ {
+ return false;
+ }
+ else
+ {
+ g_do_face_data_printing = !g_do_face_data_printing;
+ std::cout << "FaceData printing is " << (g_do_face_data_printing?"on":"off") << std::endl;
+ }
+ return true;
+ default:
+ return true;
+ }
+ }
+
+ // Setup console window geometry / font etc
+ void initConsole()
+ {
+ HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
+ // Buffer of 255 x 1024
+ ////COORD buffer_size;
+ ////buffer_size.X = 255;
+ ////buffer_size.Y = 1024;
+ ////SetConsoleScreenBufferSize(console_handle, buffer_size);
+ ////// Window size of 120 x 50
+ ////SMALL_RECT window_size;
+ ////window_size.Left = 0;
+ ////window_size.Right = 20;
+ ////window_size.Top = 0;
+ ////window_size.Bottom = 20;
+ ////SetConsoleWindowInfo(console_handle,TRUE,&window_size);
+ // Green text
+ SetConsoleTextAttribute(console_handle, FOREGROUND_GREEN | FOREGROUND_INTENSITY);
+ ShowWindow(GetConsoleWindow(), SW_HIDE);
+ }
+ }
+ }
+}
+
+#endif