Rolling median algorithm in C

前端 未结 13 1722
臣服心动
臣服心动 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:05

    Here's a simple algorithm for quantized data (months later):

    """ median1.py: moving median 1d for quantized, e.g. 8-bit data
    
    Method: cache the median, so that wider windows are faster.
        The code is simple -- no heaps, no trees.
    
    Keywords: median filter, moving median, running median, numpy, scipy
    
    See Perreault + Hebert, Median Filtering in Constant Time, 2007,
        http://nomis80.org/ctmf.html: nice 6-page paper and C code,
        mainly for 2d images
    
    Example:
        y = medians( x, window=window, nlevel=nlevel )
        uses:
        med = Median1( nlevel, window, counts=np.bincount( x[0:window] ))
        med.addsub( +, - )  -- see the picture in Perreault
        m = med.median()  -- using cached m, summ
    
    How it works:
        picture nlevel=8, window=3 -- 3 1s in an array of 8 counters:
            counts: . 1 . . 1 . 1 .
            sums:   0 1 1 1 2 2 3 3
                            ^ sums[3] < 2 <= sums[4] <=> median 4
            addsub( 0, 1 )  m, summ stay the same
            addsub( 5, 1 )  slide right
            addsub( 5, 6 )  slide left
    
    Updating `counts` in an `addsub` is trivial, updating `sums` is not.
    But we can cache the previous median `m` and the sum to m `summ`.
    The less often the median changes, the faster;
    so fewer levels or *wider* windows are faster.
    (Like any cache, run time varies a lot, depending on the input.)
    
    See also:
        scipy.signal.medfilt -- runtime roughly ~ window size
        http://stackoverflow.com/questions/1309263/rolling-median-algorithm-in-c
    
    """
    
    from __future__ import division
    import numpy as np  # bincount, pad0
    
    __date__ = "2009-10-27 oct"
    __author_email__ = "denis-bz-py at t-online dot de"
    
    
    #...............................................................................
    class Median1:
        """ moving median 1d for quantized, e.g. 8-bit data """
    
        def __init__( s, nlevel, window, counts ):
            s.nlevel = nlevel  # >= len(counts)
            s.window = window  # == sum(counts)
            s.half = (window // 2) + 1  # odd or even
            s.setcounts( counts )
    
        def median( s ):
            """ step up or down until sum cnt to m-1 < half <= sum to m """
            if s.summ - s.cnt[s.m] < s.half <= s.summ:
                return s.m
            j, sumj = s.m, s.summ
            if sumj <= s.half:
                while j < s.nlevel - 1:
                    j += 1
                    sumj += s.cnt[j]
                    # print "j sumj:", j, sumj
                    if sumj - s.cnt[j] < s.half <= sumj:  break
            else:
                while j > 0:
                    sumj -= s.cnt[j]
                    j -= 1
                    # print "j sumj:", j, sumj
                    if sumj - s.cnt[j] < s.half <= sumj:  break
            s.m, s.summ = j, sumj
            return s.m
    
        def addsub( s, add, sub ):
            s.cnt[add] += 1
            s.cnt[sub] -= 1
            assert s.cnt[sub] >= 0, (add, sub)
            if add <= s.m:
                s.summ += 1
            if sub <= s.m:
                s.summ -= 1
    
        def setcounts( s, counts ):
            assert len(counts) <= s.nlevel, (len(counts), s.nlevel)
            if len(counts) < s.nlevel:
                counts = pad0__( counts, s.nlevel )  # numpy array / list
            sumcounts = sum(counts)
            assert sumcounts == s.window, (sumcounts, s.window)
            s.cnt = counts
            s.slowmedian()
    
        def slowmedian( s ):
            j, sumj = -1, 0
            while sumj < s.half:
                j += 1
                sumj += s.cnt[j]
            s.m, s.summ = j, sumj
    
        def __str__( s ):
            return ("median %d: " % s.m) + \
                "".join([ (" ." if c == 0 else "%2d" % c) for c in s.cnt ])
    
    #...............................................................................
    def medianfilter( x, window, nlevel=256 ):
        """ moving medians, y[j] = median( x[j:j+window] )
            -> a shorter list, len(y) = len(x) - window + 1
        """
        assert len(x) >= window, (len(x), window)
        # np.clip( x, 0, nlevel-1, out=x )
            # cf http://scipy.org/Cookbook/Rebinning
        cnt = np.bincount( x[0:window] )
        med = Median1( nlevel=nlevel, window=window, counts=cnt )
        y = (len(x) - window + 1) * [0]
        y[0] = med.median()
        for j in xrange( len(x) - window ):
            med.addsub( x[j+window], x[j] )
            y[j+1] = med.median()
        return y  # list
        # return np.array( y )
    
    def pad0__( x, tolen ):
        """ pad x with 0 s, numpy array or list """
        n = tolen - len(x)
        if n > 0:
            try:
                x = np.r_[ x, np.zeros( n, dtype=x[0].dtype )]
            except NameError:
                x += n * [0]
        return x
    
    #...............................................................................
    if __name__ == "__main__":
        Len = 10000
        window = 3
        nlevel = 256
        period = 100
    
        np.set_printoptions( 2, threshold=100, edgeitems=10 )
        # print medians( np.arange(3), 3 )
    
        sinwave = (np.sin( 2 * np.pi * np.arange(Len) / period )
            + 1) * (nlevel-1) / 2
        x = np.asarray( sinwave, int )
        print "x:", x
        for window in ( 3, 31, 63, 127, 255 ):
            if window > Len:  continue
            print "medianfilter: Len=%d window=%d nlevel=%d:" % (Len, window, nlevel)
                y = medianfilter( x, window=window, nlevel=nlevel )
            print np.array( y )
    
    # end median1.py
    

提交回复
热议问题