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.
ThresholdCrossingDetector.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 
22 
23 GRT_BEGIN_NAMESPACE
24 
25 ThresholdCrossingDetector::ThresholdCrossingDetector(UINT analysisMode,UINT thresholdCrossingMode,UINT detectionTimeoutMode,Float lowerThreshold,Float upperThreshold,Float hysteresisThreshold,UINT searchWindowSize,UINT searchTimeoutDuration,UINT offsetFilterSize){
26 
27  this->analysisMode = analysisMode;
28  this->thresholdCrossingMode = thresholdCrossingMode;
29  this->detectionTimeoutMode = detectionTimeoutMode;
30  this->searchWindowSize = searchWindowSize;
31  this->searchTimeoutDuration = searchTimeoutDuration;
32  this->offsetFilterSize = offsetFilterSize;
33  this->lowerThreshold = lowerThreshold;
34  this->upperThreshold = upperThreshold;
35  this->hysteresisThreshold = hysteresisThreshold;
36 
37  enableSearch = false;
38  thresholdCrossingDetected = false;
39  analysisValue = 0;
40  searchWindowIndex = 0;
41  currentSearchState = SEARCHING_FOR_FIRST_THRESHOLD_CROSSING;
42 
43  reset();
44 }
45 
47  this->analysisValue = rhs.analysisValue;
48  this->lowerThreshold = rhs.lowerThreshold;
49  this->upperThreshold = rhs.upperThreshold;
50  this->hysteresisThreshold = rhs.hysteresisThreshold;
51  this->enableSearch = rhs.enableSearch;
52  this->thresholdCrossingDetected = rhs.thresholdCrossingDetected;
53  this->analysisMode = rhs.analysisMode;
54  this->thresholdCrossingMode = rhs.thresholdCrossingMode;
55  this->searchTimeoutCounter = rhs.searchTimeoutCounter;
56  this->searchTimeoutDuration = rhs.searchTimeoutDuration;
57  this->searchWindowSize = rhs.searchWindowSize;
58  this->searchWindowIndex = rhs.searchWindowIndex;
59  this->offsetFilterSize = rhs.offsetFilterSize;
60  this->currentSearchState = rhs.currentSearchState;
61  this->movingAverageFilter = rhs.movingAverageFilter;
62  this->derivative = rhs.derivative;
63 }
64 
66 
67 }
68 
70  if(this!=&rhs){
71  this->analysisValue = rhs.analysisValue;
72  this->lowerThreshold = rhs.lowerThreshold;
73  this->upperThreshold = rhs.upperThreshold;
74  this->hysteresisThreshold = rhs.hysteresisThreshold;
75  this->enableSearch = rhs.enableSearch;
76  this->thresholdCrossingDetected = rhs.thresholdCrossingDetected;
77  this->analysisMode = rhs.analysisMode;
78  this->thresholdCrossingMode = rhs.thresholdCrossingMode;
79  this->searchTimeoutCounter = rhs.searchTimeoutCounter;
80  this->searchTimeoutDuration = rhs.searchTimeoutDuration;
81  this->searchWindowSize = rhs.searchWindowSize;
82  this->searchWindowIndex = rhs.searchWindowIndex;
83  this->offsetFilterSize = rhs.offsetFilterSize;
84  this->currentSearchState = rhs.currentSearchState;
85  this->movingAverageFilter = rhs.movingAverageFilter;
86  this->derivative = rhs.derivative;
87  }
88  return *this;
89 }
90 
91 bool ThresholdCrossingDetector::update( const Float x ){
92 
93  thresholdCrossingDetected = false;
94 
95  Float deriv = derivative.computeDerivative( x );
96  Float offset = movingAverageFilter.filter( x );
97 
98  //If the search is disabled then we stop here
99  if( !enableSearch ){ return thresholdCrossingDetected; }
100 
101  //Perform the search
102  analysisValue = 0;
103  bool upperThresholdCrossingFound = false;
104  bool lowerThresholdCrossingFound = false;
105 
106  switch( analysisMode ){
107  case RAW_DATA_ANALYSIS_MODE:
108  analysisValue = x;
109  break;
110  case MOVING_OFFSET_ANALYSIS_MODE:
111  analysisValue = x - offset;
112  break;
113  case DERIVATIVE_ANALYSIS_MODE:
114  analysisValue = deriv;
115  break;
116  }
117 
118  //Check to see if we should start searching again
119  if( currentSearchState == NO_SEARCH_GATE_TIME_OUT ){
120 
121  switch( detectionTimeoutMode ){
122  case TIMEOUT_COUNTER:
123  // searchTimeoutDuration is cast because of a C4018 warning on visual (signed/unsigned incompatibility)
124  if (searchTimeoutCounter.getMilliSeconds() >= (signed long)searchTimeoutDuration){
125  currentSearchState = SEARCHING_FOR_FIRST_THRESHOLD_CROSSING;
126  searchTimeoutCounter.stop();
127  }else return false;
128  break;
129  case HYSTERESIS_THRESHOLD:
130  switch ( thresholdCrossingMode ) {
131  case UPPER_THRESHOLD_CROSSING:
132  if( analysisValue <= hysteresisThreshold ){
133  currentSearchState = SEARCHING_FOR_FIRST_THRESHOLD_CROSSING;
134  }else return false;
135  break;
136  case LOWER_THRESHOLD_CROSSING:
137  if( analysisValue >= hysteresisThreshold ){
138  currentSearchState = SEARCHING_FOR_FIRST_THRESHOLD_CROSSING;
139  }else return false;
140  break;
141  default:
142  break;
143  }
144 
145  break;
146  }
147 
148  }
149 
150  if( thresholdCrossingMode == UPPER_THRESHOLD_CROSSING || thresholdCrossingMode == LOWER_THRESHOLD_CROSSING || thresholdCrossingMode == UPPER_OR_LOWER_THRESHOLD_CROSSING ){
151  //Has the deriv of the input passed either threshold
152  switch ( thresholdCrossingMode ) {
153  case UPPER_THRESHOLD_CROSSING:
154  if( analysisValue >= upperThreshold ){
155  upperThresholdCrossingFound = true;
156  thresholdCrossingDetected = true;
157  currentSearchState = NO_SEARCH_GATE_TIME_OUT;
158  searchTimeoutCounter.start();
159  }
160  break;
161  case LOWER_THRESHOLD_CROSSING:
162  if( analysisValue <= lowerThreshold ){
163  lowerThresholdCrossingFound = true;
164  thresholdCrossingDetected = true;
165  currentSearchState = NO_SEARCH_GATE_TIME_OUT;
166  searchTimeoutCounter.start();
167  }
168  break;
169  case UPPER_OR_LOWER_THRESHOLD_CROSSING:
170  if( analysisValue >= upperThreshold ){
171  upperThresholdCrossingFound = true;
172  thresholdCrossingDetected = true;
173  currentSearchState = NO_SEARCH_GATE_TIME_OUT;
174  searchTimeoutCounter.start();
175  }else if( analysisValue <= lowerThreshold ){
176  lowerThresholdCrossingFound = true;
177  thresholdCrossingDetected = true;
178  currentSearchState = NO_SEARCH_GATE_TIME_OUT;
179  searchTimeoutCounter.start();
180  }
181  break;
182  default:
183  break;
184  }
185  return thresholdCrossingDetected ? true : false;
186  }else{
187 
188  //Check to make sure the search has not timed out
189  if( currentSearchState == SEARCHING_FOR_SECOND_THRESHOLD_CROSSING ){
190  if( ++searchWindowIndex == searchWindowSize ){
191  currentSearchState = SEARCHING_FOR_FIRST_THRESHOLD_CROSSING;
192  searchWindowIndex = 0;
193  }
194  }
195 
196  //If we are in the NO_SEARCH_GATE_TIME_OUT state then we need to update the counter
197  if( currentSearchState == NO_SEARCH_GATE_TIME_OUT ){
198  // searchTimeoutDuration is cast because of a C4018 warning on visual (signed/unsigned incompatibility)
199  if (searchTimeoutCounter.getMilliSeconds() >= (signed long)searchTimeoutDuration){
200  currentSearchState = SEARCHING_FOR_FIRST_THRESHOLD_CROSSING;
201  searchTimeoutCounter.stop();
202  }
203  }
204 
205  //Has the deriv of the input passed either threshold
206  if( analysisValue >= upperThreshold ){ upperThresholdCrossingFound = true; }
207  if( analysisValue <= lowerThreshold ){ lowerThresholdCrossingFound = true; }
208  if( !upperThresholdCrossingFound && !lowerThresholdCrossingFound &&
209  currentSearchState != NO_SEARCH_GATE_TIME_OUT ){ return false; }
210 
211  switch ( currentSearchState ) {
212  case SEARCHING_FOR_FIRST_THRESHOLD_CROSSING:
213  if( thresholdCrossingMode == UPPER_THEN_LOWER_THRESHOLD_CROSSING && upperThresholdCrossingFound ){
214  searchWindowIndex = 0;
215  currentSearchState = SEARCHING_FOR_SECOND_THRESHOLD_CROSSING;
216  return false;
217  }
218  if( thresholdCrossingMode == LOWER_THEN_UPPER_THRESHOLD_CROSSING && lowerThresholdCrossingFound ){
219  searchWindowIndex = 0;
220  currentSearchState = SEARCHING_FOR_SECOND_THRESHOLD_CROSSING;
221  return false;
222  }
223  break;
224  case SEARCHING_FOR_SECOND_THRESHOLD_CROSSING:
225  if( thresholdCrossingMode == UPPER_THEN_LOWER_THRESHOLD_CROSSING && lowerThresholdCrossingFound ){
226  currentSearchState = NO_SEARCH_GATE_TIME_OUT;
227  searchWindowIndex = 0;
228  searchTimeoutCounter.start();
229  thresholdCrossingDetected = true;
230  break;
231  }
232  if( thresholdCrossingMode == LOWER_THEN_UPPER_THRESHOLD_CROSSING && upperThresholdCrossingFound ){
233  currentSearchState = NO_SEARCH_GATE_TIME_OUT;
234  searchWindowIndex = 0;
235  searchTimeoutCounter.start();
236  thresholdCrossingDetected = true;
237  break;
238  }
239  break;
240  default:
241  break;
242  }
243  }
244 
245  return thresholdCrossingDetected ? true : false;
246 }
247 
249 
250  //Reset the derivative
251  derivative.init(Derivative::FIRST_DERIVATIVE, 1, 1, true, 5);
252 
253  movingAverageFilter.init(offsetFilterSize, 1);
254 
255  //Set the search state
256  currentSearchState = NO_SEARCH_GATE_TIME_OUT;
257 
258  //Enable future searches
259  enableSearch = true;
260  thresholdCrossingDetected = false;
261  analysisValue = 0;
262  searchWindowIndex = 0;
263  searchTimeoutCounter.stop();
264 
265  return true;
266 }
267 
269  currentSearchState = NO_SEARCH_GATE_TIME_OUT;
270  searchTimeoutCounter.start();
271  return true;
272 }
273 
275  return thresholdCrossingDetected;
276 }
277 
279  return enableSearch;
280 }
281 
283  return analysisMode;
284 }
285 
287  return thresholdCrossingMode;
288 }
289 
291  return searchWindowSize;
292 }
293 
295  return offsetFilterSize;
296 }
297 
299  return searchWindowIndex;
300 }
301 
303  return (UINT)searchTimeoutCounter.getMilliSeconds();
304 }
305 
307  return searchTimeoutDuration;
308 }
309 
311  return analysisValue;
312 }
313 
315  return upperThreshold;
316 }
317 
319  return lowerThreshold;
320 }
321 
323  return hysteresisThreshold;
324 }
325 
326 bool ThresholdCrossingDetector::setEnableSearch(const bool enableSearch){
327  this->enableSearch = enableSearch;
328  return true;
329 }
330 
331 bool ThresholdCrossingDetector::setAnalysisMode(const UINT analysisMode){
332  this->analysisMode = analysisMode;
333  return reset();
334 }
335 
336 bool ThresholdCrossingDetector::setThresholdCrossingMode(const UINT thresholdCrossingMode){
337  this->thresholdCrossingMode = thresholdCrossingMode;
338  return reset();
339 }
340 
341 bool ThresholdCrossingDetector::setDetectionTimeoutMode(const UINT detectionTimeoutMode){
342  this->detectionTimeoutMode = detectionTimeoutMode;
343  return reset();
344 }
345 
346 bool ThresholdCrossingDetector::setSearchWindowSize(const UINT searchWindowSize){
347  this->searchWindowSize = searchWindowSize;
348  return reset();
349 }
350 
351 bool ThresholdCrossingDetector::setOffsetFilterSize(const UINT offsetFilterSize){
352  this->offsetFilterSize = offsetFilterSize;
353  return reset();
354 }
355 
356 bool ThresholdCrossingDetector::setSearchTimeoutDuration(const UINT searchTimeoutDuration){
357  this->searchTimeoutDuration = searchTimeoutDuration;
358  return reset();
359 }
360 
361 bool ThresholdCrossingDetector::setLowerThreshold(const Float lowerThreshold){
362  this->lowerThreshold = lowerThreshold;
363  return reset();
364 }
365 
366 bool ThresholdCrossingDetector::setUpperThreshold(const Float upperThreshold){
367  this->upperThreshold = upperThreshold;
368  return reset();
369 }
370 
371 bool ThresholdCrossingDetector::setHysteresisThreshold(const Float hysteresisThreshold){
372  this->hysteresisThreshold = hysteresisThreshold;
373  return reset();
374 }
375 
376 GRT_END_NAMESPACE
bool setUpperThreshold(const Float upperThreshold)
bool init(UINT filterSize, UINT numDimensions)
bool setOffsetFilterSize(const UINT offsetFilterSize)
bool setSearchWindowSize(const UINT searchWindowSize)
bool init(UINT derivativeOrder, Float delta, UINT numDimensions, bool filterData, UINT filterSize)
Definition: Derivative.cpp:249
bool setDetectionTimeoutMode(const UINT detectionTimeoutMode)
This class implements a threshold crossing detector.
bool setHysteresisThreshold(const Float hysteresisThreshold)
signed long getMilliSeconds()
Definition: Timer.h:117
Float computeDerivative(const Float x)
Definition: Derivative.cpp:290
bool setAnalysisMode(const UINT analysisMode)
bool setLowerThreshold(const Float lowerThreshold)
Float filter(const Float x)
ThresholdCrossingDetector & operator=(const ThresholdCrossingDetector &rhs)
bool setEnableSearch(const bool enableSearch)
bool setThresholdCrossingMode(const UINT thresholdCrossingMode)
bool setSearchTimeoutDuration(const UINT searchTimeoutDuration)
bool start()
Definition: Timer.h:64
ThresholdCrossingDetector(UINT analysisMode=RAW_DATA_ANALYSIS_MODE, UINT thresholdCrossingMode=UPPER_THRESHOLD_CROSSING, UINT detectionTimeoutMode=TIMEOUT_COUNTER, Float lowerThreshold=-1, Float upperThreshold=1, Float hysteresisThreshold=0, UINT searchWindowSize=20, UINT searchTimeoutDuration=1000, UINT offsetFilterSize=10)
bool stop()
Definition: Timer.h:105