| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- (*
- This FFT demo code from http://www.vssoft.bg.
- Contact at [email protected].
- 06 June 2009
- *)
- unit uCPUFFT;
- interface
- uses
- System.SysUtils,
- System.Types,
- System.Math;
- type
- TProcessMode = (module, phase, real, imag);
- TComplex = record
- real, imag: Double;
- end;
- PComplexArray = ^TComplexDynArray;
- TComplexDynArray = array of TComplex;
- TComplex2DArray = array of TComplexDynArray;
- TByte2DArray = array of TByteDynArray;
- // perform fft (sgn=1) or inverse fft (sgn=-1) on cx[nx]
- procedure ftu(sgn: TValueSign; nx: integer; cx: TComplexDynArray); overload;
- // perform 2Dfft (sgn=1) or inverse 2Dfft (sgn=-1) on cx[len,len]
- procedure ftu(sgn: TValueSign; len: integer; cArr: TComplex2DArray); overload;
- // reorder quadrants to move 0 to the center (after fft on real signal)
- procedure Reorder(inp: TComplexDynArray); overload; // 1D
- procedure Reorder(inp: TComplex2DArray); overload // 2D
- // normalize cArr to [0,255] using function defined by mode and store result in buf
- // on return, cArr.Real contains result before normalization
- procedure Normalize(cArr: TComplexDynArray; buf: TByteDynArray;
- mode: TProcessMode); overload; // 1D
- procedure Normalize(cArr: TComplex2DArray; buf: TByte2DArray;
- mode: TProcessMode); overload; // 2D
- //-----------------------------------------
- implementation
- //-----------------------------------------
- // some complex arithmetic
- function CAdd(c1, c2: TComplex): TComplex;
- begin
- Result.Real := c1.Real + c2.Real;
- Result.imag := c1.imag + c2.imag;
- end;
- function CSubt(c1, c2: TComplex): TComplex;
- begin
- Result.Real := c1.Real - c2.Real;
- Result.imag := c1.imag - c2.imag;
- end;
- function CMult(c1, c2: TComplex): TComplex; overload;
- begin
- Result.Real := c1.Real * c2.Real - c1.imag * c2.imag;
- Result.imag := c1.Real * c2.imag + c1.imag * c2.Real;
- end;
- function CMult(c: TComplex; i: integer): TComplex; overload;
- begin
- Result.Real := c.Real * i;
- Result.imag := c.imag * i;
- end;
- function CMult(c: TComplex; d: Double): TComplex; overload;
- begin
- Result.Real := c.Real * d;
- Result.imag := c.imag * d;
- end;
- // return y = 2^i >= n
- function pad2(n: integer): integer;
- begin
- Result := 1;
- while (Result < n) do
- Result := Result * 2;
- end;
- (*
- Complex fourier transform with unitary scaling:
- 1 nx-1 sgn*2*pi*i * j*(k-1)/nx
- cx(k) = -------- * sum cx(j) * e
- sqrt(nx) j=0 for k=1,2,...,nx=2**integer
- In some applications the scale factor 1/sqrt(nx) is omitted in fft: factor=1,
- wheras in the inverse fft, it is set to: factor=1/nx.
- *)
- // Apply fft/ifft on cArr[nx] and store result in cArr:
- procedure ftu(sgn: TValueSign; nx: integer; cx: TComplexDynArray);
- var
- scale, arg: Double;
- cw, cdel, ct: TComplex;
- i, j, k, m, istep: integer;
- begin
- if (nx <> pad2(nx)) then
- raise Exception.Create('ftu: number of samples is not a power of 2');
- scale := 1 / sqrt(nx);
- for i := 0 to nx - 1 do
- cx[i] := CMult(cx[i], scale);
- j := 0;
- for i := 0 to nx - 1 do
- begin
- if (i <= j) then
- begin
- ct := cx[j];
- cx[j] := cx[i];
- cx[i] := ct;
- end;
- m := nx div 2;
- while ((j >= m) and (m > 1)) do
- begin
- j := j - m;
- m := m div 2;
- end;
- j := j + m;
- end;
- k := 1;
- repeat
- istep := 2 * k;
- cw.Real := 1;
- cw.imag := 0;
- arg := sgn * Pi / k;
- cdel.Real := cos(arg);
- cdel.imag := sin(arg);
- for m := 0 to k - 1 do
- begin
- i := m;
- while i < nx do
- begin // for i=m to nx step istep
- ct := CMult(cw, cx[i + k]);
- cx[i + k] := CSubt(cx[i], ct);
- cx[i] := CAdd(cx[i], ct);
- Inc(i, istep);
- end;
- cw := CMult(cw, cdel);
- end;
- k := istep;
- until (k >= nx);
- end;
- (* first apply 1D ftu on cArr rows and then on cArr cols; store result in cArr *)
- procedure ftu(sgn: TValueSign; len: integer; cArr: TComplex2DArray);
- var
- i, j: integer;
- tmp: TComplexDynArray;
- begin
- // apply forward fft
- for i := 0 to len - 1 do // fft rows: up-down
- ftu(sgn, len, cArr[i]);
- // fft cols: left-right; uses tmp[rows]
- SetLength(tmp, len);
- for i := 0 to len - 1 do
- begin // for each col
- for j := 0 to len - 1 do // copy the i-th col of inp[rows,cols] to tmp[rows]
- tmp[j] := cArr[j, i];
- ftu(sgn, len, tmp); // fft on the i-th col
- for j := 0 to len - 1 do // copy result back from tmp to inp
- cArr[j, i] := tmp[j];
- end;
- end;
- procedure Reorder(inp: TComplexDynArray); overload; // 1D
- var
- tmp: TComplexDynArray;
- i, h, w: integer;
- begin
- w := Length(inp);
- h := w div 2;
- SetLength(tmp, h);
- for i := 0 to h - 1 do
- tmp[i] := inp[i + h];
- for i := 0 to h - 1 do
- inp[i + h] := inp[i];
- for i := 0 to h - 1 do
- inp[i] := tmp[i];
- end;
- procedure Reorder(inp: TComplex2DArray);
- overload // 2D
- var
- tmp: TComplex2DArray;
- i, j, h, w: integer;
- begin
- w := Length(inp);
- h := w div 2;
- SetLength(tmp, h, h);
- // left,up->tmp
- for i := 0 to h - 1 do
- for j := 0 to h - 1 do
- tmp[i, j] := inp[i, j];
- // down,right->left,up
- for i := 0 to h - 1 do
- for j := 0 to h - 1 do
- inp[i, j] := inp[i + h, j + h];
- // tmp->down,right
- for i := 0 to h - 1 do
- for j := 0 to h - 1 do
- inp[i + h, j + h] := tmp[i, j];
- // right,up->tmp
- for i := 0 to h - 1 do
- for j := 0 to h - 1 do
- tmp[i, j] := inp[i + h, j];
- // down,left->right,up
- for i := 0 to h - 1 do
- for j := 0 to h - 1 do
- inp[i + h, j] := inp[i, j + h];
- // tmp->down,left
- for i := 0 to h - 1 do
- for j := 0 to h - 1 do
- inp[i, j + h] := tmp[i, j];
- end;
- procedure Normalize(cArr: TComplexDynArray; buf: TByteDynArray;
- mode: TProcessMode); // 1D
- var
- j, len: integer;
- m, n, r: Double;
- begin
- len := Length(cArr); // len := Length(cArr)-1;
- // find max and min values of cArr to normalize result
- m := MinDouble;
- n := MaxDouble;
- for j := 0 to len - 1 do
- begin // for j:=1 to len do begin
- case mode of
- module:
- r := Hypot(cArr[j].Real, cArr[j].imag);
- phase:
- r := ArcTan2(cArr[j].imag, cArr[j].Real);
- real:
- r := cArr[j].Real;
- else
- r := cArr[j].imag;
- end;
- if m < r then
- m := r;
- if n > r then
- n := r;
- // reuse real part of cArr to store result
- cArr[j].Real := r;
- end;
- // normalize result in cArr.Real to [0,255] and copy it to buf
- for j := 0 to len - 1 do
- buf[j] := Trunc(255 * (cArr[j].Real - n) / (m - n));
- end;
- procedure Normalize(cArr: TComplex2DArray; buf: TByte2DArray;
- mode: TProcessMode); // 2D
- var
- i, j, len: integer;
- m, n, r: Double;
- begin
- len := Length(cArr);
- m := MinDouble;
- n := MaxDouble;
- for i := 0 to len - 1 do
- for j := 0 to len - 1 do
- begin
- case mode of
- module:
- r := Hypot(cArr[i, j].Real, cArr[i, j].imag);
- phase:
- r := ArcTan2(cArr[i, j].imag, cArr[i, j].Real);
- real:
- r := cArr[i, j].Real;
- else
- r := cArr[i, j].imag;
- end;
- if m < r then
- m := r;
- if n > r then
- n := r;
- cArr[i, j].Real := r;
- end;
- for i := 0 to len - 1 do // move cArr[Real] -> buf
- for j := 0 to len - 1 do
- buf[i, j] := Trunc(255 * (cArr[i, j].Real - n) / (m - n));
- end;
- end.
|