Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
FHT.java
1 package ij.process;
2 import ij.*;
3 import java.awt.image.ColorModel;
4 
14 public class FHT extends FloatProcessor {
15  private boolean isFrequencyDomain;
16  private int maxN;
17  private float[] C;
18  private float[] S;
19  private int[] bitrev;
20  private float[] tempArr;
21  private boolean showProgress = true;
22 
24  public boolean quadrantSwapNeeded;
28  public int originalWidth;
30  public int originalHeight;
32  public int originalBitDepth;
34  public ColorModel originalColorModel;
35 
38  public FHT(ImageProcessor ip) {
39  super(ip.getWidth(), ip.getHeight(), (float[])((ip instanceof FloatProcessor)?ip.duplicate().getPixels():ip.convertToFloat().getPixels()), null);
40  maxN = getWidth();
41  resetRoi();
42  //IJ.log("FHT: "+maxN);
43  }
44 
46  public boolean powerOf2Size() {
47  int i=2;
48  while(i<width) i *= 2;
49  return i==width && width==height;
50  }
51 
54  public void transform() {
55  transform(false);
56  }
57 
60  public void inverseTransform() {
61  transform(true);
62  }
63 
65  //public FloatProcessor getInverseTransform() {
66  // if (!isFrequencyDomain) {
67  // throw new IllegalArgumentException("Frequency domain image required");
68  // snapshot();
69  // transform(true);
70  // FloatProcessor fp = this.duplicate();
71  // reset();
72  // isFrequencyDomain = true;
73  //}
74 
75 
76  void transform(boolean inverse) {
77  //IJ.log("transform: "+maxN+" "+inverse);
78  if (!powerOf2Size())
79  throw new IllegalArgumentException("Image not power of 2 size or not square: "+width+"x"+height);
80  maxN = width;
81  if (S==null) {
82  makeSinCosTables(maxN);
83  makeBitReverseTable(maxN);
84  tempArr = new float[maxN];
85  }
86  float[] fht = (float[])getPixels();
87  rc2DFHT(fht, inverse, maxN);
88  isFrequencyDomain = !inverse;
89  }
90 
91  void makeSinCosTables(int maxN) {
92  int n = maxN/4;
93  C = new float[n];
94  S = new float[n];
95  double theta = 0.0;
96  double dTheta = 2.0 * Math.PI/maxN;
97  for (int i=0; i<n; i++) {
98  C[i] = (float)Math.cos(theta);
99  S[i] = (float)Math.sin(theta);
100  theta += dTheta;
101  }
102  }
103 
104  void makeBitReverseTable(int maxN) {
105  bitrev = new int[maxN];
106  int nLog2 = log2(maxN);
107  for (int i=0; i<maxN; i++)
108  bitrev[i] = bitRevX(i, nLog2);
109  }
110 
112  void rc2DFHT(float[] x, boolean inverse, int maxN) {
113  //IJ.write("FFT: rc2DFHT (row-column Fast Hartley Transform)");
114  for (int row=0; row<maxN; row++)
115  dfht3(x, row*maxN, inverse, maxN);
116  progress(0.4);
117  transposeR(x, maxN);
118  progress(0.5);
119  for (int row=0; row<maxN; row++)
120  dfht3(x, row*maxN, inverse, maxN);
121  progress(0.7);
122  transposeR(x, maxN);
123  progress(0.8);
124 
125  int mRow, mCol;
126  float A,B,C,D,E;
127  for (int row=0; row<maxN/2; row++) { // Now calculate actual Hartley transform
128  for (int col=0; col<maxN/2; col++) {
129  mRow = (maxN - row) % maxN;
130  mCol = (maxN - col) % maxN;
131  A = x[row * maxN + col]; // see Bracewell, 'Fast 2D Hartley Transf.' IEEE Procs. 9/86
132  B = x[mRow * maxN + col];
133  C = x[row * maxN + mCol];
134  D = x[mRow * maxN + mCol];
135  E = ((A + D) - (B + C)) / 2;
136  x[row * maxN + col] = A - E;
137  x[mRow * maxN + col] = B + E;
138  x[row * maxN + mCol] = C + E;
139  x[mRow * maxN + mCol] = D - E;
140  }
141  }
142  progress(0.95);
143  }
144 
145  void progress(double percent) {
146  if (showProgress)
147  IJ.showProgress(percent);
148  }
149 
150  /* An optimized real FHT */
151  void dfht3 (float[] x, int base, boolean inverse, int maxN) {
152  int i, stage, gpNum, gpIndex, gpSize, numGps, Nlog2;
153  int bfNum, numBfs;
154  int Ad0, Ad1, Ad2, Ad3, Ad4, CSAd;
155  float rt1, rt2, rt3, rt4;
156 
157  Nlog2 = log2(maxN);
158  BitRevRArr(x, base, Nlog2, maxN); //bitReverse the input array
159  gpSize = 2; //first & second stages - do radix 4 butterflies once thru
160  numGps = maxN / 4;
161  for (gpNum=0; gpNum<numGps; gpNum++) {
162  Ad1 = gpNum * 4;
163  Ad2 = Ad1 + 1;
164  Ad3 = Ad1 + gpSize;
165  Ad4 = Ad2 + gpSize;
166  rt1 = x[base+Ad1] + x[base+Ad2]; // a + b
167  rt2 = x[base+Ad1] - x[base+Ad2]; // a - b
168  rt3 = x[base+Ad3] + x[base+Ad4]; // c + d
169  rt4 = x[base+Ad3] - x[base+Ad4]; // c - d
170  x[base+Ad1] = rt1 + rt3; // a + b + (c + d)
171  x[base+Ad2] = rt2 + rt4; // a - b + (c - d)
172  x[base+Ad3] = rt1 - rt3; // a + b - (c + d)
173  x[base+Ad4] = rt2 - rt4; // a - b - (c - d)
174  }
175 
176  if (Nlog2 > 2) {
177  // third + stages computed here
178  gpSize = 4;
179  numBfs = 2;
180  numGps = numGps / 2;
181  //IJ.write("FFT: dfht3 "+Nlog2+" "+numGps+" "+numBfs);
182  for (stage=2; stage<Nlog2; stage++) {
183  for (gpNum=0; gpNum<numGps; gpNum++) {
184  Ad0 = gpNum * gpSize * 2;
185  Ad1 = Ad0; // 1st butterfly is different from others - no mults needed
186  Ad2 = Ad1 + gpSize;
187  Ad3 = Ad1 + gpSize / 2;
188  Ad4 = Ad3 + gpSize;
189  rt1 = x[base+Ad1];
190  x[base+Ad1] = x[base+Ad1] + x[base+Ad2];
191  x[base+Ad2] = rt1 - x[base+Ad2];
192  rt1 = x[base+Ad3];
193  x[base+Ad3] = x[base+Ad3] + x[base+Ad4];
194  x[base+Ad4] = rt1 - x[base+Ad4];
195  for (bfNum=1; bfNum<numBfs; bfNum++) {
196  // subsequent BF's dealt with together
197  Ad1 = bfNum + Ad0;
198  Ad2 = Ad1 + gpSize;
199  Ad3 = gpSize - bfNum + Ad0;
200  Ad4 = Ad3 + gpSize;
201 
202  CSAd = bfNum * numGps;
203  rt1 = x[base+Ad2] * C[CSAd] + x[base+Ad4] * S[CSAd];
204  rt2 = x[base+Ad4] * C[CSAd] - x[base+Ad2] * S[CSAd];
205 
206  x[base+Ad2] = x[base+Ad1] - rt1;
207  x[base+Ad1] = x[base+Ad1] + rt1;
208  x[base+Ad4] = x[base+Ad3] + rt2;
209  x[base+Ad3] = x[base+Ad3] - rt2;
210 
211  } /* end bfNum loop */
212  } /* end gpNum loop */
213  gpSize *= 2;
214  numBfs *= 2;
215  numGps = numGps / 2;
216  } /* end for all stages */
217  } /* end if Nlog2 > 2 */
218 
219  if (inverse) {
220  for (i=0; i<maxN; i++)
221  x[base+i] = x[base+i] / maxN;
222  }
223  }
224 
225  void transposeR (float[] x, int maxN) {
226  int r, c;
227  float rTemp;
228 
229  for (r=0; r<maxN; r++) {
230  for (c=r; c<maxN; c++) {
231  if (r != c) {
232  rTemp = x[r*maxN + c];
233  x[r*maxN + c] = x[c*maxN + r];
234  x[c*maxN + r] = rTemp;
235  }
236  }
237  }
238  }
239 
240  int log2 (int x) {
241  int count = 15;
242  while (!btst(x, count))
243  count--;
244  return count;
245  }
246 
247 
248  private boolean btst (int x, int bit) {
249  //int mask = 1;
250  return ((x & (1<<bit)) != 0);
251  }
252 
253  void BitRevRArr (float[] x, int base, int bitlen, int maxN) {
254  for (int i=0; i<maxN; i++)
255  tempArr[i] = x[base+bitrev[i]];
256  for (int i=0; i<maxN; i++)
257  x[base+i] = tempArr[i];
258  }
259 
260  private int bitRevX (int x, int bitlen) {
261  int temp = 0;
262  for (int i=0; i<=bitlen; i++)
263  if ((x & (1<<i)) !=0)
264  temp |= (1<<(bitlen-i-1));
265  return temp & 0x0000ffff;
266  }
267 
268  private int bset (int x, int bit) {
269  x |= (1<<bit);
270  return x;
271  }
272 
276  if (!isFrequencyDomain)
277  throw new IllegalArgumentException("Frequency domain image required");
278  int base;
279  float r, scale;
280  float min = Float.MAX_VALUE;
281  float max = Float.MIN_VALUE;
282  float[] fps = new float[maxN*maxN];
283  byte[] ps = new byte[maxN*maxN];
284  float[] fht = (float[])getPixels();
285 
286  for (int row=0; row<maxN; row++) {
287  FHTps(row, maxN, fht, fps);
288  base = row * maxN;
289  for (int col=0; col<maxN; col++) {
290  r = fps[base+col];
291  if (r<min) min = r;
292  if (r>max) max = r;
293  }
294  }
295 
296  if (min<1.0)
297  min = 0f;
298  else
299  min = (float)Math.log(min);
300  max = (float)Math.log(max);
301  scale = (float)(253.0/(max-min));
302 
303  for (int row=0; row<maxN; row++) {
304  base = row*maxN;
305  for (int col=0; col<maxN; col++) {
306  r = fps[base+col];
307  if (r<1f)
308  r = 0f;
309  else
310  r = (float)Math.log(r);
311  ps[base+col] = (byte)(((r-min)*scale+0.5)+1);
312  }
313  }
314  ImageProcessor ip = new ByteProcessor(maxN, maxN, ps, null);
315  swapQuadrants(ip);
316  return ip;
317  }
318 
320  void FHTps(int row, int maxN, float[] fht, float[] ps) {
321  int base = row*maxN;
322  int l;
323  for (int c=0; c<maxN; c++) {
324  l = ((maxN-row)%maxN) * maxN + (maxN-c)%maxN;
325  ps[base+c] = (sqr(fht[base+c]) + sqr(fht[l]))/2f;
326  }
327  }
328 
329  ImageProcessor calculateAmplitude(float[] fht, int maxN) {
330  float[] amp = new float[maxN*maxN];
331  for (int row=0; row<maxN; row++) {
332  amplitude(row, maxN, fht, amp);
333  }
334  ImageProcessor ip = new FloatProcessor(maxN, maxN, amp, null);
335  swapQuadrants(ip);
336  return ip;
337  }
338 
340  void amplitude(int row, int maxN, float[] fht, float[] amplitude) {
341  int base = row*maxN;
342  int l;
343  for (int c=0; c<maxN; c++) {
344  l = ((maxN-row)%maxN) * maxN + (maxN-c)%maxN;
345  amplitude[base+c] = (float)Math.sqrt(sqr(fht[base+c]) + sqr(fht[l]));
346  }
347  }
348 
349  float sqr(float x) {
350  return x*x;
351  }
352 
360  public void swapQuadrants(ImageProcessor ip) {
361  //IJ.log("swap");
362  ImageProcessor t1, t2;
363  int size = ip.getWidth()/2;
364  ip.setRoi(size,0,size,size);
365  t1 = ip.crop();
366  ip.setRoi(0,size,size,size);
367  t2 = ip.crop();
368  ip.insert(t1,0,size);
369  ip.insert(t2,size,0);
370  ip.setRoi(0,0,size,size);
371  t1 = ip.crop();
372  ip.setRoi(size,size,size,size);
373  t2 = ip.crop();
374  ip.insert(t1,size,size);
375  ip.insert(t2,0,0);
376  ip.resetRoi();
377  }
378 
381  public void swapQuadrants () {
382  swapQuadrants(this);
383  }
384 
385  void changeValues(ImageProcessor ip, int v1, int v2, int v3) {
386  byte[] pixels = (byte[])ip.getPixels();
387  int v;
388  //IJ.log(v1+" "+v2+" "+v3+" "+pixels.length);
389  for (int i=0; i<pixels.length; i++) {
390  v = pixels[i]&255;
391  if (v>=v1 && v<=v2)
392  pixels[i] = (byte)v3;
393  }
394  }
395 
400  public FHT multiply(FHT fht) {
401  return multiply(fht, false);
402  }
403 
408  public FHT conjugateMultiply(FHT fht) {
409  return multiply(fht, true);
410  }
411 
412  FHT multiply(FHT fht, boolean conjugate) {
413  int rowMod, cMod, colMod;
414  double h2e, h2o;
415  float[] h1 = (float[])getPixels();
416  float[] h2 = (float[])fht.getPixels();
417  float[] tmp = new float[maxN*maxN];
418  for ( int r =0; r<maxN; r++) {
419  rowMod = (maxN - r) % maxN;
420  for (int c=0; c<maxN; c++) {
421  colMod = (maxN - c) % maxN;
422  h2e = (h2[r * maxN + c] + h2[rowMod * maxN + colMod]) / 2;
423  h2o = (h2[r * maxN + c] - h2[rowMod * maxN + colMod]) / 2;
424  if (conjugate)
425  tmp[r * maxN + c] = (float)(h1[r * maxN + c] * h2e - h1[rowMod * maxN + colMod] * h2o);
426  else
427  tmp[r * maxN + c] = (float)(h1[r * maxN + c] * h2e + h1[rowMod * maxN + colMod] * h2o);
428  }
429  }
430  return new FHT(new FloatProcessor(maxN, maxN, tmp, null));
431  }
432 
437  public FHT divide(FHT fht) {
438  int rowMod, cMod, colMod;
439  double mag, h2e, h2o;
440  float[] h1 = (float[])getPixels();
441  float[] h2 = (float[])fht.getPixels();
442  float[] out = new float[maxN*maxN];
443  for (int r=0; r<maxN; r++) {
444  rowMod = (maxN - r) % maxN;
445  for (int c=0; c<maxN; c++) {
446  colMod = (maxN - c) % maxN;
447  mag =h2[r*maxN+c] * h2[r*maxN+c] + h2[rowMod*maxN+colMod] * h2[rowMod*maxN+colMod];
448  if (mag<1e-20)
449  mag = 1e-20;
450  h2e = (h2[r*maxN+c] + h2[rowMod*maxN+colMod]);
451  h2o = (h2[r*maxN+c] - h2[rowMod*maxN+colMod]);
452  double tmp = (h1[r*maxN+c] * h2e - h1[rowMod*maxN+colMod] * h2o);
453  out[r*maxN+c] = (float)(tmp/mag);
454  }
455  }
456  return new FHT(new FloatProcessor(maxN, maxN, out, null));
457  }
458 
460  public void setShowProgress(boolean showProgress) {
461  this.showProgress = showProgress;
462  }
463 
465  public FHT getCopy() {
466  ImageProcessor ip = super.duplicate();
467  FHT fht = new FHT(ip);
468  fht.isFrequencyDomain = isFrequencyDomain;
470  fht.rgb = rgb;
475  return fht;
476  }
477 
479  public String toString() {
480  return "FHT, " + getWidth() + "x"+getHeight() + ", fd=" + isFrequencyDomain;
481  }
482 
483 }