Chris@153: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@153: Chris@153: /* Chris@153: QM DSP Library Chris@153: Chris@153: Centre for Digital Music, Queen Mary, University of London. Chris@153: This file Copyright 2010 Chris Cannam. Chris@153: Chris@153: This program is free software; you can redistribute it and/or Chris@153: modify it under the terms of the GNU General Public License as Chris@153: published by the Free Software Foundation; either version 2 of the Chris@153: License, or (at your option) any later version. See the file Chris@153: COPYING included with this distribution for more information. Chris@153: */ Chris@153: Chris@153: #ifndef MEDIAN_FILTER_H Chris@153: #define MEDIAN_FILTER_H Chris@153: Chris@153: #include Chris@153: #include Chris@153: #include Chris@153: #include Chris@153: #include Chris@153: Chris@153: template Chris@153: class MedianFilter Chris@153: { Chris@153: public: Chris@153: MedianFilter(int size, float percentile = 50.f) : Chris@153: m_size(size), Chris@153: m_frame(new T[size]), Chris@153: m_sorted(new T[size]), Chris@153: m_sortend(m_sorted + size - 1) { Chris@153: setPercentile(percentile); Chris@153: reset(); Chris@153: } Chris@153: Chris@153: ~MedianFilter() { Chris@153: delete[] m_frame; Chris@153: delete[] m_sorted; Chris@153: } Chris@153: Chris@153: void setPercentile(float p) { Chris@153: m_index = int((m_size * p) / 100.f); Chris@153: if (m_index >= m_size) m_index = m_size-1; Chris@153: if (m_index < 0) m_index = 0; Chris@153: } Chris@153: Chris@153: void push(T value) { Chris@371: if (value != value) { // nan Chris@371: static bool warned = false; Chris@371: if (!warned) { Chris@371: std::cerr << "WARNING: MedianFilter::push: attempt to push NaN" << std::endl; Chris@371: std::cerr << "WARNING: MedianFilter::push: (only one warning will be printed)" << std::endl; Chris@371: warned = true; Chris@371: } Chris@371: return; Chris@153: } Chris@153: drop(m_frame[0]); Chris@153: const int sz1 = m_size-1; Chris@153: for (int i = 0; i < sz1; ++i) m_frame[i] = m_frame[i+1]; Chris@153: m_frame[m_size-1] = value; Chris@153: put(value); Chris@153: } Chris@153: Chris@153: T get() const { Chris@153: return m_sorted[m_index]; Chris@153: } Chris@153: Chris@153: int getSize() const { Chris@153: return m_size; Chris@153: } Chris@153: Chris@153: T getAt(float percentile) { Chris@153: int ix = int((m_size * percentile) / 100.f); Chris@153: if (ix >= m_size) ix = m_size-1; Chris@153: if (ix < 0) ix = 0; Chris@153: return m_sorted[ix]; Chris@153: } Chris@153: Chris@153: void reset() { Chris@153: for (int i = 0; i < m_size; ++i) m_frame[i] = 0; Chris@153: for (int i = 0; i < m_size; ++i) m_sorted[i] = 0; Chris@153: } Chris@153: Chris@153: static std::vector filter(int size, const std::vector &in) { Chris@153: std::vector out; Chris@153: MedianFilter f(size); Chris@153: for (int i = 0; i < int(in.size()); ++i) { Chris@153: f.push(in[i]); Chris@153: T median = f.get(); Chris@153: if (i >= size/2) out.push_back(median); Chris@153: } Chris@153: while (out.size() < in.size()) { Chris@153: f.push(T()); Chris@153: out.push_back(f.get()); Chris@153: } Chris@153: return out; Chris@153: } Chris@153: Chris@153: private: Chris@153: const int m_size; Chris@153: T *const m_frame; Chris@153: T *const m_sorted; Chris@153: T *const m_sortend; Chris@153: int m_index; Chris@153: Chris@153: void put(T value) { Chris@153: // precondition: m_sorted contains m_size-1 values, packed at start Chris@153: // postcondition: m_sorted contains m_size values, one of which is value Chris@153: T *point = std::lower_bound(m_sorted, m_sortend, value); Chris@153: const int n = m_sortend - point; Chris@153: for (int i = n; i > 0; --i) point[i] = point[i-1]; Chris@153: *point = value; Chris@153: } Chris@153: Chris@153: void drop(T value) { Chris@153: // precondition: m_sorted contains m_size values, one of which is value Chris@153: // postcondition: m_sorted contains m_size-1 values, packed at start Chris@153: T *point = std::lower_bound(m_sorted, m_sortend + 1, value); Chris@153: if (*point != value) { Chris@153: std::cerr << "WARNING: MedianFilter::drop: *point is " << *point Chris@153: << ", expected " << value << std::endl; Chris@153: } Chris@153: const int n = m_sortend - point; Chris@153: for (int i = 0; i < n; ++i) point[i] = point[i+1]; Chris@153: *m_sortend = T(0); Chris@153: } Chris@153: Chris@153: MedianFilter(const MedianFilter &); // not provided Chris@153: MedianFilter &operator=(const MedianFilter &); // not provided Chris@153: }; Chris@153: Chris@153: #endif Chris@153: