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 #include "KisFilteredRollingMean.h"
0008 
0009 #include <algorithm>
0010 #include <numeric>
0011 #include <QtMath>
0012 #include "kis_assert.h"
0013 #include "kis_debug.h"
0014 
0015 
0016 KisFilteredRollingMean::KisFilteredRollingMean(int windowSize, qreal effectivePortion)
0017     : m_values(windowSize),
0018       m_rollingSum(0.0),
0019       m_effectivePortion(effectivePortion),
0020       m_cutOffBuffer(qCeil(0.5 * (qCeil(windowSize * (1.0 - effectivePortion)))))
0021 {
0022 }
0023 
0024 void KisFilteredRollingMean::addValue(qreal value)
0025 {
0026     if (m_values.full()) {
0027         m_rollingSum -= m_values.front();
0028     }
0029 
0030     m_values.push_back(value);
0031     m_rollingSum += value;
0032 }
0033 
0034 qreal KisFilteredRollingMean::filteredMean() const
0035 {
0036     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!m_values.empty(), 0.0);
0037 
0038     const int usefulElements = qMax(1, qRound(m_effectivePortion * m_values.size()));
0039 
0040     qreal sum = 0.0;
0041     int num = 0;
0042 
0043     const int cutOffTotal = m_values.size() - usefulElements;
0044 
0045     if (cutOffTotal > 0) {
0046         const int cutMin = qRound(0.5 * cutOffTotal);
0047         const int cutMax = cutOffTotal - cutMin;
0048 
0049         KIS_SAFE_ASSERT_RECOVER(cutMin <= m_cutOffBuffer.size()) {
0050             m_cutOffBuffer.resize(cutMin);
0051         }
0052         KIS_SAFE_ASSERT_RECOVER(cutMax <= m_cutOffBuffer.size()) {
0053             m_cutOffBuffer.resize(cutMax);
0054         }
0055 
0056         sum = m_rollingSum;
0057         num = usefulElements;
0058 
0059         std::partial_sort_copy(m_values.begin(),
0060                                m_values.end(),
0061                                m_cutOffBuffer.begin(),
0062                                m_cutOffBuffer.begin() + cutMin);
0063 
0064         sum -= std::accumulate(m_cutOffBuffer.begin(),
0065                                m_cutOffBuffer.begin() + cutMin,
0066                                0.0);
0067 
0068         std::partial_sort_copy(m_values.begin(),
0069                                m_values.end(),
0070                                m_cutOffBuffer.begin(),
0071                                m_cutOffBuffer.begin() + cutMax,
0072                                std::greater<qreal>());
0073 
0074         sum -= std::accumulate(m_cutOffBuffer.begin(),
0075                                m_cutOffBuffer.begin() + cutMax,
0076                                0.0);
0077     } else {
0078         sum = m_rollingSum;
0079         num = m_values.size();
0080     }
0081 
0082     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(num > 0, 0.0);
0083 
0084     return sum / num;
0085 }
0086 
0087 bool KisFilteredRollingMean::isEmpty() const
0088 {
0089     return m_values.empty();
0090 }