blob: 5aec4f8958d3b473773d56883228d7993ab307e3 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% FFFFF OOO U U RRRR IIIII EEEEE RRRR %
7% F O O U U R R I E R R %
8% FFF O O U U RRRR I EEE RRRR %
9% F O O U U R R I E R R %
10% F OOO UUU R R IIIII EEEEE R R %
11% %
12% %
13% MagickCore Discrete Fourier Transform Methods %
14% %
15% Software Design %
16% Sean Burke %
17% Fred Weinhaus %
18% John Cristy %
19% July 2009 %
20% %
21% %
cristy1454be72011-12-19 01:52:48 +000022% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000023% dedicated to making software imaging solutions freely available. %
24% %
25% You may not use this file except in compliance with the License. You may %
26% obtain a copy of the License at %
27% %
28% http://www.imagemagick.org/script/license.php %
29% %
30% Unless required by applicable law or agreed to in writing, software %
31% distributed under the License is distributed on an "AS IS" BASIS, %
32% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
33% See the License for the specific language governing permissions and %
34% limitations under the License. %
35% %
36%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
37%
38%
39%
40*/
41
42/*
43 Include declarations.
44*/
cristy4c08aed2011-07-01 19:47:50 +000045#include "MagickCore/studio.h"
46#include "MagickCore/attribute.h"
47#include "MagickCore/cache.h"
48#include "MagickCore/image.h"
49#include "MagickCore/image-private.h"
50#include "MagickCore/list.h"
51#include "MagickCore/fourier.h"
52#include "MagickCore/log.h"
53#include "MagickCore/memory_.h"
54#include "MagickCore/monitor.h"
55#include "MagickCore/pixel-accessor.h"
56#include "MagickCore/property.h"
57#include "MagickCore/quantum-private.h"
cristyac245f82012-05-05 17:13:57 +000058#include "MagickCore/resource_.h"
cristy4c08aed2011-07-01 19:47:50 +000059#include "MagickCore/thread-private.h"
cristy3ed852e2009-09-05 21:47:34 +000060#if defined(MAGICKCORE_FFTW_DELEGATE)
cristy56ed31c2010-03-22 00:46:21 +000061#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy3ed852e2009-09-05 21:47:34 +000062#include <complex.h>
cristy56ed31c2010-03-22 00:46:21 +000063#endif
cristy3ed852e2009-09-05 21:47:34 +000064#include <fftw3.h>
cristy47b022b2011-01-18 22:29:21 +000065#if !defined(MAGICKCORE_HAVE_CABS)
66#define cabs(z) (sqrt(z[0]*z[0]+z[1]*z[1]))
67#endif
68#if !defined(MAGICKCORE_HAVE_CARG)
cristy4da3ba32011-02-07 16:58:11 +000069#define carg(z) (atan2(cimag(z),creal(z)))
cristy47b022b2011-01-18 22:29:21 +000070#endif
71#if !defined(MAGICKCORE_HAVE_CIMAG)
72#define cimag(z) (z[1])
73#endif
74#if !defined(MAGICKCORE_HAVE_CREAL)
75#define creal(z) (z[0])
76#endif
cristy3ed852e2009-09-05 21:47:34 +000077#endif
78
79/*
80 Typedef declarations.
81*/
82typedef struct _FourierInfo
83{
cristyd3090f92011-10-18 00:05:15 +000084 PixelChannel
cristy3ed852e2009-09-05 21:47:34 +000085 channel;
86
87 MagickBooleanType
88 modulus;
89
cristybb503372010-05-27 20:51:26 +000090 size_t
cristy3ed852e2009-09-05 21:47:34 +000091 width,
92 height;
93
cristybb503372010-05-27 20:51:26 +000094 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000095 center;
96} FourierInfo;
97
98/*
99%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
100% %
101% %
102% %
103% F o r w a r d F o u r i e r T r a n s f o r m I m a g e %
104% %
105% %
106% %
107%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
108%
109% ForwardFourierTransformImage() implements the discrete Fourier transform
110% (DFT) of the image either as a magnitude / phase or real / imaginary image
111% pair.
112%
113% The format of the ForwadFourierTransformImage method is:
114%
115% Image *ForwardFourierTransformImage(const Image *image,
116% const MagickBooleanType modulus,ExceptionInfo *exception)
117%
118% A description of each parameter follows:
119%
120% o image: the image.
121%
122% o modulus: if true, return as transform as a magnitude / phase pair
123% otherwise a real / imaginary image pair.
124%
125% o exception: return any errors or warnings in this structure.
126%
127*/
128
129#if defined(MAGICKCORE_FFTW_DELEGATE)
130
cristyc4ea4a42011-01-24 01:43:30 +0000131static MagickBooleanType RollFourier(const size_t width,const size_t height,
132 const ssize_t x_offset,const ssize_t y_offset,double *fourier)
cristy3ed852e2009-09-05 21:47:34 +0000133{
134 double
135 *roll;
136
cristyc4ea4a42011-01-24 01:43:30 +0000137 register ssize_t
138 i,
139 x;
140
cristybb503372010-05-27 20:51:26 +0000141 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000142 u,
143 v,
144 y;
145
cristy3ed852e2009-09-05 21:47:34 +0000146 /*
cristy2da05352010-09-15 19:22:17 +0000147 Move zero frequency (DC, average color) from (0,0) to (width/2,height/2).
cristy3ed852e2009-09-05 21:47:34 +0000148 */
cristyc4ea4a42011-01-24 01:43:30 +0000149 roll=(double *) AcquireQuantumMemory((size_t) height,width*sizeof(*roll));
cristy3ed852e2009-09-05 21:47:34 +0000150 if (roll == (double *) NULL)
151 return(MagickFalse);
152 i=0L;
cristybb503372010-05-27 20:51:26 +0000153 for (y=0L; y < (ssize_t) height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000154 {
155 if (y_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000156 v=((y+y_offset) < 0L) ? y+y_offset+(ssize_t) height : y+y_offset;
cristy3ed852e2009-09-05 21:47:34 +0000157 else
cristybb503372010-05-27 20:51:26 +0000158 v=((y+y_offset) > ((ssize_t) height-1L)) ? y+y_offset-(ssize_t) height :
cristy3ed852e2009-09-05 21:47:34 +0000159 y+y_offset;
cristybb503372010-05-27 20:51:26 +0000160 for (x=0L; x < (ssize_t) width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000161 {
162 if (x_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000163 u=((x+x_offset) < 0L) ? x+x_offset+(ssize_t) width : x+x_offset;
cristy3ed852e2009-09-05 21:47:34 +0000164 else
cristybb503372010-05-27 20:51:26 +0000165 u=((x+x_offset) > ((ssize_t) width-1L)) ? x+x_offset-(ssize_t) width :
cristy3ed852e2009-09-05 21:47:34 +0000166 x+x_offset;
167 roll[v*width+u]=fourier[i++];
cristyc4ea4a42011-01-24 01:43:30 +0000168 }
cristy3ed852e2009-09-05 21:47:34 +0000169 }
cristyc4ea4a42011-01-24 01:43:30 +0000170 (void) CopyMagickMemory(fourier,roll,height*width*sizeof(*roll));
cristy3ed852e2009-09-05 21:47:34 +0000171 roll=(double *) RelinquishMagickMemory(roll);
172 return(MagickTrue);
173}
174
cristybb503372010-05-27 20:51:26 +0000175static MagickBooleanType ForwardQuadrantSwap(const size_t width,
176 const size_t height,double *source,double *destination)
cristy3ed852e2009-09-05 21:47:34 +0000177{
cristy3ed852e2009-09-05 21:47:34 +0000178 MagickBooleanType
179 status;
180
cristybb503372010-05-27 20:51:26 +0000181 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000182 x;
183
cristyc4ea4a42011-01-24 01:43:30 +0000184 ssize_t
185 center,
186 y;
187
cristy3ed852e2009-09-05 21:47:34 +0000188 /*
189 Swap quadrants.
190 */
cristybb503372010-05-27 20:51:26 +0000191 center=(ssize_t) floor((double) width/2L)+1L;
192 status=RollFourier((size_t) center,height,0L,(ssize_t) height/2L,source);
cristy3ed852e2009-09-05 21:47:34 +0000193 if (status == MagickFalse)
194 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000195 for (y=0L; y < (ssize_t) height; y++)
196 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000197 destination[width*y+x+width/2L]=source[center*y+x];
cristybb503372010-05-27 20:51:26 +0000198 for (y=1; y < (ssize_t) height; y++)
199 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000200 destination[width*(height-y)+width/2L-x-1L]=source[center*y+x+1L];
cristybb503372010-05-27 20:51:26 +0000201 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000202 destination[-x+width/2L-1L]=destination[x+width/2L+1L];
203 return(MagickTrue);
204}
205
cristyc4ea4a42011-01-24 01:43:30 +0000206static void CorrectPhaseLHS(const size_t width,const size_t height,
207 double *fourier)
cristy3ed852e2009-09-05 21:47:34 +0000208{
cristybb503372010-05-27 20:51:26 +0000209 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000210 x;
211
cristy9d314ff2011-03-09 01:30:28 +0000212 ssize_t
213 y;
214
cristybb503372010-05-27 20:51:26 +0000215 for (y=0L; y < (ssize_t) height; y++)
216 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000217 fourier[y*width+x]*=(-1.0);
218}
219
220static MagickBooleanType ForwardFourier(const FourierInfo *fourier_info,
221 Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
222{
223 CacheView
224 *magnitude_view,
225 *phase_view;
226
227 double
228 *magnitude_source,
229 *phase_source;
230
231 Image
232 *magnitude_image,
233 *phase_image;
234
cristy3ed852e2009-09-05 21:47:34 +0000235 MagickBooleanType
236 status;
237
cristy4c08aed2011-07-01 19:47:50 +0000238 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000239 *q;
240
cristyf5742792012-11-27 18:31:26 +0000241 register ssize_t
242 x;
243
cristyc4ea4a42011-01-24 01:43:30 +0000244 ssize_t
245 i,
246 y;
247
cristy3ed852e2009-09-05 21:47:34 +0000248 magnitude_image=GetFirstImageInList(image);
249 phase_image=GetNextImageInList(image);
250 if (phase_image == (Image *) NULL)
251 {
252 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
anthonye5b39652012-04-21 05:37:29 +0000253 "TwoOrMoreImagesRequired","'%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000254 return(MagickFalse);
255 }
256 /*
257 Create "Fourier Transform" image from constituent arrays.
258 */
259 magnitude_source=(double *) AcquireQuantumMemory((size_t)
260 fourier_info->height,fourier_info->width*sizeof(*magnitude_source));
261 if (magnitude_source == (double *) NULL)
262 return(MagickFalse);
cristyc4ea4a42011-01-24 01:43:30 +0000263 (void) ResetMagickMemory(magnitude_source,0,fourier_info->height*
264 fourier_info->width*sizeof(*magnitude_source));
cristy3ed852e2009-09-05 21:47:34 +0000265 phase_source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
266 fourier_info->width*sizeof(*phase_source));
cristyc4ea4a42011-01-24 01:43:30 +0000267 if (phase_source == (double *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000268 {
269 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000270 ResourceLimitError,"MemoryAllocationFailed","'%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000271 magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
272 return(MagickFalse);
273 }
274 status=ForwardQuadrantSwap(fourier_info->height,fourier_info->height,
275 magnitude,magnitude_source);
276 if (status != MagickFalse)
277 status=ForwardQuadrantSwap(fourier_info->height,fourier_info->height,phase,
278 phase_source);
279 CorrectPhaseLHS(fourier_info->height,fourier_info->height,phase_source);
280 if (fourier_info->modulus != MagickFalse)
281 {
282 i=0L;
cristybb503372010-05-27 20:51:26 +0000283 for (y=0L; y < (ssize_t) fourier_info->height; y++)
284 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000285 {
286 phase_source[i]/=(2.0*MagickPI);
287 phase_source[i]+=0.5;
288 i++;
289 }
290 }
cristydb070952012-04-20 14:33:00 +0000291 magnitude_view=AcquireAuthenticCacheView(magnitude_image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000292 i=0L;
cristybb503372010-05-27 20:51:26 +0000293 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000294 {
295 q=GetCacheViewAuthenticPixels(magnitude_view,0L,y,fourier_info->height,1UL,
296 exception);
cristyacd2ed22011-08-30 01:44:23 +0000297 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000298 break;
cristybb503372010-05-27 20:51:26 +0000299 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000300 {
301 switch (fourier_info->channel)
302 {
cristyd3090f92011-10-18 00:05:15 +0000303 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000304 default:
305 {
cristy4c08aed2011-07-01 19:47:50 +0000306 SetPixelRed(magnitude_image,ClampToQuantum(QuantumRange*
307 magnitude_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000308 break;
309 }
cristyd3090f92011-10-18 00:05:15 +0000310 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000311 {
cristy4c08aed2011-07-01 19:47:50 +0000312 SetPixelGreen(magnitude_image,ClampToQuantum(QuantumRange*
313 magnitude_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000314 break;
315 }
cristyd3090f92011-10-18 00:05:15 +0000316 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000317 {
cristy4c08aed2011-07-01 19:47:50 +0000318 SetPixelBlue(magnitude_image,ClampToQuantum(QuantumRange*
319 magnitude_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000320 break;
321 }
cristyd3090f92011-10-18 00:05:15 +0000322 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000323 {
cristy4c08aed2011-07-01 19:47:50 +0000324 SetPixelBlack(magnitude_image,ClampToQuantum(QuantumRange*
325 magnitude_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000326 break;
327 }
cristyd3090f92011-10-18 00:05:15 +0000328 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000329 {
cristy4c08aed2011-07-01 19:47:50 +0000330 SetPixelAlpha(magnitude_image,ClampToQuantum(QuantumRange*
331 magnitude_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000332 break;
333 }
cristy3ed852e2009-09-05 21:47:34 +0000334 }
335 i++;
cristyed231572011-07-14 02:18:59 +0000336 q+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +0000337 }
338 status=SyncCacheViewAuthenticPixels(magnitude_view,exception);
339 if (status == MagickFalse)
340 break;
341 }
cristydb070952012-04-20 14:33:00 +0000342 magnitude_view=DestroyCacheView(magnitude_view);
cristy3ed852e2009-09-05 21:47:34 +0000343 i=0L;
cristydb070952012-04-20 14:33:00 +0000344 phase_view=AcquireAuthenticCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +0000345 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000346 {
347 q=GetCacheViewAuthenticPixels(phase_view,0L,y,fourier_info->height,1UL,
348 exception);
cristyacd2ed22011-08-30 01:44:23 +0000349 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000350 break;
cristybb503372010-05-27 20:51:26 +0000351 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000352 {
353 switch (fourier_info->channel)
354 {
cristyd3090f92011-10-18 00:05:15 +0000355 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000356 default:
357 {
cristy4c08aed2011-07-01 19:47:50 +0000358 SetPixelRed(phase_image,ClampToQuantum(QuantumRange*
359 phase_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000360 break;
361 }
cristyd3090f92011-10-18 00:05:15 +0000362 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000363 {
cristy4c08aed2011-07-01 19:47:50 +0000364 SetPixelGreen(phase_image,ClampToQuantum(QuantumRange*
365 phase_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000366 break;
367 }
cristyd3090f92011-10-18 00:05:15 +0000368 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000369 {
cristy4c08aed2011-07-01 19:47:50 +0000370 SetPixelBlue(phase_image,ClampToQuantum(QuantumRange*
371 phase_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000372 break;
373 }
cristyd3090f92011-10-18 00:05:15 +0000374 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000375 {
cristy4c08aed2011-07-01 19:47:50 +0000376 SetPixelBlack(phase_image,ClampToQuantum(QuantumRange*
377 phase_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000378 break;
379 }
cristyd3090f92011-10-18 00:05:15 +0000380 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000381 {
cristy4c08aed2011-07-01 19:47:50 +0000382 SetPixelAlpha(phase_image,ClampToQuantum(QuantumRange*
383 phase_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000384 break;
385 }
cristy3ed852e2009-09-05 21:47:34 +0000386 }
387 i++;
cristyed231572011-07-14 02:18:59 +0000388 q+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +0000389 }
390 status=SyncCacheViewAuthenticPixels(phase_view,exception);
391 if (status == MagickFalse)
392 break;
393 }
394 phase_view=DestroyCacheView(phase_view);
cristy3ed852e2009-09-05 21:47:34 +0000395 phase_source=(double *) RelinquishMagickMemory(phase_source);
396 magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
397 return(status);
398}
399
400static MagickBooleanType ForwardFourierTransform(FourierInfo *fourier_info,
401 const Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
402{
403 CacheView
404 *image_view;
405
406 double
407 n,
408 *source;
409
410 fftw_complex
411 *fourier;
412
413 fftw_plan
414 fftw_r2c_plan;
415
cristy4c08aed2011-07-01 19:47:50 +0000416 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000417 *p;
418
cristybb503372010-05-27 20:51:26 +0000419 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000420 i,
421 x;
422
cristyc4ea4a42011-01-24 01:43:30 +0000423 ssize_t
424 y;
425
cristy3ed852e2009-09-05 21:47:34 +0000426 /*
427 Generate the forward Fourier transform.
428 */
429 source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
430 fourier_info->width*sizeof(*source));
431 if (source == (double *) NULL)
432 {
433 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000434 ResourceLimitError,"MemoryAllocationFailed","'%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000435 return(MagickFalse);
436 }
cristyc4ea4a42011-01-24 01:43:30 +0000437 ResetMagickMemory(source,0,fourier_info->height*fourier_info->width*
cristy3ed852e2009-09-05 21:47:34 +0000438 sizeof(*source));
439 i=0L;
cristydb070952012-04-20 14:33:00 +0000440 image_view=AcquireVirtualCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +0000441 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000442 {
443 p=GetCacheViewVirtualPixels(image_view,0L,y,fourier_info->width,1UL,
444 exception);
cristy4c08aed2011-07-01 19:47:50 +0000445 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000446 break;
cristybb503372010-05-27 20:51:26 +0000447 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000448 {
449 switch (fourier_info->channel)
450 {
cristyd3090f92011-10-18 00:05:15 +0000451 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000452 default:
453 {
cristy4c08aed2011-07-01 19:47:50 +0000454 source[i]=QuantumScale*GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000455 break;
456 }
cristyd3090f92011-10-18 00:05:15 +0000457 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000458 {
cristy4c08aed2011-07-01 19:47:50 +0000459 source[i]=QuantumScale*GetPixelGreen(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000460 break;
461 }
cristyd3090f92011-10-18 00:05:15 +0000462 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000463 {
cristy4c08aed2011-07-01 19:47:50 +0000464 source[i]=QuantumScale*GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000465 break;
466 }
cristyd3090f92011-10-18 00:05:15 +0000467 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000468 {
cristy4c08aed2011-07-01 19:47:50 +0000469 source[i]=QuantumScale*GetPixelBlack(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000470 break;
471 }
cristyd3090f92011-10-18 00:05:15 +0000472 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000473 {
cristy4c08aed2011-07-01 19:47:50 +0000474 source[i]=QuantumScale*GetPixelAlpha(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000475 break;
476 }
cristy3ed852e2009-09-05 21:47:34 +0000477 }
478 i++;
cristyed231572011-07-14 02:18:59 +0000479 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000480 }
481 }
482 image_view=DestroyCacheView(image_view);
cristyb41ee102010-10-04 16:46:15 +0000483 fourier=(fftw_complex *) AcquireQuantumMemory((size_t) fourier_info->height,
cristy3ed852e2009-09-05 21:47:34 +0000484 fourier_info->center*sizeof(*fourier));
485 if (fourier == (fftw_complex *) NULL)
486 {
487 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000488 ResourceLimitError,"MemoryAllocationFailed","'%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000489 source=(double *) RelinquishMagickMemory(source);
490 return(MagickFalse);
491 }
cristyb5d5f722009-11-04 03:03:49 +0000492#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000493 #pragma omp critical (MagickCore_ForwardFourierTransform)
494#endif
cristy70841a12012-10-27 20:52:57 +0000495 fftw_r2c_plan=fftw_plan_dft_r2c_2d(fourier_info->width,fourier_info->height,
cristy3ed852e2009-09-05 21:47:34 +0000496 source,fourier,FFTW_ESTIMATE);
497 fftw_execute(fftw_r2c_plan);
498 fftw_destroy_plan(fftw_r2c_plan);
499 source=(double *) RelinquishMagickMemory(source);
500 /*
501 Normalize Fourier transform.
502 */
503 n=(double) fourier_info->width*(double) fourier_info->width;
504 i=0L;
cristybb503372010-05-27 20:51:26 +0000505 for (y=0L; y < (ssize_t) fourier_info->height; y++)
506 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy56ed31c2010-03-22 00:46:21 +0000507 {
508#if defined(MAGICKCORE_HAVE_COMPLEX_H)
509 fourier[i]/=n;
510#else
511 fourier[i][0]/=n;
512 fourier[i][1]/=n;
513#endif
514 i++;
515 }
cristy3ed852e2009-09-05 21:47:34 +0000516 /*
517 Generate magnitude and phase (or real and imaginary).
518 */
519 i=0L;
520 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000521 for (y=0L; y < (ssize_t) fourier_info->height; y++)
522 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000523 {
524 magnitude[i]=cabs(fourier[i]);
525 phase[i]=carg(fourier[i]);
526 i++;
527 }
528 else
cristybb503372010-05-27 20:51:26 +0000529 for (y=0L; y < (ssize_t) fourier_info->height; y++)
530 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000531 {
532 magnitude[i]=creal(fourier[i]);
533 phase[i]=cimag(fourier[i]);
534 i++;
535 }
cristyb41ee102010-10-04 16:46:15 +0000536 fourier=(fftw_complex *) RelinquishMagickMemory(fourier);
cristy3ed852e2009-09-05 21:47:34 +0000537 return(MagickTrue);
538}
539
540static MagickBooleanType ForwardFourierTransformChannel(const Image *image,
cristyd3090f92011-10-18 00:05:15 +0000541 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +0000542 Image *fourier_image,ExceptionInfo *exception)
543{
544 double
545 *magnitude,
546 *phase;
547
548 fftw_complex
549 *fourier;
550
cristy56ed31c2010-03-22 00:46:21 +0000551 FourierInfo
552 fourier_info;
553
cristyc4ea4a42011-01-24 01:43:30 +0000554 MagickBooleanType
555 status;
556
cristy3ed852e2009-09-05 21:47:34 +0000557 size_t
558 extent;
559
560 fourier_info.width=image->columns;
561 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
562 ((image->rows % 2) != 0))
563 {
564 extent=image->columns < image->rows ? image->rows : image->columns;
565 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
566 }
567 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +0000568 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +0000569 fourier_info.channel=channel;
570 fourier_info.modulus=modulus;
571 magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
572 fourier_info.center*sizeof(*magnitude));
573 if (magnitude == (double *) NULL)
574 {
575 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000576 ResourceLimitError,"MemoryAllocationFailed","'%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000577 return(MagickFalse);
578 }
579 phase=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
580 fourier_info.center*sizeof(*phase));
581 if (phase == (double *) NULL)
582 {
583 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000584 ResourceLimitError,"MemoryAllocationFailed","'%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000585 magnitude=(double *) RelinquishMagickMemory(magnitude);
586 return(MagickFalse);
587 }
cristyb41ee102010-10-04 16:46:15 +0000588 fourier=(fftw_complex *) AcquireQuantumMemory((size_t) fourier_info.height,
cristy3ed852e2009-09-05 21:47:34 +0000589 fourier_info.center*sizeof(*fourier));
590 if (fourier == (fftw_complex *) NULL)
591 {
592 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000593 ResourceLimitError,"MemoryAllocationFailed","'%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000594 phase=(double *) RelinquishMagickMemory(phase);
595 magnitude=(double *) RelinquishMagickMemory(magnitude);
596 return(MagickFalse);
597 }
598 status=ForwardFourierTransform(&fourier_info,image,magnitude,phase,exception);
599 if (status != MagickFalse)
600 status=ForwardFourier(&fourier_info,fourier_image,magnitude,phase,
601 exception);
cristyb41ee102010-10-04 16:46:15 +0000602 fourier=(fftw_complex *) RelinquishMagickMemory(fourier);
cristy3ed852e2009-09-05 21:47:34 +0000603 phase=(double *) RelinquishMagickMemory(phase);
604 magnitude=(double *) RelinquishMagickMemory(magnitude);
605 return(status);
606}
607#endif
608
609MagickExport Image *ForwardFourierTransformImage(const Image *image,
610 const MagickBooleanType modulus,ExceptionInfo *exception)
611{
612 Image
613 *fourier_image;
614
615 fourier_image=NewImageList();
616#if !defined(MAGICKCORE_FFTW_DELEGATE)
617 (void) modulus;
618 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000619 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristy3ed852e2009-09-05 21:47:34 +0000620 image->filename);
621#else
622 {
623 Image
624 *magnitude_image;
625
cristybb503372010-05-27 20:51:26 +0000626 size_t
cristy3ed852e2009-09-05 21:47:34 +0000627 extent,
628 width;
629
630 width=image->columns;
631 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
632 ((image->rows % 2) != 0))
633 {
634 extent=image->columns < image->rows ? image->rows : image->columns;
635 width=(extent & 0x01) == 1 ? extent+1UL : extent;
636 }
637 magnitude_image=CloneImage(image,width,width,MagickFalse,exception);
638 if (magnitude_image != (Image *) NULL)
639 {
640 Image
641 *phase_image;
642
643 magnitude_image->storage_class=DirectClass;
644 magnitude_image->depth=32UL;
645 phase_image=CloneImage(image,width,width,MagickFalse,exception);
646 if (phase_image == (Image *) NULL)
647 magnitude_image=DestroyImage(magnitude_image);
648 else
649 {
650 MagickBooleanType
651 is_gray,
652 status;
653
cristy3ed852e2009-09-05 21:47:34 +0000654 phase_image->storage_class=DirectClass;
655 phase_image->depth=32UL;
656 AppendImageToList(&fourier_image,magnitude_image);
657 AppendImageToList(&fourier_image,phase_image);
658 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000659 is_gray=IsImageGray(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000660#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy564a5692012-01-20 23:56:26 +0000661 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +0000662#endif
cristy3ed852e2009-09-05 21:47:34 +0000663 {
cristyb34ef052010-10-07 00:12:05 +0000664#if defined(MAGICKCORE_OPENMP_SUPPORT)
665 #pragma omp section
666#endif
cristy3ed852e2009-09-05 21:47:34 +0000667 {
cristyb34ef052010-10-07 00:12:05 +0000668 MagickBooleanType
669 thread_status;
670
671 if (is_gray != MagickFalse)
672 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000673 GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000674 else
675 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000676 RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000677 if (thread_status == MagickFalse)
678 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +0000679 }
cristyb34ef052010-10-07 00:12:05 +0000680#if defined(MAGICKCORE_OPENMP_SUPPORT)
681 #pragma omp section
682#endif
683 {
684 MagickBooleanType
685 thread_status;
686
687 thread_status=MagickTrue;
688 if (is_gray == MagickFalse)
689 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000690 GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000691 if (thread_status == MagickFalse)
692 status=thread_status;
693 }
694#if defined(MAGICKCORE_OPENMP_SUPPORT)
695 #pragma omp section
696#endif
697 {
698 MagickBooleanType
699 thread_status;
700
701 thread_status=MagickTrue;
702 if (is_gray == MagickFalse)
703 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000704 BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000705 if (thread_status == MagickFalse)
706 status=thread_status;
707 }
708#if defined(MAGICKCORE_OPENMP_SUPPORT)
709 #pragma omp section
710#endif
711 {
712 MagickBooleanType
713 thread_status;
714
715 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000716 if (image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +0000717 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000718 BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000719 if (thread_status == MagickFalse)
720 status=thread_status;
721 }
722#if defined(MAGICKCORE_OPENMP_SUPPORT)
723 #pragma omp section
724#endif
725 {
726 MagickBooleanType
727 thread_status;
728
729 thread_status=MagickTrue;
cristy8a46d822012-08-28 23:32:39 +0000730 if (image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +0000731 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000732 AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000733 if (thread_status == MagickFalse)
734 status=thread_status;
735 }
cristy3ed852e2009-09-05 21:47:34 +0000736 }
737 if (status == MagickFalse)
738 fourier_image=DestroyImageList(fourier_image);
739 fftw_cleanup();
740 }
741 }
742 }
743#endif
744 return(fourier_image);
745}
746
747/*
748%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
749% %
750% %
751% %
752% I n v e r s e F o u r i e r T r a n s f o r m I m a g e %
753% %
754% %
755% %
756%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
757%
758% InverseFourierTransformImage() implements the inverse discrete Fourier
759% transform (DFT) of the image either as a magnitude / phase or real /
760% imaginary image pair.
761%
762% The format of the InverseFourierTransformImage method is:
763%
cristyc9550792009-11-13 20:05:42 +0000764% Image *InverseFourierTransformImage(const Image *magnitude_image,
765% const Image *phase_image,const MagickBooleanType modulus,
766% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000767%
768% A description of each parameter follows:
769%
cristyc9550792009-11-13 20:05:42 +0000770% o magnitude_image: the magnitude or real image.
771%
772% o phase_image: the phase or imaginary image.
cristy3ed852e2009-09-05 21:47:34 +0000773%
774% o modulus: if true, return transform as a magnitude / phase pair
775% otherwise a real / imaginary image pair.
776%
777% o exception: return any errors or warnings in this structure.
778%
779*/
780
781#if defined(MAGICKCORE_FFTW_DELEGATE)
cristybb503372010-05-27 20:51:26 +0000782static MagickBooleanType InverseQuadrantSwap(const size_t width,
783 const size_t height,const double *source,double *destination)
cristy3ed852e2009-09-05 21:47:34 +0000784{
cristyc4ea4a42011-01-24 01:43:30 +0000785 register ssize_t
786 x;
787
cristybb503372010-05-27 20:51:26 +0000788 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000789 center,
790 y;
791
cristy3ed852e2009-09-05 21:47:34 +0000792 /*
793 Swap quadrants.
794 */
cristy233fe582012-07-07 14:00:18 +0000795 center=(ssize_t) floor((double) width/2L)+1L;
cristybb503372010-05-27 20:51:26 +0000796 for (y=1L; y < (ssize_t) height; y++)
797 for (x=0L; x < (ssize_t) (width/2L+1L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000798 destination[center*(height-y)-x+width/2L]=source[y*width+x];
cristybb503372010-05-27 20:51:26 +0000799 for (y=0L; y < (ssize_t) height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000800 destination[center*y]=source[y*width+width/2L];
801 for (x=0L; x < center; x++)
802 destination[x]=source[center-x-1L];
cristybb503372010-05-27 20:51:26 +0000803 return(RollFourier(center,height,0L,(ssize_t) height/-2L,destination));
cristy3ed852e2009-09-05 21:47:34 +0000804}
805
806static MagickBooleanType InverseFourier(FourierInfo *fourier_info,
cristyc9550792009-11-13 20:05:42 +0000807 const Image *magnitude_image,const Image *phase_image,fftw_complex *fourier,
808 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000809{
810 CacheView
811 *magnitude_view,
812 *phase_view;
813
814 double
815 *magnitude,
816 *phase,
817 *magnitude_source,
818 *phase_source;
819
cristy3ed852e2009-09-05 21:47:34 +0000820 MagickBooleanType
821 status;
822
cristy4c08aed2011-07-01 19:47:50 +0000823 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000824 *p;
825
cristybb503372010-05-27 20:51:26 +0000826 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000827 i,
828 x;
829
cristyc4ea4a42011-01-24 01:43:30 +0000830 ssize_t
831 y;
832
cristy3ed852e2009-09-05 21:47:34 +0000833 /*
834 Inverse fourier - read image and break down into a double array.
835 */
cristy3ed852e2009-09-05 21:47:34 +0000836 magnitude_source=(double *) AcquireQuantumMemory((size_t)
837 fourier_info->height,fourier_info->width*sizeof(*magnitude_source));
838 if (magnitude_source == (double *) NULL)
839 {
840 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000841 ResourceLimitError,"MemoryAllocationFailed","'%s'",
cristyc9550792009-11-13 20:05:42 +0000842 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000843 return(MagickFalse);
844 }
845 phase_source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
cristyc4ea4a42011-01-24 01:43:30 +0000846 fourier_info->width*sizeof(*phase_source));
cristy3ed852e2009-09-05 21:47:34 +0000847 if (phase_source == (double *) NULL)
848 {
849 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000850 ResourceLimitError,"MemoryAllocationFailed","'%s'",
cristyc9550792009-11-13 20:05:42 +0000851 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000852 magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
853 return(MagickFalse);
854 }
855 i=0L;
cristydb070952012-04-20 14:33:00 +0000856 magnitude_view=AcquireVirtualCacheView(magnitude_image,exception);
cristybb503372010-05-27 20:51:26 +0000857 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000858 {
859 p=GetCacheViewVirtualPixels(magnitude_view,0L,y,fourier_info->width,1UL,
860 exception);
cristy4c08aed2011-07-01 19:47:50 +0000861 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000862 break;
cristybb503372010-05-27 20:51:26 +0000863 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000864 {
865 switch (fourier_info->channel)
866 {
cristyd3090f92011-10-18 00:05:15 +0000867 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000868 default:
869 {
cristy4c08aed2011-07-01 19:47:50 +0000870 magnitude_source[i]=QuantumScale*GetPixelRed(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000871 break;
872 }
cristyd3090f92011-10-18 00:05:15 +0000873 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000874 {
cristy4c08aed2011-07-01 19:47:50 +0000875 magnitude_source[i]=QuantumScale*GetPixelGreen(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000876 break;
877 }
cristyd3090f92011-10-18 00:05:15 +0000878 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000879 {
cristy4c08aed2011-07-01 19:47:50 +0000880 magnitude_source[i]=QuantumScale*GetPixelBlue(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000881 break;
882 }
cristyd3090f92011-10-18 00:05:15 +0000883 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000884 {
cristy4c08aed2011-07-01 19:47:50 +0000885 magnitude_source[i]=QuantumScale*GetPixelBlack(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000886 break;
887 }
cristyd3090f92011-10-18 00:05:15 +0000888 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000889 {
cristy4c08aed2011-07-01 19:47:50 +0000890 magnitude_source[i]=QuantumScale*GetPixelAlpha(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000891 break;
892 }
cristy3ed852e2009-09-05 21:47:34 +0000893 }
894 i++;
cristyed231572011-07-14 02:18:59 +0000895 p+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +0000896 }
897 }
898 i=0L;
cristydb070952012-04-20 14:33:00 +0000899 phase_view=AcquireVirtualCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +0000900 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000901 {
902 p=GetCacheViewVirtualPixels(phase_view,0,y,fourier_info->width,1,
903 exception);
cristy4c08aed2011-07-01 19:47:50 +0000904 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000905 break;
cristybb503372010-05-27 20:51:26 +0000906 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000907 {
908 switch (fourier_info->channel)
909 {
cristyd3090f92011-10-18 00:05:15 +0000910 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000911 default:
912 {
cristy4c08aed2011-07-01 19:47:50 +0000913 phase_source[i]=QuantumScale*GetPixelRed(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000914 break;
915 }
cristyd3090f92011-10-18 00:05:15 +0000916 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000917 {
cristy4c08aed2011-07-01 19:47:50 +0000918 phase_source[i]=QuantumScale*GetPixelGreen(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000919 break;
920 }
cristyd3090f92011-10-18 00:05:15 +0000921 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000922 {
cristy4c08aed2011-07-01 19:47:50 +0000923 phase_source[i]=QuantumScale*GetPixelBlue(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000924 break;
925 }
cristyd3090f92011-10-18 00:05:15 +0000926 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000927 {
cristy4c08aed2011-07-01 19:47:50 +0000928 phase_source[i]=QuantumScale*GetPixelBlack(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000929 break;
930 }
cristyd3090f92011-10-18 00:05:15 +0000931 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000932 {
cristy4c08aed2011-07-01 19:47:50 +0000933 phase_source[i]=QuantumScale*GetPixelAlpha(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000934 break;
935 }
cristy3ed852e2009-09-05 21:47:34 +0000936 }
937 i++;
cristyed231572011-07-14 02:18:59 +0000938 p+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +0000939 }
940 }
941 if (fourier_info->modulus != MagickFalse)
942 {
943 i=0L;
cristybb503372010-05-27 20:51:26 +0000944 for (y=0L; y < (ssize_t) fourier_info->height; y++)
945 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000946 {
947 phase_source[i]-=0.5;
948 phase_source[i]*=(2.0*MagickPI);
949 i++;
950 }
951 }
952 magnitude_view=DestroyCacheView(magnitude_view);
953 phase_view=DestroyCacheView(phase_view);
954 magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
955 fourier_info->center*sizeof(*magnitude));
956 if (magnitude == (double *) NULL)
957 {
958 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000959 ResourceLimitError,"MemoryAllocationFailed","'%s'",
cristyc9550792009-11-13 20:05:42 +0000960 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000961 magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
962 phase_source=(double *) RelinquishMagickMemory(phase_source);
963 return(MagickFalse);
964 }
965 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
966 magnitude_source,magnitude);
967 magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
cristyc4ea4a42011-01-24 01:43:30 +0000968 phase=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
969 fourier_info->width*sizeof(*phase));
cristy3ed852e2009-09-05 21:47:34 +0000970 if (phase == (double *) NULL)
971 {
972 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000973 ResourceLimitError,"MemoryAllocationFailed","'%s'",
cristyc9550792009-11-13 20:05:42 +0000974 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000975 phase_source=(double *) RelinquishMagickMemory(phase_source);
976 return(MagickFalse);
977 }
978 CorrectPhaseLHS(fourier_info->width,fourier_info->width,phase_source);
979 if (status != MagickFalse)
980 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
981 phase_source,phase);
982 phase_source=(double *) RelinquishMagickMemory(phase_source);
983 /*
984 Merge two sets.
985 */
986 i=0L;
987 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000988 for (y=0L; y < (ssize_t) fourier_info->height; y++)
989 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000990 {
cristy56ed31c2010-03-22 00:46:21 +0000991#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy3ed852e2009-09-05 21:47:34 +0000992 fourier[i]=magnitude[i]*cos(phase[i])+I*magnitude[i]*sin(phase[i]);
cristy56ed31c2010-03-22 00:46:21 +0000993#else
994 fourier[i][0]=magnitude[i]*cos(phase[i]);
995 fourier[i][1]=magnitude[i]*sin(phase[i]);
996#endif
cristy3ed852e2009-09-05 21:47:34 +0000997 i++;
998 }
999 else
cristybb503372010-05-27 20:51:26 +00001000 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1001 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001002 {
cristy56ed31c2010-03-22 00:46:21 +00001003#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy3ed852e2009-09-05 21:47:34 +00001004 fourier[i]=magnitude[i]+I*phase[i];
cristy56ed31c2010-03-22 00:46:21 +00001005#else
1006 fourier[i][0]=magnitude[i];
1007 fourier[i][1]=phase[i];
1008#endif
cristy3ed852e2009-09-05 21:47:34 +00001009 i++;
1010 }
1011 phase=(double *) RelinquishMagickMemory(phase);
1012 magnitude=(double *) RelinquishMagickMemory(magnitude);
1013 return(status);
1014}
1015
1016static MagickBooleanType InverseFourierTransform(FourierInfo *fourier_info,
1017 fftw_complex *fourier,Image *image,ExceptionInfo *exception)
1018{
1019 CacheView
1020 *image_view;
1021
1022 double
1023 *source;
1024
1025 fftw_plan
1026 fftw_c2r_plan;
1027
cristy4c08aed2011-07-01 19:47:50 +00001028 register Quantum
cristyc4ea4a42011-01-24 01:43:30 +00001029 *q;
1030
cristybb503372010-05-27 20:51:26 +00001031 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001032 i,
1033 x;
1034
cristyc4ea4a42011-01-24 01:43:30 +00001035 ssize_t
1036 y;
cristy3ed852e2009-09-05 21:47:34 +00001037
cristyc4ea4a42011-01-24 01:43:30 +00001038 source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
1039 fourier_info->width*sizeof(*source));
cristy3ed852e2009-09-05 21:47:34 +00001040 if (source == (double *) NULL)
1041 {
1042 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00001043 ResourceLimitError,"MemoryAllocationFailed","'%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001044 return(MagickFalse);
1045 }
cristyb5d5f722009-11-04 03:03:49 +00001046#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001047 #pragma omp critical (MagickCore_InverseFourierTransform)
1048#endif
cristydf079ac2010-09-10 01:45:44 +00001049 {
1050 fftw_c2r_plan=fftw_plan_dft_c2r_2d(fourier_info->width,fourier_info->height,
1051 fourier,source,FFTW_ESTIMATE);
1052 fftw_execute(fftw_c2r_plan);
1053 fftw_destroy_plan(fftw_c2r_plan);
1054 }
cristy3ed852e2009-09-05 21:47:34 +00001055 i=0L;
cristydb070952012-04-20 14:33:00 +00001056 image_view=AcquireAuthenticCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +00001057 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001058 {
cristy85812052010-09-14 17:56:15 +00001059 if (y >= (ssize_t) image->rows)
1060 break;
1061 q=GetCacheViewAuthenticPixels(image_view,0L,y,fourier_info->width >
1062 image->columns ? image->columns : fourier_info->width,1UL,exception);
cristyacd2ed22011-08-30 01:44:23 +00001063 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001064 break;
cristybb503372010-05-27 20:51:26 +00001065 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001066 {
cristy233fe582012-07-07 14:00:18 +00001067 if (x < (ssize_t) image->columns)
1068 switch (fourier_info->channel)
cristy3ed852e2009-09-05 21:47:34 +00001069 {
cristy233fe582012-07-07 14:00:18 +00001070 case RedPixelChannel:
1071 default:
1072 {
1073 SetPixelRed(image,ClampToQuantum(QuantumRange*source[i]),q);
1074 break;
1075 }
1076 case GreenPixelChannel:
1077 {
1078 SetPixelGreen(image,ClampToQuantum(QuantumRange*source[i]),q);
1079 break;
1080 }
1081 case BluePixelChannel:
1082 {
1083 SetPixelBlue(image,ClampToQuantum(QuantumRange*source[i]),q);
1084 break;
1085 }
1086 case BlackPixelChannel:
1087 {
1088 SetPixelBlack(image,ClampToQuantum(QuantumRange*source[i]),q);
1089 break;
1090 }
1091 case AlphaPixelChannel:
1092 {
1093 SetPixelAlpha(image,ClampToQuantum(QuantumRange*source[i]),q);
1094 break;
1095 }
cristy3ed852e2009-09-05 21:47:34 +00001096 }
cristy3ed852e2009-09-05 21:47:34 +00001097 i++;
cristyed231572011-07-14 02:18:59 +00001098 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001099 }
1100 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1101 break;
1102 }
1103 image_view=DestroyCacheView(image_view);
1104 source=(double *) RelinquishMagickMemory(source);
1105 return(MagickTrue);
1106}
1107
cristyc9550792009-11-13 20:05:42 +00001108static MagickBooleanType InverseFourierTransformChannel(
1109 const Image *magnitude_image,const Image *phase_image,
cristyd3090f92011-10-18 00:05:15 +00001110 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +00001111 Image *fourier_image,ExceptionInfo *exception)
1112{
1113 double
1114 *magnitude,
1115 *phase;
1116
1117 fftw_complex
1118 *fourier;
1119
1120 FourierInfo
1121 fourier_info;
1122
1123 MagickBooleanType
1124 status;
1125
1126 size_t
1127 extent;
1128
cristyc9550792009-11-13 20:05:42 +00001129 fourier_info.width=magnitude_image->columns;
1130 if ((magnitude_image->columns != magnitude_image->rows) ||
1131 ((magnitude_image->columns % 2) != 0) ||
1132 ((magnitude_image->rows % 2) != 0))
cristy3ed852e2009-09-05 21:47:34 +00001133 {
cristyc9550792009-11-13 20:05:42 +00001134 extent=magnitude_image->columns < magnitude_image->rows ?
1135 magnitude_image->rows : magnitude_image->columns;
cristy3ed852e2009-09-05 21:47:34 +00001136 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
1137 }
1138 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +00001139 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +00001140 fourier_info.channel=channel;
1141 fourier_info.modulus=modulus;
1142 magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
cristyc4ea4a42011-01-24 01:43:30 +00001143 fourier_info.center*sizeof(*magnitude));
cristy3ed852e2009-09-05 21:47:34 +00001144 if (magnitude == (double *) NULL)
1145 {
1146 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00001147 ResourceLimitError,"MemoryAllocationFailed","'%s'",
cristyc9550792009-11-13 20:05:42 +00001148 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001149 return(MagickFalse);
1150 }
1151 phase=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
cristyc4ea4a42011-01-24 01:43:30 +00001152 fourier_info.center*sizeof(*phase));
cristy3ed852e2009-09-05 21:47:34 +00001153 if (phase == (double *) NULL)
1154 {
1155 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00001156 ResourceLimitError,"MemoryAllocationFailed","'%s'",
cristyc9550792009-11-13 20:05:42 +00001157 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001158 magnitude=(double *) RelinquishMagickMemory(magnitude);
1159 return(MagickFalse);
1160 }
cristyb41ee102010-10-04 16:46:15 +00001161 fourier=(fftw_complex *) AcquireQuantumMemory((size_t) fourier_info.height,
cristy3ed852e2009-09-05 21:47:34 +00001162 fourier_info.center*sizeof(*fourier));
1163 if (fourier == (fftw_complex *) NULL)
1164 {
1165 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00001166 ResourceLimitError,"MemoryAllocationFailed","'%s'",
cristyc9550792009-11-13 20:05:42 +00001167 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001168 phase=(double *) RelinquishMagickMemory(phase);
1169 magnitude=(double *) RelinquishMagickMemory(magnitude);
1170 return(MagickFalse);
1171 }
cristyc9550792009-11-13 20:05:42 +00001172 status=InverseFourier(&fourier_info,magnitude_image,phase_image,fourier,
1173 exception);
cristy3ed852e2009-09-05 21:47:34 +00001174 if (status != MagickFalse)
1175 status=InverseFourierTransform(&fourier_info,fourier,fourier_image,
1176 exception);
cristyb41ee102010-10-04 16:46:15 +00001177 fourier=(fftw_complex *) RelinquishMagickMemory(fourier);
cristy3ed852e2009-09-05 21:47:34 +00001178 phase=(double *) RelinquishMagickMemory(phase);
1179 magnitude=(double *) RelinquishMagickMemory(magnitude);
1180 return(status);
1181}
1182#endif
1183
cristyc9550792009-11-13 20:05:42 +00001184MagickExport Image *InverseFourierTransformImage(const Image *magnitude_image,
1185 const Image *phase_image,const MagickBooleanType modulus,
1186 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001187{
1188 Image
1189 *fourier_image;
1190
cristyc9550792009-11-13 20:05:42 +00001191 assert(magnitude_image != (Image *) NULL);
1192 assert(magnitude_image->signature == MagickSignature);
1193 if (magnitude_image->debug != MagickFalse)
1194 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1195 magnitude_image->filename);
1196 if (phase_image == (Image *) NULL)
1197 {
1198 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
anthonye5b39652012-04-21 05:37:29 +00001199 "TwoOrMoreImagesRequired","'%s'",magnitude_image->filename);
cristy9372a152009-11-18 01:42:16 +00001200 return((Image *) NULL);
cristyc9550792009-11-13 20:05:42 +00001201 }
cristy3ed852e2009-09-05 21:47:34 +00001202#if !defined(MAGICKCORE_FFTW_DELEGATE)
1203 fourier_image=(Image *) NULL;
1204 (void) modulus;
1205 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00001206 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristyc9550792009-11-13 20:05:42 +00001207 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001208#else
1209 {
cristyc9550792009-11-13 20:05:42 +00001210 fourier_image=CloneImage(magnitude_image,magnitude_image->columns,
1211 magnitude_image->rows,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00001212 if (fourier_image != (Image *) NULL)
1213 {
1214 MagickBooleanType
1215 is_gray,
1216 status;
1217
cristy3ed852e2009-09-05 21:47:34 +00001218 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001219 is_gray=IsImageGray(magnitude_image,exception);
cristyc9550792009-11-13 20:05:42 +00001220 if (is_gray != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001221 is_gray=IsImageGray(phase_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001222#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb34ef052010-10-07 00:12:05 +00001223 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +00001224#endif
cristy3ed852e2009-09-05 21:47:34 +00001225 {
cristyb34ef052010-10-07 00:12:05 +00001226#if defined(MAGICKCORE_OPENMP_SUPPORT)
1227 #pragma omp section
1228#endif
cristy3ed852e2009-09-05 21:47:34 +00001229 {
cristyb34ef052010-10-07 00:12:05 +00001230 MagickBooleanType
1231 thread_status;
1232
1233 if (is_gray != MagickFalse)
1234 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001235 phase_image,GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001236 else
cristyc9550792009-11-13 20:05:42 +00001237 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001238 phase_image,RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001239 if (thread_status == MagickFalse)
1240 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +00001241 }
cristyb34ef052010-10-07 00:12:05 +00001242#if defined(MAGICKCORE_OPENMP_SUPPORT)
1243 #pragma omp section
1244#endif
1245 {
1246 MagickBooleanType
1247 thread_status;
1248
1249 thread_status=MagickTrue;
1250 if (is_gray == MagickFalse)
1251 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001252 phase_image,GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001253 if (thread_status == MagickFalse)
1254 status=thread_status;
1255 }
1256#if defined(MAGICKCORE_OPENMP_SUPPORT)
1257 #pragma omp section
1258#endif
1259 {
1260 MagickBooleanType
1261 thread_status;
1262
1263 thread_status=MagickTrue;
1264 if (is_gray == MagickFalse)
1265 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001266 phase_image,BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001267 if (thread_status == MagickFalse)
1268 status=thread_status;
1269 }
1270#if defined(MAGICKCORE_OPENMP_SUPPORT)
1271 #pragma omp section
1272#endif
1273 {
1274 MagickBooleanType
1275 thread_status;
1276
1277 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001278 if (magnitude_image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +00001279 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001280 phase_image,BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001281 if (thread_status == MagickFalse)
1282 status=thread_status;
1283 }
1284#if defined(MAGICKCORE_OPENMP_SUPPORT)
1285 #pragma omp section
1286#endif
1287 {
1288 MagickBooleanType
1289 thread_status;
1290
1291 thread_status=MagickTrue;
cristy8a46d822012-08-28 23:32:39 +00001292 if (magnitude_image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +00001293 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001294 phase_image,AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001295 if (thread_status == MagickFalse)
1296 status=thread_status;
1297 }
cristy3ed852e2009-09-05 21:47:34 +00001298 }
1299 if (status == MagickFalse)
1300 fourier_image=DestroyImage(fourier_image);
1301 }
cristyb34ef052010-10-07 00:12:05 +00001302 fftw_cleanup();
cristy3ed852e2009-09-05 21:47:34 +00001303 }
1304#endif
1305 return(fourier_image);
1306}