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>
|
template <typename T>
|
||||||
class MovingMedianStack
|
class MovingMedian : public SampleFilter<T>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MovingMedianStack(int nfilters, int filterLength, float percentile = 50.f) :
|
MovingMedian(int filterLength, float percentile = 50.f) :
|
||||||
m_buffers(nfilters, filterLength),
|
m_buffer(filterLength),
|
||||||
m_sortspace(nfilters * filterLength, {}),
|
m_sortspace(filterLength, {})
|
||||||
m_length(filterLength)
|
|
||||||
{
|
{
|
||||||
setPercentile(percentile);
|
setPercentile(percentile);
|
||||||
}
|
}
|
||||||
|
|
||||||
~MovingMedianStack() {
|
~MovingMedian() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int getNFilters() const {
|
MovingMedian(const MovingMedian &) =default;
|
||||||
return m_buffers.size();
|
MovingMedian &operator=(const MovingMedian &) =default;
|
||||||
}
|
|
||||||
|
|
||||||
int getSize() const {
|
int getSize() const {
|
||||||
return m_length;
|
return m_buffer.getSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPercentile(float p) {
|
void setPercentile(float p) {
|
||||||
m_index = int((m_length * p) / 100.f);
|
int length = getSize();
|
||||||
if (m_index >= m_length) m_index = m_length-1;
|
m_index = int((length * p) / 100.f);
|
||||||
|
if (m_index >= length) m_index = length-1;
|
||||||
if (m_index < 0) m_index = 0;
|
if (m_index < 0) m_index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void push(int filter, T value) {
|
void push(T value) {
|
||||||
if (value != value) {
|
if (value != value) {
|
||||||
std::cerr << "WARNING: MovingMedian: NaN encountered" << std::endl;
|
std::cerr << "WARNING: MovingMedian: NaN encountered" << std::endl;
|
||||||
value = T();
|
value = T();
|
||||||
}
|
}
|
||||||
auto &buf = m_buffers[filter];
|
if (m_buffer.getWriteSpace() == 0) {
|
||||||
if (buf.getWriteSpace() == 0) {
|
T toDrop = m_buffer.readOne();
|
||||||
T toDrop = buf.readOne();
|
dropAndPut(toDrop, value);
|
||||||
dropAndPut(filter, toDrop, value);
|
m_buffer.writeOne(value);
|
||||||
buf.writeOne(value);
|
|
||||||
} else {
|
} else {
|
||||||
put(filter, value);
|
put(value);
|
||||||
buf.writeOne(value);
|
m_buffer.writeOne(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
T get(int filter) const {
|
T get() const {
|
||||||
const T *sorted = sortedFor(filter);
|
return m_sortspace[m_index];
|
||||||
return sorted[m_index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
void reset() {
|
||||||
for (auto &buf : m_buffers) buf.reset();
|
m_buffer.reset();
|
||||||
v_zero(m_sortspace.data(), m_sortspace.size());
|
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:
|
private:
|
||||||
FixedVector<SingleThreadRingBuffer<T>> m_buffers;
|
SingleThreadRingBuffer<T> m_buffer;
|
||||||
FixedVector<T> m_sortspace;
|
std::vector<T> m_sortspace;
|
||||||
int m_length;
|
|
||||||
int m_index;
|
int m_index;
|
||||||
|
|
||||||
const T *sortedFor(int filter) const {
|
void dropAndPut(const T &toDrop, const T &toPut) {
|
||||||
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) {
|
|
||||||
// precondition: sorted contains m_length values, one of which is toDrop
|
// precondition: sorted contains m_length values, one of which is toDrop
|
||||||
// postcondition: sorted contains m_length values, one of which is toPut
|
// postcondition: sorted contains m_length values, one of which is toPut
|
||||||
// (and one instance of toDrop has been removed)
|
// (and one instance of toDrop has been removed)
|
||||||
@@ -114,8 +136,8 @@ private:
|
|||||||
// longer than maybe 16 items). Two binary searches plus a
|
// longer than maybe 16 items). Two binary searches plus a
|
||||||
// memmove should be faster for longer ones.
|
// memmove should be faster for longer ones.
|
||||||
|
|
||||||
const int n = m_length;
|
const int n = getSize();
|
||||||
T *sorted = sortedFor(filter);
|
T *sorted = m_sortspace.data();
|
||||||
int dropIx;
|
int dropIx;
|
||||||
if (toDrop <= *sorted) {
|
if (toDrop <= *sorted) {
|
||||||
// this is quite a common short-circuit in situations
|
// this is quite a common short-circuit in situations
|
||||||
@@ -175,12 +197,12 @@ private:
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void put(int filter, const T &toPut) {
|
void put(const T &toPut) {
|
||||||
// precondition: sorted contains fewer than m_length values,
|
// precondition: sorted contains fewer than m_length values,
|
||||||
// packed at the start
|
// packed at the start
|
||||||
// postcondition: sorted contains up to m_length values,
|
// postcondition: sorted contains up to m_length values,
|
||||||
// packed at the start, one of which is toPut
|
// 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
|
#ifdef DEBUG_MM
|
||||||
if (n >= m_length) {
|
if (n >= m_length) {
|
||||||
@@ -188,7 +210,7 @@ private:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
T *sorted = sortedFor(filter);
|
T *sorted = m_sortspace.data();
|
||||||
int putIx = std::lower_bound(sorted, sorted + n, toPut) - sorted;
|
int putIx = std::lower_bound(sorted, sorted + n, toPut) - sorted;
|
||||||
|
|
||||||
#ifdef DEBUG_MM
|
#ifdef DEBUG_MM
|
||||||
@@ -220,81 +242,46 @@ private:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
MovingMedianStack(const MovingMedianStack &) =delete;
|
|
||||||
MovingMedianStack &operator=(const MovingMedianStack &) =delete;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class MovingMedian : public SampleFilter<T>
|
class MovingMedianStack
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MovingMedian(int size, float percentile = 50.f) :
|
MovingMedianStack(int nfilters, int size, float percentile = 50.f) :
|
||||||
m_mm(1, size, percentile)
|
m_stack(nfilters, { size, percentile })
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
~MovingMedian() {
|
~MovingMedianStack() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int getSize() const {
|
int getSize() const {
|
||||||
return m_mm.getSize();
|
return m_stack[0].getSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPercentile(float p) {
|
void setPercentile(float p) {
|
||||||
m_mm.setPercentile(p);
|
for (auto &f: m_stack) {
|
||||||
|
f.setPercentile(p);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void push(T value) {
|
void push(int filter, T value) {
|
||||||
m_mm.push(0, value);
|
m_stack[filter].push(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
T get() const {
|
T get(int filter) const {
|
||||||
return m_mm.get(0);
|
return m_stack[filter].get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
void reset() {
|
||||||
m_mm.reset();
|
for (auto &f: m_stack) {
|
||||||
|
f.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 (; 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:
|
private:
|
||||||
MovingMedianStack<T> m_mm;
|
std::vector<MovingMedian<T>> m_stack;
|
||||||
|
|
||||||
MovingMedian(const MovingMedian &) =delete;
|
|
||||||
MovingMedian &operator=(const MovingMedian &) =delete;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user