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