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