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