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. 038 * 039 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 040 * 041 */ 042public class FourierTransform { 043 private FImage phase; 044 private FImage magnitude; 045 private boolean centre; 046 047 /** 048 * Construct Fourier Transform by performing a forward transform 049 * on the given image. If the centre option is set, the FFT will 050 * be re-ordered so that the DC component is in the centre. 051 * @param image the image to transform 052 * @param centre should the FFT be reordered so the centre is DC component 053 */ 054 public FourierTransform(FImage image, boolean centre) { 055 this.centre = centre; 056 057 process(image); 058 } 059 060 /** 061 * Construct Fourier Transform object from the given magnitude and 062 * phase images in the frequency domain. The resultant object can 063 * then be used to construct the image using the {@link #inverse()} 064 * method. 065 * 066 * @param magnitude the magnitude image 067 * @param phase the phase image 068 * @param centre is the DC component in the image centre? 069 */ 070 public FourierTransform(FImage magnitude, FImage phase, boolean centre) { 071 this.centre = centre; 072 this.magnitude = magnitude; 073 this.phase = phase; 074 } 075 076 /** 077 * Prepare data for a input to the FFT, padding if necessary. 078 * 079 * @param input input data 080 * @param rs desired number of rows 081 * @param cs desired number of columns 082 * @param centre if true, then the data will be prepared so that the DC component is centred. 083 * @return prepared data 084 */ 085 public static float[][] prepareData(FImage input, int rs, int cs, boolean centre) { 086 return prepareData(input.pixels, rs, cs, centre); 087 } 088 089 /** 090 * Prepare data for a input to the FFT, padding if necessary. The data is 091 * prepared as a packed 1D array. 092 * 093 * @param input input data 094 * @param rs desired number of rows 095 * @param cs desired number of columns 096 * @param centre if true, then the data will be prepared so that the DC component is centred. 097 * @return prepared data 098 */ 099 public static float[] prepareData1d(FImage input, int rs, int cs, boolean centre) { 100 return prepareData1d(input.pixels, rs, cs, centre); 101 } 102 103 /** 104 * Prepare data for a input to the FFT, padding if necessary. 105 * 106 * @param input input data 107 * @param rs desired number of rows 108 * @param cs desired number of columns 109 * @param centre if true, then the data will be prepared so that the DC component is centered. 110 * @return prepared data 111 */ 112 public static float[][] prepareData(float[][] input, int rs, int cs, boolean centre) { 113 float[][] prepared = new float[rs][cs*2]; 114 115 if (centre) { 116 for(int r = 0; r < Math.min(rs, input.length) ; r++) { 117 for(int c = 0; c < Math.min(cs, input[0].length); c++) { 118 prepared[r][c*2] = input[r][c] * (1 - 2 * ((r+c)%2) ); 119 } 120 } 121 } else { 122 for(int r = 0; r < Math.min(rs, input.length) ; r++) { 123 for(int c = 0; c < Math.min(cs, input[0].length); c++) { 124 prepared[r][c*2] = input[r][c]; 125 } 126 } 127 } 128 129 return prepared; 130 } 131 132 /** 133 * Prepare data for a input to the FFT, padding if necessary. The data is 134 * prepared as a packed 1D array. 135 * 136 * @param input input data 137 * @param rs desired number of rows 138 * @param cs desired number of columns 139 * @param centre if true, then the data will be prepared so that the DC component is centered. 140 * @return prepared data 141 */ 142 public static float[] prepareData1d(float[][] input, int rs, int cs, boolean centre) { 143 float[] prepared = new float[rs * cs * 2]; 144 145 if (centre) { 146 for(int r = 0; r < Math.min(rs, input.length) ; r++) { 147 for(int c = 0; c < Math.min(cs, input[0].length); c++) { 148 prepared[r * 2 * cs + 2*c] = input[r][c] * (1 - 2 * ((r+c)%2) ); 149 } 150 } 151 } else { 152 for(int r = 0; r < Math.min(rs, input.length) ; r++) { 153 for(int c = 0; c < Math.min(cs, input[0].length); c++) { 154 prepared[r * 2 * cs + 2*c] = input[r][c]; 155 } 156 } 157 } 158 159 return prepared; 160 } 161 162 /** 163 * Extract the actual data from prepared data. The output image 164 * must have the same number of rows as the prepared data, and 165 * half the number of columns. 166 * 167 * @param prepared the prepared data 168 * @param output the output 169 * @param centre if true, then the data will be prepared so that the DC component is centered. 170 */ 171 public static void unprepareData(float[][] prepared, FImage output, boolean centre) { 172 unprepareData(prepared, output.pixels, centre); 173 } 174 175 /** 176 * Extract the actual data from prepared data. The output image 177 * must have the same number of rows as the prepared data, and 178 * half the number of columns. 179 * 180 * @param prepared the prepared data 181 * @param output the output 182 * @param centre if true, then the data will be prepared so that the DC component is centered. 183 */ 184 public static void unprepareData(float[] prepared, FImage output, boolean centre) { 185 unprepareData(prepared, output.pixels, centre); 186 } 187 188 /** 189 * Extract the actual data from prepared data. The output array 190 * must have the same number of rows as the prepared data, and 191 * half the number of columns. 192 * 193 * @param prepared the prepared data 194 * @param output the output 195 * @param centre if true, then the data will be prepared so that the DC component is centered. 196 */ 197 public static void unprepareData(float[][] prepared, float[][] output, boolean centre) { 198 int rs = output.length; 199 int cs = output[0].length; 200 201 if (centre) { 202 for(int r = 0; r < rs ; r++) { 203 for(int c = 0; c < cs; c++) { 204 output[r][c] = prepared[r][c*2] * (1 - 2 * ((r+c)%2) ); 205 } 206 } 207 } else { 208 for(int r = 0; r < rs ; r++) { 209 for(int c = 0; c < cs; c++) { 210 output[r][c] = prepared[r][c*2]; 211 } 212 } 213 } 214 } 215 216 /** 217 * Extract the actual data from prepared data. The output array 218 * must have the same number of rows as the prepared data, and 219 * half the number of columns. 220 * 221 * @param prepared the prepared data 222 * @param output the output 223 * @param centre if true, then the data will be prepared so that the DC component is centered. 224 */ 225 public static void unprepareData(float[] prepared, float[][] output, boolean centre) { 226 int rs = output.length; 227 int cs = output[0].length; 228 229 if (centre) { 230 for(int r = 0; r < rs ; r++) { 231 for(int c = 0; c < cs; c++) { 232 output[r][c] = prepared[r * 2 * cs + 2*c] * (1 - 2 * ((r+c)%2) ); 233 } 234 } 235 } else { 236 for(int r = 0; r < rs ; r++) { 237 for(int c = 0; c < cs; c++) { 238 output[r][c] = prepared[r * 2 * cs + 2*c]; 239 } 240 } 241 } 242 } 243 244 private void process(FImage image) { 245 int cs = image.getCols(); 246 int rs = image.getRows(); 247 248 phase = new FImage(cs, rs); 249 magnitude = new FImage(cs, rs); 250 251 FloatFFT_2D fft = new FloatFFT_2D(rs,cs); 252 float[][] prepared = prepareData(image.pixels, rs, cs, centre); 253 254 fft.complexForward(prepared); 255 256 for(int y = 0; y < rs; y++){ 257 for(int x = 0; x < cs; x++){ 258 float re = prepared[y][x*2]; 259 float im = prepared[y][1 + x*2]; 260 261 phase.pixels[y][x] = (float) Math.atan2(im, re); 262 magnitude.pixels[y][x] = (float) Math.sqrt(re*re + im*im); 263 } 264 } 265 } 266 267 /** 268 * Perform the inverse FFT using the underlying magnitude 269 * and phase images. The resultant reconstructed image 270 * may need normalisation. 271 * 272 * @return the reconstructed image 273 */ 274 public FImage inverse() { 275 int cs = magnitude.getCols(); 276 int rs = magnitude.getRows(); 277 278 FloatFFT_2D fft = new FloatFFT_2D(rs,cs); 279 float[][] prepared = new float[rs][cs*2]; 280 for(int y = 0; y < rs; y++) { 281 for(int x = 0; x < cs; x++) { 282 float p = phase.pixels[y][x]; 283 float m = magnitude.pixels[y][x]; 284 285 float re = (float) (m*Math.cos(p)); 286 float im = (float) (m*Math.sin(p)); 287 288 prepared[y][x*2] = re; 289 prepared[y][1 + x*2] = im; 290 } 291 } 292 293 fft.complexInverse(prepared, true); 294 295 FImage image = new FImage(cs, rs); 296 unprepareData(prepared, image, centre); 297 298 return image; 299 } 300 301 /** 302 * Get a log-normalised copy of the magnitude image suitable 303 * for displaying. 304 * @return a log-normalised copy of the magnitude image 305 */ 306 public FImage getLogNormalisedMagnitude() { 307 FImage im = magnitude.clone(); 308 309 for (int y=0; y<im.height; y++) { 310 for (int x=0; x<im.width; x++) { 311 im.pixels[y][x] = (float) Math.log(im.pixels[y][x] + 1); 312 } 313 } 314 315 return im.normalise(); 316 } 317 318 /** 319 * @return the phase image 320 */ 321 public FImage getPhase() { 322 return phase; 323 } 324 325 /** 326 * @return the magnitude image 327 */ 328 public FImage getMagnitude() { 329 return magnitude; 330 } 331 332 /** 333 * @return true if the DC component is in the centre; false otherwise 334 */ 335 public boolean isCentre() { 336 return centre; 337 } 338}