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.
SavitzkyGolayFilter.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 #include "SavitzkyGolayFilter.h"
22 
23 GRT_BEGIN_NAMESPACE
24 
25 //Register the SavitzkyGolayFilter module with the PreProcessing base class
26 RegisterPreProcessingModule< SavitzkyGolayFilter > SavitzkyGolayFilter::registerModule("SavitzkyGolayFilter");
27 
28 SavitzkyGolayFilter::SavitzkyGolayFilter(UINT numLeftHandPoints,UINT numRightHandPoints,UINT derivativeOrder,UINT smoothingPolynomialOrder,UINT numDimensions){
29 
30  classType = "SavitzkyGolayFilter";
31  preProcessingType = classType;
32  debugLog.setProceedingText("[DEBUG SavitzkyGolayFilter]");
33  errorLog.setProceedingText("[ERROR SavitzkyGolayFilter]");
34  warningLog.setProceedingText("[WARNING SavitzkyGolayFilter]");
35  init(numLeftHandPoints,numRightHandPoints,derivativeOrder,smoothingPolynomialOrder,numDimensions);
36 }
37 
39 
40  this->numPoints = rhs.numPoints;
41  this->numLeftHandPoints = rhs.numLeftHandPoints;
42  this->numRightHandPoints = rhs.numRightHandPoints;
43  this->derivativeOrder = rhs.derivativeOrder;
44  this->smoothingPolynomialOrder = rhs.smoothingPolynomialOrder;
45  this->data = rhs.data;
46  this->yy = rhs.yy;
47  this->coeff = rhs.coeff;
48 
49  classType = "SavitzkyGolayFilter";
50  preProcessingType = classType;
51  debugLog.setProceedingText("[DEBUG SavitzkyGolayFilter]");
52  errorLog.setProceedingText("[ERROR SavitzkyGolayFilter]");
53  warningLog.setProceedingText("[WARNING SavitzkyGolayFilter]");
54 
56 }
57 
59 
60 }
61 
63  if(this!=&rhs){
64  this->numPoints = rhs.numPoints;
65  this->numLeftHandPoints = rhs.numLeftHandPoints;
66  this->numRightHandPoints = rhs.numRightHandPoints;
67  this->derivativeOrder = rhs.derivativeOrder;
68  this->smoothingPolynomialOrder = rhs.smoothingPolynomialOrder;
69  this->data = rhs.data;
70  this->yy = rhs.yy;
71  this->coeff = rhs.coeff;
73  }
74  return *this;
75 }
76 
78 
79  if( preProcessing == NULL ) return false;
80 
81  if( this->getPreProcessingType() == preProcessing->getPreProcessingType() ){
82 
83  SavitzkyGolayFilter *ptr = (SavitzkyGolayFilter*)preProcessing;
84 
85  //Clone the SavitzkyGolayFilter values
86  this->numPoints = ptr->numPoints;
87  this->numLeftHandPoints = ptr->numLeftHandPoints;
88  this->numRightHandPoints = ptr->numRightHandPoints;
89  this->derivativeOrder = ptr->derivativeOrder;
90  this->smoothingPolynomialOrder = ptr->smoothingPolynomialOrder;
91  this->data = ptr->data;
92  this->yy = ptr->yy;
93  this->coeff = ptr->coeff;
94 
95  //Clone the base class variables
96  return copyBaseVariables( preProcessing );
97  }
98 
99  errorLog << "clone(PreProcessing *preProcessing) - PreProcessing Types Do Not Match!" << std::endl;
100 
101  return false;
102 }
103 
104 bool SavitzkyGolayFilter::process(const VectorFloat &inputVector){
105 
106  if( !initialized ){
107  errorLog << "process(const VectorFloat &inputVector) - Not initialized!" << std::endl;
108  return false;
109  }
110 
111  if( inputVector.size() != numInputDimensions ){
112  errorLog << "process(const VectorFloat &inputVector) - The size of the inputVector (" << inputVector.size() << ") does not match that of the filter (" << numInputDimensions << ")!" << std::endl;
113  return false;
114  }
115 
116  processedData = filter( inputVector );
117 
118  if( processedData.size() == numOutputDimensions ) return true;
119  return false;
120 
121 }
122 
124  if( initialized ){
125  data.setAllValues(VectorFloat(numInputDimensions,0));
126  yy.clear();
127  yy.resize(numInputDimensions,0);
128  processedData.clear();
129  processedData.resize(numInputDimensions,0);
130  return true;
131  }
132  return false;
133 }
134 
135 bool SavitzkyGolayFilter::saveModelToFile( std::string filename ) const{
136 
137  if( !initialized ){
138  errorLog << "saveModelToFile(string filename) - The HighPassFilter has not been initialized" << std::endl;
139  return false;
140  }
141 
142  std::fstream file;
143  file.open(filename.c_str(), std::ios::out);
144 
145  if( !saveModelToFile( file ) ){
146  file.close();
147  return false;
148  }
149 
150  file.close();
151 
152  return true;
153 }
154 
155 bool SavitzkyGolayFilter::saveModelToFile(std::fstream &file) const{
156 
157  if( !file.is_open() ){
158  errorLog << "saveModelToFile(std::fstream &file) - The file is not open!" << std::endl;
159  return false;
160  }
161 
162  file << "GRT_SAVITZKY_GOLAY_FILTER_FILE_V1.0" << std::endl;
163 
164  file << "NumInputDimensions: " << numInputDimensions << std::endl;
165  file << "NumOutputDimensions: " << numOutputDimensions << std::endl;
166  file << "NumPoints: " << numPoints << std::endl;
167  file << "NumLeftHandPoints: " << numLeftHandPoints << std::endl;
168  file << "NumRightHandPoints: " << numRightHandPoints << std::endl;
169  file << "DerivativeOrder: " << derivativeOrder << std::endl;
170  file << "SmoothingPolynomialOrder: " << smoothingPolynomialOrder << std::endl;
171 
172  return true;
173 }
174 
175 bool SavitzkyGolayFilter::loadModelFromFile( std::string filename ){
176 
177  std::fstream file;
178  file.open(filename.c_str(), std::ios::in);
179 
180  if( !loadModelFromFile( file ) ){
181  file.close();
182  initialized = false;
183  return false;
184  }
185 
186  file.close();
187 
188  return true;
189 }
190 
191 bool SavitzkyGolayFilter::loadModelFromFile(std::fstream &file){
192 
193  if( !file.is_open() ){
194  errorLog << "loadModelFromFile(std::fstream &file) - The file is not open!" << std::endl;
195  return false;
196  }
197 
198  std::string word;
199 
200  //Load the header
201  file >> word;
202 
203  if( word != "GRT_SAVITZKY_GOLAY_FILTER_FILE_V1.0" ){
204  errorLog << "loadModelFromFile(std::fstream &file) - Invalid file format!" << std::endl;
205  return false;
206  }
207 
208  //Load the number of input dimensions
209  file >> word;
210  if( word != "NumInputDimensions:" ){
211  errorLog << "loadModelFromFile(std::fstream &file) - Failed to read NumInputDimensions header!" << std::endl;
212  return false;
213  }
214  file >> numInputDimensions;
215 
216  //Load the number of output dimensions
217  file >> word;
218  if( word != "NumOutputDimensions:" ){
219  errorLog << "loadModelFromFile(std::fstream &file) - Failed to read NumOutputDimensions header!" << std::endl;
220  return false;
221  }
222  file >> numOutputDimensions;
223 
224  //Load the numPoints
225  file >> word;
226  if( word != "NumPoints:" ){
227  errorLog << "loadModelFromFile(std::fstream &file) - Failed to read NumPoints header!" << std::endl;
228  return false;
229  }
230  file >> numPoints;
231 
232  //Load the NumLeftHandPoints
233  file >> word;
234  if( word != "NumLeftHandPoints:" ){
235  errorLog << "loadModelFromFile(std::fstream &file) - Failed to read NumLeftHandPoints header!" << std::endl;
236  return false;
237  }
238  file >> numLeftHandPoints;
239 
240  //Load the NumRightHandPoints
241  file >> word;
242  if( word != "NumRightHandPoints:" ){
243  errorLog << "loadModelFromFile(std::fstream &file) - Failed to read numRightHandPoints header!" << std::endl;
244  return false;
245  }
246  file >> numRightHandPoints;
247 
248  //Load the DerivativeOrder
249  file >> word;
250  if( word != "DerivativeOrder:" ){
251  errorLog << "loadModelFromFile(std::fstream &file) - Failed to read DerivativeOrder header!" << std::endl;
252  return false;
253  }
254  file >> derivativeOrder;
255 
256  //Load the SmoothingPolynomialOrder
257  file >> word;
258  if( word != "SmoothingPolynomialOrder:" ){
259  errorLog << "loadModelFromFile(std::fstream &file) - Failed to read SmoothingPolynomialOrder header!" << std::endl;
260  return false;
261  }
262  file >> smoothingPolynomialOrder;
263 
264  //Init the filter module to ensure everything is initialized correctly
265  return init(numLeftHandPoints,numRightHandPoints,derivativeOrder,smoothingPolynomialOrder,numInputDimensions);
266 }
267 
268 bool SavitzkyGolayFilter::init(UINT numLeftHandPoints,UINT numRightHandPoints,UINT derivativeOrder,UINT smoothingPolynomialOrder,UINT numDimensions){
269 
270  initialized = false;
271 
272  if( numDimensions == 0 ){
273  errorLog << "init(Float filterFactor,Float gain,UINT numDimensions) - NumDimensions must be greater than 0!" << std::endl;
274  return false;
275  }
276 
277  this->numPoints = numLeftHandPoints+numRightHandPoints+1;
278  this->numLeftHandPoints = numLeftHandPoints;
279  this->numRightHandPoints = numRightHandPoints;
280  this->derivativeOrder = derivativeOrder;
281  this->smoothingPolynomialOrder = smoothingPolynomialOrder;
282  coeff.resize(numPoints);
283  this->numInputDimensions = numDimensions;
284  this->numOutputDimensions = numDimensions;
285  yy.clear();
286  yy.resize(numDimensions,0);
287  processedData.clear();
288  processedData.resize(numDimensions,0);
289  data.resize(numPoints,VectorFloat(numDimensions,0));
290 
291  if( !calCoeff() ){
292  errorLog << "init(UINT NL,UINT NR,UINT LD,UINT M,UINT numDimensions) - Failed to compute filter coefficents!" << std::endl;
293  return false;
294  }
295 
296  initialized = true;
297 
298  return true;
299 }
300 
301 Float SavitzkyGolayFilter::filter(const Float x){
302 
303  //If the filter has not been initialised then return 0, otherwise filter x and return y
304  if( !initialized ){
305  errorLog << "filter(Float x) - The filter has not been initialized!" << std::endl;
306  return 0;
307  }
308 
309  VectorFloat y = filter(VectorFloat(1,x));
310 
311  if( y.size() > 0 ) return y[0];
312  return 0;
313 }
314 
316 
317  if( !initialized ){
318  errorLog << "filter(const VectorFloat &x) - Not Initialized!" << std::endl;
319  return VectorFloat();
320  }
321 
322  if( x.size() != numInputDimensions ){
323  errorLog << "filter(const VectorFloat &x) - The Number Of Input Dimensions (" << numInputDimensions << ") does not match the size of the input vector (" << x.size() << ")!" << std::endl;
324  return VectorFloat();
325  }
326 
327  //Add the new input data to the data buffer
328  data.push_back( x );
329 
330  //Filter the data
331  for(UINT j=0; j<x.size(); j++){
332  processedData[j] = 0;
333  for(UINT i=0; i<numPoints; i++)
334  processedData[j] += data[i][j] * coeff[i];
335  }
336 
337  return processedData;
338 }
339 
340 bool SavitzkyGolayFilter::calCoeff(){
341 
342  int np = (int)numPoints;
343  int nl = (int)numLeftHandPoints;
344  int nr = (int)numRightHandPoints;
345  int ld = (int)derivativeOrder;
346  int m = (int)smoothingPolynomialOrder;
347  int i,j,k,imj,ipj,kk,mm,pos;
348  Float fac,sum;
349  VectorFloat indx(m+1);
350  MatrixDouble a(m+1,m+1);
351  VectorFloat b(m+1);
352  VectorFloat c(np);
353 
354  for (ipj=0; ipj<=(m << 1); ipj++) {
355  sum=(ipj ? 0.0 : 1.0);
356 
357  for (k=1; k<=nr; k++) sum += pow(Float(k),Float(ipj));
358  for (k=1; k<=nl; k++) sum += pow(Float(-k),Float(ipj));
359 
360  mm = min_(ipj,2*m-ipj);
361 
362  for (imj = -mm; imj<=mm; imj+=2) a[(ipj+imj)/2][(ipj-imj)/2] = sum;
363  }
364 
365  LUDecomposition alud(a);
366  for (j=0;j<m+1;j++) b[j]=0.0;
367  b[ld]=1.0;
368  if( !alud.solve_vector(b,b) ){
369  return false;
370  }
371 
372  for (kk=0; kk<np; kk++) c[kk]=0.0;
373  for (k = -nl; k<=nr; k++) {
374  sum=b[0];
375  fac=1.0;
376 
377  for(mm=1; mm<=m; mm++)
378  sum += b[mm]*(fac *= k);
379 
380  kk=(np-k) % np;
381  c[kk]=sum;
382  }
383 
384  //Reorder coefficients and place them in coeff
385  //Reorder last=0 future = np-1
386  pos = nl;
387  for(i=0; i<np; i++){
388  coeff[i] = c[pos--];
389  if(pos==0)pos=np-1;
390  }
391  return true;
392 }
393 
394 GRT_END_NAMESPACE
bool push_back(const T &value)
virtual bool loadModelFromFile(std::string filename)
Float filter(const Float x)
This implements a Savitzky-Golay filter. This code is based on the Savitzky Golay filter code from Nu...
virtual bool resize(const unsigned int size)
Definition: Vector.h:133
virtual bool saveModelToFile(std::string filename) const
SavitzkyGolayFilter(UINT numLeftHandPoints=10, UINT numRightHandPoints=10, UINT derivativeOrder=0, UINT smoothingPolynomialOrder=2, UINT numDimensions=1)
std::string getPreProcessingType() const
virtual bool deepCopyFrom(const PreProcessing *preProcessing)
SavitzkyGolayFilter & operator=(const SavitzkyGolayFilter &rhs)
bool copyBaseVariables(const PreProcessing *preProcessingModule)
bool setAllValues(const T &value)
virtual bool process(const VectorFloat &inputVector)
bool resize(const unsigned int newBufferSize)