GestureRecognitionToolkit  Version: 0.2.0
The Gesture Recognition Toolkit (GRT) is a cross-platform, open-source, c++ machine learning library for real-time gesture recognition.
SwipeDetector.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 #define GRT_DLL_EXPORTS
22 #include "SwipeDetector.h"
23 
24 GRT_BEGIN_NAMESPACE
25 
26 //Register the SwipeDetector module with the Classifier base class
27 RegisterClassifierModule< SwipeDetector > SwipeDetector::registerModule("SwipeDetector");
28 
29 SwipeDetector::SwipeDetector(const unsigned int swipeIndex, const unsigned int swipeThreshold,
30  const unsigned int hysteresisThreshold, const unsigned int swipeDirection,bool useScaling)
31 {
32  this->swipeIndex = swipeIndex;
33  this->swipeThreshold = swipeThreshold;
34  this->hysteresisThreshold = hysteresisThreshold;
35  this->swipeDirection = swipeDirection;
36  this->useScaling = useScaling;
37 
38  supportsNullRejection = false;
39  contextInput = true;
40  classType = "SwipeDetector";
41  classifierType = classType;
42  classifierMode = STANDARD_CLASSIFIER_MODE;
43  debugLog.setProceedingText("[DEBUG SwipeDetector]");
44  errorLog.setProceedingText("[ERROR SwipeDetector]");
45  trainingLog.setProceedingText("[TRAINING SwipeDetector]");
46  warningLog.setProceedingText("[WARNING SwipeDetector]");
47 
48  swipeIntegrationCoeff = 0.92;
49  movementIntegrationCoeff = 0.90;
50  movementThreshold = 3000;
51  contextFilterSize = 5;
52 
53  reset();
54 }
55 
57  supportsNullRejection = false;
58  classType = "SwipeDetector";
59  classifierType = classType;
60  classifierMode = STANDARD_CLASSIFIER_MODE;
61  debugLog.setProceedingText("[DEBUG SwipeDetector]");
62  errorLog.setProceedingText("[ERROR SwipeDetector]");
63  trainingLog.setProceedingText("[TRAINING SwipeDetector]");
64  warningLog.setProceedingText("[WARNING SwipeDetector]");
65 
66  swipeIntegrationCoeff = 0.92;
67  movementIntegrationCoeff = 0.90;
68  movementThreshold = 3000;
69  contextFilterSize = 5;
70 
71  *this = rhs;
72 }
73 
75 {
76 }
77 
79  if( this != &rhs ){
80  //SwipeDetector variables
81  this->firstSample = rhs.firstSample;
82  this->swipeDetected = rhs.swipeDetected;
83  this->contextInput = rhs.contextInput;
84  this->swipeIndex = rhs.swipeIndex;
85  this->swipeDirection = rhs.swipeDirection;
86  this->contextFilterSize = rhs.contextFilterSize;
87  this->swipeIntegrationCoeff = rhs.swipeIntegrationCoeff;
88  this->movementIntegrationCoeff = rhs.movementIntegrationCoeff;
89  this->swipeThreshold = rhs.swipeThreshold;
90  this->hysteresisThreshold = rhs.hysteresisThreshold;
91  this->swipeVelocity = rhs.swipeVelocity;
92  this->movementVelocity = rhs.movementVelocity;
93  this->movementThreshold = rhs.movementThreshold;
94  this->contextFilteredValue = rhs.contextFilteredValue;
95  this->lastX = rhs.lastX;
96  this->thresholdDetector = rhs.thresholdDetector;
97  this->contextFilter = rhs.contextFilter;
98 
99  //Classifier variables
100  copyBaseVariables( (Classifier*)&rhs );
101  }
102  return *this;
103 }
104 
105 bool SwipeDetector::deepCopyFrom(const Classifier *classifier){
106 
107  if( classifier == NULL ) return false;
108 
109  if( this->getClassifierType() == classifier->getClassifierType() ){
110 
111  SwipeDetector *ptr = (SwipeDetector*)classifier;
112 
113  this->firstSample = ptr->firstSample;
114  this->swipeDetected = ptr->swipeDetected;
115  this->contextInput = ptr->contextInput;
116  this->swipeIndex = ptr->swipeIndex;
117  this->swipeDirection = ptr->swipeDirection;
118  this->contextFilterSize = ptr->contextFilterSize;
119  this->swipeIntegrationCoeff = ptr->swipeIntegrationCoeff;
120  this->movementIntegrationCoeff = ptr->movementIntegrationCoeff;
121  this->swipeThreshold = ptr->swipeThreshold;
122  this->hysteresisThreshold = ptr->hysteresisThreshold;
123  this->swipeVelocity = ptr->swipeVelocity;
124  this->movementVelocity = ptr->movementVelocity;
125  this->movementThreshold = ptr->movementThreshold;
126  this->contextFilteredValue = ptr->contextFilteredValue;
127  this->lastX = ptr->lastX;
128  this->thresholdDetector = ptr->thresholdDetector;
129  this->contextFilter = ptr->contextFilter;
130 
131  //Classifier variables
132  return copyBaseVariables( classifier );
133  }
134 
135  return false;
136 }
137 
138 bool SwipeDetector::init(const unsigned int numInputDimensions){
139 
140  //Clear any previous models
141  clear();
142 
143  this->numInputDimensions = numInputDimensions;
144  numClasses = 2; //This is always 2 for swipe detection [1 == swipe detected, everything else means no swipe detected]
145  classLabels.resize( 2 );
146  classLabels[0] = 1; //Swipe
147  classLabels[1] = 2; //No Swipe
148  nullRejectionThresholds.resize(2,0);
149 
150  //We currently have no way to automatically train the swipe detection, user needs to manually set thresholds, so just flag the model is trained
151  trained = true;
152 
153  return true;
154 }
155 
157 
158  //Clear any previous models
159  clear();
160 
161  const unsigned int M = trainingData.getNumSamples();
162  const unsigned int N = trainingData.getNumDimensions();
163 
164  if( M == 0 ){
165  errorLog << "train_(trainingData &labelledTrainingData) - Training data has zero samples!" << std::endl;
166  return false;
167  }
168 
169  numInputDimensions = N;
170  numClasses = 2; //This is always 2 for swipe detection [1 == swipe detected, everything else means no swipe detected]
171  classLabels.resize( 2 );
172  classLabels[0] = 1; //Swipe
173  classLabels[1] = 2; //No Swipe
174  nullRejectionThresholds.resize(2,0);
175  ranges = trainingData.getRanges();
176 
177  //Scale the training data if needed
178  if( useScaling ){
179  //Scale the training data between 0 and 1
180  trainingData.scale(0, 1);
181  }
182 
183  //We currently have no way to automatically train the swipe detection, user needs to manually set thresholds, so just flag the model is trained
184  trained = true;
185 
186  return true;
187 }
188 
189 
191 
192  predictedClassLabel = 0;
193  maxLikelihood = 0;
194  swipeDetected = false;
195 
196  if( !trained ){
197  errorLog << "predict_(VectorDouble &inputVector) - SwipeDetector Model Not Trained!" << std::endl;
198  return false;
199  }
200 
201  if( inputVector.size() != numInputDimensions ){
202  errorLog << "predict_(VectorDouble &inputVector) - The size of the input vector (" << inputVector.size() << ") does not match the num features in the model (" << numInputDimensions << ")" << std::endl;
203  return false;
204  }
205 
206  if( useScaling ){
207  for(UINT n=0; n<numInputDimensions; n++){
208  inputVector[n] = scale(inputVector[n], ranges[n].minValue, ranges[n].maxValue, 0, 1);
209  }
210  }
211 
212  if( classLikelihoods.size() != numClasses ) classLikelihoods.resize(numClasses,0);
213  if( classDistances.size() != numClasses ) classDistances.resize(numClasses,0);
214 
215  //Compute the integrated movement velocity for the none-swipe dimensions
216  if( !firstSample ){
217  movementVelocity *= movementIntegrationCoeff;
218  for(unsigned int i=0; i<numInputDimensions; i++){
219  if( i != swipeIndex )
220  movementVelocity += GRT::SQR(inputVector[i]-lastX[i]);
221  }
222  }else firstSample = false;
223  lastX = inputVector;
224 
225  //Compute the integrated swipe velocity and update the threshold detector
226  swipeVelocity = (swipeVelocity*swipeIntegrationCoeff) + inputVector[swipeIndex];
227  thresholdDetector.update( swipeVelocity );
228 
229  //Filter the context
230  contextFilteredValue = contextFilter.filter( contextInput ? 1 : 0 );
231 
232  //Check if a swipe has been detected
233  swipeDetected = thresholdDetector.getThresholdCrossingDetected() && movementVelocity < movementThreshold && contextFilteredValue == 1;
234 
235  if( swipeDetected ){
236  predictedClassLabel = 1;
237  classLikelihoods[0] = 1.0;
238  classDistances[1] = 0;
239  }else{
240  predictedClassLabel = 2;
241  classLikelihoods[0] = 0.0;
242  classDistances[1] = 1;
243  }
244 
245  return true;
246 }
247 
249 
250  //Clear the Classifier variables
252 
253  reset();
254 
255  return true;
256 }
257 
259 
260  movementVelocity = 0;
261  lastX.clear();
262  lastX.resize( 3, 0 );
263  firstSample = true;
264  swipeDetected = false;
265  contextInput = true;
266 
267  if( swipeDirection == POSITIVE_SWIPE ){
268  thresholdDetector.setThresholdCrossingMode( GRT::ThresholdCrossingDetector::UPPER_THRESHOLD_CROSSING );
269  thresholdDetector.setUpperThreshold( swipeThreshold );
270  }
271  if( swipeDirection == NEGATIVE_SWIPE ){
272  thresholdDetector.setThresholdCrossingMode( GRT::ThresholdCrossingDetector::LOWER_THRESHOLD_CROSSING );
273  thresholdDetector.setLowerThreshold( swipeThreshold );
274  }
275 
276  thresholdDetector.setAnalysisMode( GRT::ThresholdCrossingDetector::DERIVATIVE_ANALYSIS_MODE );
277  thresholdDetector.setDetectionTimeoutMode( GRT::ThresholdCrossingDetector::HYSTERESIS_THRESHOLD );
278  thresholdDetector.setHysteresisThreshold( hysteresisThreshold );
279 
280  contextFilter.init( contextFilterSize, 1 );
281 
282  return true;
283 }
284 
285 bool SwipeDetector::save( std::fstream &file ) const{
286 
287  if(!file.is_open())
288  {
289  errorLog <<"save(fstream &file) - The file is not open!" << std::endl;
290  return false;
291  }
292 
293  //Write the header info
294  file<<"GRT_SWIPE_DETECTION_MODEL_FILE_V1.0\n";
295 
296  //Write the classifier settings to the file
298  errorLog <<"save(fstream &file) - Failed to save classifier base settings to file!" << std::endl;
299  return false;
300  }
301 
302  if( trained ){
303 
304  file << "SwipeIndex: " << swipeIndex << std::endl;
305  file << "ContextFilterSize: " << contextFilterSize << std::endl;
306  file << "SwipeIntegrationCoeff: " << swipeIntegrationCoeff << std::endl;
307  file << "MovementIntegrationCoeff: " << movementIntegrationCoeff << std::endl;
308  file << "SwipeThreshold: " << swipeThreshold << std::endl;
309 
310  file << "HysteresisThreshold: " << hysteresisThreshold << std::endl;
311  file << "SwipeThreshold: " << swipeThreshold << std::endl;
312  file << "MovementThreshold: " << movementThreshold << std::endl;
313  file << "SwipeThreshold: " << swipeThreshold << std::endl;
314 
315  //TODO - need to save the thresholdDetector
316 
317  }
318 
319  return true;
320 }
321 
322 bool SwipeDetector::load( std::fstream &file ){
323 
324  trained = false;
325  numInputDimensions = 0;
326  numClasses = 0;
327  classLabels.clear();
328 
329  if(!file.is_open())
330  {
331  errorLog << "load(string filename) - Could not open file to load model" << std::endl;
332  return false;
333  }
334 
335  std::string word;
336 
337  file >> word;
338 
339  //Find the file type header
340  if(word != "GRT_SWIPE_DETECTION_MODEL_FILE_V1.0"){
341  errorLog << "load(string filename) - Could not find Model File Header" << std::endl;
342  return false;
343  }
344 
345  //Load the base settings from the file
347  errorLog << "load(string filename) - Failed to load base settings from file!" << std::endl;
348  return false;
349  }
350 
351  if( trained ){
352 
353  file >> word;
354  if( word != "SwipeIndex:" ){
355  errorLog << "load(string filename) - Could not load the SwipeIndex!" << std::endl;
356  return false;
357  }
358  file >> swipeIndex;
359 
360  file >> word;
361  if( word != "ContextFilterSize:" ){
362  errorLog << "load(string filename) - Could not load the ContextFilterSize!" << std::endl;
363  return false;
364  }
365  file >> contextFilterSize;
366 
367  file >> word;
368  if( word != "SwipeIntegrationCoeff:" ){
369  errorLog << "load(string filename) - Could not load the SwipeIntegrationCoeff!" << std::endl;
370  return false;
371  }
372  file >> swipeIntegrationCoeff;
373 
374  file >> word;
375  if( word != "MovementIntegrationCoeff:" ){
376  errorLog << "load(string filename) - Could not load the MovementIntegrationCoeff!" << std::endl;
377  return false;
378  }
379  file >> movementIntegrationCoeff;
380 
381  file >> word;
382  if( word != "SwipeThreshold:" ){
383  errorLog << "load(string filename) - Could not load the SwipeThreshold!" << std::endl;
384  return false;
385  }
386  file >> swipeThreshold;
387 
388  file >> word;
389  if( word != "HysteresisThreshold:" ){
390  errorLog << "load(string filename) - Could not load the HysteresisThreshold!" << std::endl;
391  return false;
392  }
393  file >> hysteresisThreshold;
394 
395  file >> word;
396  if( word != "SwipeThreshold:" ){
397  errorLog << "load(string filename) - Could not load the SwipeThreshold!" << std::endl;
398  return false;
399  }
400  file >> swipeThreshold;
401 
402  file >> word;
403  if( word != "MovementThreshold:" ){
404  errorLog << "load(string filename) - Could not load the MovementThreshold!" << std::endl;
405  return false;
406  }
407  file >> movementThreshold;
408 
409  //Resize the prediction results to make sure it is setup for realtime prediction
410  maxLikelihood = DEFAULT_NULL_LIKELIHOOD_VALUE;
411  bestDistance = DEFAULT_NULL_DISTANCE_VALUE;
412  classLikelihoods.resize(numClasses,DEFAULT_NULL_LIKELIHOOD_VALUE);
413  classDistances.resize(numClasses,DEFAULT_NULL_DISTANCE_VALUE);
414 
415  reset();
416  }
417 
418  return true;
419 }
420 
421 
423  return swipeDetected;
424 }
425 
427  return thresholdDetector.getAnalysisValue();
428 }
429 
431  return swipeThreshold;
432 }
433 
435  return hysteresisThreshold;
436 }
437 
439  return movementVelocity;
440 }
441 
443  return movementThreshold;
444 }
445 
447  return contextFilteredValue;
448 }
449 
451  return swipeIntegrationCoeff;
452 }
453 
454 bool SwipeDetector::setContext(const bool context){
455  this->contextInput = context;
456  return true;
457 }
458 
459 bool SwipeDetector::setSwipeIndex(const unsigned int swipeIndex){
460  this->swipeIndex = swipeIndex;
461  reset();
462  return true;
463 }
464 
465 bool SwipeDetector::setSwipeDirection(const unsigned int swipeDirection){
466 
467  if( swipeDirection != POSITIVE_SWIPE && swipeDirection != NEGATIVE_SWIPE ){
468  errorLog << "setSwipeDirection(const unsigned int swipeDirection) - Unknown swipeDirection!" << std::endl;
469  return false;
470  }
471 
472  this->swipeDirection = swipeDirection;
473  reset();
474 
475  return true;
476 }
477 
478 bool SwipeDetector::setSwipeThreshold(const Float swipeThreshold){
479  this->swipeThreshold = swipeThreshold;
480  reset();
481  return true;
482 }
483 
484 bool SwipeDetector::setHysteresisThreshold(const Float hysteresisThreshold){
485  this->hysteresisThreshold = hysteresisThreshold;
486  reset();
487  return true;
488 }
489 
490 bool SwipeDetector::setMovementThreshold(const Float movementThreshold){
491  this->movementThreshold = movementThreshold;
492  reset();
493  return true;
494 }
495 
496 bool SwipeDetector::setSwipeIntegrationCoeff(const Float swipeIntegrationCoeff){
497  this->swipeIntegrationCoeff = swipeIntegrationCoeff;
498  reset();
499  return true;
500 }
501 
502 GRT_END_NAMESPACE
503 
bool saveBaseSettingsToFile(std::fstream &file) const
Definition: Classifier.cpp:256
virtual bool predict_(VectorDouble &inputVector)
#define DEFAULT_NULL_LIKELIHOOD_VALUE
Definition: Classifier.h:38
virtual bool reset()
Float scale(const Float &x, const Float &minSource, const Float &maxSource, const Float &minTarget, const Float &maxTarget, const bool constrain=false)
Definition: MLBase.h:353
bool setMovementThreshold(const Float movementThreshold)
bool setSwipeIntegrationCoeff(const Float swipeIntegrationCoeff)
std::string getClassifierType() const
Definition: Classifier.cpp:161
Float getHysteresisThreshold() const
virtual bool resize(const unsigned int size)
Definition: Vector.h:133
virtual bool clear()
SwipeDetector(const unsigned int swipeIndex=0, const unsigned int swipeThreshold=100, const unsigned int hysteresisThreshold=0, const unsigned int swipeDirection=POSITIVE_SWIPE, bool useScaling=false)
virtual ~SwipeDetector(void)
bool setContext(const bool context)
bool setSwipeThreshold(const Float swipeThreshold)
Float getContextValue() const
Float getMovementVelocity() const
virtual bool save(std::fstream &file) const
bool setSwipeDirection(const unsigned int swipeDirection)
UINT getNumSamples() const
Float getMovementThreshold() const
SwipeDetector & operator=(const SwipeDetector &rhs)
This class implements a basic swipe detection classification algorithm.
bool getSwipeDetected() const
bool copyBaseVariables(const Classifier *classifier)
Definition: Classifier.cpp:93
bool loadBaseSettingsFromFile(std::fstream &file)
Definition: Classifier.cpp:303
bool setHysteresisThreshold(const Float hysteresisThreshold)
UINT getNumDimensions() const
virtual bool deepCopyFrom(const Classifier *classifier)
virtual bool load(std::fstream &file)
Vector< MinMax > getRanges() const
Float getSwipeIntegrationCoeff() const
virtual bool train_(ClassificationData &trainingData)
Float getSwipeThreshold() const
bool scale(const Float minTarget, const Float maxTarget)
Float getSwipeValue() const
virtual bool clear()
Definition: Classifier.cpp:142
bool setSwipeIndex(const unsigned int swipeIndex)