Skip to content

Commit 3d3b9c6

Browse files
committed
Make a 2d inverse fourier transform algorithm
While at it improve some code reuse Note that passing an image through the forward 2d fft and then the backward 2d fft results in the real image being shifted in the origin and the imaginary values slightly nonzero. I will need to debug this a bit.
1 parent c161812 commit 3d3b9c6

File tree

4 files changed

+281
-100
lines changed

4 files changed

+281
-100
lines changed

src/main/java/nom/bdezonia/zorbage/algorithm/FFT2D.java

Lines changed: 18 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@
3737
import nom.bdezonia.zorbage.algebra.Multiplication;
3838
import nom.bdezonia.zorbage.algebra.RealConstants;
3939
import nom.bdezonia.zorbage.algebra.SetComplex;
40-
import nom.bdezonia.zorbage.algebra.StorageConstruction;
4140
import nom.bdezonia.zorbage.algebra.Trigonometric;
4241
import nom.bdezonia.zorbage.algebra.Unity;
4342
import nom.bdezonia.zorbage.data.DimensionedDataSource;
4443
import nom.bdezonia.zorbage.data.DimensionedStorage;
44+
import nom.bdezonia.zorbage.datasource.FFTDataSource;
4545
import nom.bdezonia.zorbage.datasource.IndexedDataSource;
4646
import nom.bdezonia.zorbage.datasource.SequencedDataSource;
4747
import nom.bdezonia.zorbage.dataview.PlaneView;
@@ -60,6 +60,7 @@ public class FFT2D {
6060
private FFT2D() { }
6161

6262
/**
63+
* Do a 2-dimensional fourier transform of 1 plane of a complex DataSource
6364
*
6465
* @param <T>
6566
* @param <U>
@@ -73,30 +74,25 @@ private FFT2D() { }
7374
* @param otherPositions
7475
* @return
7576
*/
76-
@SuppressWarnings("unchecked")
7777
public static
7878
<T extends Algebra<T,U> & Addition<U> & Multiplication<U>,
79-
U extends SetComplex<W>,
79+
U extends SetComplex<W> & Allocatable<U>,
8080
V extends Algebra<V,W> & Trigonometric<W> & RealConstants<W> &
8181
Multiplication<W> & Addition<W> & Invertible<W> & Unity<W>,
8282
W>
83-
DimensionedDataSource<U> compute(T cmplxAlg, V realAlg, DimensionedDataSource<W> realData, int axis0, int axis1, IntegerIndex otherPositions)
83+
DimensionedDataSource<U> compute(T complexAlg, V realAlg, DimensionedDataSource<U> complexData, int axis0, int axis1, IntegerIndex otherPositions)
8484
{
85-
U complexValue = cmplxAlg.construct();
85+
U complexValue = complexAlg.construct();
8686

87-
W realValue = realAlg.construct();
88-
89-
W zero = realAlg.construct();
90-
9187
// grab the plane of data the user is interested in
9288

93-
PlaneView<W> view = new PlaneView<W>(realData, axis0, axis1);
89+
PlaneView<U> view = new PlaneView<U>(complexData, axis0, axis1);
9490

9591
for (int i = 0; i < otherPositions.numDimensions(); i++) {
9692
view.setPositionValue(i, otherPositions.get(i));
9793
}
9894

99-
DimensionedDataSource<W> theData = view.copyPlane(realValue);
95+
DimensionedDataSource<U> theData = view.copyPlane(complexValue);
10096

10197
// calc some important variables
10298

@@ -108,25 +104,23 @@ DimensionedDataSource<U> compute(T cmplxAlg, V realAlg, DimensionedDataSource<W>
108104

109105
// create power of two square planes for calculations
110106

111-
DimensionedDataSource<U> inputPlane = DimensionedStorage.allocate((Allocatable) complexValue, new long[] {sz,sz});
107+
DimensionedDataSource<U> inputPlane = DimensionedStorage.allocate(complexValue, new long[] {sz,sz});
112108

113-
DimensionedDataSource<U> tmpPlane = DimensionedStorage.allocate((Allocatable) complexValue, new long[] {sz,sz});
109+
DimensionedDataSource<U> tmpPlane = DimensionedStorage.allocate(complexValue, new long[] {sz,sz});
114110

115-
DimensionedDataSource<U> outputPlane = DimensionedStorage.allocate((Allocatable) complexValue, new long[] {sz,sz});
111+
DimensionedDataSource<U> outputPlane = DimensionedStorage.allocate(complexValue, new long[] {sz,sz});
116112

117113
// Copy the rectangular input image into the square plane defined for it.
118114
// The other (padded) values are zero.
119115

120-
TwoDView<W> realVw = new TwoDView<>(theData);
116+
TwoDView<U> complexVw1 = new TwoDView<>(theData);
121117

122-
TwoDView<U> complexVw = new TwoDView<>(inputPlane);
118+
TwoDView<U> complexVw2 = new TwoDView<>(inputPlane);
123119

124120
for (long y = 0; y < rows; y++) {
125121
for (long x = 0; x < cols; x++) {
126-
realVw.get(x, y, realValue);
127-
complexValue.setR(realValue);
128-
complexValue.setI(zero);
129-
complexVw.set(x, y, complexValue);
122+
complexVw1.get(x, y, complexValue);
123+
complexVw2.set(x, y, complexValue);
130124
}
131125
}
132126

@@ -138,15 +132,15 @@ DimensionedDataSource<U> compute(T cmplxAlg, V realAlg, DimensionedDataSource<W>
138132

139133
IndexedDataSource<U> inCol = new SequencedDataSource<>(inputPlane.rawData(), c, sz, sz);
140134

141-
IndexedDataSource<U> inPiped = new FixedSizeZeroPaddedDataSource<T,U>(cmplxAlg, inCol, sz);
135+
IndexedDataSource<U> inPiped = new FFTDataSource<T,U>(complexAlg, inCol, sz);
142136

143137
// setup the padded tmp piped to place FFT results in
144138

145139
IndexedDataSource<U> tmpCol = new SequencedDataSource<>(tmpPlane.rawData(), c, sz, sz);
146140

147141
// do the fft into output col
148142

149-
FFT.compute(cmplxAlg, realAlg, inPiped, tmpCol);
143+
FFT.compute(complexAlg, realAlg, inPiped, tmpCol);
150144
}
151145

152146

@@ -158,92 +152,17 @@ DimensionedDataSource<U> compute(T cmplxAlg, V realAlg, DimensionedDataSource<W>
158152

159153
IndexedDataSource<U> tmpRow = new SequencedDataSource<>(tmpPlane.rawData(), r*sz, 1, sz);
160154

161-
IndexedDataSource<U> tmpPiped = new FixedSizeZeroPaddedDataSource<T,U>(cmplxAlg, tmpRow, sz);
155+
IndexedDataSource<U> tmpPiped = new FFTDataSource<T,U>(complexAlg, tmpRow, sz);
162156

163157
// setup the padded output piped to place FFT results in
164158

165159
IndexedDataSource<U> outRow = new SequencedDataSource<>(outputPlane.rawData(), r*sz, 1, sz);
166160

167161
// do the fft into output row
168162

169-
FFT.compute(cmplxAlg, realAlg, tmpPiped, outRow);
163+
FFT.compute(complexAlg, realAlg, tmpPiped, outRow);
170164
}
171165

172166
return outputPlane;
173167
}
174-
175-
private static class FixedSizeZeroPaddedDataSource<M extends Algebra<M,N>, N>
176-
implements IndexedDataSource<N>
177-
{
178-
final M alg;
179-
final IndexedDataSource<N> data;
180-
final long paddedDataSize;
181-
final long dataSize;
182-
183-
FixedSizeZeroPaddedDataSource(M alg, IndexedDataSource<N> ds, long powerOfTwoLimit) {
184-
185-
if (powerOfTwoLimit <= 0)
186-
throw new IllegalArgumentException("power of two limit must b 1 or greater");
187-
188-
if (powerOfTwoLimit < ds.size())
189-
throw new IllegalArgumentException("size of source not contained by size limit of piped");
190-
191-
if (FFT.enclosingPowerOf2(powerOfTwoLimit) != powerOfTwoLimit)
192-
throw new IllegalArgumentException("Provided powerOfTwoLimit is not a power of two");
193-
194-
this.alg = alg;
195-
196-
this.data = ds;
197-
198-
this.paddedDataSize = powerOfTwoLimit;
199-
200-
this.dataSize = data.size();
201-
}
202-
203-
@Override
204-
public IndexedDataSource<N> duplicate() {
205-
206-
return new FixedSizeZeroPaddedDataSource<>(alg, data, paddedDataSize);
207-
}
208-
209-
@Override
210-
public StorageConstruction storageType() {
211-
212-
return data.storageType();
213-
}
214-
215-
@Override
216-
public void set(long index, N value) {
217-
218-
if (index >= 0 && index < dataSize) {
219-
220-
data.set(index, value);
221-
}
222-
}
223-
224-
@Override
225-
public void get(long index, N value) {
226-
227-
if (index >= 0 && index < dataSize) {
228-
229-
data.get(index, value);
230-
}
231-
else {
232-
233-
alg.zero().call(value);
234-
}
235-
}
236-
237-
@Override
238-
public long size() {
239-
240-
return paddedDataSize;
241-
}
242-
243-
@Override
244-
public boolean accessWithOneThread() {
245-
246-
return data.accessWithOneThread();
247-
}
248-
}
249168
}

src/main/java/nom/bdezonia/zorbage/algorithm/InvFFT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ private InvFFT() {}
7272
V extends Algebra<V,W> & Trigonometric<W> & RealConstants<W> & Unity<W> &
7373
Multiplication<W> & Addition<W> & Invertible<W>,
7474
W>
75-
void compute(T cmplxAlg, V realAlg, IndexedDataSource<U> a,IndexedDataSource<U> b)
75+
void compute(T cmplxAlg, V realAlg, IndexedDataSource<U> a, IndexedDataSource<U> b)
7676
{
7777
long aSize = a.size();
7878
long bSize = b.size();
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/*
2+
* Zorbage: an algebraic data hierarchy for use in numeric processing.
3+
*
4+
* Copyright (c) 2016-2021 Barry DeZonia All rights reserved.
5+
*
6+
* Redistribution and use in source and binary forms, with or without modification,
7+
* are permitted provided that the following conditions are met:
8+
*
9+
* Redistributions of source code must retain the above copyright notice, this list
10+
* of conditions and the following disclaimer.
11+
*
12+
* Redistributions in binary form must reproduce the above copyright notice, this
13+
* list of conditions and the following disclaimer in the documentation and/or other
14+
* materials provided with the distribution.
15+
*
16+
* Neither the name of the <copyright holder> nor the names of its contributors may
17+
* be used to endorse or promote products derived from this software without specific
18+
* prior written permission.
19+
*
20+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23+
* IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
26+
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28+
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
29+
* DAMAGE.
30+
*/
31+
package nom.bdezonia.zorbage.algorithm;
32+
33+
import nom.bdezonia.zorbage.algebra.Addition;
34+
import nom.bdezonia.zorbage.algebra.Algebra;
35+
import nom.bdezonia.zorbage.algebra.Allocatable;
36+
import nom.bdezonia.zorbage.algebra.Conjugate;
37+
import nom.bdezonia.zorbage.algebra.Invertible;
38+
import nom.bdezonia.zorbage.algebra.Multiplication;
39+
import nom.bdezonia.zorbage.algebra.RealConstants;
40+
import nom.bdezonia.zorbage.algebra.SetComplex;
41+
import nom.bdezonia.zorbage.algebra.Trigonometric;
42+
import nom.bdezonia.zorbage.algebra.Unity;
43+
import nom.bdezonia.zorbage.data.DimensionedDataSource;
44+
import nom.bdezonia.zorbage.data.DimensionedStorage;
45+
import nom.bdezonia.zorbage.datasource.FFTDataSource;
46+
import nom.bdezonia.zorbage.datasource.IndexedDataSource;
47+
import nom.bdezonia.zorbage.datasource.SequencedDataSource;
48+
import nom.bdezonia.zorbage.dataview.PlaneView;
49+
import nom.bdezonia.zorbage.dataview.TwoDView;
50+
import nom.bdezonia.zorbage.sampling.IntegerIndex;
51+
52+
/**
53+
*
54+
* @author Barry DeZonia
55+
*
56+
*/
57+
public class InvFFT2D {
58+
59+
// do not instantiate
60+
61+
private InvFFT2D() { }
62+
63+
/**
64+
* Do a 2-dimensional inverse fourier transform of 1 plane of a complex DataSource
65+
*
66+
* @param <T>
67+
* @param <U>
68+
* @param <V>
69+
* @param <W>
70+
* @param cmplxAlg
71+
* @param realAlg
72+
* @param cmplxData
73+
* @param axis0
74+
* @param axis1
75+
* @param otherPositions
76+
* @return
77+
*/
78+
public static
79+
<T extends Algebra<T,U> & Addition<U> & Multiplication<U> & Conjugate<U>,
80+
U extends SetComplex<W> & Allocatable<U>,
81+
V extends Algebra<V,W> & Trigonometric<W> & RealConstants<W> & Unity<W> &
82+
Multiplication<W> & Addition<W> & Invertible<W>,
83+
W>
84+
DimensionedDataSource<U> compute(T complexAlg, V realAlg, DimensionedDataSource<U> complexData, int axis0, int axis1, IntegerIndex otherPositions)
85+
{
86+
U complexValue = complexAlg.construct();
87+
88+
// grab the plane of data the user is interested in
89+
90+
PlaneView<U> view = new PlaneView<U>(complexData, axis0, axis1);
91+
92+
for (int i = 0; i < otherPositions.numDimensions(); i++) {
93+
view.setPositionValue(i, otherPositions.get(i));
94+
}
95+
96+
DimensionedDataSource<U> theData = view.copyPlane(complexValue);
97+
98+
// calc some important variables
99+
100+
long cols = theData.dimension(0);
101+
102+
long rows = theData.dimension(1);
103+
104+
long sz = FFT.enclosingPowerOf2(Math.max(rows, cols));
105+
106+
// create power of two square planes for calculations
107+
108+
DimensionedDataSource<U> inputPlane = DimensionedStorage.allocate(complexValue, new long[] {sz,sz});
109+
110+
DimensionedDataSource<U> tmpPlane = DimensionedStorage.allocate(complexValue, new long[] {sz,sz});
111+
112+
DimensionedDataSource<U> outputPlane = DimensionedStorage.allocate(complexValue, new long[] {sz,sz});
113+
114+
// Copy the rectangular input image into the square plane defined for it.
115+
// The other (padded) values are zero.
116+
117+
TwoDView<U> complexVw1 = new TwoDView<>(theData);
118+
119+
TwoDView<U> complexVw2 = new TwoDView<>(inputPlane);
120+
121+
for (long y = 0; y < rows; y++) {
122+
for (long x = 0; x < cols; x++) {
123+
complexVw1.get(x, y, complexValue);
124+
complexVw2.set(x, y, complexValue);
125+
}
126+
}
127+
128+
// for each column of input do a 1-d FFT and store as a column in temp data
129+
130+
for (long c = 0; c < sz; c++) {
131+
132+
// setup the padded input piped to do FFT on
133+
134+
IndexedDataSource<U> inCol = new SequencedDataSource<>(inputPlane.rawData(), c, sz, sz);
135+
136+
IndexedDataSource<U> inPiped = new FFTDataSource<T,U>(complexAlg, inCol, sz);
137+
138+
// setup the padded tmp piped to place FFT results in
139+
140+
IndexedDataSource<U> tmpCol = new SequencedDataSource<>(tmpPlane.rawData(), c, sz, sz);
141+
142+
// do the inv fft into output col
143+
144+
InvFFT.compute(complexAlg, realAlg, inPiped, tmpCol);
145+
}
146+
147+
148+
// for each row of temp data do a 1-d FFT and store as a row in output
149+
150+
for (long r = 0; r < sz; r++) {
151+
152+
// setup the padded tmp piped to do FFT on
153+
154+
IndexedDataSource<U> tmpRow = new SequencedDataSource<>(tmpPlane.rawData(), r*sz, 1, sz);
155+
156+
IndexedDataSource<U> tmpPiped = new FFTDataSource<T,U>(complexAlg, tmpRow, sz);
157+
158+
// setup the padded output piped to place FFT results in
159+
160+
IndexedDataSource<U> outRow = new SequencedDataSource<>(outputPlane.rawData(), r*sz, 1, sz);
161+
162+
// do the inv fft into output row
163+
164+
InvFFT.compute(complexAlg, realAlg, tmpPiped, outRow);
165+
}
166+
167+
return outputPlane;
168+
}
169+
}

0 commit comments

Comments
 (0)