From 7eeb8dfaede7bb54b37b8ea538135914a43ab011 Mon Sep 17 00:00:00 2001 From: Wim Vriend Date: Mon, 21 Mar 2011 21:32:13 +0000 Subject: 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 --- FTNoIR_Tracker_Base/ftnoir_tracker_base.h | 5 +- FTNoIR_Tracker_SM/FTNoIR_Tracker_SM.h | 11 +- FTNoIR_Tracker_SM/FTNoIR_Tracker_SM.vcproj | 99 +++++- FTNoIR_Tracker_SM/ftnoir_tracker_faceapi.cpp | 42 ++- FTNoIR_Tracker_UDP/FTNoIR_Tracker_UDP.h | 4 +- FTNoIR_Tracker_UDP/ftnoir_tracker_udp.cpp | 4 +- FaceTrackNoIR.sln | 6 + FaceTrackNoIR.suo | Bin 343552 -> 349184 bytes FaceTrackNoIR/AutoClosePtr.h | 89 ++++++ FaceTrackNoIR/FaceTrackNoIR.cpp | 34 +- FaceTrackNoIR/FaceTrackNoIR.h | 10 +- FaceTrackNoIR/FaceTrackNoIR.vcproj | 2 +- FaceTrackNoIR/main.cpp | 3 - FaceTrackNoIR/tracker.cpp | 117 +++---- FaceTrackNoIR/tracker.h | 11 +- List of compatible games.xls | Bin 28672 -> 29184 bytes bin/FaceTrackNoIR.exe | Bin 626688 -> 626688 bytes bin/NPClient.dll | Bin 90624 -> 90112 bytes bin/Settings/Wings of Prey.ini | 20 +- faceAPI/build_options.h | 8 + faceAPI/lock.h | 35 ++ faceAPI/lockfree.h | 65 ++++ faceAPI/main.cpp | 460 +++++++++++++++++++++++++++ faceAPI/mutex.h | 44 +++ faceAPI/stdafx.cpp | 8 + faceAPI/stdafx.h | 21 ++ faceAPI/utils.h | 346 ++++++++++++++++++++ 27 files changed, 1310 insertions(+), 134 deletions(-) create mode 100644 FaceTrackNoIR/AutoClosePtr.h create mode 100644 faceAPI/build_options.h create mode 100644 faceAPI/lock.h create mode 100644 faceAPI/lockfree.h create mode 100644 faceAPI/main.cpp create mode 100644 faceAPI/mutex.h create mode 100644 faceAPI/stdafx.cpp create mode 100644 faceAPI/stdafx.h create mode 100644 faceAPI/utils.h 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 +#include // // 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 @@ -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 _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 @@ /> @@ -171,6 +171,10 @@ RelativePath=".\ftnoir_tracker_faceapi.cpp" > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 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 Binary files a/FaceTrackNoIR.suo and b/FaceTrackNoIR.suo 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 +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 ITrackerDia typedef ITrackerDialog *(WINAPI *importGetTrackerDialog)(void); -#include +//#include #include -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 @@ /> #include -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... @@ -111,6 +113,13 @@ QLibrary *filterLib; Tracker::Y.initHeadPoseData(); 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 // @@ -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(¤t_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 Binary files a/List of compatible games.xls and b/List of compatible games.xls differ diff --git a/bin/FaceTrackNoIR.exe b/bin/FaceTrackNoIR.exe index 092b7d83..2830d3be 100644 Binary files a/bin/FaceTrackNoIR.exe and b/bin/FaceTrackNoIR.exe differ diff --git a/bin/NPClient.dll b/bin/NPClient.dll index b979550f..8da41135 100644 Binary files a/bin/NPClient.dll and b/bin/NPClient.dll 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 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 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; iconfidence > 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 +#include + +// TODO: reference additional headers your program requires here +#include +#include +#include +#include +#include +#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,©_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(p.x) << "," << static_cast(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(im.plane_addr[0]) << "," + << static_cast(im.plane_addr[1]) << "," + << static_cast(im.plane_addr[2]) << "," + << static_cast(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