blob: 8e7b21bc901cf270284ad10815e10d90a7633c37 [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 /*
cristy2da05352010-09-15 19:22:17 +0000138 Move zero frequency (DC, average color) from (0,0) to (width/2,height/2).
cristy3ed852e2009-09-05 21:47:34 +0000139 */
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);
cristyb41ee102010-10-04 16:46:15 +0000492 fourier=(fftw_complex *) AcquireQuantumMemory((size_t) fourier_info->height,
cristy3ed852e2009-09-05 21:47:34 +0000493 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 }
cristyb41ee102010-10-04 16:46:15 +0000545 fourier=(fftw_complex *) RelinquishMagickMemory(fourier);
cristy3ed852e2009-09-05 21:47:34 +0000546 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 }
cristyb41ee102010-10-04 16:46:15 +0000597 fourier=(fftw_complex *) AcquireQuantumMemory((size_t) fourier_info.height,
cristy3ed852e2009-09-05 21:47:34 +0000598 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);
cristyb41ee102010-10-04 16:46:15 +0000611 fourier=(fftw_complex *) RelinquishMagickMemory(fourier);
cristy3ed852e2009-09-05 21:47:34 +0000612 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
cristydf079ac2010-09-10 01:45:44 +00001057 {
1058 fftw_c2r_plan=fftw_plan_dft_c2r_2d(fourier_info->width,fourier_info->height,
1059 fourier,source,FFTW_ESTIMATE);
1060 fftw_execute(fftw_c2r_plan);
1061 fftw_destroy_plan(fftw_c2r_plan);
1062 }
cristy3ed852e2009-09-05 21:47:34 +00001063 i=0L;
1064 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +00001065 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001066 {
cristy85812052010-09-14 17:56:15 +00001067 if (y >= (ssize_t) image->rows)
1068 break;
1069 q=GetCacheViewAuthenticPixels(image_view,0L,y,fourier_info->width >
1070 image->columns ? image->columns : fourier_info->width,1UL,exception);
cristy3ed852e2009-09-05 21:47:34 +00001071 if (q == (PixelPacket *) NULL)
1072 break;
1073 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00001074 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001075 {
1076 switch (fourier_info->channel)
1077 {
1078 case RedChannel:
1079 default:
1080 {
cristye85007d2010-06-06 22:51:36 +00001081 q->red=ClampToQuantum(QuantumRange*source[i]);
cristy3ed852e2009-09-05 21:47:34 +00001082 break;
1083 }
1084 case GreenChannel:
1085 {
cristye85007d2010-06-06 22:51:36 +00001086 q->green=ClampToQuantum(QuantumRange*source[i]);
cristy3ed852e2009-09-05 21:47:34 +00001087 break;
1088 }
1089 case BlueChannel:
1090 {
cristye85007d2010-06-06 22:51:36 +00001091 q->blue=ClampToQuantum(QuantumRange*source[i]);
cristy3ed852e2009-09-05 21:47:34 +00001092 break;
1093 }
1094 case OpacityChannel:
1095 {
cristye85007d2010-06-06 22:51:36 +00001096 q->opacity=ClampToQuantum(QuantumRange*source[i]);
cristy3ed852e2009-09-05 21:47:34 +00001097 break;
1098 }
1099 case IndexChannel:
1100 {
cristye85007d2010-06-06 22:51:36 +00001101 indexes[x]=ClampToQuantum(QuantumRange*source[i]);
cristy3ed852e2009-09-05 21:47:34 +00001102 break;
1103 }
1104 case GrayChannels:
1105 {
cristye85007d2010-06-06 22:51:36 +00001106 q->red=ClampToQuantum(QuantumRange*source[i]);
cristy3ed852e2009-09-05 21:47:34 +00001107 q->green=q->red;
1108 q->blue=q->red;
1109 break;
1110 }
1111 }
1112 i++;
1113 q++;
1114 }
1115 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1116 break;
1117 }
1118 image_view=DestroyCacheView(image_view);
1119 source=(double *) RelinquishMagickMemory(source);
1120 return(MagickTrue);
1121}
1122
cristyc9550792009-11-13 20:05:42 +00001123static MagickBooleanType InverseFourierTransformChannel(
1124 const Image *magnitude_image,const Image *phase_image,
cristy3ed852e2009-09-05 21:47:34 +00001125 const ChannelType channel,const MagickBooleanType modulus,
1126 Image *fourier_image,ExceptionInfo *exception)
1127{
1128 double
1129 *magnitude,
1130 *phase;
1131
1132 fftw_complex
1133 *fourier;
1134
1135 FourierInfo
1136 fourier_info;
1137
1138 MagickBooleanType
1139 status;
1140
1141 size_t
1142 extent;
1143
cristyc9550792009-11-13 20:05:42 +00001144 fourier_info.width=magnitude_image->columns;
1145 if ((magnitude_image->columns != magnitude_image->rows) ||
1146 ((magnitude_image->columns % 2) != 0) ||
1147 ((magnitude_image->rows % 2) != 0))
cristy3ed852e2009-09-05 21:47:34 +00001148 {
cristyc9550792009-11-13 20:05:42 +00001149 extent=magnitude_image->columns < magnitude_image->rows ?
1150 magnitude_image->rows : magnitude_image->columns;
cristy3ed852e2009-09-05 21:47:34 +00001151 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
1152 }
1153 fourier_info.height=fourier_info.width;
cristybb503372010-05-27 20:51:26 +00001154 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2.0)+1L;
cristy3ed852e2009-09-05 21:47:34 +00001155 fourier_info.channel=channel;
1156 fourier_info.modulus=modulus;
1157 magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
1158 fourier_info.center*sizeof(double));
1159 if (magnitude == (double *) NULL)
1160 {
1161 (void) ThrowMagickException(exception,GetMagickModule(),
cristyc9550792009-11-13 20:05:42 +00001162 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1163 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001164 return(MagickFalse);
1165 }
1166 phase=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
1167 fourier_info.center*sizeof(double));
1168 if (phase == (double *) NULL)
1169 {
1170 (void) ThrowMagickException(exception,GetMagickModule(),
cristyc9550792009-11-13 20:05:42 +00001171 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1172 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001173 magnitude=(double *) RelinquishMagickMemory(magnitude);
1174 return(MagickFalse);
1175 }
cristyb41ee102010-10-04 16:46:15 +00001176 fourier=(fftw_complex *) AcquireQuantumMemory((size_t) fourier_info.height,
cristy3ed852e2009-09-05 21:47:34 +00001177 fourier_info.center*sizeof(*fourier));
1178 if (fourier == (fftw_complex *) NULL)
1179 {
1180 (void) ThrowMagickException(exception,GetMagickModule(),
cristyc9550792009-11-13 20:05:42 +00001181 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1182 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001183 phase=(double *) RelinquishMagickMemory(phase);
1184 magnitude=(double *) RelinquishMagickMemory(magnitude);
1185 return(MagickFalse);
1186 }
cristyc9550792009-11-13 20:05:42 +00001187 status=InverseFourier(&fourier_info,magnitude_image,phase_image,fourier,
1188 exception);
cristy3ed852e2009-09-05 21:47:34 +00001189 if (status != MagickFalse)
1190 status=InverseFourierTransform(&fourier_info,fourier,fourier_image,
1191 exception);
cristyb41ee102010-10-04 16:46:15 +00001192 fourier=(fftw_complex *) RelinquishMagickMemory(fourier);
cristy3ed852e2009-09-05 21:47:34 +00001193 phase=(double *) RelinquishMagickMemory(phase);
1194 magnitude=(double *) RelinquishMagickMemory(magnitude);
1195 return(status);
1196}
1197#endif
1198
cristyc9550792009-11-13 20:05:42 +00001199MagickExport Image *InverseFourierTransformImage(const Image *magnitude_image,
1200 const Image *phase_image,const MagickBooleanType modulus,
1201 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001202{
1203 Image
1204 *fourier_image;
1205
cristyc9550792009-11-13 20:05:42 +00001206 assert(magnitude_image != (Image *) NULL);
1207 assert(magnitude_image->signature == MagickSignature);
1208 if (magnitude_image->debug != MagickFalse)
1209 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1210 magnitude_image->filename);
1211 if (phase_image == (Image *) NULL)
1212 {
1213 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
1214 "ImageSequenceRequired","`%s'",magnitude_image->filename);
cristy9372a152009-11-18 01:42:16 +00001215 return((Image *) NULL);
cristyc9550792009-11-13 20:05:42 +00001216 }
cristy3ed852e2009-09-05 21:47:34 +00001217#if !defined(MAGICKCORE_FFTW_DELEGATE)
1218 fourier_image=(Image *) NULL;
1219 (void) modulus;
1220 (void) ThrowMagickException(exception,GetMagickModule(),
1221 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","`%s' (FFTW)",
cristyc9550792009-11-13 20:05:42 +00001222 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001223#else
1224 {
cristyc9550792009-11-13 20:05:42 +00001225 fourier_image=CloneImage(magnitude_image,magnitude_image->columns,
1226 magnitude_image->rows,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00001227 if (fourier_image != (Image *) NULL)
1228 {
1229 MagickBooleanType
1230 is_gray,
1231 status;
1232
cristybb503372010-05-27 20:51:26 +00001233 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001234 i;
1235
1236 status=MagickTrue;
cristyc9550792009-11-13 20:05:42 +00001237 is_gray=IsGrayImage(magnitude_image,exception);
1238 if (is_gray != MagickFalse)
1239 is_gray=IsGrayImage(phase_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001240#if defined(MAGICKCORE_OPENMP_SUPPORT)
1241 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00001242#endif
1243 for (i=0L; i < 5L; i++)
1244 {
1245 MagickBooleanType
1246 thread_status;
1247
1248 thread_status=MagickTrue;
1249 switch (i)
1250 {
1251 case 0:
1252 {
1253 if (is_gray != MagickFalse)
1254 {
cristyc9550792009-11-13 20:05:42 +00001255 thread_status=InverseFourierTransformChannel(magnitude_image,
1256 phase_image,GrayChannels,modulus,fourier_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001257 break;
1258 }
cristyc9550792009-11-13 20:05:42 +00001259 thread_status=InverseFourierTransformChannel(magnitude_image,
1260 phase_image,RedChannel,modulus,fourier_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001261 break;
1262 }
1263 case 1:
1264 {
1265 if (is_gray == MagickFalse)
cristyc9550792009-11-13 20:05:42 +00001266 thread_status=InverseFourierTransformChannel(magnitude_image,
1267 phase_image,GreenChannel,modulus,fourier_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001268 break;
1269 }
1270 case 2:
1271 {
1272 if (is_gray == MagickFalse)
cristyc9550792009-11-13 20:05:42 +00001273 thread_status=InverseFourierTransformChannel(magnitude_image,
1274 phase_image,BlueChannel,modulus,fourier_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001275 break;
1276 }
1277 case 3:
1278 {
cristyc9550792009-11-13 20:05:42 +00001279 if (magnitude_image->matte != MagickFalse)
1280 thread_status=InverseFourierTransformChannel(magnitude_image,
1281 phase_image,OpacityChannel,modulus,fourier_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001282 break;
1283 }
1284 case 4:
1285 {
cristyc9550792009-11-13 20:05:42 +00001286 if (magnitude_image->colorspace == CMYKColorspace)
1287 thread_status=InverseFourierTransformChannel(magnitude_image,
1288 phase_image,IndexChannel,modulus,fourier_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001289 break;
1290 }
1291 }
1292 if (thread_status == MagickFalse)
1293 status=thread_status;
1294 }
1295 if (status == MagickFalse)
1296 fourier_image=DestroyImage(fourier_image);
1297 }
1298 fftw_cleanup();
1299 }
1300#endif
1301 return(fourier_image);
1302}