diff base/test/TestMovingMedian.h @ 1573:f04038819c26 spectrogramparam

Introduce & make use of faster MovingMedian class (now with resize capability)
author Chris Cannam
date Thu, 08 Nov 2018 15:02:30 +0000
parents
children cfcfec216c21
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/test/TestMovingMedian.h	Thu Nov 08 15:02:30 2018 +0000
@@ -0,0 +1,168 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    
+    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 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#ifndef TEST_MOVING_MEDIAN_H
+#define TEST_MOVING_MEDIAN_H
+
+#include "../MovingMedian.h"
+
+#include <QObject>
+#include <QtTest>
+#include <QDir>
+
+#include <iostream>
+
+using namespace std;
+
+class TestMovingMedian : public QObject
+{
+    Q_OBJECT
+
+    template <typename T>
+    void checkExpected(const vector<T> &output,
+                       const vector<T> &expected) {
+        if (output.size() != expected.size()) {
+            cerr << "ERROR: output array size " << output.size()
+                 << " differs from expected size " << expected.size() << endl;
+        }
+        for (int i = 0; i < int(output.size()); ++i) {
+            if (output[i] != expected[i]) {
+                cerr << "ERROR: Value at index " << i
+                     << " in output array differs from expected" << endl;
+                cerr << "Output:   ";
+                for (auto v: output) cerr << v << " ";
+                cerr << "\nExpected: ";
+                for (auto v: expected) cerr << v << " ";
+                cerr << endl;
+                break;
+            }
+        }
+        QCOMPARE(output, expected);
+    }
+
+    template <typename T>
+    void testFixed(int n,
+                   const vector<T> &input,
+                   const vector<T> &expected,
+                   double percentile = 50.0) {
+        vector<T> output;
+        MovingMedian<T> mm(n, percentile);
+        for (auto v: input) {
+            mm.push(v);
+            mm.checkIntegrity();
+            output.push_back(mm.get());
+        }
+        mm.checkIntegrity();
+        checkExpected<T>(output, expected);
+    }
+
+private slots:
+
+    void empty() {
+        MovingMedian<double> mm(3);
+        QCOMPARE(mm.get(), 0.0);
+    }
+    
+    void zeros() {
+        vector<double> input { 0.0, 0.0, 0.0, 0.0, 0.0 };
+        vector<double> expected { 0.0, 0.0, 0.0, 0.0, 0.0 };
+        testFixed<double>(3, input, expected);
+    }
+    
+    void ascending() {
+        vector<double> input { 1.0, 2.0, 3.0, 4.0, 5.0 };
+        vector<double> expected { 0.0, 1.0, 2.0, 3.0, 4.0 };
+        testFixed<double>(3, input, expected);
+    }
+
+    void ascendingInt() {
+        vector<int> input { 1, 2, 3, 4, 5 };
+        vector<int> expected { 0, 1, 2, 3, 4 };
+        testFixed<int>(3, input, expected);
+    }
+
+    void descending() {
+        vector<double> input { 5.0, 4.0, 3.0, 2.0, 1.0 };
+        vector<double> expected { 0.0, 4.0, 4.0, 3.0, 2.0 };
+        testFixed<double>(3, input, expected);
+    }
+
+    void descendingInt() {
+        vector<int> input { 5, 4, 3, 2, 1 };
+        vector<int> expected { 0, 4, 4, 3, 2 };
+        testFixed<int>(3, input, expected);
+    }
+
+    void duplicates() {
+        vector<double> input { 2.0, 2.0, 3.0, 4.0, 3.0 };
+        vector<double> expected { 0.0, 2.0, 2.0, 3.0, 3.0 };
+        testFixed<double>(3, input, expected);
+    }
+    
+    void percentile10() {
+        vector<double> input { 1.0, 2.0, 3.0, 4.0, 5.0 };
+        vector<double> expected { 0.0, 0.0, 1.0, 2.0, 3.0 };
+        testFixed<double>(3, input, expected, 10);
+    }
+    
+    void percentile90() {
+        vector<double> input { 1.0, 2.0, 3.0, 4.0, 5.0 };
+        vector<double> expected { 1.0, 2.0, 3.0, 4.0, 5.0 };
+        testFixed<double>(3, input, expected, 90);
+    }
+
+    void even() {
+        vector<double> input { 5.0, 4.0, 3.0, 2.0, 1.0 };
+        vector<double> expected { 0.0, 4.0, 4.0, 4.0, 3.0 };
+        testFixed<double>(4, input, expected);
+    }
+
+    void growing() {
+        vector<double> input { 2.0, 4.0, 3.0, 2.5, 2.5, 3.0, 1.0, 2.0, 1.0, 0.0 };
+        vector<double> expected { 2.0, 4.0, 4.0, 3.0, 2.5, 2.5, 2.5, 2.5, 2.0, 1.0 };
+        vector<double> output;
+        MovingMedian<double> mm(1);
+        for (int i = 0; i < int(input.size()); ++i) {
+            // sizes 1, 1, 2, 2, 3, 3, 4, 4, 5, 5
+            int sz = i/2 + 1;
+            mm.resize(sz);
+            QCOMPARE(mm.size(), sz);
+            mm.push(input[i]);
+            mm.checkIntegrity();
+            output.push_back(mm.get());
+        }
+        mm.checkIntegrity();
+        checkExpected<double>(output, expected);
+    }
+        
+    void shrinking() {
+        vector<double> input { 2.0, 4.0, 3.0, 2.5, 2.5, 3.0, 1.0, 2.0, 1.0, 0.0 };
+        vector<double> expected { 0.0, 0.0, 3.0, 3.0, 2.5, 2.5, 3.0, 2.0, 1.0, 0.0 };
+        vector<double> output;
+        MovingMedian<double> mm(99);
+        for (int i = 0; i < int(input.size()); ++i) {
+            // sizes 5, 5, 4, 4, 3, 3, 2, 2, 1, 1
+            int sz = 5 - i/2;
+            mm.resize(sz);
+            QCOMPARE(mm.size(), sz);
+            mm.push(input[i]);
+            mm.checkIntegrity();
+            output.push_back(mm.get());
+        }
+        mm.checkIntegrity();
+        checkExpected<double>(output, expected);
+    }
+};
+
+#endif