Chris@166: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@166: Chris@166: #include "maths/MedianFilter.h" Chris@166: Chris@166: #include Chris@182: #include Chris@166: Chris@166: #define BOOST_TEST_DYN_LINK Chris@166: #define BOOST_TEST_MAIN Chris@166: Chris@166: #include Chris@166: Chris@166: BOOST_AUTO_TEST_SUITE(TestMedianFilter) Chris@166: Chris@166: BOOST_AUTO_TEST_CASE(odd) Chris@166: { Chris@166: // A median filter of size N always retains a pool of N elements, Chris@166: // which start out all-zero. So the output median will remain zero Chris@166: // until N/2 elements have been pushed. Chris@166: MedianFilter f(3); Chris@166: f.push(1); // 0 0 1 Chris@166: BOOST_CHECK_EQUAL(f.get(), 0); Chris@166: BOOST_CHECK_EQUAL(f.get(), 0); Chris@166: f.push(-3); // 0 1 -3 Chris@166: BOOST_CHECK_EQUAL(f.get(), 0); Chris@166: f.push(5); // 1 -3 5 Chris@166: BOOST_CHECK_EQUAL(f.get(), 1); Chris@166: f.push(7); // -3 5 7 Chris@166: BOOST_CHECK_EQUAL(f.get(), 5); Chris@166: BOOST_CHECK_EQUAL(f.get(), 5); Chris@166: f.push(3); // 5 7 3 Chris@166: BOOST_CHECK_EQUAL(f.get(), 5); Chris@166: f.push(3); // 7 3 3 Chris@166: BOOST_CHECK_EQUAL(f.get(), 3); Chris@166: } Chris@166: Chris@166: BOOST_AUTO_TEST_CASE(even) Chris@166: { Chris@166: // Our median does not halve the difference (should it??), it just Chris@166: // returns the next element from the input set Chris@166: MedianFilter f(4); Chris@166: f.push(1); // 0 0 0 1 Chris@166: BOOST_CHECK_EQUAL(f.get(), 0); Chris@166: BOOST_CHECK_EQUAL(f.get(), 0); Chris@166: f.push(-3); // 0 0 1 -3 Chris@166: BOOST_CHECK_EQUAL(f.get(), 0); Chris@166: f.push(5); // 0 1 -3 5 Chris@166: BOOST_CHECK_EQUAL(f.get(), 1); Chris@166: f.push(7); // 1 -3 5 7 Chris@166: BOOST_CHECK_EQUAL(f.get(), 5); Chris@166: BOOST_CHECK_EQUAL(f.get(), 5); Chris@166: f.push(3); // -3 5 7 3 Chris@166: BOOST_CHECK_EQUAL(f.get(), 5); Chris@166: f.push(3); // 5 7 3 3 Chris@166: BOOST_CHECK_EQUAL(f.get(), 5); Chris@166: } Chris@166: Chris@166: BOOST_AUTO_TEST_CASE(odd75) Chris@166: { Chris@166: MedianFilter f(5, 75.f); Chris@166: f.push(1); // 0 0 0 0 1 Chris@166: BOOST_CHECK_EQUAL(f.get(), 0); Chris@166: BOOST_CHECK_EQUAL(f.get(), 0); Chris@166: f.push(-3); // 0 0 0 1 -3 Chris@166: BOOST_CHECK_EQUAL(f.get(), 0); Chris@166: f.push(5); // 0 0 1 -3 5 Chris@166: BOOST_CHECK_EQUAL(f.get(), 1); Chris@166: f.push(7); // 0 1 -3 5 7 Chris@166: BOOST_CHECK_EQUAL(f.get(), 5); Chris@166: BOOST_CHECK_EQUAL(f.get(), 5); Chris@166: f.push(3); // 0 -3 5 7 3 Chris@166: BOOST_CHECK_EQUAL(f.get(), 5); Chris@166: f.push(3); // 0 5 7 3 3 Chris@166: BOOST_CHECK_EQUAL(f.get(), 5); Chris@166: } Chris@166: Chris@166: BOOST_AUTO_TEST_CASE(even75) Chris@166: { Chris@166: MedianFilter f(4, 75.f); Chris@166: f.push(1); // 0 0 0 1 Chris@166: BOOST_CHECK_EQUAL(f.get(), 1); Chris@166: BOOST_CHECK_EQUAL(f.get(), 1); Chris@166: f.push(-3); // 0 0 1 -3 Chris@166: BOOST_CHECK_EQUAL(f.get(), 1); Chris@166: f.push(5); // 0 1 -3 5 Chris@166: BOOST_CHECK_EQUAL(f.get(), 5); Chris@166: f.push(7); // 1 -3 5 7 Chris@166: BOOST_CHECK_EQUAL(f.get(), 7); Chris@166: BOOST_CHECK_EQUAL(f.get(), 7); Chris@166: f.push(3); // -3 5 7 3 Chris@166: BOOST_CHECK_EQUAL(f.get(), 7); Chris@166: f.push(3); // 5 7 3 3 Chris@166: BOOST_CHECK_EQUAL(f.get(), 7); Chris@166: } Chris@166: Chris@167: BOOST_AUTO_TEST_CASE(oddStandalone) Chris@167: { Chris@167: // The standalone (static filter() method) function is intended to Chris@167: // match e.g. MATLAB median filter, filtering a row at once Chris@167: // assuming the values beyond its edges are zeroes. This basically Chris@167: // just means it needs to compensate for the latency in the raw Chris@167: // filter: Chris@167: // Chris@167: // Raw filter Chris@167: // 1 3 5 7 -> 0 1 3 5 (N=3) Chris@167: // (because at each step only the values up to that step are Chris@167: // available) Chris@167: // Chris@167: // Standalone function: Chris@167: // 1 3 5 7 -> 1 3 5 5 (N=3) Chris@167: Chris@167: std::vector in; Chris@167: in.push_back(1); Chris@167: in.push_back(3); Chris@167: in.push_back(5); Chris@167: in.push_back(7); Chris@167: Chris@167: std::vector out = MedianFilter::filter(3, in); Chris@167: BOOST_CHECK_EQUAL(out[0], 1); Chris@167: BOOST_CHECK_EQUAL(out[1], 3); Chris@167: BOOST_CHECK_EQUAL(out[2], 5); Chris@167: BOOST_CHECK_EQUAL(out[3], 5); Chris@167: } Chris@167: Chris@167: BOOST_AUTO_TEST_CASE(evenStandalone) Chris@167: { Chris@167: // See above. But note that the even length does not match the Chris@167: // MATLAB because it doesn't halve the middle, as MATLAB does Chris@167: Chris@167: std::vector in; Chris@167: in.push_back(1); Chris@167: in.push_back(3); Chris@167: in.push_back(5); Chris@167: in.push_back(7); Chris@167: Chris@167: std::vector out = MedianFilter::filter(4, in); Chris@167: BOOST_CHECK_EQUAL(out[0], 3); Chris@167: BOOST_CHECK_EQUAL(out[1], 5); Chris@167: BOOST_CHECK_EQUAL(out[2], 5); Chris@167: BOOST_CHECK_EQUAL(out[3], 5); Chris@167: } Chris@167: Chris@167: BOOST_AUTO_TEST_CASE(standaloneEmpty) Chris@167: { Chris@167: std::vector in; Chris@167: std::vector out1 = MedianFilter::filter(3, in); Chris@167: BOOST_CHECK_EQUAL(out1.size(), 0); Chris@167: std::vector out2 = MedianFilter::filter(4, in); Chris@167: BOOST_CHECK_EQUAL(out2.size(), 0); Chris@167: } Chris@167: Chris@167: BOOST_AUTO_TEST_CASE(types) Chris@167: { Chris@167: MedianFilter dfilt(3); Chris@167: dfilt.push(1.2); Chris@167: BOOST_CHECK_EQUAL(dfilt.get(), 0.0); Chris@167: dfilt.push(2.4); Chris@167: BOOST_CHECK_EQUAL(dfilt.get(), 1.2); Chris@167: dfilt.push(1e-6); Chris@167: BOOST_CHECK_EQUAL(dfilt.get(), 1.2); Chris@167: dfilt.push(1e6); Chris@167: BOOST_CHECK_EQUAL(dfilt.get(), 2.4); Chris@167: Chris@167: MedianFilter ifilt(3); Chris@167: ifilt.push(1); Chris@167: BOOST_CHECK_EQUAL(ifilt.get(), 0); Chris@167: ifilt.push(2); Chris@167: BOOST_CHECK_EQUAL(ifilt.get(), 1); Chris@167: ifilt.push(0); Chris@167: BOOST_CHECK_EQUAL(ifilt.get(), 1); Chris@167: ifilt.push(1000); Chris@167: BOOST_CHECK_EQUAL(ifilt.get(), 2); Chris@167: } Chris@167: Chris@182: BOOST_AUTO_TEST_CASE(inf) Chris@182: { Chris@182: // Inf and -Inf should behave normally Chris@182: double pinf = strtod("Inf", 0); Chris@182: double ninf = strtod("-Inf", 0); Chris@182: MedianFilter dfilt(3); Chris@182: dfilt.push(1.0); Chris@182: dfilt.push(2.0); Chris@182: dfilt.push(pinf); Chris@182: BOOST_CHECK_EQUAL(dfilt.get(), 2.0); Chris@182: dfilt.push(pinf); Chris@182: BOOST_CHECK_EQUAL(dfilt.get(), pinf); Chris@182: dfilt.push(pinf); Chris@182: BOOST_CHECK_EQUAL(dfilt.get(), pinf); Chris@182: dfilt.push(2.0); Chris@182: BOOST_CHECK_EQUAL(dfilt.get(), pinf); Chris@182: dfilt.push(1.0); Chris@182: BOOST_CHECK_EQUAL(dfilt.get(), 2.0); Chris@182: dfilt.push(ninf); Chris@182: BOOST_CHECK_EQUAL(dfilt.get(), 1.0); Chris@182: dfilt.push(pinf); Chris@182: BOOST_CHECK_EQUAL(dfilt.get(), 1.0); Chris@182: dfilt.push(ninf); Chris@182: BOOST_CHECK_EQUAL(dfilt.get(), ninf); Chris@182: } Chris@182: Chris@182: BOOST_AUTO_TEST_CASE(nan) Chris@182: { Chris@182: // The filter should never accept a NaN, because it breaks sorting Chris@182: // and comparison and that will break the filter. Instead it Chris@182: // should insert 0. Chris@182: double nan = strtod("NaN", 0); Chris@182: MedianFilter dfilt(3); Chris@182: dfilt.push(1.0); Chris@182: dfilt.push(2.0); Chris@182: dfilt.push(nan); Chris@182: BOOST_CHECK_EQUAL(dfilt.get(), 1.0); Chris@182: dfilt.push(nan); Chris@182: BOOST_CHECK_EQUAL(dfilt.get(), 0.0); Chris@182: dfilt.push(-11.0); Chris@182: BOOST_CHECK_EQUAL(dfilt.get(), 0.0); Chris@182: dfilt.push(-1.0); Chris@182: BOOST_CHECK_EQUAL(dfilt.get(), -1.0); Chris@182: } Chris@182: Chris@166: BOOST_AUTO_TEST_SUITE_END() Chris@166: Chris@166: