Rolling median algorithm in C

前端 未结 13 1721
臣服心动
臣服心动 2020-11-27 09:24

I am currently working on an algorithm to implement a rolling median filter (analogous to a rolling mean filter) in C. From my search of the literature, there appear to be t

13条回答
  •  一整个雨季
    2020-11-27 10:00

    Based on @mathog thoughts, this is a C# implementation for a running median over an array of bytes with known range of values. Can be extended to other integer types.

      /// 
      /// Median estimation by histogram, avoids multiple sorting operations for a running median
      /// 
      public class MedianEstimator
      {
        private readonly int m_size2;
        private readonly byte[] m_counts;
    
        /// 
        /// Estimated median, available right after calling  or .
        /// 
        public byte Median { get; private set; }
    
        /// 
        /// Ctor
        /// 
        /// Median size in samples
        /// Maximum expected value in input data
        public MedianEstimator(
          int size,
          byte maxValue)
        {
          m_size2 = size / 2;
          m_counts = new byte[maxValue + 1];
        }
    
        /// 
        /// Initializes the internal histogram with the passed sample values
        /// 
        /// Array of values, usually the start of the array for a running median
        public void Init(byte[] values)
        {
          for (var i = 0; i < values.Length; i++)
            m_counts[values[i]]++;
    
          UpdateMedian();
        }
    
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void UpdateMedian()
        {
          // The median is the first value up to which counts add to size / 2
          var sum = 0;
          Median = 0;
          for (var i = 0; i < m_counts.Length; i++)
          {
            sum += m_counts[i];
            Median = (byte) i;
            if (sum > m_size2) break;
          }
        }
    
        /// 
        /// Updates the median estimation by removing  and adding . These
        /// values must be updated as the running median is applied. If the median length is N, at the sample
        /// i,  is sample at index i-N/2 and  is sample
        /// at index i+N/2+1.
        /// 
        /// Sample at the start of the moving window that is to be removed
        /// Sample at the end of the moving window + 1 that is to be added
        public void Update(byte last, byte next)
        {
          m_counts[last]--;
          m_counts[next]++;
    
          // The conditions below do not change median value so there is no need to update it
          if (last == next ||
              last < Median && next < Median || // both below median
              last > Median && next > Median) // both above median
            return;
    
          UpdateMedian();
        }
    

    Testing against a running median, with timing:

    private void TestMedianEstimator()
    {
      var r = new Random();
      const int SIZE = 15;
      const byte MAX_VAL = 80;
      var values = new byte[100000];
      for (var i = 0; i < values.Length; i++)
        values[i] = (byte) (MAX_VAL * r.NextDouble());
    
      var timer = Stopwatch.StartNew();
      // Running median
      var window = new byte[2 * SIZE + 1];
      var medians = new byte[values.Length];
      for (var i = SIZE; i < values.Length - SIZE - 1; i++)
      {
        for (int j = i - SIZE, k = 0; j <= i + SIZE; j++, k++)
          window[k] = values[j];
        Array.Sort(window);
        medians[i] = window[SIZE];
      }
      timer.Stop();
      var elapsed1 = timer.Elapsed;
    
      timer.Restart();
      var me = new MedianEstimator(2 * SIZE + 1, MAX_VAL);
      me.Init(values.Slice(0, 2 * SIZE + 1));
      var meMedians = new byte[values.Length];
      for (var i = SIZE; i < values.Length - SIZE - 1; i++)
      {
        meMedians[i] = me.Median;
        me.Update(values[i - SIZE], values[i + SIZE + 1]);
      }
      timer.Stop();
      var elapsed2 = timer.Elapsed;
    
      WriteLineToLog($"{elapsed1.TotalMilliseconds / elapsed2.TotalMilliseconds:0.00}");
    
      var diff = 0;
      for (var i = 0; i < meMedians.Length; i++)
        diff += Math.Abs(meMedians[i] - medians[i]);
      WriteLineToLog($"Diff: {diff}");
    }
    

提交回复
热议问题