LCOV - code coverage report
Current view: top level - src/Filters - MedianFilter.hpp (source / functions) Hit Total Coverage
Test: a11a239a890b6a41006679c4a235ba6b91b3e883 Lines: 13 13 100.0 %
Date: 2022-05-08 12:14:49 Functions: 4 4 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include <AH/STL/algorithm> // std::partial_sort_copy
       4             : #include <AH/STL/array>     // std::array
       5             : #include <AH/STL/cstdint>   // uint8_t
       6             : 
       7             : /// @addtogroup Filters
       8             : /// @{
       9             : 
      10             : #if 0
      11             : 
      12             : /** 
      13             :  * @brief  Class for Median Filters. 
      14             :  * 
      15             :  * Use the parenthesis or call operator (`operator()`) with the next input of 
      16             :  * the filter as an argument to update the Median filter. 
      17             :  * This operator returns the next output of the filter.
      18             :  * 
      19             :  * The output equation is:
      20             :  * @f$ y[n] = \text{median}\Big(x[n], x[n-1],\ \ldots,\ x[n-N+1]\Big) @f$
      21             :  * 
      22             :  * This version uses std::partial_sort_copy, and is about 4 times slower than 
      23             :  * the version using std::nth_element below. (Tested on an Arduino Leonardo, 
      24             :  * tested with N = 100 and N = 101.)
      25             :  * 
      26             :  * @tparam  N
      27             :  *          The number of previous values to take the median of.
      28             :  * @tparam  T 
      29             :  *          The type of the input and output values of the filter.
      30             :  */
      31             : template <uint8_t N, class T = float>
      32             : class MedianFilter {
      33             :   public:
      34             :     /**
      35             :      * @brief   Construct a new Median Filter (zero initialized).
      36             :      */
      37             :     MedianFilter() = default;
      38             : 
      39             :     /** 
      40             :      * @brief   Construct a new Median Filter, and initialize it with the given
      41             :      *          value.
      42             :      * 
      43             :      * @param   initialValue
      44             :      *          Determines the initial state of the filter:  
      45             :      *          @f$ x[-N] =\ \ldots\ = x[-2] = x[-1] = \text{initialValue} @f$
      46             :      */
      47             :     MedianFilter(T initialValue) {
      48             :         std::fill(previousInputs.begin(), previousInputs.end(), initialValue);
      49             :     }
      50             : 
      51             :     /** 
      52             :      * @brief   Calculate the output @f$ y[n] @f$ for a given input 
      53             :      *          @f$ x[n] @f$.
      54             :      * 
      55             :      * @f$ y[n] = \text{median}\Big(x[n], x[n-1],\ \ldots,\ x[n-N+1]\Big) @f$
      56             :      * 
      57             :      * @param   x
      58             :      *          The new input @f$ x[n] @f$.
      59             :      * @return  The new output @f$ y[n] @f$.
      60             :      */
      61             :     T operator()(T x) {
      62             :         // Insert the new input into the ring buffer, overwriting the oldest
      63             :         // input.
      64             :         previousInputs[index] = x;
      65             :         if (++index == N)
      66             :             index = 0;
      67             : 
      68             :         // Calculate the median of the buffer by sorting the first half. A copy
      69             :         // should be made to keep the order of the buffer intact.
      70             :         const uint8_t halfSize = N / 2 + 1;
      71             :         std::array<T, halfSize> sorted;
      72             :         std::partial_sort_copy(previousInputs.begin(), previousInputs.end(),
      73             :                                sorted.begin(), sorted.end());
      74             : 
      75             :         // If the length of the ring buffer is odd, then the median is its
      76             :         // center element, which is the final element of `sorted`.
      77             :         // If the length of the ring buffer is even, then we need to take the
      78             :         // average of its two center elements, which are the final two elements
      79             :         // of `sorted`.
      80             :         if (N % 2 == 0)
      81             :             return (sorted.end()[-2] + sorted.end()[-1]) / 2;
      82             :         else
      83             :             return sorted.end()[-1];
      84             :     }
      85             : 
      86             :   private:
      87             :     /// The last index in the ring buffer.
      88             :     uint8_t index = 0;
      89             :     /// A ring buffer to keep track of the N last inputs.
      90             :     std::array<T, N> previousInputs = {};
      91             : };
      92             : 
      93             : #else
      94             : 
      95             : /** 
      96             :  * @brief  Class for Median Filters. 
      97             :  * 
      98             :  * Use the parenthesis or call operator (`operator()`) with the next input of 
      99             :  * the filter as an argument to update the Median filter. 
     100             :  * This operator returns the next output of the filter.
     101             :  * 
     102             :  * The output equation is:
     103             :  * @f$ y[n] = \text{median}\Big(x[n], x[n-1],\ \ldots,\ x[n-N+1]\Big) @f$
     104             :  * 
     105             :  * @tparam  N
     106             :  *          The number of previous values to take the median of.
     107             :  * @tparam  T 
     108             :  *          The type of the input and output values of the filter.
     109             :  */
     110             : template <uint8_t N, class T = float>
     111             : class MedianFilter {
     112             :   public:
     113             :     /**
     114             :      * @brief   Construct a new Median Filter (zero initialized).
     115             :      */
     116             :     MedianFilter() = default;
     117             : 
     118             :     /** 
     119             :      * @brief   Construct a new Median Filter, and initialize it with the given
     120             :      *          value.
     121             :      * 
     122             :      * @param   initialValue
     123             :      *          Determines the initial state of the filter:  
     124             :      *          @f$ x[-N] =\ \ldots\ = x[-2] = x[-1] = \text{initialValue} @f$
     125             :      */
     126           2 :     MedianFilter(T initialValue) {
     127           2 :         std::fill(previousInputs.begin(), previousInputs.end(), initialValue);
     128           2 :     }
     129             : 
     130             :     /** 
     131             :      * @brief   Calculate the output @f$ y[n] @f$ for a given input 
     132             :      *          @f$ x[n] @f$.
     133             :      * 
     134             :      * @f$ y[n] = \text{median}\Big(x[n], x[n-1],\ \ldots,\ x[n-N+1]\Big) @f$
     135             :      * 
     136             :      * @param   x
     137             :      *          The new input @f$ x[n] @f$.
     138             :      * @return  The new output @f$ y[n] @f$.
     139             :      */
     140          24 :     T operator()(T x) {
     141             :         // Insert the new input into the ring buffer, overwriting the oldest
     142             :         // input.
     143          24 :         previousInputs[index] = x;
     144          24 :         if (++index == N)
     145           4 :             index = 0;
     146             : 
     147             :         // Calculate the median of the buffer by sorting it just enough to find
     148             :         // the middle element. A copy should be made to keep the order of the
     149             :         // buffer intact.
     150          24 :         std::array<T, N> sorted = previousInputs;
     151          24 :         auto halfWay = sorted.begin() + N / 2;
     152          24 :         std::nth_element(sorted.begin(), halfWay, sorted.end());
     153             : 
     154             :         // If the length of the ring buffer is odd, then the median is its
     155             :         // center element.
     156             :         // If the length of the ring buffer is even, then we need to take the
     157             :         // average of its two center elements.
     158             :         if (N % 2 == 0) {
     159          12 :             auto beforeHalfWay = std::max_element(sorted.begin(), halfWay);
     160          12 :             return (*halfWay + *beforeHalfWay) / 2;
     161             :         } else {
     162          12 :             return *halfWay;
     163             :         }
     164             :     }
     165             : 
     166             :   private:
     167             :     /// The last index in the ring buffer.
     168             :     uint8_t index = 0;
     169             :     /// A ring buffer to keep track of the N last inputs.
     170             :     std::array<T, N> previousInputs = {{}};
     171             : };
     172             : 
     173             : #endif
     174             : 
     175             : /// @}

Generated by: LCOV version 1.15