Skip to content

Commit 5d62290

Browse files
committed
adding files
0 parents  commit 5d62290

File tree

5 files changed

+407
-0
lines changed

5 files changed

+407
-0
lines changed

.gitignore

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
lib-cov
2+
*.seed
3+
*.log
4+
*.csv
5+
*.dat
6+
*.out
7+
*.pid
8+
*.gz
9+
10+
pids
11+
logs
12+
results
13+
14+
npm-debug.log
15+
node_modules/*

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
ndarray-fft
2+
===========
3+
A fast Fourier transform implementation for [ndarrays](https://github.com/mikolalysenko/ndarray). You can use this to do image processing operations on big, higher dimensional typed arrays in JavaScript.
4+
5+
**WORK IN PROGRESS**
6+
7+
## Example
8+
9+
```javascript
10+
var ndarray = require("ndarray")
11+
var ops = require("ndarray-ops")
12+
var fft = require("ndarray-fft")
13+
14+
var x = ops.random(ndarray.zeros([256, 256]))
15+
, y = ops.random(ndarray.zeros([256, 256]))
16+
17+
//Forward transform x/y
18+
fft(1, x, y)
19+
20+
//Invert transform
21+
fft(-1, x, y)
22+
```
23+
24+
### `require("ndarray-fft")(dir, x, y)`
25+
Executes a fast Fourier transform on the complex valued array x/y.
26+
27+
* `dir` - Either +/- 1. Determines whether to use a forward or inverse FFT
28+
* `x` the real part of the typed array
29+
* `y` the imaginary part of the typed array.
30+
31+
**Note** This code is fastest when the components of the shapes arrays are all powers of two. For non-power of two shapes, Bluestein's fft is used which is somewhat slower. Also note that this code clobbers [scratch](https://github.com/mikolalysenko/scratch) memory.
32+
33+
# Credits
34+
(c) 2013 Mikola Lysenko. MIT License.
35+
36+
Radix 2 FFT based on code by Paul Bourke.

fft.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
"use strict"
2+
3+
var scratch = require("scratch")
4+
var ops = require("ndarray-ops")
5+
var cwise = require("cwise")
6+
var ndarray = require("ndarray")
7+
var fftm = require("./lib/fft-matrix.js")
8+
9+
function ndfft(dir, x, y) {
10+
var shape = x.shape
11+
, d = shape.length
12+
, size = 1
13+
, stride = new Array(d)
14+
, pad = 0
15+
, i, j
16+
for(i=d-1; i>=0; --i) {
17+
stride[i] = size
18+
size *= shape[i]
19+
pad = Math.max(pad, fftm.scratchMemory(shape[i]))
20+
}
21+
pad = Math.max(2 * size, pad)
22+
23+
var buffer = scratch(2 * size + pad, "double")
24+
var x1 = ndarray(buffer, shape.slice(0), stride, 0)
25+
, y1 = ndarray(buffer, shape.slice(0), stride.slice(0), size)
26+
, x2 = ndarray(buffer, shape.slice(0), stride.slice(0), 2*size)
27+
, x3 = ndarray(buffer, shape.slice(0), stride.slice(0), 3*size)
28+
, tmp, n, s1, s2
29+
30+
//Copy into x1/y1
31+
ops.assign(x1, x)
32+
ops.assign(y1, y)
33+
for(i=d-1; i>=0; --i) {
34+
fftm(dir, size/shape[i], shape[i], buffer, x1.offset, y1.offset, x2.offset)
35+
if(i === 0) {
36+
break
37+
}
38+
39+
//Compute new stride for x2/y2
40+
n = 1
41+
s1 = x2.stride
42+
s2 = y2.stride
43+
for(j=i-1; j<d; ++j) {
44+
s2[j] = s1[j] = n
45+
n *= shape[j]
46+
}
47+
for(j=i-2; j>=0; --j) {
48+
s2[j] = s1[j] = n
49+
n *= shape[j]
50+
}
51+
52+
//Transpose
53+
ops.assign(x2, x1)
54+
ops.assign(y2, y1)
55+
56+
//Swap buffers
57+
tmp = x1
58+
x1 = x2
59+
x2 = tmp
60+
tmp = y1
61+
y1 = y2
62+
y2 = tmp
63+
}
64+
//Copy result back into x
65+
ops.assign(x, x1)
66+
ops.assign(y, y1)
67+
}
68+
69+
module.exports = ndfft

lib/fft-matrix.js

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
var bits = require("bit-twiddle")
2+
3+
function fft(dir, nrows, ncols, buffer, x_ptr, y_ptr, scratch_ptr) {
4+
dir |= 0
5+
nrows |= 0
6+
ncols |= 0
7+
x_ptr |= 0
8+
y_ptr |= 0
9+
if(bits.isPow2(ncols)) {
10+
fftRadix2(dir, nrows, ncols, buffer, x_ptr, y_ptr)
11+
} else {
12+
fftBluestein(dir, nrows, ncols, buffer, x_ptr, y_ptr, scratch_ptr)
13+
}
14+
}
15+
module.exports = fft
16+
17+
function scratchMemory(n) {
18+
if(bits.isPow2(n)) {
19+
return 0
20+
}
21+
return 2 * n + 4 * bits.nextPow2(2*n + 1)
22+
}
23+
module.exports.scratchMemory = scratchMemory
24+
25+
26+
//Radix 2 FFT Adapted from Paul Bourke's C Implementation
27+
function fftRadix2(dir, nrows, ncols, buffer, x_ptr, y_ptr) {
28+
dir |= 0
29+
nrows |= 0
30+
ncols |= 0
31+
x_ptr |= 0
32+
y_ptr |= 0
33+
var nn,i,i1,j,k,i2,l,l1,l2
34+
var c1,c2,tx,ty,t1,t2,u1,u2,z,row
35+
36+
// Calculate the number of points
37+
nn = ncols
38+
m = bits.log2(nn)
39+
40+
for(row=0; row<nrows; ++row) {
41+
// Do the bit reversal
42+
i2 = nn >> 1;
43+
j = 0;
44+
for (i=0;i<nn-1;i++) {
45+
if (i < j) {
46+
tx = buffer[x_ptr+i]
47+
buffer[x_ptr+i] = buffer[x_ptr+j]
48+
buffer[x_ptr+j] = tx
49+
ty = buffer[y_ptr+i]
50+
buffer[y_ptr+i] = buffer[y_ptr+j]
51+
buffer[y_ptr+j] = ty
52+
}
53+
k = i2
54+
while (k <= j) {
55+
j -= k
56+
k >>= 1
57+
}
58+
j += k
59+
}
60+
61+
// Compute the FFT
62+
c1 = -1.0
63+
c2 = 0.0
64+
l2 = 1
65+
for (l=0;l<m;l++) {
66+
l1 = l2
67+
l2 <<= 1
68+
u1 = 1.0
69+
u2 = 0.0
70+
for (j=0;j<l1;j++) {
71+
for (i=j;i<nn;i+=l2) {
72+
i1 = i + l1
73+
t1 = u1 * buffer[x_ptr+i1] - u2 * buffer[y_ptr+i1]
74+
t2 = u1 * buffer[y_ptr+i1] + u2 * buffer[x_ptr+i1]
75+
buffer[x_ptr+i1] = buffer[x_ptr+i] - t1
76+
buffer[y_ptr+i1] = buffer[y_ptr+i] - t2
77+
buffer[x_ptr+i] += t1
78+
buffer[y_ptr+i] += t2
79+
}
80+
z = u1 * c1 - u2 * c2
81+
u2 = u1 * c2 + u2 * c1
82+
u1 = z
83+
}
84+
c2 = Math.sqrt((1.0 - c1) / 2.0);
85+
if (dir === 1)
86+
c2 = -c2;
87+
c1 = Math.sqrt((1.0 + c1) / 2.0);
88+
}
89+
90+
// Scaling for forward transform
91+
if (dir === -1) {
92+
var scale_f = 1.0 / nn
93+
for (i=0;i<nn;i++) {
94+
buffer[x_ptr+i] *= scale_f
95+
buffer[y_ptr+i] *= scale_f
96+
}
97+
}
98+
99+
// Advance pointers
100+
x_ptr += ncols
101+
y_ptr += ncols
102+
}
103+
}
104+
105+
// Use Bluestein algorithm for npot FFTs
106+
// Scratch memory required: 2 * ncols + 4 * bits.nextPow2(2*ncols + 1)
107+
function fftBluestein(dir, nrows, ncols, buffer, x_ptr, y_ptr, scratch_ptr) {
108+
dir |= 0
109+
nrows |= 0
110+
ncols |= 0
111+
x_ptr |= 0
112+
y_ptr |= 0
113+
scratch_ptr |= 0
114+
115+
// Initialize tables
116+
var m = bits.nextPow2(2 * ncols + 1)
117+
, cos_ptr = scratch_ptr
118+
, sin_ptr = cos_ptr + ncols
119+
, xs_ptr = sin_ptr + ncols
120+
, ys_ptr = xs_ptr + m
121+
, cft_ptr = ys_ptr + m
122+
, sft_ptr = cft_ptr + m
123+
, w = Math.PI / ncols
124+
, row, a, b, c, d, k1, k2, k3
125+
, i
126+
for(i=0; i<ncols; ++i) {
127+
a = w * ((i * i) % (ncols * 2))
128+
c = Math.cos(a)
129+
d = Math.sin(a)
130+
buffer[cft_ptr+(m-i)] = buffer[cft_ptr+i] = buffer[cos_ptr+i] = c
131+
buffer[sft_ptr+(m-i)] = buffer[sft_ptr+i] = buffer[sin_ptr+i] = d
132+
}
133+
for(i=ncols; i<=m-ncols; ++i) {
134+
buffer[cft_ptr+i] = 0.0
135+
}
136+
for(i=ncols; i<=m-ncols; ++i) {
137+
buffer[sft_ptr+i] = 0.0
138+
}
139+
140+
fftRadix2(1, 1, m, buffer, cft_ptr, sft_ptr)
141+
142+
//Compute scale factor
143+
if(dir === -1) {
144+
w = 1.0 / ncols
145+
} else {
146+
w = 1.0
147+
}
148+
149+
//Handle direction
150+
for(row=0; row<nrows; ++row) {
151+
152+
// Copy row into scratch memory, multiply weights
153+
for(i=0; i<ncols; ++i) {
154+
a = buffer[x_ptr+i]
155+
b = buffer[y_ptr+i]
156+
c = buffer[cos_ptr+i]
157+
d = -buffer[sin_ptr+i]
158+
k1 = c * (a + b)
159+
k2 = a * (d - c)
160+
k3 = b * (c + d)
161+
buffer[xs_ptr+i] = k1 - k3
162+
buffer[ys_ptr+i] = k1 + k2
163+
}
164+
//Zero out the rest
165+
for(i=ncols; i<m; ++i) {
166+
buffer[xs_ptr+i] = 0.0
167+
}
168+
for(i=ncols; i<m; ++i) {
169+
buffer[ys_ptr+i] = 0.0
170+
}
171+
172+
// FFT buffer
173+
fftRadix2(1, 1, m, buffer, xs_ptr, ys_ptr)
174+
175+
// Apply multiplier
176+
for(i=0; i<m; ++i) {
177+
a = buffer[xs_ptr+i]
178+
b = buffer[ys_ptr+i]
179+
c = buffer[cft_ptr+i]
180+
d = buffer[sft_ptr+i]
181+
k1 = c * (a + b)
182+
k2 = a * (d - c)
183+
k3 = b * (c + d)
184+
buffer[xs_ptr+i] = k1 - k3
185+
buffer[ys_ptr+i] = k1 + k2
186+
}
187+
188+
// Inverse FFT buffer
189+
fftRadix2(-1, 1, m, buffer, xs_ptr, ys_ptr)
190+
191+
// Copy result back into x/y
192+
for(i=0; i<ncols; ++i) {
193+
a = buffer[xs_ptr+i]
194+
b = -buffer[ys_ptr+i]
195+
c = buffer[cos_ptr+i]
196+
d = buffer[sin_ptr+i]
197+
k1 = c * (a + b)
198+
k2 = a * (d - c)
199+
k3 = b * (c + d)
200+
buffer[x_ptr+i] = w * (k1 - k3)
201+
buffer[y_ptr+i] = w * (k1 + k2)
202+
}
203+
204+
x_ptr += ncols
205+
y_ptr += ncols
206+
}
207+
}

0 commit comments

Comments
 (0)