/********************************************************************************
* 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
//#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::Initialize()
{
qDebug() << "FTNoIR_Filter::Initialize says: Starting ";
loadSettings();
return;
}
//
// Load the current Settings from the currently 'active' INI-file.
//
void FTNoIR_Filter::loadSettings() {
qDebug() << "FTNoIR_Filter::loadSettings says: Starting ";
QSettings settings("Abbequerque Inc.", "FaceTrackNoIR"); // 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(double *current_camera_position, double *target_camera_position, double *new_camera_position, double *last_post_filter)
{
//non-optimised version for clarity
double prev_output[6];
double target[6];
double output_delta[6];
double scale[]={0.025f,0.025f,0.025f,6.0f,6.0f,6.0f};
double norm_output_delta[6];
double output[6];
for (int i = 0; i < 6; i++)
{
prev_output[i] = current_camera_position[i];
target[i] = target_camera_position[i];
}
if (first_run==true)
{
//on the first run, output=target
for (int i=0;i<6;i++)
{
output[i]=target[i];
prev_alpha[i] = 0.0f;
}
for (int i = 0; i < 6; i++)
new_camera_position[i] = target[i];
first_run=false;
//we can bail
return;
}
//how far does the camera need to move to catch up?
for (int i=0;i<6;i++)
{
output_delta[i]=(target[i]-prev_output[i]);
}
//normalise the deltas
for (int i=0;i<6;i++)
{
norm_output_delta[i]=std::min(std::max(fabs(output_delta[i])/scale[i],0.0),1.0);
}
//calculate the alphas
//work out the dynamic smoothing factors
// if (newTarget) {
for (int i=0;i<6;i++)
{
alpha[i]=1.0/(kMinSmoothing+((1.0-pow(norm_output_delta[i],kSmoothingScaleCurve))*smoothing_frames_range));
smoothed_alpha[i]=(alpha_smoothing*alpha[i])+((1.0f-alpha_smoothing)*prev_alpha[i]);
}
// }
//qDebug() << "FTNoIR_Filter::FilterHeadPoseData() smoothing frames = " << smoothing_frames_range;
//qDebug() << "FTNoIR_Filter::FilterHeadPoseData() alpha[3] = " << alpha[3];
//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++)
{
if (smoothed_alpha[i]>=largest_alpha)
{
largest_alpha=smoothed_alpha[i];
}
}
//move the camera
for (int i=0;i<6;i++)
{
output[i]=(largest_alpha*target[i])+((1.0f-largest_alpha)*prev_output[i]);
// output[i]=(smoothed_alpha[i]*target[i])+((1.0f-smoothed_alpha[i])*prev_output[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 << "output:\t" << output[0] << "\t" << output[1] << "\t" << output[2] << "\t" << output[3] << "\t" << output[4] << "\t" << output[5] << '\n';
out << "target:\t" << target[0] << "\t" << target[1] << "\t" << target[2] << "\t" << target[3] << "\t" << target[4] << "\t" << target[5] << '\n';
out << "prev_output:\t" << prev_output[0] << "\t" << prev_output[1] << "\t" << prev_output[2] << "\t" << prev_output[3] << "\t" << prev_output[4] << "\t" << prev_output[5] << '\n';
out << "largest_alpha:\t" << largest_alpha << '\n';
}
#endif
for (int i = 0; i < 6; i++)
{
new_camera_position[i] = output[i];
current_camera_position[i] = output[i];
}
//update filter memories ready for next sample
for (int i=0;i<6;i++)
{
prev_alpha[i]=smoothed_alpha[i];
}
return;
}
////////////////////////////////////////////////////////////////////////////////
// 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 void* CALLING_CONVENTION GetConstructor()
{
return (IFilter*) new FTNoIR_Filter;
}