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