GestureRecognitionToolkit  Version: 0.1.0
The Gesture Recognition Toolkit (GRT) is a cross-platform, open-source, c++ machine learning library for real-time gesture recognition.
PeakDetection.cpp
1 /*
2  GRT MIT License
3  Copyright (c) <2012> <Nicholas Gillian, Media Lab, MIT>
4 
5  Permission is hereby granted, free of charge, to any person obtaining a copy of this software
6  and associated documentation files (the "Software"), to deal in the Software without restriction,
7  including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
9  subject to the following conditions:
10 
11  The above copyright notice and this permission notice shall be included in all copies or substantial
12  portions of the Software.
13 
14  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
15  LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
17  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
18  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19  */
20 
21 #include "PeakDetection.h"
22 
23 GRT_BEGIN_NAMESPACE
24 
25 PeakDetection::PeakDetection(const UINT lowPassFilterSize,const UINT searchWindowSize){
26 
27  this->lowPassFilterSize = lowPassFilterSize;
28  this->searchWindowSize = searchWindowSize;
29 
30  inputTimeoutCounter = 0;
31  maximaCounter = 0;
32  minimaCounter = 0;
33  inputTimeoutLimit = 10;
34  searchHistorySize = 10;
35  deadZoneThreshold = 0.01;
36  enableSearch = false;
37  peakDetected = false;
38 
39  setSearchWindowSize( searchWindowSize );
40 }
41 
42 PeakDetection::PeakDetection(const PeakDetection &rhs){
43 
44  this->lowPassFilterSize = rhs.lowPassFilterSize;
45  this->searchWindowSize = rhs.searchWindowSize;
46  this->lowPassFilter = rhs.lowPassFilter;
47 }
48 
49 PeakDetection::~PeakDetection(){
50 
51 }
52 
53 PeakDetection& PeakDetection::operator=(const PeakDetection &rhs){
54  if(this!=&rhs){
55  this->lowPassFilterSize = rhs.lowPassFilterSize;
56  this->searchWindowSize = rhs.searchWindowSize;
57  this->lowPassFilter = rhs.lowPassFilter;
58  }
59  return *this;
60 }
61 
62 bool PeakDetection::update( const Float x){
63 
64  //Update the input counter
65  if( ++inputTimeoutCounter >= inputTimeoutLimit ){
66  inputTimeoutCounter = inputTimeoutLimit;
67  enableSearch = true;
68  }else enableSearch = false;
69 
70  peakDetected = false;
71  peakInfo.clear();
72 
73  //Low pass filter the input data to remove some noise
74  Float filteredValue = lowPassFilter.filter(x);
75 
76  //Compute the first deriv
77  Float firstDeriv = filteredValue - filteredDataBuffer.getBack();
78 
79  //Compute the second deriv
80  Float secondDeriv = firstDeriv - firstDerivBuffer.getBack();
81 
82  //Filter the second deriv using the deadzone filter, this removes any jitter around the 0
83  secondDeriv = deadZoneFilter.filter( secondDeriv );
84 
85  //Store the values in the buffers
86  filteredDataBuffer.push_back( filteredValue );
87  firstDerivBuffer.push_back( firstDeriv );
88  secondDerivBuffer.push_back( secondDeriv );
89 
90  //Search for any maxima/minima within the window
91  unsigned int peakType = NO_PEAK_FOUND;
92 
93  //No matter what, set the global minima and maxima peak info to NO_PEAK_FOUND
94  globalMaximaPeakInfo.peakType = NO_PEAK_FOUND;
95  globalMinimaPeakInfo.peakType = NO_PEAK_FOUND;
96 
97  //Reset the global maxima value if it has exceeded the search history size
98  if( ++maximaCounter >= searchHistorySize ){
99  maximaCounter = 0;
100  globalMaximaPeakInfo.peakValue = DEFAULT_GLOBAL_MAXIMA_VALUE;
101  }
102 
103  //Reset the global minima value if it has exceeded the search history size
104  if( ++minimaCounter >= searchHistorySize ){
105  minimaCounter = 0;
106  globalMinimaPeakInfo.peakValue = DEFAULT_GLOBAL_MINIMA_VALUE;
107  }
108 
109  if( enableSearch ){
110  for(unsigned int i=1; i<searchWindowSize-1; i++){
111  peakType = NO_PEAK_FOUND;
112  if( (secondDerivBuffer[i-1] <= 0 && secondDerivBuffer[i] > 0) || (secondDerivBuffer[i-1] <= 0 && secondDerivBuffer[i+1] > 0) ){
113 
114  peakDetected = true;
115  peakType = LOCAL_MAXIMA_FOUND;
116 
117  //Check for a global maxima
118  if( filteredDataBuffer[i] > globalMaximaPeakInfo.peakValue ){
119  maximaCounter = 0;
120  globalMaximaPeakInfo.peakType = GLOBAL_MAXIMA_FOUND;
121  globalMaximaPeakInfo.peakIndex = i;
122  globalMaximaPeakInfo.peakValue = filteredDataBuffer[i];
123  }
124 
125  peakInfo.push_back( PeakInfo(peakType,i,filteredDataBuffer[i]) );
126  break;
127  }
128 
129 
130  if( (secondDerivBuffer[i-1] >= 0 && secondDerivBuffer[i] < 0) || (secondDerivBuffer[i-1] >= 0 && secondDerivBuffer[i+1] < 0) ){
131 
132  peakDetected = true;
133  peakType = LOCAL_MINIMA_FOUND;
134 
135  if( filteredDataBuffer[i] < globalMinimaPeakInfo.peakValue ){
136  minimaCounter = 0;
137  globalMinimaPeakInfo.peakType = GLOBAL_MINIMA_FOUND;
138  globalMinimaPeakInfo.peakIndex = i;
139  globalMinimaPeakInfo.peakValue = filteredDataBuffer[i];
140  }
141 
142  peakInfo.push_back( PeakInfo(peakType,i,filteredDataBuffer[i]) );
143  break;
144  }
145  }
146  }
147 
148  peakTypesBuffer.push_back( peakType );
149 
150  return peakDetected ? true : false;
151 }
152 
153 bool PeakDetection::reset(){
154  //Reset the low pass filter
155  lowPassFilter.init(lowPassFilterSize,1);
156 
157  //Reset the deadzone filter
158  deadZoneFilter.init(-deadZoneThreshold,deadZoneThreshold,1);
159 
160  //Setup the data buffers
161  filteredDataBuffer.clear();
162  firstDerivBuffer.clear();
163  secondDerivBuffer.clear();
164  peakTypesBuffer.clear();
165 
166  filteredDataBuffer.resize( searchWindowSize, 0 );
167  firstDerivBuffer.resize( searchWindowSize, 0 );
168  secondDerivBuffer.resize( searchWindowSize, 0 );
169  peakTypesBuffer.resize( searchWindowSize, NO_PEAK_FOUND );
170 
171  //Enable future searches
172  peakDetected = false;
173  inputTimeoutCounter = 0;
174  maximaCounter = 0;
175  minimaCounter = 0;
176 
177  globalMaximaPeakInfo.peakType = NO_PEAK_FOUND;
178  globalMaximaPeakInfo.peakIndex = 0;
179  globalMaximaPeakInfo.peakValue = DEFAULT_GLOBAL_MAXIMA_VALUE;
180  globalMinimaPeakInfo.peakType = NO_PEAK_FOUND;
181  globalMinimaPeakInfo.peakIndex = 0;
182  globalMinimaPeakInfo.peakValue = DEFAULT_GLOBAL_MINIMA_VALUE;
183 
184  return true;
185 }
186 
187 bool PeakDetection::setSearchWindowSize(const UINT searchWindowSize){
188  this->searchWindowSize = searchWindowSize;
189  reset();
190  return true;
191 }
192 
193 GRT_END_NAMESPACE
bool push_back(const T &value)
Float filter(const Float x)
Definition: DeadZone.cpp:233
T getBack() const
bool init(UINT filterSize, UINT numDimensions)
bool init(Float lowerLimit, Float upperLimit, UINT numDimensions)
Definition: DeadZone.cpp:208
bool resize(const unsigned int newBufferSize)