001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package org.openimaj.image.processing.algorithm;
031
032import org.openimaj.image.FImage;
033
034import edu.emory.mathcs.jtransforms.fft.FloatFFT_2D;
035
036/**
037 * Perform forward and inverse Fast Fourier Transforms on image data. This class
038 * computes the result of the transform in polar form (i.e. in terms of phase
039 * and magnitude). If you need the result of the transform in the raw complex
040 * form, then use the {@link FourierTransformComplex} class.
041 *
042 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
043 *
044 */
045public class FourierTransform {
046        private FImage phase;
047        private FImage magnitude;
048        private boolean centre;
049
050        /**
051         * Construct Fourier Transform by performing a forward transform on the
052         * given image. If the centre option is set, the FFT will be re-ordered so
053         * that the DC component is in the centre.
054         *
055         * @param image
056         *            the image to transform
057         * @param centre
058         *            should the FFT be reordered so the centre is DC component
059         */
060        public FourierTransform(FImage image, boolean centre) {
061                this.centre = centre;
062
063                process(image);
064        }
065
066        /**
067         * Construct Fourier Transform object from the given magnitude and phase
068         * images in the frequency domain. The resultant object can then be used to
069         * construct the image using the {@link #inverse()} method.
070         *
071         * @param magnitude
072         *            the magnitude image
073         * @param phase
074         *            the phase image
075         * @param centre
076         *            is the DC component in the image centre?
077         */
078        public FourierTransform(FImage magnitude, FImage phase, boolean centre) {
079                this.centre = centre;
080                this.magnitude = magnitude;
081                this.phase = phase;
082        }
083
084        /**
085         * Prepare data for a input to the FFT, padding if necessary.
086         *
087         * @param input
088         *            input data
089         * @param rs
090         *            desired number of rows
091         * @param cs
092         *            desired number of columns
093         * @param centre
094         *            if true, then the data will be prepared so that the DC
095         *            component is centred.
096         * @return prepared data
097         */
098        public static float[][] prepareData(FImage input, int rs, int cs, boolean centre) {
099                return prepareData(input.pixels, rs, cs, centre);
100        }
101
102        /**
103         * Prepare data for a input to the FFT, padding if necessary. The data is
104         * prepared as a packed 1D array.
105         *
106         * @param input
107         *            input data
108         * @param rs
109         *            desired number of rows
110         * @param cs
111         *            desired number of columns
112         * @param centre
113         *            if true, then the data will be prepared so that the DC
114         *            component is centred.
115         * @return prepared data
116         */
117        public static float[] prepareData1d(FImage input, int rs, int cs, boolean centre) {
118                return prepareData1d(input.pixels, rs, cs, centre);
119        }
120
121        /**
122         * Prepare data for a input to the FFT, padding if necessary.
123         *
124         * @param input
125         *            input data
126         * @param rs
127         *            desired number of rows
128         * @param cs
129         *            desired number of columns
130         * @param centre
131         *            if true, then the data will be prepared so that the DC
132         *            component is centered.
133         * @return prepared data
134         */
135        public static float[][] prepareData(float[][] input, int rs, int cs, boolean centre) {
136                final float[][] prepared = new float[rs][cs * 2];
137
138                if (centre) {
139                        for (int r = 0; r < Math.min(rs, input.length); r++) {
140                                for (int c = 0; c < Math.min(cs, input[0].length); c++) {
141                                        prepared[r][c * 2] = input[r][c] * (1 - 2 * ((r + c) % 2));
142                                }
143                        }
144                } else {
145                        for (int r = 0; r < Math.min(rs, input.length); r++) {
146                                for (int c = 0; c < Math.min(cs, input[0].length); c++) {
147                                        prepared[r][c * 2] = input[r][c];
148                                }
149                        }
150                }
151
152                return prepared;
153        }
154
155        /**
156         * Prepare data for a input to the FFT, padding if necessary. The data is
157         * prepared as a packed 1D array.
158         *
159         * @param input
160         *            input data
161         * @param rs
162         *            desired number of rows
163         * @param cs
164         *            desired number of columns
165         * @param centre
166         *            if true, then the data will be prepared so that the DC
167         *            component is centered.
168         * @return prepared data
169         */
170        public static float[] prepareData1d(float[][] input, int rs, int cs, boolean centre) {
171                final float[] prepared = new float[rs * cs * 2];
172
173                if (centre) {
174                        for (int r = 0; r < Math.min(rs, input.length); r++) {
175                                for (int c = 0; c < Math.min(cs, input[0].length); c++) {
176                                        prepared[r * 2 * cs + 2 * c] = input[r][c] * (1 - 2 * ((r + c) % 2));
177                                }
178                        }
179                } else {
180                        for (int r = 0; r < Math.min(rs, input.length); r++) {
181                                for (int c = 0; c < Math.min(cs, input[0].length); c++) {
182                                        prepared[r * 2 * cs + 2 * c] = input[r][c];
183                                }
184                        }
185                }
186
187                return prepared;
188        }
189
190        /**
191         * Extract the actual data from prepared data. The output image must have
192         * the same number of rows as the prepared data, and half the number of
193         * columns.
194         *
195         * @param prepared
196         *            the prepared data
197         * @param output
198         *            the output
199         * @param centre
200         *            if true, then the data will be prepared so that the DC
201         *            component is centered.
202         */
203        public static void unprepareData(float[][] prepared, FImage output, boolean centre) {
204                unprepareData(prepared, output.pixels, centre);
205        }
206
207        /**
208         * Extract the actual data from prepared data. The output image must have
209         * the same number of rows as the prepared data, and half the number of
210         * columns.
211         *
212         * @param prepared
213         *            the prepared data
214         * @param output
215         *            the output
216         * @param centre
217         *            if true, then the data will be prepared so that the DC
218         *            component is centered.
219         */
220        public static void unprepareData(float[] prepared, FImage output, boolean centre) {
221                unprepareData(prepared, output.pixels, centre);
222        }
223
224        /**
225         * Extract the actual data from prepared data. The output array must have
226         * the same number of rows as the prepared data, and half the number of
227         * columns.
228         *
229         * @param prepared
230         *            the prepared data
231         * @param output
232         *            the output
233         * @param centre
234         *            if true, then the data will be prepared so that the DC
235         *            component is centered.
236         */
237        public static void unprepareData(float[][] prepared, float[][] output, boolean centre) {
238                final int rs = output.length;
239                final int cs = output[0].length;
240
241                if (centre) {
242                        for (int r = 0; r < rs; r++) {
243                                for (int c = 0; c < cs; c++) {
244                                        output[r][c] = prepared[r][c * 2] * (1 - 2 * ((r + c) % 2));
245                                }
246                        }
247                } else {
248                        for (int r = 0; r < rs; r++) {
249                                for (int c = 0; c < cs; c++) {
250                                        output[r][c] = prepared[r][c * 2];
251                                }
252                        }
253                }
254        }
255
256        /**
257         * Extract the actual data from prepared data. The output array must have
258         * the same number of rows as the prepared data, and half the number of
259         * columns.
260         *
261         * @param prepared
262         *            the prepared data
263         * @param output
264         *            the output
265         * @param centre
266         *            if true, then the data will be prepared so that the DC
267         *            component is centered.
268         */
269        public static void unprepareData(float[] prepared, float[][] output, boolean centre) {
270                final int rs = output.length;
271                final int cs = output[0].length;
272
273                if (centre) {
274                        for (int r = 0; r < rs; r++) {
275                                for (int c = 0; c < cs; c++) {
276                                        output[r][c] = prepared[r * 2 * cs + 2 * c] * (1 - 2 * ((r + c) % 2));
277                                }
278                        }
279                } else {
280                        for (int r = 0; r < rs; r++) {
281                                for (int c = 0; c < cs; c++) {
282                                        output[r][c] = prepared[r * 2 * cs + 2 * c];
283                                }
284                        }
285                }
286        }
287
288        private void process(FImage image) {
289                final int cs = image.getCols();
290                final int rs = image.getRows();
291
292                phase = new FImage(cs, rs);
293                magnitude = new FImage(cs, rs);
294
295                final FloatFFT_2D fft = new FloatFFT_2D(rs, cs);
296                final float[][] prepared = prepareData(image.pixels, rs, cs, centre);
297
298                fft.complexForward(prepared);
299
300                for (int y = 0; y < rs; y++) {
301                        for (int x = 0; x < cs; x++) {
302                                final float re = prepared[y][x * 2];
303                                final float im = prepared[y][1 + x * 2];
304
305                                phase.pixels[y][x] = (float) Math.atan2(im, re);
306                                magnitude.pixels[y][x] = (float) Math.sqrt(re * re + im * im);
307                        }
308                }
309        }
310
311        /**
312         * Perform the inverse FFT using the underlying magnitude and phase images.
313         * The resultant reconstructed image may need normalisation.
314         *
315         * @return the reconstructed image
316         */
317        public FImage inverse() {
318                final int cs = magnitude.getCols();
319                final int rs = magnitude.getRows();
320
321                final FloatFFT_2D fft = new FloatFFT_2D(rs, cs);
322                final float[][] prepared = new float[rs][cs * 2];
323                for (int y = 0; y < rs; y++) {
324                        for (int x = 0; x < cs; x++) {
325                                final float p = phase.pixels[y][x];
326                                final float m = magnitude.pixels[y][x];
327
328                                final float re = (float) (m * Math.cos(p));
329                                final float im = (float) (m * Math.sin(p));
330
331                                prepared[y][x * 2] = re;
332                                prepared[y][1 + x * 2] = im;
333                        }
334                }
335
336                fft.complexInverse(prepared, true);
337
338                final FImage image = new FImage(cs, rs);
339                unprepareData(prepared, image, centre);
340
341                return image;
342        }
343
344        /**
345         * Get a log-normalised copy of the magnitude image suitable for displaying.
346         *
347         * @return a log-normalised copy of the magnitude image
348         */
349        public FImage getLogNormalisedMagnitude() {
350                final FImage im = magnitude.clone();
351
352                for (int y = 0; y < im.height; y++) {
353                        for (int x = 0; x < im.width; x++) {
354                                im.pixels[y][x] = (float) Math.log(im.pixels[y][x] + 1);
355                        }
356                }
357
358                return im.normalise();
359        }
360
361        /**
362         * @return the phase image
363         */
364        public FImage getPhase() {
365                return phase;
366        }
367
368        /**
369         * @return the magnitude image
370         */
371        public FImage getMagnitude() {
372                return magnitude;
373        }
374
375        /**
376         * @return true if the DC component is in the centre; false otherwise
377         */
378        public boolean isCentre() {
379                return centre;
380        }
381}