/********************************************************************************
* FaceTrackNoIR This program is a private project of some enthusiastic *
* gamers from Holland, who don't like to pay much for *
* head-tracking. *
* *
* Copyright (C) 2012 Wim Vriend (Developing) *
* Ron Hendriks (Researching and Testing) *
* *
* Homepage *
* *
* This program is free software; you can redistribute it and/or modify it *
* under the terms of the GNU General Public License as published by the *
* Free Software Foundation; either version 3 of the License, or (at your *
* option) any later version. *
* *
* This program is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for *
* more details. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, see . *
* *
********************************************************************************/
#include "ftnoir_filter_ewma2.h"
#include "math.h"
#include
#include
#include "facetracknoir/global-settings.h"
#include
#include
//#define LOG_OUTPUT
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// EWMA Filter: Exponentially Weighted Moving Average filter with dynamic smoothing parameter
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
FTNoIR_Filter::FTNoIR_Filter()
{
first_run = true;
alpha_smoothing = 0.02f; // this is a constant for now, might be a parameter later
loadSettings(); // Load the Settings
}
FTNoIR_Filter::~FTNoIR_Filter()
{
}
void FTNoIR_Filter::receiveSettings(double smin, double smax, double sexpt)
{
QMutexLocker foo(&mutex);
kMinSmoothing = smin;
kMaxSmoothing = smax;
kSmoothingScaleCurve = sexpt;
}
//
// Load the current Settings from the currently 'active' INI-file.
//
void FTNoIR_Filter::loadSettings() {
qDebug() << "FTNoIR_Filter::loadSettings says: Starting ";
QSettings settings("opentrack"); // Registry settings (in HK_USER)
QString currentFile = settings.value ( "SettingsFile", QCoreApplication::applicationDirPath() + "/settings/default.ini" ).toString();
QSettings iniFile( currentFile, QSettings::IniFormat ); // Application settings (in INI-file)
qDebug() << "FTNoIR_Filter::loadSettings says: iniFile = " << currentFile;
//
// The EWMA2-filter-settings are in the Tracking group: this is because they used to be on the Main Form of FaceTrackNoIR
//
iniFile.beginGroup ( "Tracking" );
kMinSmoothing = iniFile.value ( "minSmooth", 15 ).toInt();
kMaxSmoothing = iniFile.value ( "maxSmooth", 50 ).toInt();
kSmoothingScaleCurve = iniFile.value ( "powCurve", 10 ).toInt();
iniFile.endGroup ();
}
void FTNoIR_Filter::FilterHeadPoseData(const double *target_camera_position,
double *new_camera_position,
const double *last_post_filter)
{
double delta;
double new_alpha;
double scale[]={0.025f,0.025f,0.025f,6.0f,6.0f,6.0f};
//On the first run, initialize to output=target and return.
if (first_run==true) {
for (int i=0;i<6;i++) {
new_camera_position[i] = target_camera_position[i];
current_camera_position[i] = target_camera_position[i];
alpha[i] = 0.0f;
}
first_run=false;
return;
}
QMutexLocker foo(&mutex);
for (int i=0;i<6;i++) {
// Calculate the delta.
delta=target_camera_position[i]-current_camera_position[i];
// Normalise the delta.
delta=std::min(std::max(fabs(delta)/scale[i],0.0),1.0);
// Calculate the new alpha from the normalized delta.
new_alpha=1.0/(kMinSmoothing+((1.0-pow(delta,kSmoothingScaleCurve))*(kMaxSmoothing-kMinSmoothing)));
// Update the smoothed alpha.
alpha[i]=(alpha_smoothing*new_alpha)+((1.0f-alpha_smoothing)*alpha[i]);
}
// Use the same (largest) smoothed alpha for each channel
//NB: larger alpha = *less* lag (opposite to what you'd expect)
float largest_alpha=0.0f;
for (int i=0;i<6;i++) {
largest_alpha=std::max(largest_alpha, alpha[i]);
}
// Calculate the new camera position.
for (int i=0;i<6;i++) {
new_camera_position[i]=(largest_alpha*target_camera_position[i])+((1.0f-largest_alpha)*current_camera_position[i]);
//new_camera_position[i]=(alpha[i]*target_camera_position[i])+((1.0f-alpha[i])*current_camera_position[i]);
}
#ifdef LOG_OUTPUT
// Use this for some debug-output to file...
QFile data(QCoreApplication::applicationDirPath() + "\\EWMA_output.txt");
if (data.open(QFile::WriteOnly | QFile::Append)) {
QTextStream out(&data);
out << "current:\t" << current_camera_position[0]
<< "\t" << current_camera_position[1]
<< "\t" << current_camera_position[2]
<< "\t" << current_camera_position[3]
<< "\t" << current_camera_position[4]
<< "\t" << current_camera_position[5] << '\n';
out << "target:\t" << target_camera_position[0]
<< "\t" << target_camera_position[1]
<< "\t" << target_camera_position[2]
<< "\t" << target_camera_position[3]
<< "\t" << target_camera_position[4]
<< "\t" << target_camera_position[5] << '\n';
out << "output:\t" << new_camera_position[0]
<< "\t" << new_camera_position[1]
<< "\t" << new_camera_position[2]
<< "\t" << new_camera_position[3]
<< "\t" << new_camera_position[4]
<< "\t" << new_camera_position[5] << '\n';
out << "largest_alpha:\t" << largest_alpha << '\n';
}
#endif
// Update the current camera position to the new position.
for (int i = 0; i < 6; i++) {
current_camera_position[i] = new_camera_position[i];
}
}
////////////////////////////////////////////////////////////////////////////////
// Factory function that creates instances if the Filter object.
// Export both decorated and undecorated names.
// GetFilter - Undecorated name, which can be easily used with GetProcAddress
// Win32 API function.
// _GetFilter@0 - Common name decoration for __stdcall functions in C language.
//#pragma comment(linker, "/export:GetFilter=_GetFilter@0")
extern "C" FTNOIR_FILTER_BASE_EXPORT IFilter* CALLING_CONVENTION GetConstructor()
{
return new FTNoIR_Filter;
}