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.
MovementTrajectoryFeatures.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
23 
24 GRT_BEGIN_NAMESPACE
25 
26 //Register the ZeroCrossingCounter module with the FeatureExtraction base class
27 RegisterFeatureExtractionModule< MovementTrajectoryFeatures > MovementTrajectoryFeatures::registerModule("MovementTrajectoryFeatures");
28 
29 MovementTrajectoryFeatures::MovementTrajectoryFeatures(UINT trajectoryLength,UINT numCentroids,UINT featureMode,UINT numHistogramBins,UINT numDimensions,bool useTrajStartAndEndValues,bool useWeightedMagnitudeValues){
30 
31  classType = "MovementTrajectoryFeatures";
32  featureExtractionType = classType;
33  debugLog.setProceedingText("[DEBUG MovementTrajectoryFeatures]");
34  errorLog.setProceedingText("[ERROR MovementTrajectoryFeatures]");
35  warningLog.setProceedingText("[WARNING ZeroCrossingCounter]");
36 
37  init(trajectoryLength,numCentroids,featureMode,numHistogramBins,numDimensions,useTrajStartAndEndValues,useWeightedMagnitudeValues);
38 }
39 
41 
42  classType = "MovementTrajectoryFeatures";
43  featureExtractionType = classType;
44  debugLog.setProceedingText("[DEBUG MovementTrajectoryFeatures]");
45  errorLog.setProceedingText("[ERROR MovementTrajectoryFeatures]");
46  warningLog.setProceedingText("[WARNING ZeroCrossingCounter]");
47 
48  //Invoke the equals operator to copy the data from the rhs instance to this instance
49  *this = rhs;
50 }
51 
53 
54 }
55 
57  if(this!=&rhs){
58  this->trajectoryLength = rhs.trajectoryLength;
59  this->numCentroids = rhs.numCentroids;
60  this->featureMode = rhs.featureMode;
61  this->numHistogramBins = rhs.numHistogramBins;
62  this->useTrajStartAndEndValues = rhs.useTrajStartAndEndValues;
63  this->useWeightedMagnitudeValues = rhs.useWeightedMagnitudeValues;
64  this->trajectoryDataBuffer = rhs.trajectoryDataBuffer;
65  this->centroids = rhs.centroids;
66 
67  //Copy the base variables
69  }
70  return *this;
71 }
72 
74 
75  if( featureExtraction == NULL ) return false;
76 
77  if( this->getFeatureExtractionType() == featureExtraction->getFeatureExtractionType() ){
78 
79  //Invoke the equals operator to copy the data from the rhs instance to this instance
80  *this = *(MovementTrajectoryFeatures*)featureExtraction;
81 
82  return true;
83  }
84 
85  errorLog << "clone(FeatureExtraction *featureExtraction) - FeatureExtraction Types Do Not Match!" << std::endl;
86 
87  return false;
88 }
89 
91 
92  if( !initialized ){
93  errorLog << "computeFeatures(const VectorFloat &inputVector) - Not initialized!" << std::endl;
94  return false;
95  }
96 
97  if( inputVector.getSize() != numInputDimensions ){
98  errorLog << "computeFeatures(const VectorFloat &inputVector) - The size of the inputVector (" << inputVector.getSize() << ") does not match that of the filter (" << numInputDimensions << ")!" << std::endl;
99  return false;
100  }
101 
102  featureVector = update( inputVector );
103 
104  return true;
105 }
106 
108  if( initialized ){
109  return init(trajectoryLength,numCentroids,featureMode,numHistogramBins,numInputDimensions,useTrajStartAndEndValues,useWeightedMagnitudeValues);
110  }
111  return false;
112 }
113 
114 bool MovementTrajectoryFeatures::saveModelToFile( std::string filename ) const{
115 
116  std::fstream file;
117  file.open(filename.c_str(), std::ios::out);
118 
119  if( !saveModelToFile( file ) ){
120  return false;
121  }
122 
123  file.close();
124 
125  return true;
126 }
127 
129 
130  std::fstream file;
131  file.open(filename.c_str(), std::ios::in);
132 
133  if( !loadModelFromFile( file ) ){
134  return false;
135  }
136 
137  //Close the file
138  file.close();
139 
140  return true;
141 }
142 
143 bool MovementTrajectoryFeatures::saveModelToFile( std::fstream &file ) const{
144 
145  if( !file.is_open() ){
146  errorLog << "saveModelToFile(fstream &file) - The file is not open!" << std::endl;
147  return false;
148  }
149 
150  //Write the file header
151  file << "GRT_MOVEMENT_TRAJECTORY_FEATURES_FILE_V1.0" << std::endl;
152 
153  //Save the base settings to the file
155  errorLog << "saveFeatureExtractionSettingsToFile(fstream &file) - Failed to save base feature extraction settings to file!" << std::endl;
156  return false;
157  }
158 
159  //Write the movement trajectory settings to the file
160  file << "TrajectoryLength: " << trajectoryLength << std::endl;
161  file << "NumCentroids: " << numCentroids << std::endl;
162  file << "FeatureMode: " << featureMode << std::endl;
163  file << "NumHistogramBins: " << numHistogramBins << std::endl;
164  file << "UseTrajStartAndEndValues: " << useTrajStartAndEndValues << std::endl;
165  file << "UseWeightedMagnitudeValues: " << useWeightedMagnitudeValues << std::endl;
166 
167  return true;
168 }
169 
171 
172  if( !file.is_open() ){
173  errorLog << "loadModelFromFile(fstream &file) - The file is not open!" << std::endl;
174  return false;
175  }
176 
177  std::string word;
178 
179  //Load the header
180  file >> word;
181 
182  if( word != "GRT_MOVEMENT_TRAJECTORY_FEATURES_FILE_V1.0" ){
183  errorLog << "loadModelFromFile(fstream &file) - Invalid file format!" << std::endl;
184  return false;
185  }
186 
188  errorLog << "loadFeatureExtractionSettingsFromFile(fstream &file) - Failed to load base feature extraction settings from file!" << std::endl;
189  return false;
190  }
191 
192  //Load the TrajectoryLength
193  file >> word;
194  if( word != "TrajectoryLength:" ){
195  errorLog << "loadModelFromFile(fstream &file) - Failed to read TrajectoryLength header!" << std::endl;
196  return false;
197  }
198  file >> trajectoryLength;
199 
200  //Load the NumCentroids
201  file >> word;
202  if( word != "NumCentroids:" ){
203  errorLog << "loadModelFromFile(fstream &file) - Failed to read NumCentroids header!" << std::endl;
204  return false;
205  }
206  file >> numCentroids;
207 
208  //Load the FeatureMode
209  file >> word;
210  if( word != "FeatureMode:" ){
211  errorLog << "loadModelFromFile(fstream &file) - Failed to read FeatureMode header!" << std::endl;
212  return false;
213  }
214  file >> featureMode;
215 
216  //Load the NumHistogramBins
217  file >> word;
218  if( word != "NumHistogramBins:" ){
219  errorLog << "loadModelFromFile(fstream &file) - Failed to read NumHistogramBins header!" << std::endl;
220  return false;
221  }
222  file >> numHistogramBins;
223 
224  //Load the UseTrajStartAndEndValues
225  file >> word;
226  if( word != "UseTrajStartAndEndValues:" ){
227  errorLog << "loadModelFromFile(fstream &file) - Failed to read UseTrajStartAndEndValues header!" << std::endl;
228  return false;
229  }
230  file >> useTrajStartAndEndValues;
231 
232  //Load the UseWeightedMagnitudeValues
233  file >> word;
234  if( word != "UseWeightedMagnitudeValues:" ){
235  errorLog << "loadModelFromFile(fstream &file) - Failed to read UseWeightedMagnitudeValues header!" << std::endl;
236  return false;
237  }
238  file >> useWeightedMagnitudeValues;
239 
240  //Init the ZeroCrossingCounter module to ensure everything is initialized correctly
241  return init(trajectoryLength,numCentroids,featureMode,numHistogramBins,numInputDimensions,useTrajStartAndEndValues,useWeightedMagnitudeValues);
242 }
243 
244 bool MovementTrajectoryFeatures::init(UINT trajectoryLength,UINT numCentroids,UINT featureMode,UINT numHistogramBins,UINT numDimensions,bool useTrajStartAndEndValues,bool useWeightedMagnitudeValues){
245 
246  initialized = false;
247 
248  if( numCentroids > trajectoryLength ){
249  errorLog << "init(...) - The number of centroids parameter can not be larger than the trajectory length parameter!" << std::endl;
250  return false;
251  }
252  if( trajectoryLength % numCentroids != 0 ){
253  errorLog << "init(...) - The trajectory length parameter must be divisible with no remainders by the number of centroids parameter!" << std::endl;
254  return false;
255  }
256 
257  if( featureMode == CENTROID_ANGLE_2D && numDimensions % 2 != 0 ){
258  errorLog << "init(...) - If the featureMode is CENTROID_ANGLE_2D then the numberOfDimensions should be divisble by 2 (as each pair of points should represent {x,y})!" << std::endl;
259  return false;
260  }
261 
262  if( numHistogramBins == 0 && featureMode == CENTROID_ANGLE_2D ){
263  errorLog << "init(...) - If the featureMode is CENTROID_ANGLE_2D then the numHistogramBins parameter must greater than 0!" << std::endl;
264  return false;
265  }
266 
267  //Setup the search variables
268  this->trajectoryLength = trajectoryLength;
269  this->numCentroids = numCentroids;
270  this->featureMode = featureMode;
271  this->numHistogramBins = numHistogramBins;
272  this->numInputDimensions = numDimensions;
273  this->useTrajStartAndEndValues = useTrajStartAndEndValues;
274  this->useWeightedMagnitudeValues = useWeightedMagnitudeValues;
275  featureDataReady = false;
276 
277  //Set the number of output dimensions
278  numOutputDimensions = 0;
279  switch( featureMode ){
280  case CENTROID_VALUE:
281  //In the centroid value mode the useTrajStartAndEndValues is ignored (as the start and end centroids are used by default)
282  numOutputDimensions = numInputDimensions*numCentroids;
283  break;
284  case NORMALIZED_CENTROID_VALUE:
285  numOutputDimensions = numInputDimensions*numCentroids;
286  if( useTrajStartAndEndValues ){
287  numOutputDimensions += numInputDimensions*2;
288  }
289  break;
290  case CENTROID_DERIVATIVE:
291  numOutputDimensions = numInputDimensions*(numCentroids-1);
292  if( useTrajStartAndEndValues ){
293  numOutputDimensions += numInputDimensions*2;
294  }
295  break;
296  case CENTROID_ANGLE_2D:
297  numOutputDimensions = numHistogramBins*(numDimensions/2);
298  break;
299  default:
300  errorLog << "init(...)- Unknown featureMode!" << std::endl;
301  return false;
302  break;
303  }
304 
305  if( numOutputDimensions == 0 ){
306  errorLog << "init(...) - The numOutputDimensions is zero!" << std::endl;
307  return false;
308  }
309 
310  //Resize the feature Vector
311  featureVector.resize(numOutputDimensions);
312 
313  //Resize the raw trajectory data buffer
314  trajectoryDataBuffer.resize( trajectoryLength, VectorFloat(numInputDimensions,0) );
315 
316  //Resize the centroids buffer
317  centroids.resize(numCentroids,numInputDimensions);
318 
319  //Flag that the zero crossing counter has been initialized
320  initialized = true;
321 
322  return true;
323 }
324 
326  return update(VectorFloat(1,x));
327 }
328 
330 
331  if( !initialized ){
332  errorLog << "update(const VectorFloat &x) - Not Initialized!" << std::endl;
333  return VectorFloat();
334  }
335 
336  if( x.getSize() != numInputDimensions ){
337  errorLog << "update(const VectorFloat &x)- The Number Of Input Dimensions (" << numInputDimensions << ") does not match the size of the input Vector (" << x.getSize() << ")!" << std::endl;
338  return VectorFloat();
339  }
340 
341  //Add the new data to the trajectory data buffer
342  trajectoryDataBuffer.push_back( x );
343 
344  //Only flag that the feature data is ready if the trajectory data is full
345  if( trajectoryDataBuffer.getBufferFilled() ){
346  featureDataReady = true;
347  }else featureDataReady = false;
348 
349  //Compute the centroids
350  centroids.setAllValues(0);
351 
352  UINT dataBufferIndex = 0;
353  UINT numValuesPerCentroid = (UINT)floor(Float(trajectoryLength/numCentroids));
354  for(UINT n=0; n<numInputDimensions; n++){
355  dataBufferIndex = 0;
356  for(UINT i=0; i<numCentroids; i++){
357  for(UINT j=0; j<numValuesPerCentroid; j++){
358  centroids[i][n] += trajectoryDataBuffer[dataBufferIndex++][n];
359  }
360  centroids[i][n] /= Float(numValuesPerCentroid);
361  }
362  }
363 
364  //Copmute the features
365  UINT featureIndex = 0;
366  Vector< MinMax > centroidNormValues(numInputDimensions);
367  VectorFloat histSumValues;
368  Vector< Vector< AngleMagnitude > > angleMagnitudeValues;
369  switch( featureMode ){
370  case CENTROID_VALUE:
371  //Simply set the feature Vector as the list of centroids
372  for(UINT n=0; n<numInputDimensions; n++){
373  for(UINT i=0; i<numCentroids; i++){
374  featureVector[ featureIndex++ ] = centroids[i][n];
375  }
376  }
377  break;
378  case NORMALIZED_CENTROID_VALUE:
379  for(UINT n=0; n<numInputDimensions; n++){
380 
381  //Find the min and max values
382  for(UINT i=0; i<numCentroids; i++){
383  centroidNormValues[n].updateMinMax( centroids[i][n] );
384  }
385 
386  //Use the normalized centroids as the features
387  for(UINT i=0; i<numCentroids; i++){
388  if( centroidNormValues[n].maxValue != centroidNormValues[n].minValue ){
389  featureVector[ featureIndex++ ] = Util::scale(centroids[i][n],centroidNormValues[n].minValue,centroidNormValues[n].maxValue,0,1);
390  }else featureVector[ featureIndex++ ] = 0;
391  }
392 
393  //Add the start and end centroid values if needed
394  if( useTrajStartAndEndValues ){
395  featureVector[ featureIndex++ ] = centroids[0][n];
396  featureVector[ featureIndex++ ] = centroids[numCentroids-1][n];
397  }
398  }
399  break;
400  case CENTROID_DERIVATIVE:
401  for(UINT n=0; n<numInputDimensions; n++){
402 
403  //Compute the derivative between centroid i and centroid i+1
404  for(UINT i=0; i<numCentroids-1; i++){
405  featureVector[ featureIndex++ ] = centroids[i+1][n]-centroids[i][n];
406  }
407 
408  //Add the start and end centroid values if needed
409  if( useTrajStartAndEndValues ){
410  featureVector[ featureIndex++ ] = centroids[0][n];
411  featureVector[ featureIndex++ ] = centroids[numCentroids-1][n];
412  }
413  }
414  break;
415  case CENTROID_ANGLE_2D:
416  histSumValues.resize( numInputDimensions/2, 0);
417  angleMagnitudeValues.resize( numInputDimensions/2 );
418 
419  //Zero the feature Vector
420  fill(featureVector.begin(),featureVector.end(),0);
421 
422  //Compute the angle and magnitude betweem each of the centroids, do this for each pair of points
423  for(UINT n=0; n<numInputDimensions/2; n++){
424  //Resize the nth buffer to hold the values for each centroid
425  angleMagnitudeValues[n].resize(numCentroids-1);
426  for(UINT i=0; i<numCentroids-1; i++){
427  Util::cartToPolar(centroids[i+1][n*2]-centroids[i][n*2], centroids[i+1][n*2+1]-centroids[i][n*2+1], angleMagnitudeValues[n][i].magnitude, angleMagnitudeValues[n][i].angle);
428  }
429 
430  //Add the angles to the histogram
431  for(UINT i=0; i<numCentroids-1; i++){
432  UINT histBin = 0;
433  Float degreesPerBin = 360.0/numHistogramBins;
434  Float binStartValue = 0;
435  Float binEndValue = degreesPerBin;
436 
437  if( angleMagnitudeValues[n][i].angle < 0 || angleMagnitudeValues[n][i].angle > 360.0 ){
438  warningLog << "The angle of a point is not between [0 360]. Angle: " << angleMagnitudeValues[n][i].angle << std::endl;
439  return VectorFloat();
440  }
441 
442  //Find which hist bin the current angle is in
443  while( true ){
444  if( angleMagnitudeValues[n][i].angle >= binStartValue && angleMagnitudeValues[n][i].angle < binEndValue ){
445  break;
446  }
447  histBin++;
448  binStartValue += degreesPerBin;
449  binEndValue += degreesPerBin;
450  }
451 
452  histSumValues[ n ] += useWeightedMagnitudeValues ? angleMagnitudeValues[n][i].magnitude : 1;
453  featureVector[ n*numHistogramBins + histBin ] += useWeightedMagnitudeValues ? angleMagnitudeValues[n][i].magnitude : 1;
454  }
455 
456  //Normalize the hist bins
457  for(UINT n=0; n<numInputDimensions/2; n++){
458  if( histSumValues[ n ] > 0 ){
459  for(UINT i=0; i<numHistogramBins; i++){
460  featureVector[ n*numHistogramBins + i ] /= histSumValues[ n ];
461  }
462  }
463  }
464  }
465  break;
466  default:
467  errorLog << "update(VectorFloat x)- Unknown featureMode!" << std::endl;
468  return featureVector;
469  break;
470  }
471 
472  return featureVector;
473 }
474 
476  if( initialized ){
477  return trajectoryDataBuffer;
478  }
480 }
481 
483  if( initialized ){
484  return centroids;
485  }
486  return MatrixFloat();
487 }
488 
490  if( initialized ){
491  return featureMode;
492  }
493  return 0;
494 }
495 
496 GRT_END_NAMESPACE
virtual bool computeFeatures(const VectorFloat &inputVector)
bool push_back(const T &value)
static Float scale(const Float &x, const Float &minSource, const Float &maxSource, const Float &minTarget, const Float &maxTarget, const bool constrain=false)
Definition: Util.cpp:53
bool saveFeatureExtractionSettingsToFile(std::fstream &file) const
virtual bool saveModelToFile(std::string filename) const
bool getBufferFilled() const
virtual bool resize(const unsigned int size)
Definition: Vector.h:133
UINT getSize() const
Definition: Vector.h:191
std::string getFeatureExtractionType() const
This class implements the MovementTrajectory feature extraction module.
bool setAllValues(const T &value)
Definition: Matrix.h:336
static void cartToPolar(const Float x, const Float y, Float &r, Float &theta)
Definition: Util.cpp:343
MovementTrajectoryFeatures & operator=(const MovementTrajectoryFeatures &rhs)
virtual bool deepCopyFrom(const FeatureExtraction *featureExtraction)
virtual bool loadModelFromFile(std::string filename)
bool loadFeatureExtractionSettingsFromFile(std::fstream &file)
MovementTrajectoryFeatures(UINT trajectoryLength=100, UINT numCentroids=10, UINT featureMode=CENTROID_VALUE, UINT numHistogramBins=10, UINT numDimensions=1, bool useTrajStartAndEndValues=false, bool useWeightedMagnitudeValues=true)
virtual bool resize(const unsigned int r, const unsigned int c)
Definition: Matrix.h:232
bool copyBaseVariables(const FeatureExtraction *featureExtractionModule)
CircularBuffer< VectorFloat > getTrajectoryData()
bool resize(const unsigned int newBufferSize)