/******************************************************************************** * 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_DZ1.h" #include "math.h" #include //#define LOG_OUTPUT ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // EWMA Filter: Exponentially Weighted Moving Average filter with dynamic smoothing parameter // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FTNoIR_Filter::FTNoIR_Filter() { //populate the description strings filterFullName = "Deadzone Filter Mk1"; filterShortName = "DZ1"; filterDescription = "Deadzone Filter"; first_run = true; loadSettings(); // Load the Settings } FTNoIR_Filter::~FTNoIR_Filter() { } void FTNoIR_Filter::Release() { delete this; } 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 ( "Filter_DZ1" ); kCameraHz = iniFile.value ( "cameraHz", 30 ).toInt(); kDeadZone = iniFile.value ( "DeadZone", 0.1f ).toFloat(); kMoveLast = (iniFile.value ( "MoveLast", 24 ).toFloat()) / 100; // Convert from int to float percentage kMaxDiff = iniFile.value ( "MaxDiff", 1.75f ).toFloat(); kMoveSaved = ((iniFile.value ( "MoveSaved", 35 ).toFloat()) / 100) / kCameraHz; // Convert from int to float percentage and divide by Hz iniFile.endGroup (); } void FTNoIR_Filter::FilterHeadPoseData(THeadPoseData *current_camera_position, THeadPoseData *target_camera_position, THeadPoseData *new_camera_position, bool newTarget) { //non-optimised version for clarity double target[6]; double prev_output[6]; double scale[]={3.0, 3.0, 3.0, 3.0, 3.0, 3.0}; // static double deadzones[] = { DEADZONE, DEADZONE, DEADZONE, DEADZONE, DEADZONE, DEADZONE }; float output[6]; int i=0, j=0; //find out how far the head has moved prev_output[0]=current_camera_position->x; prev_output[1]=current_camera_position->y; prev_output[2]=current_camera_position->z; prev_output[3]=current_camera_position->yaw; prev_output[4]=current_camera_position->pitch; prev_output[5]=current_camera_position->roll; target[0]=target_camera_position->x; target[1]=target_camera_position->y; target[2]=target_camera_position->z; target[3]=target_camera_position->yaw; target[4]=target_camera_position->pitch; target[5]=target_camera_position->roll; if (first_run) { //on the first run, output=target for (i=0;i<6;i++) { last_positions[i] = target[i]; saved_positions[i] = target[i]; prev_positions[i] = target[i]; smooth_remember[i] = 0; smoothing[i] = false; smooth_speed[i] = -1.0; } new_camera_position->x=target[0]; new_camera_position->y=target[1]; new_camera_position->z=target[2]; new_camera_position->yaw=target[3]; new_camera_position->pitch=target[4]; new_camera_position->roll=target[5]; first_run=false; //we can bail return; } else if (!newTarget) { int i; for (i = 0; i < 6; i++) { output[i] = prev_positions[i]; } goto end; } for (i=0;i<6;i++) { double e2 = target[i]; bool slowp = fabs(last_positions[i] - e2) < kDeadZone && fabs(e2 - saved_positions[i]) < kMaxDiff; // DeadZone and MAXDIFF from INI-file if (smoothing[i] || !slowp) { double start = smooth_start[i]; double vec = e2 - start; int sign = vec < 0 ? -1 : 1; int sign2 = e2 < 0 ? -1 : 1; if (sign != sign2) sign2 = -sign2; double diff = (kCameraHz / REMEMBER_SMOOTHNESS) - smooth_remember[i]; smooth_speed[i] *= -(diff * diff) / (double) ((kCameraHz / REMEMBER_SMOOTHNESS) * (kCameraHz / REMEMBER_SMOOTHNESS)) + 1.001; if (smooth_speed[i] < INITIAL_SMOOTH_SPEED) smooth_speed[i] = INITIAL_SMOOTH_SPEED; vec = fabs(vec); double iter = vec * SMOOTH_FACTOR / (double) kCameraHz; smooth_speed[i] += iter; double bleh = smooth_speed[i] * sign; double foo = smooth_start[i] + bleh; bool done = sign2 > 0 ? foo > e2 : foo < e2; smooth_remember[i] = (kCameraHz / REMEMBER_SMOOTHNESS); if (done) { smoothing[i] = false; output[i] = e2; saved_positions[i] = e2; last_positions[i] = e2; prev_positions[i] = e2; } else { smoothing[i] = true; prev_positions[i] = output[i] = foo; } } else { if (smooth_remember[i] <= 0) { smooth_speed[i] = -1.0; } else { smooth_remember[i]--; } last_positions[i] = last_positions[i] + (e2 - last_positions[i]) * kMoveLast; // MOVE_LAST from INI-file saved_positions[i] = saved_positions[i] + (e2 - saved_positions[i]) * kMoveSaved; // MOVE_SAVED from INI-file output[i] = prev_positions[i] = prev_positions[i] + (e2 - prev_positions[i]) * (SLOW_SPEED/kCameraHz); // Get CameraHz from INI smooth_start[i] = output[i]; smoothing[i] = false; #if 0 if (i == 3) { FILE* dlog = fopen("debug-log.txt", "a"); fprintf(dlog, "slowed=%f frames=%d diff=%f\n", output[i], slow_move_count[i], e1 - last_positions[i]); fclose(dlog); } #endif } } end: new_camera_position->x=output[0]; new_camera_position->y=output[1]; new_camera_position->z=output[2]; new_camera_position->yaw=output[3] * MULT_X; new_camera_position->pitch=output[4] * (output[4] < 0 ? MULT_Y_NEG : MULT_Y_POS) - COCKPIT_PITCH; new_camera_position->roll=output[5]; #if 1 // // Also update the 'current' position, for the next iteration. // current_camera_position->x=output[0]; current_camera_position->y=output[1]; current_camera_position->z=output[2]; current_camera_position->yaw=output[3] * MULT_X; current_camera_position->pitch=output[4] * (output[4] < 0 ? MULT_Y_NEG : MULT_Y_POS) + COCKPIT_PITCH; current_camera_position->roll=output[5]; #endif } void FTNoIR_Filter::getFilterFullName(QString *strToBeFilled) { *strToBeFilled = filterFullName; }; void FTNoIR_Filter::getFilterShortName(QString *strToBeFilled) { *strToBeFilled = filterShortName; }; void FTNoIR_Filter::getFilterDescription(QString *strToBeFilled) { *strToBeFilled = filterDescription; }; //////////////////////////////////////////////////////////////////////////////// // 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") FTNOIR_FILTER_BASE_EXPORT FILTERHANDLE __stdcall GetFilter() { return new FTNoIR_Filter; }