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.
TimeDomainFeatures.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 "TimeDomainFeatures.h"
23 
24 GRT_BEGIN_NAMESPACE
25 
26 //Define the string that will be used to identify the object
27 std::string TimeDomainFeatures::id = "TimeDomainFeatures";
28 std::string TimeDomainFeatures::getId() { return TimeDomainFeatures::id; }
29 
30 //Register the TimeDomainFeatures module with the FeatureExtraction base class
32 
33 TimeDomainFeatures::TimeDomainFeatures(const UINT bufferLength,const UINT numFrames,const UINT numDimensions,const bool offsetInput,const bool useMean,const bool useStdDev,const bool useEuclideanNorm,const bool useRMS) : FeatureExtraction( TimeDomainFeatures::getId() )
34 {
35  init(bufferLength,numFrames,numDimensions,offsetInput,useMean,useStdDev,useEuclideanNorm,useRMS);
36 }
37 
38 TimeDomainFeatures::TimeDomainFeatures(const TimeDomainFeatures &rhs) : FeatureExtraction( TimeDomainFeatures::getId() )
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->bufferLength = rhs.bufferLength;
51  this->numFrames = rhs.numFrames;
52  this->offsetInput = rhs.offsetInput;
53  this->useMean = rhs.useMean;
54  this->useStdDev = rhs.useStdDev;
55  this->useEuclideanNorm = rhs.useEuclideanNorm;
56  this->useRMS = rhs.useRMS;
57  this->dataBuffer = rhs.dataBuffer;
58 
59  //Copy the base variables
61  }
62  return *this;
63 }
64 
65 bool TimeDomainFeatures::deepCopyFrom(const FeatureExtraction *featureExtraction){
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 TimeDomainFeatures*>(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(bufferLength,numFrames,numInputDimensions,offsetInput,useMean,useStdDev,useEuclideanNorm,useRMS);
102  }
103  return false;
104 }
105 
106 bool TimeDomainFeatures::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_TIME_DOMAIN_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 time domain settings to the file
123  file << "BufferLength: " << bufferLength << std::endl;
124  file << "NumFrames: " << numFrames << std::endl;
125  file << "OffsetInput: " << offsetInput << std::endl;
126  file << "UseMean: " << useMean << std::endl;
127  file << "UseStdDev: " << useStdDev << std::endl;
128  file << "UseEuclideanNorm: " << useEuclideanNorm << std::endl;
129  file << "UseRMS: " << useRMS << std::endl;
130 
131  return true;
132 }
133 
134 bool TimeDomainFeatures::load( std::fstream &file ){
135 
136  if( !file.is_open() ){
137  errorLog << "load(fstream &file) - The file is not open!" << std::endl;
138  return false;
139  }
140 
141  std::string word;
142 
143  //Load the header
144  file >> word;
145 
146  if( word != "GRT_TIME_DOMAIN_FEATURES_FILE_V1.0" ){
147  errorLog << "load(fstream &file) - Invalid file format!" << std::endl;
148  return false;
149  }
150 
152  errorLog << "loadFeatureExtractionSettingsFromFile(fstream &file) - Failed to load base feature extraction settings from file!" << std::endl;
153  return false;
154  }
155 
156  //Load the BufferLength
157  file >> word;
158  if( word != "BufferLength:" ){
159  errorLog << "load(fstream &file) - Failed to read BufferLength header!" << std::endl;
160  return false;
161  }
162  file >> bufferLength;
163 
164  //Load the NumFrames
165  file >> word;
166  if( word != "NumFrames:" ){
167  errorLog << "load(fstream &file) - Failed to read NumFrames header!" << std::endl;
168  return false;
169  }
170  file >> numFrames;
171 
172  //Load the OffsetInput
173  file >> word;
174  if( word != "OffsetInput:" ){
175  errorLog << "load(fstream &file) - Failed to read OffsetInput header!" << std::endl;
176  return false;
177  }
178  file >> offsetInput;
179 
180  //Load the UseMean
181  file >> word;
182  if( word != "UseMean:" ){
183  errorLog << "load(fstream &file) - Failed to read UseMean header!" << std::endl;
184  return false;
185  }
186  file >> useMean;
187 
188  //Load the UseStdDev
189  file >> word;
190  if( word != "UseStdDev:" ){
191  errorLog << "load(fstream &file) - Failed to read UseStdDev header!" << std::endl;
192  return false;
193  }
194  file >> useStdDev;
195 
196  //Load the UseEuclideanNorm
197  file >> word;
198  if( word != "UseEuclideanNorm:" ){
199  errorLog << "load(fstream &file) - Failed to read UseEuclideanNorm header!" << std::endl;
200  return false;
201  }
202  file >> useEuclideanNorm;
203 
204  //Load the UseRMS
205  file >> word;
206  if( word != "UseRMS:" ){
207  errorLog << "load(fstream &file) - Failed to read UseRMS header!" << std::endl;
208  return false;
209  }
210  file >> useRMS;
211 
212  //Init the TimeDomainFeatures module to ensure everything is initialized correctly
213  return init(bufferLength,numFrames,numInputDimensions,offsetInput,useMean,useStdDev,useEuclideanNorm,useRMS);
214 }
215 
216 bool TimeDomainFeatures::init(const UINT bufferLength, const UINT numFrames,const UINT numDimensions,const bool offsetInput,const bool useMean,const bool useStdDev,const bool useEuclideanNorm,const bool useRMS){
217 
218  initialized = false;
219 
220  if( numFrames > bufferLength ){
221  errorLog << "init(...) - The number of numFrames parameter can not be larger than the buffer length parameter!" << std::endl;
222  return false;
223  }
224  if( bufferLength % numFrames != 0 ){
225  errorLog << "init(...) - The buffer length parameter must be divisible with no remainders by the number of numFrames parameter!" << std::endl;
226  return false;
227  }
228 
229  this->bufferLength = bufferLength;
230  this->numFrames = numFrames;
231  this->numInputDimensions = numDimensions;
232  this->offsetInput = offsetInput;
233  this->useMean = useMean;
234  this->useStdDev = useStdDev;
235  this->useEuclideanNorm = useEuclideanNorm;
236  this->useRMS = useRMS;
237  featureDataReady = false;
238 
239  //Set the number of output dimensions
240  numOutputDimensions = 0;
241  if( useMean ){
242  numOutputDimensions += numInputDimensions*numFrames;
243  }
244  if( useStdDev ){
245  numOutputDimensions += numInputDimensions*numFrames;
246  }
247  if( useEuclideanNorm ){
248  numOutputDimensions += numInputDimensions*numFrames;
249  }
250  if( useRMS ){
251  numOutputDimensions += numInputDimensions*numFrames;
252  }
253  if( numOutputDimensions == 0 ){
254  errorLog << "init(...) - The numOutputDimensions is zero!" << std::endl;
255  return false;
256  }
257 
258  //Resize the feature vector
259  featureVector.resize(numOutputDimensions);
260 
261  //Resize the raw data buffer
262  dataBuffer.resize( bufferLength, VectorFloat(numInputDimensions,0) );
263 
264  //Flag that the time domain features has been initialized
265  initialized = true;
266 
267  return true;
268 }
269 
270 
272  return update(VectorFloat(1,x));
273 }
274 
276 
277  if( !initialized ){
278  errorLog << "update(const VectorFloat &x) - Not Initialized!" << std::endl;
279  return VectorFloat();
280  }
281 
282  if( x.getSize() != numInputDimensions ){
283  errorLog << "update(const VectorFloat &x)- The Number Of Input Dimensions (" << numInputDimensions << ") does not match the size of the input vector (" << x.getSize() << ")!" << std::endl;
284  return VectorFloat();
285  }
286 
287  //Add the new data to the data buffer
288  dataBuffer.push_back( x );
289 
290  //Only flag that the feature data is ready if the data is full
291  if( dataBuffer.getBufferFilled() ){
292  featureDataReady = true;
293  }else featureDataReady = false;
294 
295  MatrixFloat meanFeatures(numInputDimensions,numFrames);
296  MatrixFloat stdDevFeatures(numInputDimensions,numFrames);
297  MatrixFloat normFeatures(numInputDimensions,numFrames);
298  MatrixFloat rmsFeatures(numInputDimensions,numFrames);
299  MatrixFloat data(bufferLength,numInputDimensions);
300 
301  if( offsetInput ){
302  for(UINT n=0; n<numInputDimensions; n++){
303  data[0][n] = dataBuffer[0][n];
304  for(UINT i=1; i<bufferLength; i++){
305  data[i][n] = dataBuffer[i][n]-dataBuffer[0][n];
306  }
307  }
308  }else{
309  for(UINT n=0; n<numInputDimensions; n++){
310  for(UINT i=0; i<bufferLength; i++){
311  data[i][n] = dataBuffer[i][n];
312  }
313  }
314  }
315 
316  if( useMean || useStdDev ){ meanFeatures.setAllValues(0); stdDevFeatures.setAllValues(0); }
317  if( useEuclideanNorm ) normFeatures.setAllValues(0);
318  if( useRMS ) rmsFeatures.setAllValues(0);
319 
320  UINT frameSize = bufferLength / numFrames;
321  UINT frame = 0;
322  UINT index = 0;
323  for(UINT n=0; n<numInputDimensions; n++){
324  frame = 0;
325  index = 0;
326  for(UINT i=0; i<bufferLength; i++){
327  //Update the mean
328  meanFeatures[n][frame] += data[i][n];
329 
330  //Update the norm features
331  if( useEuclideanNorm )
332  normFeatures[n][frame] += data[i][n]*data[i][n];
333 
334  //Update the rms features
335  if( useRMS )
336  rmsFeatures[n][frame] += data[i][n]*data[i][n];
337 
338  if( ++index == frameSize ){
339  frame++;
340  index = 0;
341  }
342  }
343 
344  //Update the mean
345  for(UINT j=0; j<numFrames; j++){
346  meanFeatures[n][j] /= frameSize;
347  }
348 
349  //Update the std dev if needed
350  if( useStdDev ){
351  frame = 0;
352  index = 0;
353  for(UINT i=0; i<bufferLength; i++){
354  stdDevFeatures[n][frame] += (data[i][n]-meanFeatures[n][frame]) * (data[i][n]-meanFeatures[n][frame]);
355  if( ++index == frameSize ){
356  frame++;
357  index = 0;
358  }
359  }
360  Float norm = frameSize>1 ? frameSize-1 : 1;
361  for(UINT j=0; j<numFrames; j++){
362  stdDevFeatures[n][j] = sqrt( stdDevFeatures[n][j]/norm );
363  }
364  }
365 
366  //Update the euclidean norm if needed
367  if( useEuclideanNorm ){
368  for(UINT j=0; j<numFrames; j++){
369  normFeatures[n][j] = sqrt( normFeatures[n][j] );
370  }
371  }
372 
373  //Update the rms if needed
374  if( useRMS ){
375  for(UINT j=0; j<numFrames; j++){
376  rmsFeatures[n][j] = sqrt( rmsFeatures[n][j] / frameSize );
377  }
378  }
379  }
380 
381  //Update the features
382  index = 0;
383  frame = 0;
384  for(UINT n=0; n<numInputDimensions; n++){
385  for(UINT j=0; j<numFrames; j++){
386  if( useMean ){
387  featureVector[index++] = meanFeatures[n][j];
388  }
389  if( useStdDev ){
390  featureVector[index++] = stdDevFeatures[n][j];
391  }
392  if( useEuclideanNorm ){
393  featureVector[index++] = normFeatures[n][j];
394  }
395  if( useRMS ){
396  featureVector[index++] = rmsFeatures[n][j];
397  }
398  }
399  }
400 
401  return featureVector;
402 }
403 
405  return dataBuffer;
406 }
407 
408 GRT_END_NAMESPACE
bool push_back(const T &value)
std::string getId() const
Definition: GRTBase.cpp:85
virtual bool save(std::fstream &file) const
virtual bool load(std::fstream &file)
static std::string getId()
bool saveFeatureExtractionSettingsToFile(std::fstream &file) const
bool getBufferFilled() const
virtual bool resize(const unsigned int size)
Definition: Vector.h:133
virtual bool computeFeatures(const VectorFloat &inputVector)
UINT getSize() const
Definition: Vector.h:201
bool setAllValues(const T &value)
Definition: Matrix.h:366
const CircularBuffer< VectorFloat > & getBufferData() const
VectorFloat update(const Float x)
bool loadFeatureExtractionSettingsFromFile(std::fstream &file)
bool copyBaseVariables(const FeatureExtraction *featureExtractionModule)
This class implements the TimeDomainFeatures feature extraction module.
virtual bool deepCopyFrom(const FeatureExtraction *featureExtraction)
TimeDomainFeatures & operator=(const TimeDomainFeatures &rhs)
FeatureExtraction(const std::string id="")
bool resize(const unsigned int newBufferSize)