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