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