File indexing completed on 2024-05-12 15:57:02

0001 /*
0002  *  SPDX-FileCopyrightText: 2022 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #ifndef KISFILTEREDROLLINGMEAN_H
0008 #define KISFILTEREDROLLINGMEAN_H
0009 
0010 #include "kritaglobal_export.h"
0011 
0012 #include <vector>
0013 #include <boost/circular_buffer.hpp>
0014 #include <QtGlobal>
0015 
0016 /**
0017  * A special class that calculates a rolling mean of the
0018  * values stream, but filtering out random extreme deviations.
0019  *
0020  * On each step the algorithm sorts the entire sampling window
0021  * and drops a small portion of the lowest and the highest
0022  * values. The rest of the values are used to calculate the
0023  * mean of the window.
0024  *
0025  * Basically, it takes the median and a few surrounding values
0026  * to calculate the mean.
0027  *
0028  * PS:
0029  * The actualy implementation does a few optimizations. For
0030  * example, it doesn't sort the entire range. But the idea is
0031  * the same.
0032  */
0033 class KRITAGLOBAL_EXPORT KisFilteredRollingMean
0034 {
0035 public:
0036     /**
0037      * Creates a mean accumulator
0038      *
0039      * \p windowSize is the size of the samples window
0040      * \p effectivePortion the portion of the samples window
0041      *    that is used for actual mean calclulation. On each
0042      *    side of the sorted range (0.5 * (1.0 - effectivePortion) *
0043      *    windowSize) values are dropped and are not counted for
0044      *    the mean calculation.
0045      */
0046     KisFilteredRollingMean(int windowSize, qreal effectivePortion);
0047 
0048     /**
0049      * Adds a value for the rolling mean calculation. The
0050      * complexity of the call is O(1).
0051      */
0052     void addValue(qreal value);
0053 
0054     /**
0055      * Calculates the filtered mean value of the current range.
0056      * The function is slow, its complexity is O(N) + O(N*log(M)),
0057      * where N is the size of the rolling window, M is the number
0058      * of elements dropped from the window
0059      * (1.0 - effectivePortion) * windowSize).
0060      */
0061     qreal filteredMean() const;
0062 
0063     /**
0064      * Returns true if the accumulator has at least some value
0065      */
0066     bool isEmpty() const;
0067 
0068 private:
0069     boost::circular_buffer<qreal> m_values;
0070     qreal m_rollingSum;
0071     qreal m_effectivePortion;
0072     mutable std::vector<qreal> m_cutOffBuffer;
0073 };
0074 
0075 #endif // KISFILTEREDROLLINGMEAN_H