/Users/lyon/j4p/src/math/transforms/FFT.java

1    /*************************************************************************** 
2    // @(#)vsFFT.java 
3    // 
4    // Perform a non recursive in place FFT. 
5    // 
6    // @version 1.0, 1 JULY 1997 
7    // @author  D. Lyon, V. Silva 
8     ****************************************************************************/ 
9    package math.transforms; 
10    
11   import ip.vs.ColorUtils; 
12   import ip.vs.ImageUtils; 
13   import math.Mat2; 
14   import utils.Timer; 
15    
16    
17   public class FFT { 
18     private final int FORWARD_FFT = -1; 
19     private final int REVERSE_FFT = 1; 
20     private float direction = (float) FORWARD_FFT; 
21    
22     static final float twoPI = (float) (2 * Math.PI); 
23     private int N; 
24     private int numBits; 
25     private int width, height; 
26     private float minPSD = 9999999; 
27     private float maxPSD = -9999999; 
28    
29     ColorUtils CU = new ColorUtils(); 
30     ImageUtils iUtils = new ImageUtils(); 
31     public boolean DisplayLogPSD = false; 
32    
33     // These arrays hold the complex image data. 
34     public float cR_r[][]; // Red real 
35     public float cG_r[][]; // Green real 
36     public float cB_r[][]; // Blue real 
37     public float cR_i[][]; // Red imaginary 
38     public float cG_i[][]; // Green imaginary 
39     public float cB_i[][]; // Blue imaginary 
40    
41     // These arrays hold the complex 1D FFT data. 
42     // These will hold either the full 1D FFT data, or the 
43     // data from performing an FFT on one image row. 
44     float r_data[] = null; 
45     float i_data[] = null; 
46    
47     public void printStats() { 
48       Mat2.printStats("cR_r", cR_r); 
49       Mat2.printStats("cG_r", cG_r); 
50       Mat2.printStats("cB_r", cB_r); 
51       Mat2.printStats("cR_i", cR_i); 
52       Mat2.printStats("cG_i", cG_i); 
53       Mat2.printStats("cB_i", cB_i); 
54    
55     } 
56    
57     public void normalize() { 
58       float sf = 1.0f / (cR_r.length * cR_r[0].length); 
59       Mat2.scale(cR_r, sf); 
60       Mat2.scale(cG_r, sf); 
61       Mat2.scale(cB_r, sf); 
62       Mat2.scale(cR_i, sf); 
63       Mat2.scale(cG_i, sf); 
64       Mat2.scale(cB_i, sf); 
65     } 
66    
67     public void complexMult(FFT fft) { 
68       for (int x = 0; x < cR_r.length; x++) 
69         for (int y = 0; y < cR_r[0].length; y++) { 
70           cR_r[x][y] = cR_r[x][y] * fft.cR_r[x][y]; 
71           cG_r[x][y] = cG_r[x][y] * fft.cG_r[x][y]; 
72           cB_r[x][y] = cB_r[x][y] * fft.cB_r[x][y]; 
73    
74           cR_i[x][y] = cR_i[x][y] * fft.cR_i[x][y]; 
75           cG_i[x][y] = cG_i[x][y] * fft.cG_i[x][y]; 
76           cB_i[x][y] = cB_i[x][y] * fft.cB_i[x][y]; 
77         } 
78    
79     } 
80    
81     private void init() { 
82       minPSD = 9999999; 
83       maxPSD = -9999999; 
84     } 
85    
86    
87     public int[] getPsd() { 
88       //------------------------------------------------------------- 
89       // Take PSD on complex RGB values. 
90       //------------------------------------------------------------- 
91       // Magnitude of result. 
92       float[] magnitudeR = magnitudeSpectrum(cR_r, cR_i); 
93       float[] magnitudeG = magnitudeSpectrum(cG_r, cG_i); 
94       float[] magnitudeB = magnitudeSpectrum(cB_r, cB_i); 
95    
96       double scaleFactor = 100; //255.0/Math.log(256); 
97    
98    
99       System.out.println("Max psd = " + maxPSD); 
100   
101      scaleFactor = 255.0 / (Math.log(1 + maxPSD)); 
102      //System.out.println("Scalefactor = "+scaleFactor); 
103   
104   
105   
106      // Adjust 2-D FFT data so that the minimum PSD is 
107      // based at a value close to a black pixel. 
108   
109      for (int i = 0; i < N; i++) { 
110        magnitudeR[i] = (float) 
111            (scaleFactor * Math.log(1 + magnitudeR[i])); 
112        magnitudeG[i] = (float) 
113            (scaleFactor * Math.log(1 + magnitudeG[i])); 
114        magnitudeB[i] = (float) 
115            (scaleFactor * Math.log(1 + magnitudeB[i])); 
116      } 
117   
118      //System.out.println("Minimum PSD value: " + minPSD); 
119   
120      // Convert to single ARGB int array and return. 
121      return (CU.imagetoInt(magnitudeR, magnitudeG, magnitudeB)); 
122    } 
123   
124    public int[] getPhaseImage() { 
125      //------------------------------------------------------------- 
126      // Take PSD on complex RGB values. 
127      //------------------------------------------------------------- 
128      // Magnitude of result. 
129      float[] magnitudeR = getPhaseImage(cR_r, cR_i); 
130      float[] magnitudeG = getPhaseImage(cG_r, cG_i); 
131      float[] magnitudeB = getPhaseImage(cB_r, cB_i); 
132   
133      double scaleFactor = 100; //255.0/Math.log(256); 
134   
135   
136      System.out.println("Max psd = " + maxPSD); 
137   
138      //scaleFactor = 255.0/(Math.log(1+maxPSD)); 
139      scaleFactor = 255; 
140      System.out.println("Scalefactor = " + scaleFactor); 
141   
142   
143   
144      // Adjust 2-D FFT data so that the minimum PSD is 
145      // based at a value close to a black pixel. 
146   
147      for (int i = 0; i < N; i++) { 
148        magnitudeR[i] = (float) 
149            (scaleFactor * Math.log(1 + magnitudeR[i])); 
150        magnitudeG[i] = (float) 
151            (scaleFactor * Math.log(1 + magnitudeG[i])); 
152        magnitudeB[i] = (float) 
153            (scaleFactor * Math.log(1 + magnitudeB[i])); 
154      } 
155   
156      System.out.println("Minimum PSD value: " + minPSD); 
157   
158      // Convert to single ARGB int array and return. 
159      return (CU.imagetoInt(magnitudeR, magnitudeG, magnitudeB)); 
160    } 
161   
162    public int[] forward2dFFT(float[] imageData_R, float[] imageData_G, 
163                              float[] imageData_B, int imageWidth, int imageHeight) { 
164      init(); 
165   
166      // save image size to locals. 
167      width = imageWidth; 
168      height = imageHeight; 
169   
170      // We know the size of the image now, allocate 
171      // the required arrays to hold the complex image data. 
172      cR_r = new float[height][width]; 
173      cR_i = new float[height][width]; 
174      cG_r = new float[height][width]; 
175      cG_i = new float[height][width]; 
176      cB_r = new float[height][width]; 
177      cB_i = new float[height][width]; 
178   
179      // Total number of pixels. 
180      N = width * height; 
181   
182      // Number of bits in one direction, i.e. 256x256 image m = 8 
183      // assuming image is square, which has already been checked. 
184      numBits = log2(width); 
185   
186      //------------------------------------------------------------- 
187      // This section performs a the first forward FFT on RGB values. 
188      //------------------------------------------------------------- 
189      //vsTimer t1 = new vsTimer(); 
190      //t1.clear(); 
191      //t1.mark(); 
192   
193      // FFT on RGB values 1st set. 
194      // NOTE!! cX_r and cX_i are modified, they are used to hold 
195      // the returned FFT complex data. 
196      // RED 
197      copyFloatToComplex(cR_r, cR_i, imageData_R); 
198      copyFloatToComplex(cG_r, cG_i, imageData_G); 
199      copyFloatToComplex(cB_r, cB_i, imageData_B); 
200      // Do FFT by row. 
201      for (int i = 0; i < height; i++) { 
202        // Red 
203        forwardFFT(cR_r[i], cR_i[i]); 
204        // Green 
205        forwardFFT(cG_r[i], cG_i[i]); 
206        // Blue 
207        forwardFFT(cB_r[i], cB_i[i]); 
208      } 
209   
210      //System.out.println("Triple FFT on rows:"); 
211      // Stop the timer and report. 
212      //t1.record(); 
213      //t1.report(); 
214   
215      //------------------------------------------------------------- 
216      // This section rotates complex RGB arrays CW 90 degrees. 
217      //------------------------------------------------------------- 
218      //t1.clear(); 
219      //t1.mark(); 
220      // Rotate array 90 degrees, returns a reference to a 
221      // a new array, array that is passed in is lost. 
222      cR_r = Rotate90(cR_r); 
223      cR_i = Rotate90(cR_i); 
224      cG_r = Rotate90(cG_r); 
225      cG_i = Rotate90(cG_i); 
226      cB_r = Rotate90(cB_r); 
227      cB_i = Rotate90(cB_i); 
228   
229      //System.out.println("Rotate 90 degrees CW:"); 
230      // Stop the timer and report. 
231      //t1.record(); 
232      //t1.report(); 
233   
234      //------------------------------------------------------------- 
235      // This section performs a the second forward FFT on RGB values. 
236      //------------------------------------------------------------- 
237      //t1.clear(); 
238      //t1.mark(); 
239   
240      // FFT on complex data from 1st FFT. 
241      // NOTE!! cX_r and cX_i that are passed in are destroyed. 
242      // They are used to hold the returned FFT complex data. 
243      // Do FFT by row. 
244      for (int i = 0; i < height; i++) { 
245        // Red 
246        forwardFFT(cR_r[i], cR_i[i]); 
247        // Green 
248        forwardFFT(cG_r[i], cG_i[i]); 
249        // Blue 
250        forwardFFT(cB_r[i], cB_i[i]); 
251      } 
252   
253      ///System.out.println("Triple FFT on columns:"); 
254      // Stop the timer and report. 
255      //t1.record(); 
256      //t1.report(); 
257      return getPsd(); 
258   
259    } 
260   
261    public int[] forward2dFFT(short[] imageData_R, short[] imageData_G, 
262                              short[] imageData_B, int imageWidth, int imageHeight) { 
263      init(); 
264   
265      // save image size to locals. 
266      width = imageWidth; 
267      height = imageHeight; 
268   
269      // We know the size of the image now, allocate 
270      // the required arrays to hold the complex image data. 
271      cR_r = new float[height][width]; 
272      cR_i = new float[height][width]; 
273      cG_r = new float[height][width]; 
274      cG_i = new float[height][width]; 
275      cB_r = new float[height][width]; 
276      cB_i = new float[height][width]; 
277   
278      // Total number of pixels. 
279      N = width * height; 
280   
281      // Number of bits in one direction, i.e. 256x256 image m = 8 
282      // assuming image is square, which has already been checked. 
283      numBits = log2(width); 
284   
285      //------------------------------------------------------------- 
286      // This section performs a the first forward FFT on RGB values. 
287      //------------------------------------------------------------- 
288      //vsTimer t1 = new vsTimer(); 
289      //t1.clear(); 
290      //t1.mark(); 
291   
292      // FFT on RGB values 1st set. 
293      // NOTE!! cX_r and cX_i are modified, they are used to hold 
294      // the returned FFT complex data. 
295      // RED 
296      copyShortToComplex(cR_r, cR_i, imageData_R); 
297      copyShortToComplex(cG_r, cG_i, imageData_G); 
298      copyShortToComplex(cB_r, cB_i, imageData_B); 
299   
300      // Do FFT by row. 
301      for (int i = 0; i < height; i++) { 
302        // Red 
303        forwardFFT(cR_r[i], cR_i[i]); 
304        // Green 
305        forwardFFT(cG_r[i], cG_i[i]); 
306        // Blue 
307        forwardFFT(cB_r[i], cB_i[i]); 
308      } 
309   
310      //System.out.println("Triple FFT on rows:"); 
311      // Stop the timer and report. 
312      //t1.record(); 
313      //t1.report(); 
314   
315      //------------------------------------------------------------- 
316      // This section rotates complex RGB arrays CW 90 degrees. 
317      //------------------------------------------------------------- 
318      //t1.clear(); 
319      //t1.mark(); 
320      // Rotate array 90 degrees, returns a reference to a 
321      // a new array, array that is passed in is lost. 
322      cR_r = Rotate90(cR_r); 
323      cR_i = Rotate90(cR_i); 
324      cG_r = Rotate90(cG_r); 
325      cG_i = Rotate90(cG_i); 
326      cB_r = Rotate90(cB_r); 
327      cB_i = Rotate90(cB_i); 
328   
329      //System.out.println("Rotate 90 degrees CW:"); 
330      // Stop the timer and report. 
331      //t1.record(); 
332      //t1.report(); 
333   
334      //------------------------------------------------------------- 
335      // This section performs a the second forward FFT on RGB values. 
336      //------------------------------------------------------------- 
337      //t1.clear(); 
338      //t1.mark(); 
339   
340      // FFT on complex data from 1st FFT. 
341      // NOTE!! cX_r and cX_i that are passed in are destroyed. 
342      // They are used to hold the returned FFT complex data. 
343      // Do FFT by row. 
344      for (int i = 0; i < height; i++) { 
345        // Red 
346        forwardFFT(cR_r[i], cR_i[i]); 
347        // Green 
348        forwardFFT(cG_r[i], cG_i[i]); 
349        // Blue 
350        forwardFFT(cB_r[i], cB_i[i]); 
351      } 
352   
353      //System.out.println("Triple FFT on columns:"); 
354      // Stop the timer and report. 
355      //t1.record(); 
356      //t1.report(); 
357      return getPsd(); 
358    } 
359   
360    int log2(double n) { 
361      return (int) (Math.log(n) / Math.log(2)); 
362    } 
363   
364    public int[] reverse2dFFT() { 
365      init(); 
366   
367      // Total number of pixels. 
368      N = width * height; 
369   
370      // Number of bits in one direction, i.e. 256x256 image m = 8 
371      // assuming image is square, which has already been checked. 
372      numBits = log2(width); 
373   
374      //------------------------------------------------------------- 
375      // This section performs the first reverse FFT on 
376      // the complex RGB values obtained from a forward FFT 
377      // on an image. 
378      //------------------------------------------------------------- 
379      //vsTimer t1 = new vsTimer(); 
380      //t1.clear(); 
381      //t1.mark(); 
382   
383      // Do IFFT by row. 
384      for (int i = 0; i < height; i++) { 
385        // Red 
386        reverseFFT(cR_r[i], cR_i[i]); 
387        // Green 
388        reverseFFT(cG_r[i], cG_i[i]); 
389        // Blue 
390        reverseFFT(cB_r[i], cB_i[i]); 
391      } 
392   
393      //System.out.println("Triple IFFT on rows:"); 
394      // Stop the timer and report. 
395      //t1.record(); 
396      //t1.report(); 
397   
398      //------------------------------------------------------------- 
399      // This section performs a rotate CW 90 degrees to 
400      // prepare the complex data for the second reverse FFT. 
401      //------------------------------------------------------------- 
402      //t1.clear(); 
403      //t1.mark(); 
404      // Rotate array 90 degrees, returns a reference to a 
405      // a new array, array that is passed in is lost. 
406      cR_r = Rotate90(cR_r); 
407      cR_i = Rotate90(cR_i); 
408      cG_r = Rotate90(cG_r); 
409      cG_i = Rotate90(cG_i); 
410      cB_r = Rotate90(cB_r); 
411      cB_i = Rotate90(cB_i); 
412   
413      //System.out.println("Rotate 90 degrees CW:"); 
414      // Stop the timer and report. 
415      //t1.record(); 
416      //t1.report(); 
417   
418      //------------------------------------------------------------- 
419      // This section performs the second reverse FFT on 
420      // the complex RGB values obtained from a forward FFT 
421      // on an image. 
422      //------------------------------------------------------------- 
423      //t1.clear(); 
424      //t1.mark(); 
425   
426      // IFFT on complex data from 1st IFFT. 
427      // NOTE!! cX_r and cX_i that are passed in are destroyed. 
428      // They are used to hold the returned FFT complex data. 
429      // Do FFT by row. 
430      for (int i = 0; i < height; i++) { 
431        // Red 
432        reverseFFT(cR_r[i], cR_i[i]); 
433        // Green 
434        reverseFFT(cG_r[i], cG_i[i]); 
435        // Blue 
436        reverseFFT(cB_r[i], cB_i[i]); 
437      } 
438   
439      //System.out.println("Triple IFFT on columns:"); 
440      // Stop the timer and report. 
441      //t1.record(); 
442      //t1.report(); 
443   
444   
445      //------------------------------------------------------------- 
446      // We now rotate the image 180 degrees otherwise it would be 
447      // upside down.  This is a due to the process we used to 
448      // apply the FFT to columns (i.e. we rotated 90 degrees twice). 
449      //------------------------------------------------------------- 
450      rotateInPlace180(cR_r); 
451      rotateInPlace180(cR_i); 
452      rotateInPlace180(cG_r); 
453      rotateInPlace180(cG_i); 
454      rotateInPlace180(cB_r); 
455      rotateInPlace180(cB_i); 
456   
457      //------------------------------------------------------------- 
458      // Take PSD - power spectral density giving us original 
459      // RGB values back. 
460      //------------------------------------------------------------- 
461      float[] realR = magnitudeSpectrum(cR_r, cR_i); 
462      float[] realG = magnitudeSpectrum(cG_r, cG_i); 
463      float[] realB = magnitudeSpectrum(cB_r, cB_i); 
464   
465      // Convert to single ARGB int array and return. 
466      return (CU.imagetoInt(realR, realG, realB)); 
467    } 
468   
469   
470    /** 
471     * A way to visually test the 1D FFT on a small amount of data. 
472     */ 
473    public void TestFFT() { 
474      init(); 
475      System.out.println("Testing FFT engine in 2D class..."); 
476   
477      numBits = 3; 
478      N = 8; 
479   
480      float[] in_r = new float[N]; 
481      float[] in_i = new float[N]; 
482   
483      float[] holder_r = new float[N]; 
484      float[] holder_i = new float[N]; 
485   
486      // Create a test ramp signal. 
487      for (int i = 0; i < N; i++) { 
488        in_r[i] = (float) i / N; 
489        in_i[i] = (float) 0; 
490      } 
491   
492      forwardFFT(in_r, in_i); 
493   
494      // Copy to new array because IFFT will destroy the FFT results. 
495      for (int i = 0; i < N; i++) { 
496        holder_r[i] = in_r[i]; 
497        holder_i[i] = in_i[i]; 
498      } 
499   
500      for (int i = 0; i < N; i++) { 
501        in_r[i] = in_r[i]; 
502        in_i[i] = in_i[i]; 
503      } 
504   
505      reverseFFT(in_r, in_i); 
506      float[] mag = magnitudeSpectrum(in_r, in_i); 
507   
508      System.out.println("i    x1[i]    re[i]             im[i]           tv[i]"); 
509      for (int i = 0; i < N; i++) { 
510        System.out.println(i + "\t" + i + "\t" + holder_r[i] + "\t\t" + 
511                           holder_i[i] + "\t\t" + mag[i]); 
512      } 
513   
514      // Time a 256K FFT 
515      in_r = new float[262144]; 
516      in_i = new float[262144]; 
517   
518      // Create a test ramp signal. 
519      N = 262144; 
520      numBits = 18; 
521   
522      for (int i = 0; i < N; i++) { 
523        in_r[i] = (float) i / N; 
524        in_i[i] = (float) 0; 
525      } 
526   
527      Timer t1 = new Timer(); 
528      t1.clear(); 
529      t1.start(); 
530   
531      forwardFFT(in_r, in_i); 
532   
533      System.out.println("256K point 1D FFT (using float):"); 
534      // Stop the timer and report. 
535      t1.stop(); 
536      t1.report(); 
537   
538    } 
539   
540    /** 
541     * 1D FFT utility functions. 
542     */ 
543    public void swap(int i) { 
544      float tempr; 
545      int j = bitr(i); 
546      String js = Integer.toBinaryString(j); 
547      String is = Integer.toBinaryString(i); 
548   
549      // System.out.println("swap "+is+","+js); 
550      // System.out.println(Integer.toBinaryString(i)+"bitr="+Integer.toBinaryString(bitr(i))); 
551      tempr = r_data[j]; 
552      r_data[j] = r_data[i]; 
553      r_data[i] = tempr; 
554      tempr = i_data[j]; 
555      i_data[j] = i_data[i]; 
556      i_data[i] = tempr; 
557    } 
558   
559    // swap Zi with Zj 
560    public static void swapInt(float[] i_data1, float[] r_data1, int i, int j) { 
561      float tempr; 
562      int ti; 
563      int tj; 
564      ti = i - 1; 
565      tj = j - 1; 
566      tempr = r_data1[tj]; 
567      r_data1[tj] = r_data1[ti]; 
568      r_data1[ti] = tempr; 
569      tempr = i_data1[tj]; 
570      i_data1[tj] = i_data1[ti]; 
571      i_data1[ti] = tempr; 
572    } 
573   
574    double getMaxValue(double in[]) { 
575      double max; 
576      max = -0.99e30; 
577   
578      for (int i = 0; i < in.length; i++) 
579        if (in[i] > max) 
580          max = in[i]; 
581      return max; 
582   
583    } 
584   
585    void bitReverse2() { 
586      /* bit reversal */ 
587      int n = r_data.length; 
588      int j = 1; 
589      int k; 
590   
591      for (int i = 1; i < n; i++) { 
592        if (i < j) { 
593          swapInt(i_data, r_data, i, j); 
594        } //if 
595   
596        k = n / 2; 
597        while (k >= 1 && k < j) { 
598          j = j - k; 
599          k = k / 2; 
600        } 
601        j = j + k; 
602   
603      } // for 
604    } 
605   
606    void bitReverse() { 
607      /* bit reversal */ 
608      int n = r_data.length; 
609      int j = 1; 
610      int k; 
611   
612      for (int i = 1; i < n; i++) { 
613        if (i < j) { 
614          // this does not work... 
615          // why? 
616          swap(i); 
617        } //if 
618        j = bitr(i); 
619   
620      } // for 
621    } 
622   
623   
624    int bitr(int j) { 
625      int ans = 0; 
626      for (int i = 0; i < numBits; i++) { 
627        ans = (ans << 1) + (j & 1); 
628        j = j >> 1; 
629      } 
630      return ans; 
631    } 
632   
633    public void forwardFFT(float[] in_r, float[] in_i) { 
634      direction = FORWARD_FFT; 
635      fft(in_r, in_i); 
636    } 
637   
638    public void reverseFFT(float[] in_r, float[] in_i) { 
639      direction = REVERSE_FFT; 
640      fft(in_r, in_i); 
641    } 
642   
643    /** 
644     * FFT engine. 
645     * 1D Complex fft with floats. 
646     * It is Radix 2. 
647     * Therefore, the size must be an integral 
648     * power of 2, 2*4, 32,64, 128,256,512,1024 
649     */ 
650    public void fft(float in_r[], float in_i[]) { 
651      int id; 
652      // radix 2 number if sample, 1D of course. 
653      int localN; 
654      float wtemp, Wjk_r, Wjk_i, Wj_r, Wj_i; 
655      float theta, tempr, tempi; 
656      int ti, tj; 
657   
658      // Truncate input data to a power of two 
659      int length = 1 << numBits; // length = 2**nu 
660      int n = length; 
661   
662      // Copy passed references to variables to be used within 
663      // transforms.fft routines & utilities 
664      r_data = in_r; 
665      i_data = in_i; 
666   
667      bitReverse2(); 
668      for (int m = 1; m <= numBits; m++) { 
669        // localN = 2^m; 
670        localN = 1 << m; 
671        Wjk_r = 1; 
672        Wjk_i = 0; 
673   
674        theta = twoPI / localN; 
675   
676   
677        Wj_r = (float) Math.cos(theta); 
678        Wj_i = (float) (direction * Math.sin(theta)); 
679   
680        int nby2 = localN / 2; 
681        for (int j = 0; j < nby2; j++) { 
682          // This is the FFT innermost loop 
683          // Any optimizations that can be made here will yield 
684          // great rewards. 
685          for (int k = j; k < n; k += localN) { 
686            id = k + nby2; 
687            tempr = Wjk_r * r_data[id] - Wjk_i * i_data[id]; 
688            tempi = Wjk_r * i_data[id] + Wjk_i * r_data[id]; 
689   
690            // Zid = Zi -C 
691            r_data[id] = r_data[k] - tempr; 
692            i_data[id] = i_data[k] - tempi; 
693            r_data[k] += tempr; 
694            i_data[k] += tempi; 
695          } 
696   
697          // (eq 6.23) and (eq 6.24) 
698   
699          wtemp = Wjk_r; 
700   
701          Wjk_r = Wj_r * Wjk_r - Wj_i * Wjk_i; 
702          Wjk_i = Wj_r * Wjk_i + Wj_i * wtemp; 
703        } 
704      } 
705    } 
706      /** 
707        * FFT engine. 
708        * 1D Complex fft with floats. 
709        * It is Radix 2. 
710        * Therefore, the size must be an integral 
711        * power of 2, 2*4, 32,64, 128,256,512,1024 
712        */ 
713       public static void fftStatic(FFT fft, float in_r[], float in_i[], int numBits) { 
714         int id; 
715         // radix 2 number if sample, 1D of course. 
716         int localN; 
717         float wtemp, Wjk_r, Wjk_i, Wj_r, Wj_i; 
718         float theta, tempr, tempi; 
719         int ti, tj; 
720   
721         // Truncate input data to a power of two 
722         int length = 1 << numBits; // length = 2**nu 
723         int n = length; 
724   
725         // Copy passed references to variables to be used within 
726         // transforms.fft routines & utilities 
727         fft.r_data = in_r; 
728         fft.i_data = in_i; 
729   
730         fft.bitReverse2(); 
731         for (int m = 1; m <= numBits; m++) { 
732           // localN = 2^m; 
733           localN = 1 << m; 
734           Wjk_r = 1; 
735           Wjk_i = 0; 
736   
737           theta = twoPI / localN; 
738   
739   
740           Wj_r = (float) Math.cos(theta); 
741           Wj_i = (float) (fft.direction * Math.sin(theta)); 
742   
743           int nby2 = localN / 2; 
744           for (int j = 0; j < nby2; j++) { 
745             // This is the FFT innermost loop 
746             // Any optimizations that can be made here will yield 
747             // great rewards. 
748             for (int k = j; k < n; k += localN) { 
749               id = k + nby2; 
750               tempr = Wjk_r * fft.r_data[id] - Wjk_i * fft.i_data[id]; 
751               tempi = Wjk_r * fft.i_data[id] + Wjk_i * fft.r_data[id]; 
752   
753               // Zid = Zi -C 
754               fft.r_data[id] = fft.r_data[k] - tempr; 
755               fft.i_data[id] = fft.i_data[k] - tempi; 
756               fft.r_data[k] += tempr; 
757               fft.i_data[k] += tempi; 
758             } 
759   
760             // (eq 6.23) and (eq 6.24) 
761   
762             wtemp = Wjk_r; 
763   
764             Wjk_r = Wj_r * Wjk_r - Wj_i * Wjk_i; 
765             Wjk_i = Wj_r * Wjk_i + Wj_i * wtemp; 
766           } 
767         } 
768       } 
769   
770    public float[][] getRedReal() { 
771      return (cR_r); 
772    } 
773   
774    public float[][] getRedImaginary() { 
775      return (cR_i); 
776    } 
777   
778    public float[][] getGreenReal() { 
779      return (cG_r); 
780    } 
781   
782    public float[][] getGreenImaginary() { 
783      return (cG_i); 
784    } 
785   
786    public float[][] getBlueReal() { 
787      return (cB_r); 
788    } 
789   
790    public float[][] getBlueImaginary() { 
791      return (cB_i); 
792    } 
793   
794    /** 
795    * The two arrays must be the same size. 
796    */ 
797    private void copy2dArray(float[][] dst, float[][] src) { 
798      for (int i = 0; i < height; i++) { 
799        for (int j = 0; j < width; j++) { 
800          dst[i][j] = src[i][j]; 
801        } 
802      } 
803    } 
804   
805    private void copyFloatToComplex(float[][] dst_r, float[][] dst_i, 
806                                    float[] imageData) { 
807      int k = 0; 
808   
809      float alternateSign = 1; 
810   
811      for (int i = 0; i < height; i++) { 
812        for (int j = 0; j < width; j++) { 
813          // Calculate (-1)**(i+j) 
814          alternateSign = ((i + j) % 2 == 0) ? -1 : 1; 
815   
816          // 1. Put short image array into a complex pair, 
817          // (-1)**(i+j) is to put the zero frequency in the 
818          // center of the image when it is displayed. 
819          // 2. We also take this opportunity to scale the input 
820          // by N (width * height). 
821          dst_r[i][j] = (float) (imageData[k++] * alternateSign / N); 
822          dst_i[i][j] = (float) 0.0; 
823        } 
824      } 
825    } 
826   
827    private void copyShortToComplex(float[][] dst_r, float[][] dst_i, 
828                                    short[] imageData) { 
829      int k = 0; 
830   
831      float alternateSign = 1; 
832   
833      for (int i = 0; i < height; i++) 
834        for (int j = 0; j < width; j++) { 
835          // Calculate (-1)**(i+j) 
836          alternateSign = ((i + j) % 2 == 0) ? -1 : 1; 
837   
838          // 1. Put short image array into a complex pair, 
839          // (-1)**(i+j) is to put the zero frequency in the 
840          // center of the image when it is displayed. 
841          // 2. We also take this opportunity to scale the input 
842          // by N (width * height). 
843          dst_r[i][j] = (float) (imageData[k++] * alternateSign / N); 
844          dst_i[i][j] = (float) 0.0; 
845        } 
846    } 
847   
848    private boolean isNotInRange(int x, int low, int high) { 
849      if (x < low) return true; 
850      if (x >= high) return true; 
851      return false; 
852    } 
853   
854    private float[][] Rotate90(float[][] in) { 
855      float[][] out = new float[height][width]; 
856   
857      for (int i = 0; i < height; i++) { 
858        for (int j = 0; j < width; j++) { 
859          int x = height - j - 1; 
860          int y = i; 
861          if (isNotInRange(x, 0, in.length)) continue; 
862          if (isNotInRange(y, 0, in[0].length)) continue; 
863          out[i][j] = in[x][y]; 
864        } 
865      } 
866   
867      return (out); 
868    } 
869   
870    private void rotateInPlace180(float[][] in) { 
871      float temp; 
872   
873      for (int i = 0; i < height / 2; i++) { 
874        for (int j = 0; j < width; j++) { 
875          temp = in[i][j]; 
876          in[i][j] = in[height - i - 1][width - j - 1]; 
877          in[height - i - 1][width - j - 1] = temp; 
878        } 
879      } 
880    } 
881   
882    private float[] copyRealToFloat(float[][] in_r) { 
883      float[] f_data = new float[N]; 
884      int k = 0; 
885   
886      for (int i = 0; i < height; i++) { 
887        for (int j = 0; j < width; j++) { 
888          f_data[k++] = in_r[i][j]; 
889        } 
890      } 
891   
892      return (f_data); 
893    } 
894   
895    private float[] magnitudeSpectrum(float[][] in_r, float[][] in_i) { 
896      float[] mag = new float[N]; 
897      int k = 0; 
898   
899      for (int i = 0; i < height; i++) { 
900        for (int j = 0; j < width; j++) { 
901          mag[k] = (float) Math.sqrt(in_r[i][j] * in_r[i][j] + 
902                                     in_i[i][j] * in_i[i][j]); 
903   
904          // Since we're iterating through the loop anyway, see what min is. 
905          if (minPSD > mag[k]) 
906            minPSD = mag[k]; 
907          if (maxPSD < mag[k]) 
908            maxPSD = mag[k]; 
909   
910          k++; 
911        } 
912      } 
913   
914      return (mag); 
915    } 
916   
917    private float[] getPhaseImage(float[][] in_r, float[][] in_i) { 
918      float phase[] = new float[N]; 
919      int k = 0; 
920   
921      for (int i = 0; i < height; i++) 
922        for (int j = 0; j < width; j++) { 
923          phase[k] = (float) (in_r[i][j] / in_i[i][j]); 
924   
925          // Since we're iterating through the loop anyway, see what min is. 
926          if (minPSD > phase[k]) 
927            minPSD = phase[k]; 
928          if (maxPSD < phase[k]) 
929            maxPSD = phase[k]; 
930   
931          k++; 
932        } 
933   
934      return (phase); 
935    } 
936   
937    public float[] magnitudeSpectrum(float[] in_r, float[] in_i) { 
938      N = in_r.length; 
939      float[] mag = new float[N]; 
940   
941      for (int i = 0; i < N; i++) { 
942        mag[i] = (float) Math.sqrt(in_r[i] * in_r[i] + 
943                                   in_i[i] * in_i[i]); 
944   
945        // Since we're iterating through the loop anyway, 
946        // find what min is. 
947        if (minPSD > mag[i]) { 
948          minPSD = mag[i]; 
949        } 
950      } 
951   
952      return (mag); 
953    } 
954   
955  } 
956