diff options
-rw-r--r-- | facetracknoir/gain-control.hpp | 198 | ||||
-rw-r--r-- | facetracknoir/tracker.cpp | 6 | ||||
-rw-r--r-- | ftnoir_filter_ewma2/ftnoir_ewma_filtercontrols.ui | 662 | ||||
-rw-r--r-- | ftnoir_filter_ewma2/ftnoir_filter_ewma2.cpp | 91 | ||||
-rw-r--r-- | ftnoir_filter_ewma2/ftnoir_filter_ewma2.h | 8 | ||||
-rw-r--r-- | qfunctionconfigurator/functionconfig.cpp | 4 |
6 files changed, 330 insertions, 639 deletions
diff --git a/facetracknoir/gain-control.hpp b/facetracknoir/gain-control.hpp deleted file mode 100644 index 081d4b6f..00000000 --- a/facetracknoir/gain-control.hpp +++ /dev/null @@ -1,198 +0,0 @@ -#pragma once - -/* still WIP, not usable yet! -sh 20141012 */ - -#include <algorithm> -#undef NDEBUG -#include <cassert> -#include <iterator> -#include <tuple> -#include <deque> -#include <vector> - -#include <cstdio> - -#include "timer.hpp" - -#include <opencv2/core/core.hpp> -#include <opencv2/highgui/highgui.hpp> -#include <opencv2/imgproc/imgproc.hpp> - -#include <QDebug> - -namespace detail { - template<typename t1, typename t2, typename t, typename m = t> - class zip_iterator : public std::iterator<std::forward_iterator_tag, t> - { - private: - using self = zip_iterator<t1, t2, t, m>; - t1 x1, z1; - t2 x2, z2; - void maybe_end() { if (x1 == z1 || x2 == z2) *this = end(); } - public: - zip_iterator(const t1& it1, const t1& end1, const t2& it2, const t2& end2) - : x1(it1), z1(end1), x2(it2), z2(end2) { maybe_end(); } - constexpr zip_iterator() {} - - static constexpr self end() { return self(); } - - self operator++() { x1++; x2++; self tmp = *this; maybe_end(); return tmp; } - self operator++(int) { self tmp(*this); x1++; x2++; maybe_end(); return tmp; } - bool operator==(const self& rhs) const { return x1 == rhs.x1 && x2 == rhs.x2; } - bool operator!=(const self& rhs) const { return !this->operator ==(rhs); } - t operator*() { return m(*x1, *x2); } - }; -} - -class Gain { -private: - static constexpr bool use_box_filter = true; - static constexpr int box_size = 16 / 640.; - static constexpr double control_upper_bound = 1.0; // XXX FIXME implement for logitech crapola - static constexpr int GAIN_HISTORY_COUNT = 50, GAIN_HISTORY_EVERY_MS = 998; - - using t_frame = cv::Mat_<unsigned char>; - - int control; - double step, eps; - - t_frame last_frame; - std::deque<double> means_history; - Timer debug_timer, history_timer; - - typedef unsigned char px; - template<typename t1, typename t2, typename t, typename m = t> - using zip_iterator = detail::zip_iterator<t1, t2, t, m>; - - static double mean(const cv::Mat& frame) - { - // grayscale only - assert(frame.channels() == 1); - assert(frame.elemSize() == 1); - assert(!frame.empty()); - - return std::accumulate(frame.begin<px>(), frame.end<px>(), 0.) / (frame.rows * frame.cols); - } - - static double get_variance(const cv::Mat& frame, double mean) - { - struct variance { - private: - double mu; - public: - variance(double mu) : mu(mu) {} - double operator()(double seed, px p) - { - double tmp = p - mu; - return seed + tmp * tmp; - } - } logic(mean); - - return std::accumulate(frame.begin<unsigned char>(), frame.end<unsigned char>(), 0., logic) / (frame.rows * frame.cols); - } - - static double get_covariance(const cv::Mat& frame, const cv::Mat& old_frame) - { - double mean_0 = mean(frame), mean_1 = mean(old_frame); - - struct covariance { - public: - using pair = std::tuple<px, px>; - private: - double mu_0, mu_1; - - inline double Cov(double seed, const pair& t) - { - px p0 = std::get<0>(t); - px p1 = std::get<1>(t); - return seed + (p0 - mu_0) * (p1 - mu_1); - } - public: - covariance(double mu_0, double mu_1) : mu_0(mu_0), mu_1(mu_1) {} - - double operator()(double seed, const pair& t) - { - return Cov(seed, t); - } - } logic(mean_0, mean_1); - - const double N = frame.rows * frame.cols; - - using zipper = zip_iterator<cv::MatConstIterator_<px>, - cv::MatConstIterator_<px>, - std::tuple<px, px>>; - - zipper zip(frame.begin<px>(), - frame.end<px>(), - old_frame.begin<px>(), - old_frame.end<px>()); - std::vector<covariance::pair> values(zip, zipper::end()); - - return std::accumulate(values.begin(), values.end(), 0., logic) / N; - } - -#pragma GCC diagnostic ignored "-Wsign-compare" - -public: - Gain(int control = CV_CAP_PROP_GAIN, double step = 0.3, double eps = 0.02) : - control(control), step(step), eps(eps) - { - } - - void tick(cv::VideoCapture&, const cv::Mat& frame_) - { - cv::Mat frame; - - if (use_box_filter) - { - cv::Mat tmp(frame_); - static constexpr int min_box = 3; - static constexpr int box = 2 * box_size; - cv::blur(frame_, tmp, cv::Size(min_box + box * frame_.cols, min_box + box * frame_.rows)); - frame = tmp; - } - else - frame = frame_; - - if (last_frame.rows != frame.rows || last_frame.cols != frame.cols) - last_frame = t_frame(); - - if (last_frame.empty()) - { - last_frame = frame.clone(); - //return; - } - - if (history_timer.elapsed_ms() > GAIN_HISTORY_EVERY_MS) - { - //const double cov = get_covariance(frame, last_frame); - history_timer.start(); - last_frame = frame.clone(); - - if (means_history.size() == GAIN_HISTORY_COUNT) - means_history.pop_back(); - } - - if (debug_timer.elapsed_ms() > 1000) - { - const double mu = mean(frame); - // XXX move to HSL/HSV color space for it to work! -sh 20141012 - const double var = get_variance(frame, mu); - - debug_timer.start(); - qDebug() << "---- gain:" << "mean" << mu << "variance" << var; - - const int sz = means_history.size(); - - if (sz) - { - fprintf(stderr, "covs{%d}: ", sz); - - for (int i = 0; i < sz; i++) - fprintf(stderr, "%f ", means_history[i]); - - fprintf(stderr, "\n"); - } - } - } -}; diff --git a/facetracknoir/tracker.cpp b/facetracknoir/tracker.cpp index f17b257f..0f299153 100644 --- a/facetracknoir/tracker.cpp +++ b/facetracknoir/tracker.cpp @@ -150,8 +150,10 @@ void Tracker::run() { logic(); - const long q = 1000L * std::max(0L, sleep_ms - t.elapsed_ms()); - usleep(q); + double q = sleep_ms * 1000L; + q -= t.elapsed(); + q = std::max(0., q); + usleep((long)q); } #if defined(_WIN32) diff --git a/ftnoir_filter_ewma2/ftnoir_ewma_filtercontrols.ui b/ftnoir_filter_ewma2/ftnoir_ewma_filtercontrols.ui index 1a798a45..9387f0d5 100644 --- a/ftnoir_filter_ewma2/ftnoir_ewma_filtercontrols.ui +++ b/ftnoir_filter_ewma2/ftnoir_ewma_filtercontrols.ui @@ -9,16 +9,10 @@ <rect> <x>0</x> <y>0</y> - <width>404</width> + <width>448</width> <height>380</height> </rect> </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>380</height> - </size> - </property> <property name="windowTitle"> <string>EWMA2 filter settings</string> </property> @@ -35,43 +29,258 @@ <property name="styleSheet"> <string notr="true"/> </property> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="geometry"> - <rect> - <x>204</x> - <y>351</y> - <width>164</width> - <height>25</height> - </rect> - </property> - <property name="standardButtons"> - <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> - </property> - </widget> - <widget class="QLabel" name="label_4"> - <property name="geometry"> - <rect> - <x>4</x> - <y>143</y> - <width>396</width> - <height>204</height> - </rect> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - <property name="styleSheet"> - <string notr="true">background-color: rgb(214, 214, 214); + <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><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> + </property> + <property name="frameShape"> + <enum>QFrame::Box</enum> + </property> + <property name="text"> + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> @@ -87,287 +296,20 @@ p, li { white-space: pre-wrap; } <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2';">Defines the filters 'readiness' to respond to speed changes;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2';">Higher value = </span><span style=" font-family:'MS Shell Dlg 2'; font-weight:600;">faster</span><span style=" font-family:'MS Shell Dlg 2';"> response;</span></p> <p style="-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';"><br /></p></body></html></string> - </property> - <property name="margin"> - <number>5</number> - </property> - </widget> - <widget class="QLabel" name="lblInvert1_6"> - <property name="geometry"> - <rect> - <x>5</x> - <y>5</y> - <width>27</width> - <height>16</height> - </rect> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>16777215</width> - <height>16777215</height> - </size> - </property> - <property name="styleSheet"> - <string notr="true"/> - </property> - <property name="text"> - <string>Min</string> - </property> - </widget> - <widget class="QSlider" name="maxSmooth"> - <property name="geometry"> - <rect> - <x>47</x> - <y>32</y> - <width>84</width> - <height>23</height> - </rect> - </property> - <property name="minimumSize"> - <size> - <width>45</width> - <height>15</height> - </size> - </property> - <property name="minimum"> - <number>1</number> - </property> - <property name="maximum"> - <number>100</number> - </property> - <property name="pageStep"> - <number>10</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> - <widget class="QSpinBox" name="spinMaxSmooth"> - <property name="geometry"> - <rect> - <x>135</x> - <y>32</y> - <width>52</width> - <height>23</height> - </rect> - </property> - <property name="minimumSize"> - <size> - <width>40</width> - <height>22</height> - </size> - </property> - <property name="styleSheet"> - <string notr="true">background:none;</string> - </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> - <widget class="QSpinBox" name="spinPowCurve"> - <property name="geometry"> - <rect> - <x>135</x> - <y>59</y> - <width>52</width> - <height>23</height> - </rect> - </property> - <property name="minimumSize"> - <size> - <width>40</width> - <height>22</height> - </size> - </property> - <property name="styleSheet"> - <string notr="true">background:none;</string> - </property> - <property name="maximum"> - <number>100</number> - </property> - <property name="singleStep"> - <number>5</number> - </property> - <property name="value"> - <number>10</number> - </property> - </widget> - <widget class="QSlider" name="powCurve"> - <property name="geometry"> - <rect> - <x>47</x> - <y>59</y> - <width>84</width> - <height>23</height> - </rect> - </property> - <property name="minimumSize"> - <size> - <width>45</width> - <height>15</height> - </size> - </property> - <property name="minimum"> - <number>1</number> - </property> - <property name="maximum"> - <number>100</number> - </property> - <property name="pageStep"> - <number>10</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> - <widget class="QLabel" name="lblInvert1_8"> - <property name="geometry"> - <rect> - <x>5</x> - <y>59</y> - <width>38</width> - <height>16</height> - </rect> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>16777215</width> - <height>16777215</height> - </size> - </property> - <property name="styleSheet"> - <string notr="true"/> - </property> - <property name="text"> - <string>Curve</string> - </property> - </widget> - <widget class="QLabel" name="lblInvert1_7"> - <property name="geometry"> - <rect> - <x>5</x> - <y>32</y> - <width>32</width> - <height>16</height> - </rect> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>16777215</width> - <height>16777215</height> - </size> - </property> - <property name="styleSheet"> - <string notr="true"/> - </property> - <property name="text"> - <string>Max</string> - </property> - </widget> - <widget class="QSlider" name="minSmooth"> - <property name="geometry"> - <rect> - <x>47</x> - <y>5</y> - <width>84</width> - <height>23</height> - </rect> - </property> - <property name="minimumSize"> - <size> - <width>45</width> - <height>15</height> - </size> - </property> - <property name="minimum"> - <number>1</number> - </property> - <property name="maximum"> - <number>100</number> - </property> - <property name="pageStep"> - <number>10</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> - <widget class="QSpinBox" name="spinMinSmooth"> - <property name="geometry"> - <rect> - <x>135</x> - <y>5</y> - <width>52</width> - <height>23</height> - </rect> - </property> - <property name="minimumSize"> - <size> - <width>40</width> - <height>22</height> - </size> - </property> - <property name="styleSheet"> - <string notr="true">background:none;</string> - </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> + </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"/> @@ -380,28 +322,12 @@ p, li { white-space: pre-wrap; } <slot>setValue(int)</slot> <hints> <hint type="sourcelabel"> - <x>199</x> - <y>22</y> + <x>303</x> + <y>33</y> </hint> <hint type="destinationlabel"> - <x>337</x> - <y>23</y> - </hint> - </hints> - </connection> - <connection> - <sender>spinMinSmooth</sender> - <signal>valueChanged(int)</signal> - <receiver>minSmooth</receiver> - <slot>setValue(int)</slot> - <hints> - <hint type="sourcelabel"> - <x>330</x> - <y>12</y> - </hint> - <hint type="destinationlabel"> - <x>185</x> - <y>17</y> + <x>391</x> + <y>36</y> </hint> </hints> </connection> @@ -412,28 +338,12 @@ p, li { white-space: pre-wrap; } <slot>setValue(int)</slot> <hints> <hint type="sourcelabel"> - <x>181</x> - <y>48</y> - </hint> - <hint type="destinationlabel"> - <x>335</x> - <y>54</y> - </hint> - </hints> - </connection> - <connection> - <sender>spinMaxSmooth</sender> - <signal>valueChanged(int)</signal> - <receiver>maxSmooth</receiver> - <slot>setValue(int)</slot> - <hints> - <hint type="sourcelabel"> - <x>324</x> - <y>42</y> + <x>281</x> + <y>61</y> </hint> <hint type="destinationlabel"> - <x>259</x> - <y>43</y> + <x>390</x> + <y>74</y> </hint> </hints> </connection> @@ -444,28 +354,12 @@ p, li { white-space: pre-wrap; } <slot>setValue(int)</slot> <hints> <hint type="sourcelabel"> - <x>145</x> - <y>74</y> - </hint> - <hint type="destinationlabel"> - <x>339</x> - <y>78</y> - </hint> - </hints> - </connection> - <connection> - <sender>spinPowCurve</sender> - <signal>valueChanged(int)</signal> - <receiver>powCurve</receiver> - <slot>setValue(int)</slot> - <hints> - <hint type="sourcelabel"> - <x>330</x> - <y>69</y> + <x>236</x> + <y>101</y> </hint> <hint type="destinationlabel"> - <x>176</x> - <y>76</y> + <x>391</x> + <y>98</y> </hint> </hints> </connection> diff --git a/ftnoir_filter_ewma2/ftnoir_filter_ewma2.cpp b/ftnoir_filter_ewma2/ftnoir_filter_ewma2.cpp index c7169faa..320b95ad 100644 --- a/ftnoir_filter_ewma2/ftnoir_filter_ewma2.cpp +++ b/ftnoir_filter_ewma2/ftnoir_filter_ewma2.cpp @@ -1,19 +1,9 @@ -/*** Written by Donovan Baarda -* -* 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 <http://www.gnu.org/licenses/>. * -* * -********************************************************************************/ +/* 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> @@ -22,12 +12,26 @@ #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() : first_run(true), - // Deltas are smoothed over the last 3 frames (0.1sec at 30fps). - delta_smoothing(1.0/3.0), - // Noise is smoothed over the last 3600 frames (~2mins at 30fps). - noise_smoothing(1.0/3600.0) + // Currently facetracknoir/tracker.cpp updates every dt=3ms. All + // filter alpha values are calculated as alpha=dt/(dt+RC) and + // need to be updated when tracker.cpp changes. + // TODO(abo): Change this to use a dynamic dt using a timer. + // Deltas are smoothed over the last 1/60sec (16ms). + delta_alpha(0.003/(0.003 + 0.016)), + // Noise is smoothed over the last 60sec. + noise_alpha(0.003/(0.003 + 60.0)) { } @@ -40,54 +44,39 @@ void FTNoIR_Filter::FilterHeadPoseData(const double *target_camera_position, double *new_camera_position) { double new_delta, new_noise, norm_noise; - double alpha; + double smoothing, RC, alpha; - //On the first run, initialize to output=target and return. + //On the first run, initialize filter states to target intput. 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]; + output[i] = target_camera_position[i]; delta[i] = 0.0; noise[i] = 0.0; } first_run=false; - return; - } - - bool new_frame = false; - - for (int i = 0; i < 6; i++) - { - if (target_camera_position[i] != current_camera_position[i]) - { - new_frame = true; - break; - } - } - - if (!new_frame) - { - for (int i = 0; i < 6; i++) - new_camera_position[i] = current_camera_position[i]; - return; } // Calculate the new camera position. for (int i=0;i<6;i++) { // Calculate the current and smoothed delta. - new_delta = target_camera_position[i]-current_camera_position[i]; - delta[i] = delta_smoothing*new_delta + (1.0-delta_smoothing)*delta[i]; + new_delta = target_camera_position[i]-output[i]; + delta[i] = delta_alpha*new_delta + (1.0-delta_alpha)*delta[i]; // Calculate the current and smoothed noise variance. new_noise = delta[i]*delta[i]; - noise[i] = noise_smoothing*new_noise + (1.0-noise_smoothing)*noise[i]; + noise[i] = noise_alpha*new_noise + (1.0-noise_alpha)*noise[i]; // Normalise the noise between 0->1 for 0->9 variances (0->3 stddevs). norm_noise = std::min<double>(new_noise/(9.0*noise[i]), 1.0); - // Calculate the alpha from the normalized noise. + // 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). - alpha = 1.0/(s.kMinSmoothing+(1.0-pow(norm_noise,s.kSmoothingScaleCurve/20.0))*(s.kMaxSmoothing-s.kMinSmoothing)); - // Update the current camera position to the new position. - double pos = alpha*target_camera_position[i] + (1.0-alpha)*current_camera_position[i]; - new_camera_position[i] = current_camera_position[i] = pos; + 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. + RC = 3.0*(s.kMinSmoothing + smoothing*(s.kMaxSmoothing - s.kMinSmoothing))/100.0; + // TODO(abo): Change this to use a dynamic dt using a timer. + alpha = 0.003/(0.003 + RC); + // Calculate the new output position. + output[i] = alpha*target_camera_position[i] + (1.0-alpha)*output[i]; + new_camera_position[i] = output[i]; } } diff --git a/ftnoir_filter_ewma2/ftnoir_filter_ewma2.h b/ftnoir_filter_ewma2/ftnoir_filter_ewma2.h index 8863348c..aacf4916 100644 --- a/ftnoir_filter_ewma2/ftnoir_filter_ewma2.h +++ b/ftnoir_filter_ewma2/ftnoir_filter_ewma2.h @@ -24,17 +24,17 @@ class FTNoIR_Filter : public IFilter { public: FTNoIR_Filter(); - void reset() {} + void reset() { first_run=true; } void FilterHeadPoseData(const double *target_camera_position, double *new_camera_position); void receiveSettings(); private: bool first_run; - double delta_smoothing; - double noise_smoothing; + double delta_alpha; + double noise_alpha; double delta[6]; double noise[6]; - double current_camera_position[6]; + double output[6]; settings s; }; diff --git a/qfunctionconfigurator/functionconfig.cpp b/qfunctionconfigurator/functionconfig.cpp index a4d03ed8..a0715754 100644 --- a/qfunctionconfigurator/functionconfig.cpp +++ b/qfunctionconfigurator/functionconfig.cpp @@ -179,6 +179,10 @@ void Map::loadSettings(QSettings& settings, const QString& title) { } settings.endGroup(); + + if (max == 0) + points.append(QPointF(maxInput(), maxOutput())); + cur.input = points; reload(); saved = cur; |