blob: 887b80a0087a827e3af302623776d381e1d0100b [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% %
cristy16af1cb2009-12-11 21:38:29 +000022% Copyright 1999-2010 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*/
45#include "magick/studio.h"
46#include "magick/cache.h"
47#include "magick/image.h"
48#include "magick/image-private.h"
49#include "magick/list.h"
50#include "magick/fourier.h"
51#include "magick/log.h"
52#include "magick/memory_.h"
53#include "magick/monitor.h"
54#include "magick/property.h"
cristye3038982010-05-17 02:20:52 +000055#include "magick/quantum-private.h"
cristy3ed852e2009-09-05 21:47:34 +000056#include "magick/thread-private.h"
57#if defined(MAGICKCORE_FFTW_DELEGATE)
cristy56ed31c2010-03-22 00:46:21 +000058#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy3ed852e2009-09-05 21:47:34 +000059#include <complex.h>
cristy56ed31c2010-03-22 00:46:21 +000060#else
61#define cabs(z) (sqrt(z[0]*z[0]+z[1]*z[1]))
62#define carg(z) (atan2(z[1],z[0]))
63#define creal(z) (z[0])
64#define cimag(z) (z[1])
65#endif
cristy3ed852e2009-09-05 21:47:34 +000066#include <fftw3.h>
67#endif
68
69/*
70 Typedef declarations.
71*/
72typedef struct _FourierInfo
73{
74 ChannelType
75 channel;
76
77 MagickBooleanType
78 modulus;
79
cristybb503372010-05-27 20:51:26 +000080 size_t
cristy3ed852e2009-09-05 21:47:34 +000081 width,
82 height;
83
cristybb503372010-05-27 20:51:26 +000084 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000085 center;
86} FourierInfo;
87
88/*
89%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
90% %
91% %
92% %
93% 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 %
94% %
95% %
96% %
97%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
98%
99% ForwardFourierTransformImage() implements the discrete Fourier transform
100% (DFT) of the image either as a magnitude / phase or real / imaginary image
101% pair.
102%
103% The format of the ForwadFourierTransformImage method is:
104%
105% Image *ForwardFourierTransformImage(const Image *image,
106% const MagickBooleanType modulus,ExceptionInfo *exception)
107%
108% A description of each parameter follows:
109%
110% o image: the image.
111%
112% o modulus: if true, return as transform as a magnitude / phase pair
113% otherwise a real / imaginary image pair.
114%
115% o exception: return any errors or warnings in this structure.
116%
117*/
118
119#if defined(MAGICKCORE_FFTW_DELEGATE)
120
cristybb503372010-05-27 20:51:26 +0000121static MagickBooleanType RollFourier(const size_t width,
122 const size_t height,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +0000123 double *fourier)
124{
125 double
126 *roll;
127
cristybb503372010-05-27 20:51:26 +0000128 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000129 u,
130 v,
131 y;
132
cristybb503372010-05-27 20:51:26 +0000133 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000134 i,
135 x;
136
137 /*
138 Move the zero frequency (DC) from (0,0) to (width/2,height/2).
139 */
140 roll=(double *) AcquireQuantumMemory((size_t) width,height*sizeof(*roll));
141 if (roll == (double *) NULL)
142 return(MagickFalse);
143 i=0L;
cristybb503372010-05-27 20:51:26 +0000144 for (y=0L; y < (ssize_t) height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000145 {
146 if (y_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000147 v=((y+y_offset) < 0L) ? y+y_offset+(ssize_t) height : y+y_offset;
cristy3ed852e2009-09-05 21:47:34 +0000148 else
cristybb503372010-05-27 20:51:26 +0000149 v=((y+y_offset) > ((ssize_t) height-1L)) ? y+y_offset-(ssize_t) height :
cristy3ed852e2009-09-05 21:47:34 +0000150 y+y_offset;
cristybb503372010-05-27 20:51:26 +0000151 for (x=0L; x < (ssize_t) width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000152 {
153 if (x_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000154 u=((x+x_offset) < 0L) ? x+x_offset+(ssize_t) width : x+x_offset;
cristy3ed852e2009-09-05 21:47:34 +0000155 else
cristybb503372010-05-27 20:51:26 +0000156 u=((x+x_offset) > ((ssize_t) width-1L)) ? x+x_offset-(ssize_t) width :
cristy3ed852e2009-09-05 21:47:34 +0000157 x+x_offset;
158 roll[v*width+u]=fourier[i++];
159 }
160 }
161 (void) CopyMagickMemory(fourier,roll,width*height*sizeof(*roll));
162 roll=(double *) RelinquishMagickMemory(roll);
163 return(MagickTrue);
164}
165
cristybb503372010-05-27 20:51:26 +0000166static MagickBooleanType ForwardQuadrantSwap(const size_t width,
167 const size_t height,double *source,double *destination)
cristy3ed852e2009-09-05 21:47:34 +0000168{
cristybb503372010-05-27 20:51:26 +0000169 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000170 center,
171 y;
172
173 MagickBooleanType
174 status;
175
cristybb503372010-05-27 20:51:26 +0000176 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000177 x;
178
179 /*
180 Swap quadrants.
181 */
cristybb503372010-05-27 20:51:26 +0000182 center=(ssize_t) floor((double) width/2L)+1L;
183 status=RollFourier((size_t) center,height,0L,(ssize_t) height/2L,source);
cristy3ed852e2009-09-05 21:47:34 +0000184 if (status == MagickFalse)
185 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000186 for (y=0L; y < (ssize_t) height; y++)
187 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000188 destination[width*y+x+width/2L]=source[center*y+x];
cristybb503372010-05-27 20:51:26 +0000189 for (y=1; y < (ssize_t) height; y++)
190 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000191 destination[width*(height-y)+width/2L-x-1L]=source[center*y+x+1L];
cristybb503372010-05-27 20:51:26 +0000192 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000193 destination[-x+width/2L-1L]=destination[x+width/2L+1L];
194 return(MagickTrue);
195}
196
cristybb503372010-05-27 20:51:26 +0000197static void CorrectPhaseLHS(const size_t width,
198 const size_t height,double *fourier)
cristy3ed852e2009-09-05 21:47:34 +0000199{
cristybb503372010-05-27 20:51:26 +0000200 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000201 y;
202
cristybb503372010-05-27 20:51:26 +0000203 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000204 x;
205
cristybb503372010-05-27 20:51:26 +0000206 for (y=0L; y < (ssize_t) height; y++)
207 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000208 fourier[y*width+x]*=(-1.0);
209}
210
211static MagickBooleanType ForwardFourier(const FourierInfo *fourier_info,
212 Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
213{
214 CacheView
215 *magnitude_view,
216 *phase_view;
217
218 double
219 *magnitude_source,
220 *phase_source;
221
222 Image
223 *magnitude_image,
224 *phase_image;
225
cristybb503372010-05-27 20:51:26 +0000226 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000227 i,
228 y;
229
230 MagickBooleanType
231 status;
232
233 register IndexPacket
234 *indexes;
235
cristybb503372010-05-27 20:51:26 +0000236 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000237 x;
238
239 register PixelPacket
240 *q;
241
242 magnitude_image=GetFirstImageInList(image);
243 phase_image=GetNextImageInList(image);
244 if (phase_image == (Image *) NULL)
245 {
246 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
247 "ImageSequenceRequired","`%s'",image->filename);
248 return(MagickFalse);
249 }
250 /*
251 Create "Fourier Transform" image from constituent arrays.
252 */
253 magnitude_source=(double *) AcquireQuantumMemory((size_t)
254 fourier_info->height,fourier_info->width*sizeof(*magnitude_source));
255 if (magnitude_source == (double *) NULL)
256 return(MagickFalse);
257 (void) ResetMagickMemory(magnitude_source,0,fourier_info->width*
258 fourier_info->height*sizeof(*magnitude_source));
259 phase_source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
260 fourier_info->width*sizeof(*phase_source));
261 if (magnitude_source == (double *) NULL)
262 {
263 (void) ThrowMagickException(exception,GetMagickModule(),
264 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
265 magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
266 return(MagickFalse);
267 }
268 status=ForwardQuadrantSwap(fourier_info->height,fourier_info->height,
269 magnitude,magnitude_source);
270 if (status != MagickFalse)
271 status=ForwardQuadrantSwap(fourier_info->height,fourier_info->height,phase,
272 phase_source);
273 CorrectPhaseLHS(fourier_info->height,fourier_info->height,phase_source);
274 if (fourier_info->modulus != MagickFalse)
275 {
276 i=0L;
cristybb503372010-05-27 20:51:26 +0000277 for (y=0L; y < (ssize_t) fourier_info->height; y++)
278 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000279 {
280 phase_source[i]/=(2.0*MagickPI);
281 phase_source[i]+=0.5;
282 i++;
283 }
284 }
285 magnitude_view=AcquireCacheView(magnitude_image);
286 phase_view=AcquireCacheView(phase_image);
287 i=0L;
cristybb503372010-05-27 20:51:26 +0000288 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000289 {
290 q=GetCacheViewAuthenticPixels(magnitude_view,0L,y,fourier_info->height,1UL,
291 exception);
292 if (q == (PixelPacket *) NULL)
293 break;
294 indexes=GetCacheViewAuthenticIndexQueue(magnitude_view);
cristybb503372010-05-27 20:51:26 +0000295 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000296 {
297 switch (fourier_info->channel)
298 {
299 case RedChannel:
300 default:
301 {
cristye85007d2010-06-06 22:51:36 +0000302 q->red=ClampToQuantum(QuantumRange*magnitude_source[i]);
cristy3ed852e2009-09-05 21:47:34 +0000303 break;
304 }
305 case GreenChannel:
306 {
cristye85007d2010-06-06 22:51:36 +0000307 q->green=ClampToQuantum(QuantumRange*magnitude_source[i]);
cristy3ed852e2009-09-05 21:47:34 +0000308 break;
309 }
310 case BlueChannel:
311 {
cristye85007d2010-06-06 22:51:36 +0000312 q->blue=ClampToQuantum(QuantumRange*magnitude_source[i]);
cristy3ed852e2009-09-05 21:47:34 +0000313 break;
314 }
315 case OpacityChannel:
316 {
cristye85007d2010-06-06 22:51:36 +0000317 q->opacity=ClampToQuantum(QuantumRange*magnitude_source[i]);
cristy3ed852e2009-09-05 21:47:34 +0000318 break;
319 }
320 case IndexChannel:
321 {
cristye85007d2010-06-06 22:51:36 +0000322 indexes[x]=ClampToQuantum(QuantumRange*magnitude_source[i]);
cristy3ed852e2009-09-05 21:47:34 +0000323 break;
324 }
325 case GrayChannels:
326 {
cristye85007d2010-06-06 22:51:36 +0000327 q->red=ClampToQuantum(QuantumRange*magnitude_source[i]);
cristy3ed852e2009-09-05 21:47:34 +0000328 q->green=q->red;
329 q->blue=q->red;
330 break;
331 }
332 }
333 i++;
334 q++;
335 }
336 status=SyncCacheViewAuthenticPixels(magnitude_view,exception);
337 if (status == MagickFalse)
338 break;
339 }
340 i=0L;
cristybb503372010-05-27 20:51:26 +0000341 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000342 {
343 q=GetCacheViewAuthenticPixels(phase_view,0L,y,fourier_info->height,1UL,
344 exception);
345 if (q == (PixelPacket *) NULL)
346 break;
347 indexes=GetCacheViewAuthenticIndexQueue(phase_view);
cristybb503372010-05-27 20:51:26 +0000348 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000349 {
350 switch (fourier_info->channel)
351 {
352 case RedChannel:
353 default:
354 {
cristye85007d2010-06-06 22:51:36 +0000355 q->red=ClampToQuantum(QuantumRange*phase_source[i]);
cristy3ed852e2009-09-05 21:47:34 +0000356 break;
357 }
358 case GreenChannel:
359 {
cristye85007d2010-06-06 22:51:36 +0000360 q->green=ClampToQuantum(QuantumRange*phase_source[i]);
cristy3ed852e2009-09-05 21:47:34 +0000361 break;
362 }
363 case BlueChannel:
364 {
cristye85007d2010-06-06 22:51:36 +0000365 q->blue=ClampToQuantum(QuantumRange*phase_source[i]);
cristy3ed852e2009-09-05 21:47:34 +0000366 break;
367 }
368 case OpacityChannel:
369 {
cristye85007d2010-06-06 22:51:36 +0000370 q->opacity=ClampToQuantum(QuantumRange*phase_source[i]);
cristy3ed852e2009-09-05 21:47:34 +0000371 break;
372 }
373 case IndexChannel:
374 {
cristye85007d2010-06-06 22:51:36 +0000375 indexes[x]=ClampToQuantum(QuantumRange*phase_source[i]);
cristy3ed852e2009-09-05 21:47:34 +0000376 break;
377 }
378 case GrayChannels:
379 {
cristye85007d2010-06-06 22:51:36 +0000380 q->red=ClampToQuantum(QuantumRange*phase_source[i]);
cristy3ed852e2009-09-05 21:47:34 +0000381 q->green=q->red;
382 q->blue=q->red;
383 break;
384 }
385 }
386 i++;
387 q++;
388 }
389 status=SyncCacheViewAuthenticPixels(phase_view,exception);
390 if (status == MagickFalse)
391 break;
392 }
393 phase_view=DestroyCacheView(phase_view);
394 magnitude_view=DestroyCacheView(magnitude_view);
395 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
cristybb503372010-05-27 20:51:26 +0000416 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000417 y;
418
419 register const IndexPacket
420 *indexes;
421
422 register const PixelPacket
423 *p;
424
cristybb503372010-05-27 20:51:26 +0000425 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000426 i,
427 x;
428
429 /*
430 Generate the forward Fourier transform.
431 */
432 source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
433 fourier_info->width*sizeof(*source));
434 if (source == (double *) NULL)
435 {
436 (void) ThrowMagickException(exception,GetMagickModule(),
437 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
438 return(MagickFalse);
439 }
440 ResetMagickMemory(source,0,fourier_info->width*fourier_info->height*
441 sizeof(*source));
442 i=0L;
443 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +0000444 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000445 {
446 p=GetCacheViewVirtualPixels(image_view,0L,y,fourier_info->width,1UL,
447 exception);
448 if (p == (const PixelPacket *) NULL)
449 break;
450 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000451 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000452 {
453 switch (fourier_info->channel)
454 {
455 case RedChannel:
456 default:
457 {
cristyce70c172010-01-07 17:15:30 +0000458 source[i]=QuantumScale*GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000459 break;
460 }
461 case GreenChannel:
462 {
cristyce70c172010-01-07 17:15:30 +0000463 source[i]=QuantumScale*GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000464 break;
465 }
466 case BlueChannel:
467 {
cristyce70c172010-01-07 17:15:30 +0000468 source[i]=QuantumScale*GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000469 break;
470 }
471 case OpacityChannel:
472 {
cristyce70c172010-01-07 17:15:30 +0000473 source[i]=QuantumScale*GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000474 break;
475 }
476 case IndexChannel:
477 {
478 source[i]=QuantumScale*indexes[x];
479 break;
480 }
481 case GrayChannels:
482 {
cristyce70c172010-01-07 17:15:30 +0000483 source[i]=QuantumScale*GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000484 break;
485 }
486 }
487 i++;
488 p++;
489 }
490 }
491 image_view=DestroyCacheView(image_view);
492 fourier=(fftw_complex *) AcquireAlignedMemory((size_t) fourier_info->height,
493 fourier_info->center*sizeof(*fourier));
494 if (fourier == (fftw_complex *) NULL)
495 {
496 (void) ThrowMagickException(exception,GetMagickModule(),
497 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
498 source=(double *) RelinquishMagickMemory(source);
499 return(MagickFalse);
500 }
cristyb5d5f722009-11-04 03:03:49 +0000501#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000502 #pragma omp critical (MagickCore_ForwardFourierTransform)
503#endif
504 fftw_r2c_plan=fftw_plan_dft_r2c_2d(fourier_info->width,fourier_info->width,
505 source,fourier,FFTW_ESTIMATE);
506 fftw_execute(fftw_r2c_plan);
507 fftw_destroy_plan(fftw_r2c_plan);
508 source=(double *) RelinquishMagickMemory(source);
509 /*
510 Normalize Fourier transform.
511 */
512 n=(double) fourier_info->width*(double) fourier_info->width;
513 i=0L;
cristybb503372010-05-27 20:51:26 +0000514 for (y=0L; y < (ssize_t) fourier_info->height; y++)
515 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy56ed31c2010-03-22 00:46:21 +0000516 {
517#if defined(MAGICKCORE_HAVE_COMPLEX_H)
518 fourier[i]/=n;
519#else
520 fourier[i][0]/=n;
521 fourier[i][1]/=n;
522#endif
523 i++;
524 }
cristy3ed852e2009-09-05 21:47:34 +0000525 /*
526 Generate magnitude and phase (or real and imaginary).
527 */
528 i=0L;
529 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000530 for (y=0L; y < (ssize_t) fourier_info->height; y++)
531 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000532 {
533 magnitude[i]=cabs(fourier[i]);
534 phase[i]=carg(fourier[i]);
535 i++;
536 }
537 else
cristybb503372010-05-27 20:51:26 +0000538 for (y=0L; y < (ssize_t) fourier_info->height; y++)
539 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000540 {
541 magnitude[i]=creal(fourier[i]);
542 phase[i]=cimag(fourier[i]);
543 i++;
544 }
545 fourier=(fftw_complex *) RelinquishAlignedMemory(fourier);
546 return(MagickTrue);
547}
548
549static MagickBooleanType ForwardFourierTransformChannel(const Image *image,
550 const ChannelType channel,const MagickBooleanType modulus,
551 Image *fourier_image,ExceptionInfo *exception)
552{
553 double
554 *magnitude,
555 *phase;
556
557 fftw_complex
558 *fourier;
559
cristy9f3502b2010-03-21 02:39:26 +0000560 MagickBooleanType
561 status;
562
cristy56ed31c2010-03-22 00:46:21 +0000563 FourierInfo
564 fourier_info;
565
cristy3ed852e2009-09-05 21:47:34 +0000566 size_t
567 extent;
568
569 fourier_info.width=image->columns;
570 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
571 ((image->rows % 2) != 0))
572 {
573 extent=image->columns < image->rows ? image->rows : image->columns;
574 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
575 }
576 fourier_info.height=fourier_info.width;
cristybb503372010-05-27 20:51:26 +0000577 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2.0)+1L;
cristy3ed852e2009-09-05 21:47:34 +0000578 fourier_info.channel=channel;
579 fourier_info.modulus=modulus;
580 magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
581 fourier_info.center*sizeof(*magnitude));
582 if (magnitude == (double *) NULL)
583 {
584 (void) ThrowMagickException(exception,GetMagickModule(),
585 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
586 return(MagickFalse);
587 }
588 phase=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
589 fourier_info.center*sizeof(*phase));
590 if (phase == (double *) NULL)
591 {
592 (void) ThrowMagickException(exception,GetMagickModule(),
593 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
594 magnitude=(double *) RelinquishMagickMemory(magnitude);
595 return(MagickFalse);
596 }
597 fourier=(fftw_complex *) AcquireAlignedMemory((size_t) fourier_info.height,
598 fourier_info.center*sizeof(*fourier));
599 if (fourier == (fftw_complex *) NULL)
600 {
601 (void) ThrowMagickException(exception,GetMagickModule(),
602 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
603 phase=(double *) RelinquishMagickMemory(phase);
604 magnitude=(double *) RelinquishMagickMemory(magnitude);
605 return(MagickFalse);
606 }
607 status=ForwardFourierTransform(&fourier_info,image,magnitude,phase,exception);
608 if (status != MagickFalse)
609 status=ForwardFourier(&fourier_info,fourier_image,magnitude,phase,
610 exception);
611 fourier=(fftw_complex *) RelinquishAlignedMemory(fourier);
612 phase=(double *) RelinquishMagickMemory(phase);
613 magnitude=(double *) RelinquishMagickMemory(magnitude);
614 return(status);
615}
616#endif
617
618MagickExport Image *ForwardFourierTransformImage(const Image *image,
619 const MagickBooleanType modulus,ExceptionInfo *exception)
620{
621 Image
622 *fourier_image;
623
624 fourier_image=NewImageList();
625#if !defined(MAGICKCORE_FFTW_DELEGATE)
626 (void) modulus;
627 (void) ThrowMagickException(exception,GetMagickModule(),
628 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","`%s' (FFTW)",
629 image->filename);
630#else
631 {
632 Image
633 *magnitude_image;
634
cristybb503372010-05-27 20:51:26 +0000635 size_t
cristy3ed852e2009-09-05 21:47:34 +0000636 extent,
637 width;
638
639 width=image->columns;
640 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
641 ((image->rows % 2) != 0))
642 {
643 extent=image->columns < image->rows ? image->rows : image->columns;
644 width=(extent & 0x01) == 1 ? extent+1UL : extent;
645 }
646 magnitude_image=CloneImage(image,width,width,MagickFalse,exception);
647 if (magnitude_image != (Image *) NULL)
648 {
649 Image
650 *phase_image;
651
652 magnitude_image->storage_class=DirectClass;
653 magnitude_image->depth=32UL;
654 phase_image=CloneImage(image,width,width,MagickFalse,exception);
655 if (phase_image == (Image *) NULL)
656 magnitude_image=DestroyImage(magnitude_image);
657 else
658 {
659 MagickBooleanType
660 is_gray,
661 status;
662
cristybb503372010-05-27 20:51:26 +0000663 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000664 i;
665
666 phase_image->storage_class=DirectClass;
667 phase_image->depth=32UL;
668 AppendImageToList(&fourier_image,magnitude_image);
669 AppendImageToList(&fourier_image,phase_image);
670 status=MagickTrue;
671 is_gray=IsGrayImage(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000672#if defined(MAGICKCORE_OPENMP_SUPPORT)
673 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +0000674#endif
675 for (i=0L; i < 5L; i++)
676 {
677 MagickBooleanType
678 thread_status;
679
680 thread_status=MagickTrue;
681 switch (i)
682 {
683 case 0:
684 {
685 if (is_gray != MagickFalse)
686 {
687 thread_status=ForwardFourierTransformChannel(image,
688 GrayChannels,modulus,fourier_image,exception);
689 break;
690 }
691 thread_status=ForwardFourierTransformChannel(image,RedChannel,
692 modulus,fourier_image,exception);
693 break;
694 }
695 case 1:
696 {
697 if (is_gray == MagickFalse)
698 thread_status=ForwardFourierTransformChannel(image,
699 GreenChannel,modulus,fourier_image,exception);
700 break;
701 }
702 case 2:
703 {
704 if (is_gray == MagickFalse)
705 thread_status=ForwardFourierTransformChannel(image,
706 BlueChannel,modulus,fourier_image,exception);
707 break;
708 }
709 case 4:
710 {
711 if (image->matte != MagickFalse)
712 thread_status=ForwardFourierTransformChannel(image,
713 OpacityChannel,modulus,fourier_image,exception);
714 break;
715 }
716 case 5:
717 {
718 if (image->colorspace == CMYKColorspace)
719 thread_status=ForwardFourierTransformChannel(image,
720 IndexChannel,modulus,fourier_image,exception);
721 break;
722 }
723 }
724 if (thread_status == MagickFalse)
725 status=thread_status;
726 }
727 if (status == MagickFalse)
728 fourier_image=DestroyImageList(fourier_image);
729 fftw_cleanup();
730 }
731 }
732 }
733#endif
734 return(fourier_image);
735}
736
737/*
738%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
739% %
740% %
741% %
742% 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 %
743% %
744% %
745% %
746%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
747%
748% InverseFourierTransformImage() implements the inverse discrete Fourier
749% transform (DFT) of the image either as a magnitude / phase or real /
750% imaginary image pair.
751%
752% The format of the InverseFourierTransformImage method is:
753%
cristyc9550792009-11-13 20:05:42 +0000754% Image *InverseFourierTransformImage(const Image *magnitude_image,
755% const Image *phase_image,const MagickBooleanType modulus,
756% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000757%
758% A description of each parameter follows:
759%
cristyc9550792009-11-13 20:05:42 +0000760% o magnitude_image: the magnitude or real image.
761%
762% o phase_image: the phase or imaginary image.
cristy3ed852e2009-09-05 21:47:34 +0000763%
764% o modulus: if true, return transform as a magnitude / phase pair
765% otherwise a real / imaginary image pair.
766%
767% o exception: return any errors or warnings in this structure.
768%
769*/
770
771#if defined(MAGICKCORE_FFTW_DELEGATE)
cristybb503372010-05-27 20:51:26 +0000772static MagickBooleanType InverseQuadrantSwap(const size_t width,
773 const size_t height,const double *source,double *destination)
cristy3ed852e2009-09-05 21:47:34 +0000774{
cristybb503372010-05-27 20:51:26 +0000775 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000776 center,
777 y;
778
cristybb503372010-05-27 20:51:26 +0000779 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000780 x;
781
782 /*
783 Swap quadrants.
784 */
cristybb503372010-05-27 20:51:26 +0000785 center=(ssize_t) floor((double) width/2.0)+1L;
786 for (y=1L; y < (ssize_t) height; y++)
787 for (x=0L; x < (ssize_t) (width/2L+1L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000788 destination[center*(height-y)-x+width/2L]=source[y*width+x];
cristybb503372010-05-27 20:51:26 +0000789 for (y=0L; y < (ssize_t) height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000790 destination[center*y]=source[y*width+width/2L];
791 for (x=0L; x < center; x++)
792 destination[x]=source[center-x-1L];
cristybb503372010-05-27 20:51:26 +0000793 return(RollFourier(center,height,0L,(ssize_t) height/-2L,destination));
cristy3ed852e2009-09-05 21:47:34 +0000794}
795
796static MagickBooleanType InverseFourier(FourierInfo *fourier_info,
cristyc9550792009-11-13 20:05:42 +0000797 const Image *magnitude_image,const Image *phase_image,fftw_complex *fourier,
798 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000799{
800 CacheView
801 *magnitude_view,
802 *phase_view;
803
804 double
805 *magnitude,
806 *phase,
807 *magnitude_source,
808 *phase_source;
809
cristybb503372010-05-27 20:51:26 +0000810 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000811 y;
812
813 MagickBooleanType
814 status;
815
816 register const IndexPacket
817 *indexes;
818
819 register const PixelPacket
820 *p;
821
cristybb503372010-05-27 20:51:26 +0000822 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000823 i,
824 x;
825
826 /*
827 Inverse fourier - read image and break down into a double array.
828 */
cristy3ed852e2009-09-05 21:47:34 +0000829 magnitude_source=(double *) AcquireQuantumMemory((size_t)
830 fourier_info->height,fourier_info->width*sizeof(*magnitude_source));
831 if (magnitude_source == (double *) NULL)
832 {
833 (void) ThrowMagickException(exception,GetMagickModule(),
cristyc9550792009-11-13 20:05:42 +0000834 ResourceLimitError,"MemoryAllocationFailed","`%s'",
835 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000836 return(MagickFalse);
837 }
838 phase_source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
839 fourier_info->height*sizeof(*phase_source));
840 if (phase_source == (double *) NULL)
841 {
842 (void) ThrowMagickException(exception,GetMagickModule(),
cristyc9550792009-11-13 20:05:42 +0000843 ResourceLimitError,"MemoryAllocationFailed","`%s'",
844 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000845 magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
846 return(MagickFalse);
847 }
848 i=0L;
849 magnitude_view=AcquireCacheView(magnitude_image);
cristybb503372010-05-27 20:51:26 +0000850 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000851 {
852 p=GetCacheViewVirtualPixels(magnitude_view,0L,y,fourier_info->width,1UL,
853 exception);
854 if (p == (const PixelPacket *) NULL)
855 break;
856 indexes=GetCacheViewAuthenticIndexQueue(magnitude_view);
cristybb503372010-05-27 20:51:26 +0000857 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000858 {
859 switch (fourier_info->channel)
860 {
861 case RedChannel:
862 default:
863 {
cristyce70c172010-01-07 17:15:30 +0000864 magnitude_source[i]=QuantumScale*GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000865 break;
866 }
867 case GreenChannel:
868 {
cristyce70c172010-01-07 17:15:30 +0000869 magnitude_source[i]=QuantumScale*GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000870 break;
871 }
872 case BlueChannel:
873 {
cristyce70c172010-01-07 17:15:30 +0000874 magnitude_source[i]=QuantumScale*GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000875 break;
876 }
877 case OpacityChannel:
878 {
cristyce70c172010-01-07 17:15:30 +0000879 magnitude_source[i]=QuantumScale*GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000880 break;
881 }
882 case IndexChannel:
883 {
884 magnitude_source[i]=QuantumScale*indexes[x];
885 break;
886 }
887 case GrayChannels:
888 {
cristyce70c172010-01-07 17:15:30 +0000889 magnitude_source[i]=QuantumScale*GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000890 break;
891 }
892 }
893 i++;
894 p++;
895 }
896 }
897 i=0L;
898 phase_view=AcquireCacheView(phase_image);
cristybb503372010-05-27 20:51:26 +0000899 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000900 {
901 p=GetCacheViewVirtualPixels(phase_view,0,y,fourier_info->width,1,
902 exception);
903 if (p == (const PixelPacket *) NULL)
904 break;
905 indexes=GetCacheViewAuthenticIndexQueue(phase_view);
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 {
910 case RedChannel:
911 default:
912 {
cristyce70c172010-01-07 17:15:30 +0000913 phase_source[i]=QuantumScale*GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000914 break;
915 }
916 case GreenChannel:
917 {
cristyce70c172010-01-07 17:15:30 +0000918 phase_source[i]=QuantumScale*GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000919 break;
920 }
921 case BlueChannel:
922 {
cristyce70c172010-01-07 17:15:30 +0000923 phase_source[i]=QuantumScale*GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000924 break;
925 }
926 case OpacityChannel:
927 {
cristyce70c172010-01-07 17:15:30 +0000928 phase_source[i]=QuantumScale*GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000929 break;
930 }
931 case IndexChannel:
932 {
933 phase_source[i]=QuantumScale*indexes[x];
934 break;
935 }
936 case GrayChannels:
937 {
cristyce70c172010-01-07 17:15:30 +0000938 phase_source[i]=QuantumScale*GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000939 break;
940 }
941 }
942 i++;
943 p++;
944 }
945 }
946 if (fourier_info->modulus != MagickFalse)
947 {
948 i=0L;
cristybb503372010-05-27 20:51:26 +0000949 for (y=0L; y < (ssize_t) fourier_info->height; y++)
950 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000951 {
952 phase_source[i]-=0.5;
953 phase_source[i]*=(2.0*MagickPI);
954 i++;
955 }
956 }
957 magnitude_view=DestroyCacheView(magnitude_view);
958 phase_view=DestroyCacheView(phase_view);
959 magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
960 fourier_info->center*sizeof(*magnitude));
961 if (magnitude == (double *) NULL)
962 {
963 (void) ThrowMagickException(exception,GetMagickModule(),
cristyc9550792009-11-13 20:05:42 +0000964 ResourceLimitError,"MemoryAllocationFailed","`%s'",
965 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000966 magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
967 phase_source=(double *) RelinquishMagickMemory(phase_source);
968 return(MagickFalse);
969 }
970 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
971 magnitude_source,magnitude);
972 magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
973 phase=(double *) AcquireQuantumMemory((size_t) fourier_info->width,
974 fourier_info->height*sizeof(*phase));
975 if (phase == (double *) NULL)
976 {
977 (void) ThrowMagickException(exception,GetMagickModule(),
cristyc9550792009-11-13 20:05:42 +0000978 ResourceLimitError,"MemoryAllocationFailed","`%s'",
979 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000980 phase_source=(double *) RelinquishMagickMemory(phase_source);
981 return(MagickFalse);
982 }
983 CorrectPhaseLHS(fourier_info->width,fourier_info->width,phase_source);
984 if (status != MagickFalse)
985 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
986 phase_source,phase);
987 phase_source=(double *) RelinquishMagickMemory(phase_source);
988 /*
989 Merge two sets.
990 */
991 i=0L;
992 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000993 for (y=0L; y < (ssize_t) fourier_info->height; y++)
994 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000995 {
cristy56ed31c2010-03-22 00:46:21 +0000996#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy3ed852e2009-09-05 21:47:34 +0000997 fourier[i]=magnitude[i]*cos(phase[i])+I*magnitude[i]*sin(phase[i]);
cristy56ed31c2010-03-22 00:46:21 +0000998#else
999 fourier[i][0]=magnitude[i]*cos(phase[i]);
1000 fourier[i][1]=magnitude[i]*sin(phase[i]);
1001#endif
cristy3ed852e2009-09-05 21:47:34 +00001002 i++;
1003 }
1004 else
cristybb503372010-05-27 20:51:26 +00001005 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1006 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001007 {
cristy56ed31c2010-03-22 00:46:21 +00001008#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy3ed852e2009-09-05 21:47:34 +00001009 fourier[i]=magnitude[i]+I*phase[i];
cristy56ed31c2010-03-22 00:46:21 +00001010#else
1011 fourier[i][0]=magnitude[i];
1012 fourier[i][1]=phase[i];
1013#endif
cristy3ed852e2009-09-05 21:47:34 +00001014 i++;
1015 }
1016 phase=(double *) RelinquishMagickMemory(phase);
1017 magnitude=(double *) RelinquishMagickMemory(magnitude);
1018 return(status);
1019}
1020
1021static MagickBooleanType InverseFourierTransform(FourierInfo *fourier_info,
1022 fftw_complex *fourier,Image *image,ExceptionInfo *exception)
1023{
1024 CacheView
1025 *image_view;
1026
1027 double
1028 *source;
1029
1030 fftw_plan
1031 fftw_c2r_plan;
1032
cristybb503372010-05-27 20:51:26 +00001033 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001034 y;
1035
1036 register IndexPacket
1037 *indexes;
1038
cristybb503372010-05-27 20:51:26 +00001039 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001040 i,
1041 x;
1042
1043 register PixelPacket
1044 *q;
1045
1046 source=(double *) AcquireQuantumMemory((size_t) fourier_info->width,
1047 fourier_info->height*sizeof(double));
1048 if (source == (double *) NULL)
1049 {
1050 (void) ThrowMagickException(exception,GetMagickModule(),
1051 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
1052 return(MagickFalse);
1053 }
cristyb5d5f722009-11-04 03:03:49 +00001054#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001055 #pragma omp critical (MagickCore_InverseFourierTransform)
1056#endif
1057 fftw_c2r_plan=fftw_plan_dft_c2r_2d(fourier_info->width,fourier_info->height,
1058 fourier,source,FFTW_ESTIMATE);
1059 fftw_execute(fftw_c2r_plan);
1060 fftw_destroy_plan(fftw_c2r_plan);
1061 i=0L;
1062 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +00001063 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001064 {
1065 q=GetCacheViewAuthenticPixels(image_view,0L,y,fourier_info->width,1UL,
1066 exception);
1067 if (q == (PixelPacket *) NULL)
1068 break;
1069 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00001070 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001071 {
1072 switch (fourier_info->channel)
1073 {
1074 case RedChannel:
1075 default:
1076 {
cristye85007d2010-06-06 22:51:36 +00001077 q->red=ClampToQuantum(QuantumRange*source[i]);
cristy3ed852e2009-09-05 21:47:34 +00001078 break;
1079 }
1080 case GreenChannel:
1081 {
cristye85007d2010-06-06 22:51:36 +00001082 q->green=ClampToQuantum(QuantumRange*source[i]);
cristy3ed852e2009-09-05 21:47:34 +00001083 break;
1084 }
1085 case BlueChannel:
1086 {
cristye85007d2010-06-06 22:51:36 +00001087 q->blue=ClampToQuantum(QuantumRange*source[i]);
cristy3ed852e2009-09-05 21:47:34 +00001088 break;
1089 }
1090 case OpacityChannel:
1091 {
cristye85007d2010-06-06 22:51:36 +00001092 q->opacity=ClampToQuantum(QuantumRange*source[i]);
cristy3ed852e2009-09-05 21:47:34 +00001093 break;
1094 }
1095 case IndexChannel:
1096 {
cristye85007d2010-06-06 22:51:36 +00001097 indexes[x]=ClampToQuantum(QuantumRange*source[i]);
cristy3ed852e2009-09-05 21:47:34 +00001098 break;
1099 }
1100 case GrayChannels:
1101 {
cristye85007d2010-06-06 22:51:36 +00001102 q->red=ClampToQuantum(QuantumRange*source[i]);
cristy3ed852e2009-09-05 21:47:34 +00001103 q->green=q->red;
1104 q->blue=q->red;
1105 break;
1106 }
1107 }
1108 i++;
1109 q++;
1110 }
1111 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1112 break;
1113 }
1114 image_view=DestroyCacheView(image_view);
1115 source=(double *) RelinquishMagickMemory(source);
1116 return(MagickTrue);
1117}
1118
cristyc9550792009-11-13 20:05:42 +00001119static MagickBooleanType InverseFourierTransformChannel(
1120 const Image *magnitude_image,const Image *phase_image,
cristy3ed852e2009-09-05 21:47:34 +00001121 const ChannelType channel,const MagickBooleanType modulus,
1122 Image *fourier_image,ExceptionInfo *exception)
1123{
1124 double
1125 *magnitude,
1126 *phase;
1127
1128 fftw_complex
1129 *fourier;
1130
1131 FourierInfo
1132 fourier_info;
1133
1134 MagickBooleanType
1135 status;
1136
1137 size_t
1138 extent;
1139
cristyc9550792009-11-13 20:05:42 +00001140 fourier_info.width=magnitude_image->columns;
1141 if ((magnitude_image->columns != magnitude_image->rows) ||
1142 ((magnitude_image->columns % 2) != 0) ||
1143 ((magnitude_image->rows % 2) != 0))
cristy3ed852e2009-09-05 21:47:34 +00001144 {
cristyc9550792009-11-13 20:05:42 +00001145 extent=magnitude_image->columns < magnitude_image->rows ?
1146 magnitude_image->rows : magnitude_image->columns;
cristy3ed852e2009-09-05 21:47:34 +00001147 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
1148 }
1149 fourier_info.height=fourier_info.width;
cristybb503372010-05-27 20:51:26 +00001150 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2.0)+1L;
cristy3ed852e2009-09-05 21:47:34 +00001151 fourier_info.channel=channel;
1152 fourier_info.modulus=modulus;
1153 magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
1154 fourier_info.center*sizeof(double));
1155 if (magnitude == (double *) NULL)
1156 {
1157 (void) ThrowMagickException(exception,GetMagickModule(),
cristyc9550792009-11-13 20:05:42 +00001158 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1159 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001160 return(MagickFalse);
1161 }
1162 phase=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
1163 fourier_info.center*sizeof(double));
1164 if (phase == (double *) NULL)
1165 {
1166 (void) ThrowMagickException(exception,GetMagickModule(),
cristyc9550792009-11-13 20:05:42 +00001167 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1168 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001169 magnitude=(double *) RelinquishMagickMemory(magnitude);
1170 return(MagickFalse);
1171 }
1172 fourier=(fftw_complex *) AcquireAlignedMemory((size_t) fourier_info.height,
1173 fourier_info.center*sizeof(*fourier));
1174 if (fourier == (fftw_complex *) NULL)
1175 {
1176 (void) ThrowMagickException(exception,GetMagickModule(),
cristyc9550792009-11-13 20:05:42 +00001177 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1178 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001179 phase=(double *) RelinquishMagickMemory(phase);
1180 magnitude=(double *) RelinquishMagickMemory(magnitude);
1181 return(MagickFalse);
1182 }
cristyc9550792009-11-13 20:05:42 +00001183 status=InverseFourier(&fourier_info,magnitude_image,phase_image,fourier,
1184 exception);
cristy3ed852e2009-09-05 21:47:34 +00001185 if (status != MagickFalse)
1186 status=InverseFourierTransform(&fourier_info,fourier,fourier_image,
1187 exception);
1188 fourier=(fftw_complex *) RelinquishAlignedMemory(fourier);
1189 phase=(double *) RelinquishMagickMemory(phase);
1190 magnitude=(double *) RelinquishMagickMemory(magnitude);
1191 return(status);
1192}
1193#endif
1194
cristyc9550792009-11-13 20:05:42 +00001195MagickExport Image *InverseFourierTransformImage(const Image *magnitude_image,
1196 const Image *phase_image,const MagickBooleanType modulus,
1197 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001198{
1199 Image
1200 *fourier_image;
1201
cristyc9550792009-11-13 20:05:42 +00001202 assert(magnitude_image != (Image *) NULL);
1203 assert(magnitude_image->signature == MagickSignature);
1204 if (magnitude_image->debug != MagickFalse)
1205 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1206 magnitude_image->filename);
1207 if (phase_image == (Image *) NULL)
1208 {
1209 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
1210 "ImageSequenceRequired","`%s'",magnitude_image->filename);
cristy9372a152009-11-18 01:42:16 +00001211 return((Image *) NULL);
cristyc9550792009-11-13 20:05:42 +00001212 }
cristy3ed852e2009-09-05 21:47:34 +00001213#if !defined(MAGICKCORE_FFTW_DELEGATE)
1214 fourier_image=(Image *) NULL;
1215 (void) modulus;
1216 (void) ThrowMagickException(exception,GetMagickModule(),
1217 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","`%s' (FFTW)",
cristyc9550792009-11-13 20:05:42 +00001218 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001219#else
1220 {
cristyc9550792009-11-13 20:05:42 +00001221 fourier_image=CloneImage(magnitude_image,magnitude_image->columns,
1222 magnitude_image->rows,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00001223 if (fourier_image != (Image *) NULL)
1224 {
1225 MagickBooleanType
1226 is_gray,
1227 status;
1228
cristybb503372010-05-27 20:51:26 +00001229 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001230 i;
1231
1232 status=MagickTrue;
cristyc9550792009-11-13 20:05:42 +00001233 is_gray=IsGrayImage(magnitude_image,exception);
1234 if (is_gray != MagickFalse)
1235 is_gray=IsGrayImage(phase_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001236#if defined(MAGICKCORE_OPENMP_SUPPORT)
1237 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00001238#endif
1239 for (i=0L; i < 5L; i++)
1240 {
1241 MagickBooleanType
1242 thread_status;
1243
1244 thread_status=MagickTrue;
1245 switch (i)
1246 {
1247 case 0:
1248 {
1249 if (is_gray != MagickFalse)
1250 {
cristyc9550792009-11-13 20:05:42 +00001251 thread_status=InverseFourierTransformChannel(magnitude_image,
1252 phase_image,GrayChannels,modulus,fourier_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001253 break;
1254 }
cristyc9550792009-11-13 20:05:42 +00001255 thread_status=InverseFourierTransformChannel(magnitude_image,
1256 phase_image,RedChannel,modulus,fourier_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001257 break;
1258 }
1259 case 1:
1260 {
1261 if (is_gray == MagickFalse)
cristyc9550792009-11-13 20:05:42 +00001262 thread_status=InverseFourierTransformChannel(magnitude_image,
1263 phase_image,GreenChannel,modulus,fourier_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001264 break;
1265 }
1266 case 2:
1267 {
1268 if (is_gray == MagickFalse)
cristyc9550792009-11-13 20:05:42 +00001269 thread_status=InverseFourierTransformChannel(magnitude_image,
1270 phase_image,BlueChannel,modulus,fourier_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001271 break;
1272 }
1273 case 3:
1274 {
cristyc9550792009-11-13 20:05:42 +00001275 if (magnitude_image->matte != MagickFalse)
1276 thread_status=InverseFourierTransformChannel(magnitude_image,
1277 phase_image,OpacityChannel,modulus,fourier_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001278 break;
1279 }
1280 case 4:
1281 {
cristyc9550792009-11-13 20:05:42 +00001282 if (magnitude_image->colorspace == CMYKColorspace)
1283 thread_status=InverseFourierTransformChannel(magnitude_image,
1284 phase_image,IndexChannel,modulus,fourier_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001285 break;
1286 }
1287 }
1288 if (thread_status == MagickFalse)
1289 status=thread_status;
1290 }
1291 if (status == MagickFalse)
1292 fourier_image=DestroyImage(fourier_image);
1293 }
1294 fftw_cleanup();
1295 }
1296#endif
1297 return(fourier_image);
1298}