summaryrefslogtreecommitdiffhomepage
path: root/filter-ewma2
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2015-10-30 07:37:41 +0100
committerStanislaw Halik <sthalik@misaki.pl>2015-10-30 08:39:32 +0100
commitaa066bdd4622d4f6824fee864f6be6806813f04d (patch)
tree3df328b8b364cba2373a85827191b259bd78d546 /filter-ewma2
parentd6a54431d178632a2bf466c9904f74abd143afe6 (diff)
move to subdirectory-based build system
Closes #224
Diffstat (limited to 'filter-ewma2')
-rw-r--r--filter-ewma2/CMakeLists.txt1
-rw-r--r--filter-ewma2/ftnoir_ewma_filtercontrols.ui372
-rw-r--r--filter-ewma2/ftnoir_filter_ewma2.cpp81
-rw-r--r--filter-ewma2/ftnoir_filter_ewma2.h66
-rw-r--r--filter-ewma2/ftnoir_filter_ewma2_dialog.cpp43
5 files changed, 563 insertions, 0 deletions
diff --git a/filter-ewma2/CMakeLists.txt b/filter-ewma2/CMakeLists.txt
new file mode 100644
index 00000000..af786487
--- /dev/null
+++ b/filter-ewma2/CMakeLists.txt
@@ -0,0 +1 @@
+opentrack_boilerplate(opentrack-filter-ewma)
diff --git a/filter-ewma2/ftnoir_ewma_filtercontrols.ui b/filter-ewma2/ftnoir_ewma_filtercontrols.ui
new file mode 100644
index 00000000..9387f0d5
--- /dev/null
+++ b/filter-ewma2/ftnoir_ewma_filtercontrols.ui
@@ -0,0 +1,372 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UICFilterControls</class>
+ <widget class="QWidget" name="UICFilterControls">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>448</width>
+ <height>380</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>EWMA2 filter settings</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="ewma-filter.qrc">
+ <normaloff>:/images/filter-16.png</normaloff>:/images/filter-16.png</iconset>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QFrame" name="frame">
+ <layout class="QGridLayout" name="gridLayout_2">
+ <property name="topMargin">
+ <number>2</number>
+ </property>
+ <property name="bottomMargin">
+ <number>2</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="lblInvert1_6">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Min</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QSlider" name="minSmooth">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="pageStep">
+ <number>5</number>
+ </property>
+ <property name="value">
+ <number>2</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="tickPosition">
+ <enum>QSlider::NoTicks</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QSpinBox" name="spinMinSmooth">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">background:none;</string>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::PlusMinus</enum>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>120</number>
+ </property>
+ <property name="singleStep">
+ <number>5</number>
+ </property>
+ <property name="value">
+ <number>2</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="lblInvert1_7">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Max</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSlider" name="maxSmooth">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="pageStep">
+ <number>5</number>
+ </property>
+ <property name="value">
+ <number>10</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="tickPosition">
+ <enum>QSlider::NoTicks</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QSpinBox" name="spinMaxSmooth">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">background:none;</string>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::PlusMinus</enum>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>120</number>
+ </property>
+ <property name="singleStep">
+ <number>5</number>
+ </property>
+ <property name="value">
+ <number>10</number>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="lblInvert1_8">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Curve</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QSlider" name="powCurve">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="pageStep">
+ <number>5</number>
+ </property>
+ <property name="value">
+ <number>10</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="tickPosition">
+ <enum>QSlider::NoTicks</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QSpinBox" name="spinPowCurve">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">background:none;</string>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::PlusMinus</enum>
+ </property>
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="singleStep">
+ <number>5</number>
+ </property>
+ <property name="value">
+ <number>10</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_4">
+ <property name="styleSheet">
+ <string notr="true">background-color: rgb(214, 214, 214);
+border-color: rgb(0, 0, 0);</string>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::Box</enum>
+ </property>
+ <property name="text">
+ <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2';&quot;&gt;Min:&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2';&quot;&gt;Defines the way the filter responds to fast movements;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2';&quot;&gt;Higher value: slower response;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2';&quot;&gt;Max:&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2';&quot;&gt;Defines the way the filter responds to slow movements;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2';&quot;&gt;Higher value: slower response;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2';&quot;&gt;Pow:&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2';&quot;&gt;Defines the filters 'readiness' to respond to speed changes;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2';&quot;&gt;Higher value = &lt;/span&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-weight:600;&quot;&gt;faster&lt;/span&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2';&quot;&gt; response;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="ewma-filter.qrc"/>
+ </resources>
+ <connections>
+ <connection>
+ <sender>minSmooth</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>spinMinSmooth</receiver>
+ <slot>setValue(int)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>303</x>
+ <y>33</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>391</x>
+ <y>36</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>maxSmooth</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>spinMaxSmooth</receiver>
+ <slot>setValue(int)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>281</x>
+ <y>61</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>390</x>
+ <y>74</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>powCurve</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>spinPowCurve</receiver>
+ <slot>setValue(int)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>236</x>
+ <y>101</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>391</x>
+ <y>98</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/filter-ewma2/ftnoir_filter_ewma2.cpp b/filter-ewma2/ftnoir_filter_ewma2.cpp
new file mode 100644
index 00000000..c09fb912
--- /dev/null
+++ b/filter-ewma2/ftnoir_filter_ewma2.cpp
@@ -0,0 +1,81 @@
+/* Copyright (c) 2014 Donovan Baarda <abo@minkirri.apana.org.au>
+ *
+ * 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_ewma2.h"
+#include <cmath>
+#include <QDebug>
+#include <QWidget>
+#include "opentrack/plugin-api.hpp"
+#include <algorithm>
+#include <QMutexLocker>
+
+// Exponentially Weighted Moving Average (EWMA) Filter with dynamic smoothing.
+//
+// This filter tries to adjust the amount of filtering to minimize lag when
+// moving, and minimize noise when still. It uses the delta filtered over the
+// last 1/60sec (16ms) compared to the delta's average noise variance over
+// the last 60sec to try and detect movement vs noise. As the delta increases
+// from 0 to 3 stdevs of the noise, the filtering scales down from maxSmooth
+// to minSmooth at a rate controlled by the powCurve setting.
+
+
+FTNoIR_Filter::FTNoIR_Filter()
+{
+ reset();
+}
+
+void FTNoIR_Filter::receiveSettings()
+{
+ s.b->reload();
+}
+
+void FTNoIR_Filter::reset()
+{
+ timer.invalidate();
+}
+
+void FTNoIR_Filter::filter(const double *input, double *output)
+{
+ // Start the timer and initialise filter state if it's not running.
+ if (!timer.isValid()) {
+ timer.start();
+ for (int i=0;i<6;i++) {
+ last_output[i] = input[i];
+ last_delta[i] = 0.0;
+ last_noise[i] = 0.0;
+ }
+ }
+ // Get the time in seconds since last run and restart the timer.
+ auto dt = timer.restart() / 1000.0f;
+ // Calculate delta_alpha and noise_alpha from dt.
+ double delta_alpha = dt/(dt + delta_RC);
+ double noise_alpha = dt/(dt + noise_RC);
+ // Calculate the new camera position.
+ for (int i=0;i<6;i++) {
+ // Calculate the current and smoothed delta.
+ double delta = input[i] - last_output[i];
+ last_delta[i] = delta_alpha*delta + (1.0-delta_alpha)*last_delta[i];
+ // Calculate the current and smoothed noise variance.
+ double noise = last_delta[i]*last_delta[i];
+ last_noise[i] = noise_alpha*noise + (1.0-noise_alpha)*last_noise[i];
+ // Normalise the noise between 0->1 for 0->9 variances (0->3 stddevs).
+ double norm_noise = std::min<double>(noise/(9.0*last_noise[i]), 1.0);
+ if (std::isnan(norm_noise))
+ norm_noise = 0;
+ // Calculate the smoothing 0.0->1.0 from the normalized noise.
+ // TODO(abo): change kSmoothingScaleCurve to a float where 1.0 is sqrt(norm_noise).
+ double smoothing = 1.0 - pow(norm_noise, s.kSmoothingScaleCurve/20.0);
+ // Currently min/max smoothing are ints 0->100. We want 0.0->3.0 seconds.
+ // TODO(abo): Change kMinSmoothing, kMaxSmoothing to floats 0.0->3.0 seconds RC.
+ double RC = 3.0*(s.kMinSmoothing + smoothing*(s.kMaxSmoothing - s.kMinSmoothing))/100.0;
+ // Calculate the dynamic alpha.
+ double alpha = dt/(dt + RC);
+ // Calculate the new output position.
+ output[i] = last_output[i] = alpha*input[i] + (1.0-alpha)*last_output[i];
+ }
+}
+
+OPENTRACK_DECLARE_FILTER(FTNoIR_Filter, FilterControls, FTNoIR_FilterDll)
diff --git a/filter-ewma2/ftnoir_filter_ewma2.h b/filter-ewma2/ftnoir_filter_ewma2.h
new file mode 100644
index 00000000..bf4e83ad
--- /dev/null
+++ b/filter-ewma2/ftnoir_filter_ewma2.h
@@ -0,0 +1,66 @@
+#pragma once
+
+#include "opentrack/plugin-api.hpp"
+#include "ui_ftnoir_ewma_filtercontrols.h"
+#include <QElapsedTimer>
+#include <QWidget>
+#include <QMutex>
+#include "opentrack/options.hpp"
+using namespace options;
+
+struct settings : opts {
+ // these are sadly sliders for now due to int/double mismatch -sh
+ value<int> kMinSmoothing, kMaxSmoothing, kSmoothingScaleCurve;
+ settings() :
+ opts("ewma-filter"),
+ kMinSmoothing(b, "min-smoothing", 15),
+ kMaxSmoothing(b, "max-smoothing", 50),
+ kSmoothingScaleCurve(b, "smoothing-scale-curve", 10)
+ {}
+};
+
+
+class FTNoIR_Filter : public IFilter
+{
+public:
+ FTNoIR_Filter();
+ void reset();
+ void filter(const double *input, double *output);
+ void receiveSettings();
+private:
+ // Deltas are smoothed over the last 1/60sec (16ms).
+ const double delta_RC = 0.016;
+ // Noise is smoothed over the last 60sec.
+ const double noise_RC = 60.0;
+ double last_delta[6];
+ double last_noise[6];
+ double last_output[6];
+ QElapsedTimer timer;
+ settings s;
+};
+
+class FilterControls: public IFilterDialog
+{
+ Q_OBJECT
+public:
+ FilterControls();
+ void register_filter(IFilter* flt);
+ void unregister_filter();
+
+private:
+ Ui::UICFilterControls ui;
+ void save();
+ settings s;
+ FTNoIR_Filter* pFilter;
+
+private slots:
+ void doOK();
+ void doCancel();
+};
+
+class FTNoIR_FilterDll : public Metadata
+{
+public:
+ QString name() { return QString("EWMA"); }
+ QIcon icon() { return QIcon(":/images/filter-16.png"); }
+};
diff --git a/filter-ewma2/ftnoir_filter_ewma2_dialog.cpp b/filter-ewma2/ftnoir_filter_ewma2_dialog.cpp
new file mode 100644
index 00000000..30fb6003
--- /dev/null
+++ b/filter-ewma2/ftnoir_filter_ewma2_dialog.cpp
@@ -0,0 +1,43 @@
+#include "ftnoir_filter_ewma2.h"
+#include <cmath>
+#include <QDebug>
+#include "opentrack/plugin-api.hpp"
+#include "ui_ftnoir_ewma_filtercontrols.h"
+
+FilterControls::FilterControls() : pFilter(NULL)
+{
+ ui.setupUi( this );
+
+ connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
+ connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
+
+ tie_setting(s.kMaxSmoothing, ui.maxSmooth);
+ tie_setting(s.kMinSmoothing, ui.minSmooth);
+ tie_setting(s.kSmoothingScaleCurve, ui.powCurve);
+}
+
+void FilterControls::register_filter(IFilter* flt)
+{
+ pFilter = (FTNoIR_Filter*) flt;
+}
+
+void FilterControls::unregister_filter()
+{
+ pFilter = NULL;
+}
+
+void FilterControls::doOK() {
+ save();
+ this->close();
+}
+
+void FilterControls::doCancel() {
+ s.b->reload();
+ this->close();
+}
+
+void FilterControls::save() {
+ s.b->save();
+ if (pFilter)
+ pFilter->receiveSettings();
+}