From 1535f956a73ea3be78bffa82a224ab7bcc6dd14d Mon Sep 17 00:00:00 2001 From: Stanislaw Halik Date: Thu, 27 Jun 2013 16:07:41 +0200 Subject: Add a half-broken Kalman filter for further development --- CMakeLists.txt | 15 +- facetracknoir/tracker.cpp | 3 + ftnoir_filter_accela/ftnoir_filter_accela.h | 5 +- ftnoir_filter_base/ftnoir_filter_base.h | 1 + ftnoir_filter_ewma2/ftnoir_filter_ewma2.h | 1 + ftnoir_filter_kalman/ftnoir_filter_kalman.h | 87 +++++++++ .../ftnoir_kalman_filtercontrols.ui | 211 +++++++++++++++++++++ ftnoir_filter_kalman/images/filter-16-ac.png | Bin 0 -> 725 bytes ftnoir_filter_kalman/kalman-filter.qrc | 5 + ftnoir_filter_kalman/kalman.cpp | 156 +++++++++++++++ 10 files changed, 481 insertions(+), 3 deletions(-) create mode 100644 ftnoir_filter_kalman/ftnoir_filter_kalman.h create mode 100644 ftnoir_filter_kalman/ftnoir_kalman_filtercontrols.ui create mode 100644 ftnoir_filter_kalman/images/filter-16-ac.png create mode 100644 ftnoir_filter_kalman/kalman-filter.qrc create mode 100644 ftnoir_filter_kalman/kalman.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 65e2f01b..f51d38e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,6 +132,14 @@ if(NOT SDK_FACEAPI_ONLY) QT4_WRAP_UI(opentrack-filter-accela-uih ${opentrack-filter-accela-ui}) QT4_ADD_RESOURCES(opentrack-filter-accela-rcc ${opentrack-filter-accela-rc}) + file(GLOB opentrack-filter-kalman-c "ftnoir_filter_kalman/*.cpp") + file(GLOB opentrack-filter-kalman-h "ftnoir_filter_kalman/*.h") + QT4_WRAP_CPP(opentrack-filter-kalman-moc ${opentrack-filter-kalman-h}) + file(GLOB opentrack-filter-kalman-ui "ftnoir_filter_kalman/*.ui") + file(GLOB opentrack-filter-kalman-rc "ftnoir_filter_kalman/*.qrc") + QT4_WRAP_UI(opentrack-filter-kalman-uih ${opentrack-filter-kalman-ui}) + QT4_ADD_RESOURCES(opentrack-filter-kalman-rcc ${opentrack-filter-kalman-rc}) + file(GLOB opentrack-filter-ewma-c "ftnoir_filter_ewma2/*.cpp") file(GLOB opentrack-filter-ewma-h "ftnoir_filter_ewma2/*.h") QT4_WRAP_CPP(opentrack-filter-ewma-moc ${opentrack-filter-ewma-h}) @@ -318,6 +326,11 @@ endif() add_library(opentrack-filter-accela SHARED ${opentrack-filter-accela-c} ${opentrack-filter-accela-moc} ${opentrack-filter-accela-uih} ${opentrack-filter-accela-rcc}) target_link_libraries(opentrack-filter-accela ${MY_QT_LIBS} opentrack-spline-widget) + if(OpenCV_FOUND) + add_library(opentrack-filter-kalman SHARED ${opentrack-filter-kalman-c} ${opentrack-filter-kalman-moc} ${opentrack-filter-kalman-uih} ${opentrack-filter-kalman-rcc}) + target_link_libraries(opentrack-filter-kalman ${MY_QT_LIBS} ${OpenCV_LIBS}) + endif() + if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_GNUCC) SET_TARGET_PROPERTIES(opentrack-filter-accela PROPERTIES LINK_FLAGS "-Wl,--version-script=${CMAKE_SOURCE_DIR}/facetracknoir/posix-version-script.txt") @@ -533,7 +546,7 @@ if(NOT SDK_FACEAPI_ONLY) endif() if(OpenCV_FOUND) - install(TARGETS opentrack-tracker-pt RUNTIME DESTINATION . LIBRARY DESTINATION .) + install(TARGETS opentrack-tracker-pt opentrack-filter-kalman RUNTIME DESTINATION . LIBRARY DESTINATION .) endif() install(TARGETS diff --git a/facetracknoir/tracker.cpp b/facetracknoir/tracker.cpp index 380d1748..25e09126 100644 --- a/facetracknoir/tracker.cpp +++ b/facetracknoir/tracker.cpp @@ -161,6 +161,9 @@ void Tracker::run() { if (Libraries->pSecondTracker) Libraries->pSecondTracker->NotifyCenter(); + + if (Libraries->pFilter) + Libraries->pFilter->Initialize(); } if (getTrackingActive()) { diff --git a/ftnoir_filter_accela/ftnoir_filter_accela.h b/ftnoir_filter_accela/ftnoir_filter_accela.h index 7939b783..fa1e9bcd 100644 --- a/ftnoir_filter_accela/ftnoir_filter_accela.h +++ b/ftnoir_filter_accela/ftnoir_filter_accela.h @@ -48,6 +48,7 @@ public: FTNoIR_Filter(); ~FTNoIR_Filter(); void FilterHeadPoseData(double *current_camera_position, double *target_camera_position, double *new_camera_position, double *last_post_filter_values); + void Initialize() {} private: void loadSettings(); // Load the settings from the INI-file @@ -93,8 +94,8 @@ private: private slots: void doOK(); void doCancel(); - void settingChanged(bool) { settingsDirty = true; }; - void settingChanged(int) { settingsDirty = true; }; + void settingChanged(bool) { settingsDirty = true; } + void settingChanged(int) { settingsDirty = true; } }; //******************************************************************************************************* diff --git a/ftnoir_filter_base/ftnoir_filter_base.h b/ftnoir_filter_base/ftnoir_filter_base.h index 0dcf640a..b38fc226 100644 --- a/ftnoir_filter_base/ftnoir_filter_base.h +++ b/ftnoir_filter_base/ftnoir_filter_base.h @@ -27,6 +27,7 @@ struct IFilter { virtual ~IFilter() {} virtual void FilterHeadPoseData(double *current_camera_position, double *target_camera_position, double *new_camera_position, double *last_post_filter) = 0; + virtual void Initialize() = 0; }; // Factory function that creates instances of the Filter object. diff --git a/ftnoir_filter_ewma2/ftnoir_filter_ewma2.h b/ftnoir_filter_ewma2/ftnoir_filter_ewma2.h index 7fc9c77e..910fc7c1 100644 --- a/ftnoir_filter_ewma2/ftnoir_filter_ewma2.h +++ b/ftnoir_filter_ewma2/ftnoir_filter_ewma2.h @@ -41,6 +41,7 @@ class FTNoIR_Filter : public IFilter public: FTNoIR_Filter(); ~FTNoIR_Filter(); + void Initialize() {} void FilterHeadPoseData(double *current_camera_position, double *target_camera_position, double *new_camera_position, double *last_post_filter); diff --git a/ftnoir_filter_kalman/ftnoir_filter_kalman.h b/ftnoir_filter_kalman/ftnoir_filter_kalman.h new file mode 100644 index 00000000..59169612 --- /dev/null +++ b/ftnoir_filter_kalman/ftnoir_filter_kalman.h @@ -0,0 +1,87 @@ +#pragma once +/* Copyright (c) 2013 Stanisław Halik + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ +#ifndef INCLUDED_FTN_FILTER_H +#define INCLUDED_FTN_FILTER_H + +#undef FTNOIR_TRACKER_BASE_LIB +#define FTNOIR_TRACKER_BASE_EXPORT Q_DECL_IMPORT + +#include "ftnoir_filter_base/ftnoir_filter_base.h" +#include "ui_ftnoir_kalman_filtercontrols.h" +#include "facetracknoir/global-settings.h" +#include +#include +#include +#include +#include +#include + +class FTNOIR_FILTER_BASE_EXPORT FTNoIR_Filter : public IFilter +{ +public: + FTNoIR_Filter(); + virtual ~FTNoIR_Filter() { + } + void Initialize(); + void FilterHeadPoseData(double *current_camera_position, + double *target_camera_position, + double *new_camera_position, + double *last_post_filter_values); + cv::KalmanFilter kalman; + double prev_position[6]; +}; + +void kalman_load_settings(FTNoIR_Filter& self); +void kalman_save_settings(FTNoIR_Filter& self); + +class FTNOIR_FILTER_BASE_EXPORT FTNoIR_FilterDll : public Metadata +{ +public: + void getFullName(QString *strToBeFilled) { *strToBeFilled = QString("Kalman filter"); } + void getShortName(QString *strToBeFilled) { *strToBeFilled = QString("Kalman filter"); } + void getDescription(QString *strToBeFilled) { *strToBeFilled = QString("Kalman filter"); } + void getIcon(QIcon *icon){ *icon = QIcon(":/images/filter-16.png"); } +}; + +class FTNOIR_FILTER_BASE_EXPORT FilterControls: public QWidget, Ui::KalmanUICFilterControls, public IFilterDialog +{ + Q_OBJECT +public: + explicit FilterControls() : settingsDirty(false) { + ui.setupUi(this); + 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) + + iniFile.beginGroup("ftnoir-filter-kalman"); + iniFile.endGroup(); + connect(ui.btnOk, SIGNAL(clicked()), this, SLOT(doOK())); + connect(ui.btnCancel, SIGNAL(clicked()), this, SLOT(doCancel())); + show(); + } + virtual ~FilterControls() {} + void showEvent ( QShowEvent * event ) { + show(); + } + + void Initialize(QWidget *parent, IFilter* ptr) { + } + + bool settingsDirty; + Ui::KalmanUICFilterControls ui; + +public slots: + void doOK(); + void doCancel(); + void settingsChanged(double unused) { + settingsDirty = true; + } +}; + +#endif diff --git a/ftnoir_filter_kalman/ftnoir_kalman_filtercontrols.ui b/ftnoir_filter_kalman/ftnoir_kalman_filtercontrols.ui new file mode 100644 index 00000000..7b71712a --- /dev/null +++ b/ftnoir_filter_kalman/ftnoir_kalman_filtercontrols.ui @@ -0,0 +1,211 @@ + + + KalmanUICFilterControls + + + Qt::ApplicationModal + + + + 0 + 0 + 334 + 100 + + + + + 0 + 0 + + + + Filter settings + + + + + images/facetracknoir.png + images/facetracknoir.png + + + Qt::LeftToRight + + + false + + + + + + + + 173 + 70 + 73 + 25 + + + + OK + + + + + + 250 + 70 + 73 + 25 + + + + Cancel + + + + + + 9 + 30 + 169 + 16 + + + + process-noise-covariance + + + + + + 180 + 26 + 150 + 22 + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + 14 + + + 1.000000000000000 + + + 0.000001000000000 + + + 0.500000000000000 + + + + + + 180 + 6 + 150 + 22 + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + 14 + + + 1.000000000000000 + + + 0.000001000000000 + + + 0.500000000000000 + + + + + + 9 + 10 + 165 + 16 + + + + post-error-matrix + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + 0 + + + + + + 9 + 55 + 109 + 16 + + + + accel-coefficient + + + + + + 181 + 47 + 150 + 22 + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + 6 + + + 1000000000.000000000000000 + + + 0.000001000000000 + + + 1.000000000000000 + + + + + + + + 10 + + + 10 + + + false + + + false + + + true + + + + startEngineClicked() + stopEngineClicked() + cameraSettingsClicked() + + diff --git a/ftnoir_filter_kalman/images/filter-16-ac.png b/ftnoir_filter_kalman/images/filter-16-ac.png new file mode 100644 index 00000000..d263db2d Binary files /dev/null and b/ftnoir_filter_kalman/images/filter-16-ac.png differ diff --git a/ftnoir_filter_kalman/kalman-filter.qrc b/ftnoir_filter_kalman/kalman-filter.qrc new file mode 100644 index 00000000..9a7d75fa --- /dev/null +++ b/ftnoir_filter_kalman/kalman-filter.qrc @@ -0,0 +1,5 @@ + + + images/filter-16-ac.png + + diff --git a/ftnoir_filter_kalman/kalman.cpp b/ftnoir_filter_kalman/kalman.cpp new file mode 100644 index 00000000..51af35e1 --- /dev/null +++ b/ftnoir_filter_kalman/kalman.cpp @@ -0,0 +1,156 @@ +/* Copyright (c) 2013 Stanisław Halik + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ +#include "ftnoir_filter_kalman.h" +#include "facetracknoir/global-settings.h" +#include +#include + +void kalman_load_settings(FTNoIR_Filter& self) { + 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) + + iniFile.beginGroup("ftnoir-filter-kalman"); + iniFile.endGroup(); +} + +void kalman_save_settings(FilterControls& self) { + 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) + + iniFile.beginGroup("ftnoir-filter-kalman"); + iniFile.endGroup(); +} + +FTNoIR_Filter::FTNoIR_Filter() { + kalman_load_settings(*this); + Initialize(); +} + +// the following was written by Donovan Baarda +// https://sourceforge.net/p/facetracknoir/discussion/1150909/thread/418615e1/?limit=25#af75/084b +void FTNoIR_Filter::Initialize() { + const double accel_variance = 1e-4; + const double noise_variance = 1e1; + kalman.init(12, 6, 0, CV_64F); + kalman.transitionMatrix = (cv::Mat_(12, 12) << + 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1); + double a = 0.25 * accel_variance; + double b = 0.5 * accel_variance; + double c = 1.0 * accel_variance; + kalman.processNoiseCov = (cv::Mat_(12, 12) << + a, 0, 0, 0, 0, 0, b, 0, 0, 0, 0, 0, + 0, a, 0, 0, 0, 0, 0, b, 0, 0, 0, 0, + 0, 0, a, 0, 0, 0, 0, 0, b, 0, 0, 0, + 0, 0, 0, a, 0, 0, 0, 0, 0, b, 0, 0, + 0, 0, 0, 0, a, 0, 0, 0, 0, 0, b, 0, + 0, 0, 0, 0, 0, a, 0, 0, 0, 0, 0, b, + b, 0, 0, 0, 0, 0, c, 0, 0, 0, 0, 0, + 0, b, 0, 0, 0, 0, 0, c, 0, 0, 0, 0, + 0, 0, b, 0, 0, 0, 0, 0, c, 0, 0, 0, + 0, 0, 0, b, 0, 0, 0, 0, 0, c, 0, 0, + 0, 0, 0, 0, b, 0, 0, 0, 0, 0, c, 0, + 0, 0, 0, 0, 0, b, 0, 0, 0, 0, 0, c); + cv::setIdentity(kalman.measurementMatrix); + cv::setIdentity(kalman.measurementNoiseCov, cv::Scalar::all(noise_variance)); + cv::setIdentity(kalman.errorCovPost, cv::Scalar::all(accel_variance * 1e4)); + for (int i = 0; i < 6; i++) + prev_position[i] = 0; +} + +void FTNoIR_Filter::FilterHeadPoseData(double *current_camera_position, + double *target_camera_position, + double *new_camera_position, + double *last_post_filter_values) +{ + bool new_target = false; + + for (int i = 0; i < 6; i++) + if (prev_position[i] != target_camera_position[i]) + { + new_target = true; + break; + } + + cv::Mat output = kalman.predict(); + + if (new_target) { + cv::Mat measurement(6, 1, CV_64F); + for (int i = 0; i < 3; i++) { + measurement.at(i) = target_camera_position[i+3]; + measurement.at(i+3) = target_camera_position[i]; + } + kalman.correct(measurement); + } + + for (int i = 0; i < 3; i++) { + new_camera_position[i] = output.at(i+3); + new_camera_position[i+3] = output.at(i); + prev_position[i] = target_camera_position[i]; + prev_position[i+3] = target_camera_position[i+3]; + } +} + +void FilterControls::doOK() { + kalman_save_settings(*this); + close(); +} + +void FilterControls::doCancel() { + if (settingsDirty) { + int ret = QMessageBox::question ( this, "Settings have changed", "Do you want to save the settings?", QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Discard ); + + qDebug() << "doCancel says: answer =" << ret; + + switch (ret) { + case QMessageBox::Save: + kalman_save_settings(*this); + this->close(); + break; + case QMessageBox::Discard: + this->close(); + break; + case QMessageBox::Cancel: + // Cancel was clicked + break; + default: + // should never be reached + break; + } + } + else { + this->close(); + } +} + +extern "C" FTNOIR_FILTER_BASE_EXPORT Metadata* CALLING_CONVENTION GetMetadata() +{ + return new FTNoIR_FilterDll; +} + +extern "C" FTNOIR_FILTER_BASE_EXPORT IFilter* CALLING_CONVENTION GetConstructor() +{ + return new FTNoIR_Filter; +} + +extern "C" FTNOIR_FILTER_BASE_EXPORT IFilterDialog* CALLING_CONVENTION GetDialog() { + return new FilterControls; +} -- cgit v1.2.3