Restore MovingMedian to the simpler single filter and provide ..Stack separately. Seems little point in coalescing memory there, now we have separate ring buffers anyway
This commit is contained in:
@@ -38,74 +38,96 @@ namespace RubberBand
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
class MovingMedianStack
|
||||
class MovingMedian : public SampleFilter<T>
|
||||
{
|
||||
public:
|
||||
MovingMedianStack(int nfilters, int filterLength, float percentile = 50.f) :
|
||||
m_buffers(nfilters, filterLength),
|
||||
m_sortspace(nfilters * filterLength, {}),
|
||||
m_length(filterLength)
|
||||
MovingMedian(int filterLength, float percentile = 50.f) :
|
||||
m_buffer(filterLength),
|
||||
m_sortspace(filterLength, {})
|
||||
{
|
||||
setPercentile(percentile);
|
||||
}
|
||||
|
||||
~MovingMedianStack() {
|
||||
~MovingMedian() {
|
||||
}
|
||||
|
||||
int getNFilters() const {
|
||||
return m_buffers.size();
|
||||
}
|
||||
|
||||
MovingMedian(const MovingMedian &) =default;
|
||||
MovingMedian &operator=(const MovingMedian &) =default;
|
||||
|
||||
int getSize() const {
|
||||
return m_length;
|
||||
return m_buffer.getSize();
|
||||
}
|
||||
|
||||
void setPercentile(float p) {
|
||||
m_index = int((m_length * p) / 100.f);
|
||||
if (m_index >= m_length) m_index = m_length-1;
|
||||
int length = getSize();
|
||||
m_index = int((length * p) / 100.f);
|
||||
if (m_index >= length) m_index = length-1;
|
||||
if (m_index < 0) m_index = 0;
|
||||
}
|
||||
|
||||
void push(int filter, T value) {
|
||||
void push(T value) {
|
||||
if (value != value) {
|
||||
std::cerr << "WARNING: MovingMedian: NaN encountered" << std::endl;
|
||||
value = T();
|
||||
}
|
||||
auto &buf = m_buffers[filter];
|
||||
if (buf.getWriteSpace() == 0) {
|
||||
T toDrop = buf.readOne();
|
||||
dropAndPut(filter, toDrop, value);
|
||||
buf.writeOne(value);
|
||||
if (m_buffer.getWriteSpace() == 0) {
|
||||
T toDrop = m_buffer.readOne();
|
||||
dropAndPut(toDrop, value);
|
||||
m_buffer.writeOne(value);
|
||||
} else {
|
||||
put(filter, value);
|
||||
buf.writeOne(value);
|
||||
put(value);
|
||||
m_buffer.writeOne(value);
|
||||
}
|
||||
}
|
||||
|
||||
T get(int filter) const {
|
||||
const T *sorted = sortedFor(filter);
|
||||
return sorted[m_index];
|
||||
T get() const {
|
||||
return m_sortspace[m_index];
|
||||
}
|
||||
|
||||
void reset() {
|
||||
for (auto &buf : m_buffers) buf.reset();
|
||||
m_buffer.reset();
|
||||
v_zero(m_sortspace.data(), m_sortspace.size());
|
||||
}
|
||||
|
||||
// Convenience function that applies a given filter to an array
|
||||
// in-place. Array has length n. Modifies both the filter and the
|
||||
// array.
|
||||
//
|
||||
static void filter(MovingMedian<T> &mm, T *v, int n) {
|
||||
int fn = mm.getSize();
|
||||
int lag = fn / 2;
|
||||
mm.reset();
|
||||
int i = 0;
|
||||
for (; i < lag; ++i) {
|
||||
if (i < n) mm.push(v[i]);
|
||||
}
|
||||
for (; i < n; ++i) {
|
||||
mm.push(v[i]);
|
||||
v[i-lag] = mm.get();
|
||||
}
|
||||
for (; i < lag; ++i) {
|
||||
// just for the unusual case where lag > n
|
||||
mm.push(T());
|
||||
(void)mm.get();
|
||||
}
|
||||
for (; i < n + lag; ++i) {
|
||||
mm.push(T());
|
||||
v[i-lag] = mm.get();
|
||||
}
|
||||
}
|
||||
|
||||
// As above but with a vector argument
|
||||
//
|
||||
static void filter(MovingMedian<T> &mm, std::vector<T> &v) {
|
||||
filter(mm, v.data(), v.size());
|
||||
}
|
||||
|
||||
private:
|
||||
FixedVector<SingleThreadRingBuffer<T>> m_buffers;
|
||||
FixedVector<T> m_sortspace;
|
||||
int m_length;
|
||||
SingleThreadRingBuffer<T> m_buffer;
|
||||
std::vector<T> m_sortspace;
|
||||
int m_index;
|
||||
|
||||
const T *sortedFor(int filter) const {
|
||||
return m_sortspace.data() + filter * m_length;
|
||||
}
|
||||
T *sortedFor(int filter) {
|
||||
return m_sortspace.data() + filter * m_length;
|
||||
}
|
||||
|
||||
void dropAndPut(int filter, const T &toDrop, const T &toPut) {
|
||||
void dropAndPut(const T &toDrop, const T &toPut) {
|
||||
// precondition: sorted contains m_length values, one of which is toDrop
|
||||
// postcondition: sorted contains m_length values, one of which is toPut
|
||||
// (and one instance of toDrop has been removed)
|
||||
@@ -114,8 +136,8 @@ private:
|
||||
// longer than maybe 16 items). Two binary searches plus a
|
||||
// memmove should be faster for longer ones.
|
||||
|
||||
const int n = m_length;
|
||||
T *sorted = sortedFor(filter);
|
||||
const int n = getSize();
|
||||
T *sorted = m_sortspace.data();
|
||||
int dropIx;
|
||||
if (toDrop <= *sorted) {
|
||||
// this is quite a common short-circuit in situations
|
||||
@@ -175,12 +197,12 @@ private:
|
||||
#endif
|
||||
}
|
||||
|
||||
void put(int filter, const T &toPut) {
|
||||
void put(const T &toPut) {
|
||||
// precondition: sorted contains fewer than m_length values,
|
||||
// packed at the start
|
||||
// postcondition: sorted contains up to m_length values,
|
||||
// packed at the start, one of which is toPut
|
||||
const int n = m_buffers[filter].getReadSpace(); // items in sorted
|
||||
const int n = m_buffer.getReadSpace(); // items in sorted
|
||||
|
||||
#ifdef DEBUG_MM
|
||||
if (n >= m_length) {
|
||||
@@ -188,7 +210,7 @@ private:
|
||||
}
|
||||
#endif
|
||||
|
||||
T *sorted = sortedFor(filter);
|
||||
T *sorted = m_sortspace.data();
|
||||
int putIx = std::lower_bound(sorted, sorted + n, toPut) - sorted;
|
||||
|
||||
#ifdef DEBUG_MM
|
||||
@@ -220,81 +242,46 @@ private:
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
MovingMedianStack(const MovingMedianStack &) =delete;
|
||||
MovingMedianStack &operator=(const MovingMedianStack &) =delete;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class MovingMedian : public SampleFilter<T>
|
||||
class MovingMedianStack
|
||||
{
|
||||
public:
|
||||
MovingMedian(int size, float percentile = 50.f) :
|
||||
m_mm(1, size, percentile)
|
||||
MovingMedianStack(int nfilters, int size, float percentile = 50.f) :
|
||||
m_stack(nfilters, { size, percentile })
|
||||
{
|
||||
}
|
||||
|
||||
~MovingMedian() {
|
||||
~MovingMedianStack() {
|
||||
}
|
||||
|
||||
int getSize() const {
|
||||
return m_mm.getSize();
|
||||
return m_stack[0].getSize();
|
||||
}
|
||||
|
||||
void setPercentile(float p) {
|
||||
m_mm.setPercentile(p);
|
||||
for (auto &f: m_stack) {
|
||||
f.setPercentile(p);
|
||||
}
|
||||
}
|
||||
|
||||
void push(T value) {
|
||||
m_mm.push(0, value);
|
||||
void push(int filter, T value) {
|
||||
m_stack[filter].push(value);
|
||||
}
|
||||
|
||||
T get() const {
|
||||
return m_mm.get(0);
|
||||
T get(int filter) const {
|
||||
return m_stack[filter].get();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
m_mm.reset();
|
||||
}
|
||||
|
||||
// Convenience function that applies a given filter to an array
|
||||
// in-place. Array has length n. Modifies both the filter and the
|
||||
// array.
|
||||
//
|
||||
static void filter(MovingMedian<T> &mm, T *v, int n) {
|
||||
int fn = mm.getSize();
|
||||
int lag = fn / 2;
|
||||
mm.reset();
|
||||
int i = 0;
|
||||
for (; i < lag; ++i) {
|
||||
if (i < n) mm.push(v[i]);
|
||||
for (auto &f: m_stack) {
|
||||
f.reset();
|
||||
}
|
||||
for (; i < n; ++i) {
|
||||
mm.push(v[i]);
|
||||
v[i-lag] = mm.get();
|
||||
}
|
||||
for (; i < lag; ++i) {
|
||||
// just for the unusual case where lag > n
|
||||
mm.push(T());
|
||||
(void)mm.get();
|
||||
}
|
||||
for (; i < n + lag; ++i) {
|
||||
mm.push(T());
|
||||
v[i-lag] = mm.get();
|
||||
}
|
||||
}
|
||||
|
||||
// As above but with a vector argument
|
||||
//
|
||||
static void filter(MovingMedian<T> &mm, std::vector<T> &v) {
|
||||
filter(mm, v.data(), v.size());
|
||||
}
|
||||
|
||||
private:
|
||||
MovingMedianStack<T> m_mm;
|
||||
|
||||
MovingMedian(const MovingMedian &) =delete;
|
||||
MovingMedian &operator=(const MovingMedian &) =delete;
|
||||
std::vector<MovingMedian<T>> m_stack;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user