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.
FastFourierTransform.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 /*
22  This code is based on Dominic Mazzoni's c++ wrapper, which is based on a free implementation of an FFT
23  by Don Cross (http://www.intersrv.com/~dcross/fft.html) and the FFT algorithms from Numerical Recipes.
24  */
25 
26 #define GRT_DLL_EXPORTS
27 #include "FastFourierTransform.h"
28 
29 GRT_BEGIN_NAMESPACE
30 
31 FastFourierTransform::FastFourierTransform(){
32  initialized = false;
33  computeMagnitude = true;
34  computePhase = true;
35  enableZeroPadding = true;
36  windowSize = 0;
37  windowFunction = RECTANGULAR_WINDOW;
38  averagePower = 0;
39 
40  infoLog.setProceedingText("[FastFourierTransform]");
41  warningLog.setProceedingText("[WARNING FastFourierTransform]");
42  errorLog.setProceedingText("[ERROR FastFourierTransform]");
43 
44  initFFT();
45 }
46 
47 FastFourierTransform::FastFourierTransform(const FastFourierTransform &rhs){
48 
49  this->initialized = rhs.initialized;
50  this->computeMagnitude = rhs.computeMagnitude;
51  this->computePhase = rhs.computePhase;
52  this->enableZeroPadding = rhs.enableZeroPadding;
53  this->windowSize = rhs.windowSize;
54  this->windowFunction = rhs.windowFunction;
55  this->averagePower = 0;
56  this->initFFT();
57  this->infoLog = rhs.infoLog;
58  this->warningLog = rhs.warningLog;
59  this->errorLog = rhs.errorLog;
60 
61  if( rhs.initialized ){
62  this->init(rhs.windowSize,rhs.windowFunction,rhs.computeMagnitude,rhs.computePhase);
63 
64  //Copy the fft results
65  for(UINT i=0; i<this->windowSize; i++){
66  this->fftReal[i] = rhs.fftReal[i];
67  this->fftImag[i] = rhs.fftImag[i];
68  this->magnitude[i] = rhs.magnitude[i];
69  this->phase[i] = rhs.phase[i];
70  this->power[i] = rhs.power[i];
71  }
72  }
73 }
74 
75 FastFourierTransform::~FastFourierTransform() {
76 
77 }
78 
79 FastFourierTransform& FastFourierTransform::operator=(const FastFourierTransform &rhs){
80 
81  if( this != &rhs ){
82  this->initialized = rhs.initialized;
83  this->computeMagnitude = rhs.computeMagnitude;
84  this->computePhase = rhs.computePhase;
85  this->enableZeroPadding = rhs.enableZeroPadding;
86  this->windowSize = rhs.windowSize;
87  this->windowFunction = rhs.windowFunction;
88  this->averagePower = 0;
89  this->initFFT();
90 
91  if( rhs.initialized ){
92  this->init(rhs.windowSize,rhs.windowFunction,rhs.computeMagnitude,rhs.computePhase);
93 
94  //Copy the fft results
95  for(UINT i=0; i<this->windowSize; i++){
96  this->fftReal[i] = rhs.fftReal[i];
97  this->fftImag[i] = rhs.fftImag[i];
98  this->magnitude[i] = rhs.magnitude[i];
99  this->phase[i] = rhs.phase[i];
100  this->power[i] = rhs.power[i];
101  }
102  }
103  }
104  return *this;
105 }
106 
107 bool FastFourierTransform::init(const unsigned int windowSize,const unsigned int windowFunction,const bool computeMagnitude,const bool computePhase,const bool enableZeroPadding){
108 
109  initialized = false;
110  averagePower = 0;
111 
112  //Validate the window size
113  if( !isPowerOfTwo( windowSize ) ){
114  return false;
115  }
116 
117  if( windowFunction != RECTANGULAR_WINDOW && windowFunction != BARTLETT_WINDOW &&
118  windowFunction != HAMMING_WINDOW && windowFunction != HANNING_WINDOW ){
119  return false;
120  }
121 
122  initFFT();
123 
124  this->windowSize = windowSize;
125  this->windowFunction = windowFunction;
126  this->computeMagnitude = computeMagnitude;
127  this->computePhase = computePhase;
128  this->enableZeroPadding = enableZeroPadding;
129 
130  //Init the memory
131  fftReal.resize( windowSize );
132  fftImag.resize( windowSize );
133  tmpReal.resize( windowSize/2 );
134  tmpImag.resize( windowSize/2 );
135  magnitude.resize( windowSize );
136  phase.resize( windowSize );
137  power.resize( windowSize );
138  averagePower = 0;
139 
140  //Zero the memory
141  for(UINT i=0; i<windowSize/2; i++){
142  tmpReal[i] = 0;
143  tmpImag[i] = 0;
144  }
145  for(UINT i=0; i<windowSize; i++){
146  fftReal[i] = 0;
147  fftImag[i] = 0;
148  magnitude[i] = 0;
149  phase[i] = 0;
150  power[i] = 0;
151  }
152 
153  //Flag that the FFT has been initialized
154  initialized = true;
155 
156  return true;
157 }
158 
159 bool FastFourierTransform::computeFFT( VectorFloat &data ){
160 
161  if( !initialized ){
162  return false;
163  }
164 
165  //Validate the input Vector
166  if( !enableZeroPadding ){
167  if( (unsigned int)data.size() != windowSize ){
168  errorLog << "The size of the data Vector (" << data.size() << ") does not match the windowSize: " << windowSize << std::endl;
169  return false;
170  }
171  }else{
172  if( (unsigned int)data.size() > windowSize ){
173  errorLog << "The size of the data Vector (" << data.size() << ") is greater than the windowSize: " << windowSize << std::endl;
174  return false;
175  }
176  }
177 
178  //Window the input data
179  if( !windowData( data ) ){
180  return false;
181  }
182 
183  //Zero padd the data if needed
184  if( enableZeroPadding ){
185  if( ((unsigned int)data.size()) != windowSize ){
186  const unsigned int oldSize = (unsigned int)data.size();
187  data.resize( windowSize );
188  for(unsigned int i=oldSize; i<windowSize; i++){
189  data[i] = 0;
190  }
191  }
192  }
193 
194  //Perform the FFT
195  realFFT(data, &fftReal[0], &fftImag[0]);
196 
197  averagePower = 0;
198 
199  for(unsigned int i = 0; i<windowSize/2; i++){
200 
201  if( computeMagnitude ){
202  power[i] = fftReal[i]*fftReal[i] + fftImag[i]*fftImag[i];
203  averagePower += power[i];
204  magnitude[i] = 2.0*sqrt( power[i] );
205  }
206 
207  if( computePhase ){
208  phase[i] = atan2(fftImag[i],fftReal[i]);
209  }
210  }
211 
212  //Compute the average power
213  averagePower = averagePower / (Float)(windowSize/2);
214 
215  return true;
216 }
217 
218 bool FastFourierTransform::windowData( VectorFloat &data ){
219 
220  const unsigned int N = (unsigned int)data.size();
221  const unsigned int K = N/2;
222 
223  switch( windowFunction ){
224  case RECTANGULAR_WINDOW:
225  return true;
226  break;
227  case BARTLETT_WINDOW:
228  for(unsigned int i=0; i<K; i++) {
229  data[i] *= (i / (Float) (K));
230  data[i + K] *= (1.0 - (i / (Float)K));
231  }
232  return true;
233  break;
234  case HAMMING_WINDOW:
235  for(unsigned int i=0; i<N; i++)
236  data[i] *= 0.54 - 0.46 * cos(2 * PI * i / (N - 1));
237  return true;
238  break;
239  case HANNING_WINDOW:
240  for(unsigned int i=0; i <N; i++)
241  data[i] *= 0.50 - 0.50 * cos(2 * PI * i / (N - 1));
242  return true;
243  break;
244  default:
245  return false;
246  break;
247  }
248 
249  return false;
250 }
251 
252 VectorFloat FastFourierTransform::getMagnitudeData(){
253 
254  if( !initialized ) return VectorFloat();
255 
256  const unsigned int N = windowSize/2;
257  VectorFloat magnitudeData(N);
258 
259  for(unsigned int i=0; i<N; i++){
260  magnitudeData[i] = magnitude[i];
261  }
262 
263  return magnitudeData;
264 }
265 
266 VectorFloat FastFourierTransform::getPhaseData(){
267  if( !initialized ) return VectorFloat();
268 
269  const unsigned int N = windowSize/2;
270  VectorFloat phaseData(N);
271 
272  for(unsigned int i=0; i<N; i++){
273  phaseData[i] = phase[i];
274  }
275 
276  return phaseData;
277 }
278 
279 VectorFloat FastFourierTransform::getPowerData(){
280  if( !initialized ) return VectorFloat();
281 
282  VectorFloat powerData(windowSize/2);
283 
284  for(unsigned int i=0; i<windowSize/2; i++){
285  powerData[i] = power[i];
286  }
287 
288  return powerData;
289 }
290 
291 Float FastFourierTransform::getAveragePower(){
292  return averagePower;
293 }
294 
295 Float* FastFourierTransform::getMagnitudeDataPtr(){
296  return &magnitude[0];
297 }
298 
299 Float* FastFourierTransform::getPhaseDataPtr(){
300  return &phase[0];
301 }
302 
303 Float* FastFourierTransform::getPowerDataPtr(){
304  return &power[0];
305 }
306 
307 /*
308  * Real Fast Fourier Transform
309  *
310  * This function was based on the code in Numerical Recipes in C.
311  * In Num. Rec., the inner loop is based on a single 1-based array
312  * of interleaved real and imaginary numbers. Because we have two
313  * separate zero-based arrays, our indices are quite different.
314  * Here is the correspondence between Num. Rec. indices and our indices:
315  *
316  * i1 <-> real[i]
317  * i2 <-> imag[i]
318  * i3 <-> real[n/2-i]
319  * i4 <-> imag[n/2-i]
320  */
321 
322 bool FastFourierTransform::realFFT( const VectorFloat &realIn, Float *realOut, Float *imagOut ){
323  int NumSamples = (int)windowSize;
324  int Half = NumSamples / 2;
325  int i;
326 
327  Float theta = PI / Half;
328 
329  for (i = 0; i < Half; i++) {
330  tmpReal[i] = realIn[2 * i];
331  tmpImag[i] = realIn[2 * i + 1];
332  }
333 
334  if( !FFT(Half, 0, &tmpReal[0], &tmpImag[0], realOut, imagOut) ){
335  return false;
336  }
337 
338  Float wtemp = Float(sin(0.5 * theta));
339 
340  Float wpr = -2.0 * wtemp * wtemp;
341  Float wpi = Float (sin(theta));
342  Float wr = 1.0 + wpr;
343  Float wi = wpi;
344 
345  int i3;
346 
347  Float h1r, h1i, h2r, h2i;
348 
349  for (i = 1; i < Half / 2; i++) {
350 
351  i3 = Half - i;
352 
353  h1r = 0.5 * (realOut[i] + realOut[i3]);
354  h1i = 0.5 * (imagOut[i] - imagOut[i3]);
355  h2r = 0.5 * (imagOut[i] + imagOut[i3]);
356  h2i = -0.5 * (realOut[i] - realOut[i3]);
357 
358  realOut[i] = h1r + wr * h2r - wi * h2i;
359  imagOut[i] = h1i + wr * h2i + wi * h2r;
360  realOut[i3] = h1r - wr * h2r + wi * h2i;
361  imagOut[i3] = -h1i + wr * h2i + wi * h2r;
362 
363  wr = (wtemp = wr) * wpr - wi * wpi + wr;
364  wi = wi * wpr + wtemp * wpi + wi;
365  }
366 
367  realOut[0] = (h1r = realOut[0]) + imagOut[0];
368  imagOut[0] = h1r - imagOut[0];
369 
370  return true;
371 }
372 
373 bool FastFourierTransform::FFT(int numSamples,bool inverseTransform,Float *realIn, Float *imagIn, Float *realOut, Float *imagOut){
374  int NumBits; /* Number of bits needed to store indices */
375  int i, j, k, n;
376  int BlockSize, BlockEnd;
377 
378  Float angle_numerator = 2.0 * PI;
379  Float tr, ti; /* temp real, temp imaginary */
380 
381  if( !isPowerOfTwo(numSamples) ) {
382  fprintf(stderr, "%d is not a power of two\n", numSamples);
383  return false;
384  }
385 
386  if( bitTable.size() == 0 ) initFFT();
387 
388  if( inverseTransform ) angle_numerator = -angle_numerator;
389 
390  NumBits = numberOfBitsNeeded(numSamples);
391 
392  //Simultaneously data copy and bit-reversal ordering into outputs...
393  for(i = 0; i < numSamples; i++) {
394  j = fastReverseBits(i, NumBits);
395  realOut[j] = realIn[i];
396  imagOut[j] = (imagIn == NULL) ? 0.0 : imagIn[i];
397  }
398 
399  //Do the FFT
400  BlockEnd = 1;
401  for (BlockSize = 2; BlockSize <= numSamples; BlockSize <<= 1) {
402 
403  Float delta_angle = angle_numerator / (Float) BlockSize;
404 
405  Float sm2 = sin(-2 * delta_angle);
406  Float sm1 = sin(-delta_angle);
407  Float cm2 = cos(-2 * delta_angle);
408  Float cm1 = cos(-delta_angle);
409  Float w = 2 * cm1;
410  Float ar0, ar1, ar2, ai0, ai1, ai2;
411 
412  for (i = 0; i < numSamples; i += BlockSize) {
413  ar2 = cm2;
414  ar1 = cm1;
415 
416  ai2 = sm2;
417  ai1 = sm1;
418 
419  for (j = i, n = 0; n < BlockEnd; j++, n++) {
420  ar0 = w * ar1 - ar2;
421  ar2 = ar1;
422  ar1 = ar0;
423 
424  ai0 = w * ai1 - ai2;
425  ai2 = ai1;
426  ai1 = ai0;
427 
428  k = j + BlockEnd;
429  tr = ar0 * realOut[k] - ai0 * imagOut[k];
430  ti = ar0 * imagOut[k] + ai0 * realOut[k];
431 
432  realOut[k] = realOut[j] - tr;
433  imagOut[k] = imagOut[j] - ti;
434 
435  realOut[j] += tr;
436  imagOut[j] += ti;
437  }
438  }
439 
440  BlockEnd = BlockSize;
441  }
442 
443  //Need to normalize the results if we are computing the inverse transform
444  if( inverseTransform ){
445  Float denom = (Float) numSamples;
446 
447  for(i = 0; i < numSamples; i++) {
448  realOut[i] /= denom;
449  imagOut[i] /= denom;
450  }
451  }
452 
453  return true;
454 }
455 
456 int FastFourierTransform::numberOfBitsNeeded(int powerOfTwo)
457 {
458  for (int i = 0;; i++){
459  if (powerOfTwo & (1 << i)){
460  return i;
461  }
462  }
463  return 0;
464 }
465 
466 int FastFourierTransform::reverseBits(int index, int numBits)
467 {
468  int i, rev;
469 
470  for (i = rev = 0; i < numBits; i++) {
471  rev = (rev << 1) | (index & 1);
472  index >>= 1;
473  }
474 
475  return rev;
476 }
477 
478 void FastFourierTransform::initFFT()
479 {
480  bitTable.resize( MAX_FAST_BITS );
481 
482  int len = 2;
483  for (int b = 1; b <= MAX_FAST_BITS; b++) {
484 
485  bitTable[b - 1].resize(len);
486 
487  for (int i = 0; i < len; i++)
488  bitTable[b - 1][i] = reverseBits(i, b);
489 
490  len <<= 1;
491  }
492 }
493 
494 inline int FastFourierTransform::fastReverseBits(const int i, const int numBits)
495 {
496  if (numBits <= MAX_FAST_BITS)
497  return bitTable[numBits - 1][i];
498  else
499  return reverseBits(i, numBits);
500 }
501 
502 inline bool FastFourierTransform::isPowerOfTwo(const unsigned int x){
503  if (x < 2) return false;
504  if (x & (x - 1)) return false;
505  return true;
506 }
507 
508 GRT_END_NAMESPACE
509 
virtual bool resize(const unsigned int size)
Definition: Vector.h:133
Definition: FFT.h:56