Chris@153
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@153
|
2
|
Chris@153
|
3 /*
|
Chris@181
|
4 Silvet
|
Chris@153
|
5
|
Chris@181
|
6 A Vamp plugin for note transcription.
|
Chris@181
|
7 Centre for Digital Music, Queen Mary University of London.
|
Chris@181
|
8 This file Copyright 2010 Chris Cannam.
|
Chris@153
|
9
|
Chris@181
|
10 This program is free software; you can redistribute it and/or
|
Chris@181
|
11 modify it under the terms of the GNU General Public License as
|
Chris@181
|
12 published by the Free Software Foundation; either version 2 of the
|
Chris@181
|
13 License, or (at your option) any later version. See the file
|
Chris@181
|
14 COPYING included with this distribution for more information.
|
Chris@153
|
15 */
|
Chris@153
|
16
|
Chris@153
|
17 #ifndef MEDIAN_FILTER_H
|
Chris@153
|
18 #define MEDIAN_FILTER_H
|
Chris@153
|
19
|
Chris@153
|
20 #include <algorithm>
|
Chris@153
|
21 #include <cassert>
|
Chris@153
|
22 #include <cmath>
|
Chris@153
|
23 #include <iostream>
|
Chris@153
|
24 #include <vector>
|
Chris@153
|
25
|
Chris@153
|
26 template <typename T>
|
Chris@153
|
27 class MedianFilter
|
Chris@153
|
28 {
|
Chris@153
|
29 public:
|
Chris@153
|
30 MedianFilter(int size, float percentile = 50.f) :
|
Chris@153
|
31 m_size(size),
|
Chris@153
|
32 m_frame(new T[size]),
|
Chris@153
|
33 m_sorted(new T[size]),
|
Chris@153
|
34 m_sortend(m_sorted + size - 1) {
|
Chris@153
|
35 setPercentile(percentile);
|
Chris@153
|
36 reset();
|
Chris@153
|
37 }
|
Chris@153
|
38
|
Chris@153
|
39 ~MedianFilter() {
|
Chris@153
|
40 delete[] m_frame;
|
Chris@153
|
41 delete[] m_sorted;
|
Chris@153
|
42 }
|
Chris@153
|
43
|
Chris@153
|
44 void setPercentile(float p) {
|
Chris@153
|
45 m_index = int((m_size * p) / 100.f);
|
Chris@153
|
46 if (m_index >= m_size) m_index = m_size-1;
|
Chris@153
|
47 if (m_index < 0) m_index = 0;
|
Chris@153
|
48 }
|
Chris@153
|
49
|
Chris@153
|
50 void push(T value) {
|
Chris@153
|
51 if (value != value) {
|
Chris@153
|
52 std::cerr << "WARNING: MedianFilter::push: attempt to push NaN" << std::endl;
|
Chris@153
|
53 return; // nan
|
Chris@153
|
54 }
|
Chris@153
|
55 drop(m_frame[0]);
|
Chris@153
|
56 const int sz1 = m_size-1;
|
Chris@153
|
57 for (int i = 0; i < sz1; ++i) m_frame[i] = m_frame[i+1];
|
Chris@153
|
58 m_frame[m_size-1] = value;
|
Chris@153
|
59 put(value);
|
Chris@153
|
60 }
|
Chris@153
|
61
|
Chris@153
|
62 T get() const {
|
Chris@153
|
63 return m_sorted[m_index];
|
Chris@153
|
64 }
|
Chris@153
|
65
|
Chris@153
|
66 int getSize() const {
|
Chris@153
|
67 return m_size;
|
Chris@153
|
68 }
|
Chris@153
|
69
|
Chris@153
|
70 T getAt(float percentile) {
|
Chris@153
|
71 int ix = int((m_size * percentile) / 100.f);
|
Chris@153
|
72 if (ix >= m_size) ix = m_size-1;
|
Chris@153
|
73 if (ix < 0) ix = 0;
|
Chris@153
|
74 return m_sorted[ix];
|
Chris@153
|
75 }
|
Chris@153
|
76
|
Chris@153
|
77 void reset() {
|
Chris@153
|
78 for (int i = 0; i < m_size; ++i) m_frame[i] = 0;
|
Chris@153
|
79 for (int i = 0; i < m_size; ++i) m_sorted[i] = 0;
|
Chris@153
|
80 }
|
Chris@153
|
81
|
Chris@153
|
82 static std::vector<T> filter(int size, const std::vector<T> &in) {
|
Chris@153
|
83 std::vector<T> out;
|
Chris@153
|
84 MedianFilter<T> f(size);
|
Chris@153
|
85 for (int i = 0; i < int(in.size()); ++i) {
|
Chris@153
|
86 f.push(in[i]);
|
Chris@153
|
87 T median = f.get();
|
Chris@153
|
88 if (i >= size/2) out.push_back(median);
|
Chris@153
|
89 }
|
Chris@153
|
90 while (out.size() < in.size()) {
|
Chris@153
|
91 f.push(T());
|
Chris@153
|
92 out.push_back(f.get());
|
Chris@153
|
93 }
|
Chris@153
|
94 return out;
|
Chris@153
|
95 }
|
Chris@153
|
96
|
Chris@153
|
97 private:
|
Chris@153
|
98 const int m_size;
|
Chris@153
|
99 T *const m_frame;
|
Chris@153
|
100 T *const m_sorted;
|
Chris@153
|
101 T *const m_sortend;
|
Chris@153
|
102 int m_index;
|
Chris@153
|
103
|
Chris@153
|
104 void put(T value) {
|
Chris@153
|
105 // precondition: m_sorted contains m_size-1 values, packed at start
|
Chris@153
|
106 // postcondition: m_sorted contains m_size values, one of which is value
|
Chris@153
|
107 T *point = std::lower_bound(m_sorted, m_sortend, value);
|
Chris@153
|
108 const int n = m_sortend - point;
|
Chris@153
|
109 for (int i = n; i > 0; --i) point[i] = point[i-1];
|
Chris@153
|
110 *point = value;
|
Chris@153
|
111 }
|
Chris@153
|
112
|
Chris@153
|
113 void drop(T value) {
|
Chris@153
|
114 // precondition: m_sorted contains m_size values, one of which is value
|
Chris@153
|
115 // postcondition: m_sorted contains m_size-1 values, packed at start
|
Chris@153
|
116 T *point = std::lower_bound(m_sorted, m_sortend + 1, value);
|
Chris@153
|
117 if (*point != value) {
|
Chris@153
|
118 std::cerr << "WARNING: MedianFilter::drop: *point is " << *point
|
Chris@153
|
119 << ", expected " << value << std::endl;
|
Chris@153
|
120 }
|
Chris@153
|
121 const int n = m_sortend - point;
|
Chris@153
|
122 for (int i = 0; i < n; ++i) point[i] = point[i+1];
|
Chris@153
|
123 *m_sortend = T(0);
|
Chris@153
|
124 }
|
Chris@153
|
125
|
Chris@153
|
126 MedianFilter(const MedianFilter &); // not provided
|
Chris@153
|
127 MedianFilter &operator=(const MedianFilter &); // not provided
|
Chris@153
|
128 };
|
Chris@153
|
129
|
Chris@153
|
130 #endif
|
Chris@153
|
131
|