v2.0.0
Loading...
Searching...
No Matches
peak_finder.cpp
Go to the documentation of this file.
1//=============================================================================================================
12
13//=============================================================================================================
14// INCLUDES
15//=============================================================================================================
16
17#include "peak_finder.h"
18
19//=============================================================================================================
20// STL INCLUDES
21//=============================================================================================================
22
23#include <algorithm>
24#include <cmath>
25
26//=============================================================================================================
27// USED NAMESPACES
28//=============================================================================================================
29
30using namespace UTILSLIB;
31using namespace Eigen;
32
33//=============================================================================================================
34// DEFINE FUNCTIONS
35//=============================================================================================================
36
37VectorXd UTILSLIB::peakProminences(const VectorXd& data, const QList<int>& peakIndices)
38{
39 const int n = static_cast<int>(data.size());
40 VectorXd proms(peakIndices.size());
41
42 for (int p = 0; p < peakIndices.size(); ++p) {
43 int idx = peakIndices[p];
44 double peakVal = data(idx);
45
46 // Search left for highest valley before a higher peak
47 double leftMin = peakVal;
48 for (int i = idx - 1; i >= 0; --i) {
49 if (data(i) > peakVal)
50 break;
51 leftMin = std::min(leftMin, data(i));
52 }
53
54 // Search right for highest valley before a higher peak
55 double rightMin = peakVal;
56 for (int i = idx + 1; i < n; ++i) {
57 if (data(i) > peakVal)
58 break;
59 rightMin = std::min(rightMin, data(i));
60 }
61
62 // Prominence = peak height minus highest of the two valleys
63 double highestValley = std::max(leftMin, rightMin);
64 proms(p) = peakVal - highestValley;
65 }
66
67 return proms;
68}
69
70//=============================================================================================================
71
72QList<QPair<int, double>> UTILSLIB::peakFinder(const VectorXd& data,
73 const PeakFinderParams& params)
74{
75 const int n = static_cast<int>(data.size());
76 QList<QPair<int, double>> peaks;
77
78 if (n < 3) {
79 return peaks;
80 }
81
82 // Step 1: Find all local maxima (strictly greater than both neighbours)
83 QList<int> peakIdx;
84 for (int i = 1; i < n - 1; ++i) {
85 if (data(i) > data(i - 1) && data(i) > data(i + 1)) {
86 if (data(i) >= params.dMinHeight) {
87 peakIdx.append(i);
88 }
89 }
90 }
91
92 // Step 2: Filter by prominence
93 if (params.dProminence > 0.0 && !peakIdx.isEmpty()) {
94 VectorXd proms = peakProminences(data, peakIdx);
95 QList<int> filtered;
96 for (int i = 0; i < peakIdx.size(); ++i) {
97 if (proms(i) >= params.dProminence) {
98 filtered.append(peakIdx[i]);
99 }
100 }
101 peakIdx = filtered;
102 }
103
104 // Step 3: Filter by minimum distance (keep highest peaks)
105 if (params.iMinDistance > 1 && !peakIdx.isEmpty()) {
106 // Sort peaks by height descending
107 std::vector<int> sortedIdx(peakIdx.begin(), peakIdx.end());
108 std::sort(sortedIdx.begin(), sortedIdx.end(),
109 [&data](int a, int b) { return data(a) > data(b); });
110
111 QList<int> kept;
112 std::vector<bool> suppressed(static_cast<size_t>(n), false);
113
114 for (int idx : sortedIdx) {
115 if (suppressed[static_cast<size_t>(idx)])
116 continue;
117 kept.append(idx);
118
119 // Suppress nearby peaks
120 int lo = std::max(0, idx - params.iMinDistance);
121 int hi = std::min(n - 1, idx + params.iMinDistance);
122 for (int s = lo; s <= hi; ++s) {
123 if (s != idx) {
124 suppressed[static_cast<size_t>(s)] = true;
125 }
126 }
127 }
128
129 // Re-sort by index
130 std::sort(kept.begin(), kept.end());
131 peakIdx = kept;
132 }
133
134 // Build output
135 for (int idx : peakIdx) {
136 peaks.append(qMakePair(idx, data(idx)));
137 }
138
139 return peaks;
140}
Local-maxima peak detection in 1-D signals with prominence filtering.
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
DSPSHARED_EXPORT QList< QPair< int, double > > peakFinder(const Eigen::VectorXd &data, const PeakFinderParams &params=PeakFinderParams())
Find peaks in a 1D signal.
DSPSHARED_EXPORT Eigen::VectorXd peakProminences(const Eigen::VectorXd &data, const QList< int > &peakIndices)
Compute prominence of each peak.