blob: 852928d384e16440a0d72521c65f96099736b7dc [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% %
cristy7e41fe82010-12-04 23:12:08 +000022% Copyright 1999-2011 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#endif
cristy3ed852e2009-09-05 21:47:34 +000061#include <fftw3.h>
cristy47b022b2011-01-18 22:29:21 +000062#if !defined(MAGICKCORE_HAVE_CABS)
63#define cabs(z) (sqrt(z[0]*z[0]+z[1]*z[1]))
64#endif
65#if !defined(MAGICKCORE_HAVE_CARG)
66#define carg(z) (atan2(cimag(z[1]),creal(z[0])))
67#endif
68#if !defined(MAGICKCORE_HAVE_CIMAG)
69#define cimag(z) (z[1])
70#endif
71#if !defined(MAGICKCORE_HAVE_CREAL)
72#define creal(z) (z[0])
73#endif
cristy3ed852e2009-09-05 21:47:34 +000074#endif
75
76/*
77 Typedef declarations.
78*/
79typedef struct _FourierInfo
80{
81 ChannelType
82 channel;
83
84 MagickBooleanType
85 modulus;
86
cristybb503372010-05-27 20:51:26 +000087 size_t
cristy3ed852e2009-09-05 21:47:34 +000088 width,
89 height;
90
cristybb503372010-05-27 20:51:26 +000091 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000092 center;
93} FourierInfo;
94
95/*
96%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
97% %
98% %
99% %
100% 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 %
101% %
102% %
103% %
104%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
105%
106% ForwardFourierTransformImage() implements the discrete Fourier transform
107% (DFT) of the image either as a magnitude / phase or real / imaginary image
108% pair.
109%
110% The format of the ForwadFourierTransformImage method is:
111%
112% Image *ForwardFourierTransformImage(const Image *image,
113% const MagickBooleanType modulus,ExceptionInfo *exception)
114%
115% A description of each parameter follows:
116%
117% o image: the image.
118%
119% o modulus: if true, return as transform as a magnitude / phase pair
120% otherwise a real / imaginary image pair.
121%
122% o exception: return any errors or warnings in this structure.
123%
124*/
125
126#if defined(MAGICKCORE_FFTW_DELEGATE)
127
cristybb503372010-05-27 20:51:26 +0000128static MagickBooleanType RollFourier(const size_t width,
129 const size_t height,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +0000130 double *fourier)
131{
132 double
133 *roll;
134
cristybb503372010-05-27 20:51:26 +0000135 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000136 u,
137 v,
138 y;
139
cristybb503372010-05-27 20:51:26 +0000140 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000141 i,
142 x;
143
144 /*
cristy2da05352010-09-15 19:22:17 +0000145 Move zero frequency (DC, average color) from (0,0) to (width/2,height/2).
cristy3ed852e2009-09-05 21:47:34 +0000146 */
147 roll=(double *) AcquireQuantumMemory((size_t) width,height*sizeof(*roll));
148 if (roll == (double *) NULL)
149 return(MagickFalse);
150 i=0L;
cristybb503372010-05-27 20:51:26 +0000151 for (y=0L; y < (ssize_t) height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000152 {
153 if (y_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000154 v=((y+y_offset) < 0L) ? y+y_offset+(ssize_t) height : y+y_offset;
cristy3ed852e2009-09-05 21:47:34 +0000155 else
cristybb503372010-05-27 20:51:26 +0000156 v=((y+y_offset) > ((ssize_t) height-1L)) ? y+y_offset-(ssize_t) height :
cristy3ed852e2009-09-05 21:47:34 +0000157 y+y_offset;
cristybb503372010-05-27 20:51:26 +0000158 for (x=0L; x < (ssize_t) width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000159 {
160 if (x_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000161 u=((x+x_offset) < 0L) ? x+x_offset+(ssize_t) width : x+x_offset;
cristy3ed852e2009-09-05 21:47:34 +0000162 else
cristybb503372010-05-27 20:51:26 +0000163 u=((x+x_offset) > ((ssize_t) width-1L)) ? x+x_offset-(ssize_t) width :
cristy3ed852e2009-09-05 21:47:34 +0000164 x+x_offset;
165 roll[v*width+u]=fourier[i++];
166 }
167 }
168 (void) CopyMagickMemory(fourier,roll,width*height*sizeof(*roll));
169 roll=(double *) RelinquishMagickMemory(roll);
170 return(MagickTrue);
171}
172
cristybb503372010-05-27 20:51:26 +0000173static MagickBooleanType ForwardQuadrantSwap(const size_t width,
174 const size_t height,double *source,double *destination)
cristy3ed852e2009-09-05 21:47:34 +0000175{
cristybb503372010-05-27 20:51:26 +0000176 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000177 center,
178 y;
179
180 MagickBooleanType
181 status;
182
cristybb503372010-05-27 20:51:26 +0000183 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000184 x;
185
186 /*
187 Swap quadrants.
188 */
cristybb503372010-05-27 20:51:26 +0000189 center=(ssize_t) floor((double) width/2L)+1L;
190 status=RollFourier((size_t) center,height,0L,(ssize_t) height/2L,source);
cristy3ed852e2009-09-05 21:47:34 +0000191 if (status == MagickFalse)
192 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000193 for (y=0L; y < (ssize_t) height; y++)
194 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000195 destination[width*y+x+width/2L]=source[center*y+x];
cristybb503372010-05-27 20:51:26 +0000196 for (y=1; y < (ssize_t) height; y++)
197 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000198 destination[width*(height-y)+width/2L-x-1L]=source[center*y+x+1L];
cristybb503372010-05-27 20:51:26 +0000199 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000200 destination[-x+width/2L-1L]=destination[x+width/2L+1L];
201 return(MagickTrue);
202}
203
cristybb503372010-05-27 20:51:26 +0000204static void CorrectPhaseLHS(const size_t width,
205 const size_t height,double *fourier)
cristy3ed852e2009-09-05 21:47:34 +0000206{
cristybb503372010-05-27 20:51:26 +0000207 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000208 y;
209
cristybb503372010-05-27 20:51:26 +0000210 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000211 x;
212
cristybb503372010-05-27 20:51:26 +0000213 for (y=0L; y < (ssize_t) height; y++)
214 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000215 fourier[y*width+x]*=(-1.0);
216}
217
218static MagickBooleanType ForwardFourier(const FourierInfo *fourier_info,
219 Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
220{
221 CacheView
222 *magnitude_view,
223 *phase_view;
224
225 double
226 *magnitude_source,
227 *phase_source;
228
229 Image
230 *magnitude_image,
231 *phase_image;
232
cristybb503372010-05-27 20:51:26 +0000233 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000234 i,
235 y;
236
237 MagickBooleanType
238 status;
239
240 register IndexPacket
241 *indexes;
242
cristybb503372010-05-27 20:51:26 +0000243 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000244 x;
245
246 register PixelPacket
247 *q;
248
249 magnitude_image=GetFirstImageInList(image);
250 phase_image=GetNextImageInList(image);
251 if (phase_image == (Image *) NULL)
252 {
253 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
254 "ImageSequenceRequired","`%s'",image->filename);
255 return(MagickFalse);
256 }
257 /*
258 Create "Fourier Transform" image from constituent arrays.
259 */
260 magnitude_source=(double *) AcquireQuantumMemory((size_t)
261 fourier_info->height,fourier_info->width*sizeof(*magnitude_source));
262 if (magnitude_source == (double *) NULL)
263 return(MagickFalse);
264 (void) ResetMagickMemory(magnitude_source,0,fourier_info->width*
265 fourier_info->height*sizeof(*magnitude_source));
266 phase_source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
267 fourier_info->width*sizeof(*phase_source));
268 if (magnitude_source == (double *) NULL)
269 {
270 (void) ThrowMagickException(exception,GetMagickModule(),
271 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
272 magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
273 return(MagickFalse);
274 }
275 status=ForwardQuadrantSwap(fourier_info->height,fourier_info->height,
276 magnitude,magnitude_source);
277 if (status != MagickFalse)
278 status=ForwardQuadrantSwap(fourier_info->height,fourier_info->height,phase,
279 phase_source);
280 CorrectPhaseLHS(fourier_info->height,fourier_info->height,phase_source);
281 if (fourier_info->modulus != MagickFalse)
282 {
283 i=0L;
cristybb503372010-05-27 20:51:26 +0000284 for (y=0L; y < (ssize_t) fourier_info->height; y++)
285 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000286 {
287 phase_source[i]/=(2.0*MagickPI);
288 phase_source[i]+=0.5;
289 i++;
290 }
291 }
292 magnitude_view=AcquireCacheView(magnitude_image);
293 phase_view=AcquireCacheView(phase_image);
294 i=0L;
cristybb503372010-05-27 20:51:26 +0000295 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000296 {
297 q=GetCacheViewAuthenticPixels(magnitude_view,0L,y,fourier_info->height,1UL,
298 exception);
299 if (q == (PixelPacket *) NULL)
300 break;
301 indexes=GetCacheViewAuthenticIndexQueue(magnitude_view);
cristybb503372010-05-27 20:51:26 +0000302 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000303 {
304 switch (fourier_info->channel)
305 {
306 case RedChannel:
307 default:
308 {
cristye85007d2010-06-06 22:51:36 +0000309 q->red=ClampToQuantum(QuantumRange*magnitude_source[i]);
cristy3ed852e2009-09-05 21:47:34 +0000310 break;
311 }
312 case GreenChannel:
313 {
cristye85007d2010-06-06 22:51:36 +0000314 q->green=ClampToQuantum(QuantumRange*magnitude_source[i]);
cristy3ed852e2009-09-05 21:47:34 +0000315 break;
316 }
317 case BlueChannel:
318 {
cristye85007d2010-06-06 22:51:36 +0000319 q->blue=ClampToQuantum(QuantumRange*magnitude_source[i]);
cristy3ed852e2009-09-05 21:47:34 +0000320 break;
321 }
322 case OpacityChannel:
323 {
cristye85007d2010-06-06 22:51:36 +0000324 q->opacity=ClampToQuantum(QuantumRange*magnitude_source[i]);
cristy3ed852e2009-09-05 21:47:34 +0000325 break;
326 }
327 case IndexChannel:
328 {
cristye85007d2010-06-06 22:51:36 +0000329 indexes[x]=ClampToQuantum(QuantumRange*magnitude_source[i]);
cristy3ed852e2009-09-05 21:47:34 +0000330 break;
331 }
332 case GrayChannels:
333 {
cristye85007d2010-06-06 22:51:36 +0000334 q->red=ClampToQuantum(QuantumRange*magnitude_source[i]);
cristy3ed852e2009-09-05 21:47:34 +0000335 q->green=q->red;
336 q->blue=q->red;
337 break;
338 }
339 }
340 i++;
341 q++;
342 }
343 status=SyncCacheViewAuthenticPixels(magnitude_view,exception);
344 if (status == MagickFalse)
345 break;
346 }
347 i=0L;
cristybb503372010-05-27 20:51:26 +0000348 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000349 {
350 q=GetCacheViewAuthenticPixels(phase_view,0L,y,fourier_info->height,1UL,
351 exception);
352 if (q == (PixelPacket *) NULL)
353 break;
354 indexes=GetCacheViewAuthenticIndexQueue(phase_view);
cristybb503372010-05-27 20:51:26 +0000355 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000356 {
357 switch (fourier_info->channel)
358 {
359 case RedChannel:
360 default:
361 {
cristye85007d2010-06-06 22:51:36 +0000362 q->red=ClampToQuantum(QuantumRange*phase_source[i]);
cristy3ed852e2009-09-05 21:47:34 +0000363 break;
364 }
365 case GreenChannel:
366 {
cristye85007d2010-06-06 22:51:36 +0000367 q->green=ClampToQuantum(QuantumRange*phase_source[i]);
cristy3ed852e2009-09-05 21:47:34 +0000368 break;
369 }
370 case BlueChannel:
371 {
cristye85007d2010-06-06 22:51:36 +0000372 q->blue=ClampToQuantum(QuantumRange*phase_source[i]);
cristy3ed852e2009-09-05 21:47:34 +0000373 break;
374 }
375 case OpacityChannel:
376 {
cristye85007d2010-06-06 22:51:36 +0000377 q->opacity=ClampToQuantum(QuantumRange*phase_source[i]);
cristy3ed852e2009-09-05 21:47:34 +0000378 break;
379 }
380 case IndexChannel:
381 {
cristye85007d2010-06-06 22:51:36 +0000382 indexes[x]=ClampToQuantum(QuantumRange*phase_source[i]);
cristy3ed852e2009-09-05 21:47:34 +0000383 break;
384 }
385 case GrayChannels:
386 {
cristye85007d2010-06-06 22:51:36 +0000387 q->red=ClampToQuantum(QuantumRange*phase_source[i]);
cristy3ed852e2009-09-05 21:47:34 +0000388 q->green=q->red;
389 q->blue=q->red;
390 break;
391 }
392 }
393 i++;
394 q++;
395 }
396 status=SyncCacheViewAuthenticPixels(phase_view,exception);
397 if (status == MagickFalse)
398 break;
399 }
400 phase_view=DestroyCacheView(phase_view);
401 magnitude_view=DestroyCacheView(magnitude_view);
402 phase_source=(double *) RelinquishMagickMemory(phase_source);
403 magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
404 return(status);
405}
406
407static MagickBooleanType ForwardFourierTransform(FourierInfo *fourier_info,
408 const Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
409{
410 CacheView
411 *image_view;
412
413 double
414 n,
415 *source;
416
417 fftw_complex
418 *fourier;
419
420 fftw_plan
421 fftw_r2c_plan;
422
cristybb503372010-05-27 20:51:26 +0000423 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000424 y;
425
426 register const IndexPacket
427 *indexes;
428
429 register const PixelPacket
430 *p;
431
cristybb503372010-05-27 20:51:26 +0000432 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000433 i,
434 x;
435
436 /*
437 Generate the forward Fourier transform.
438 */
439 source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
440 fourier_info->width*sizeof(*source));
441 if (source == (double *) NULL)
442 {
443 (void) ThrowMagickException(exception,GetMagickModule(),
444 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
445 return(MagickFalse);
446 }
447 ResetMagickMemory(source,0,fourier_info->width*fourier_info->height*
448 sizeof(*source));
449 i=0L;
450 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +0000451 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000452 {
453 p=GetCacheViewVirtualPixels(image_view,0L,y,fourier_info->width,1UL,
454 exception);
455 if (p == (const PixelPacket *) NULL)
456 break;
457 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000458 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000459 {
460 switch (fourier_info->channel)
461 {
462 case RedChannel:
463 default:
464 {
cristyce70c172010-01-07 17:15:30 +0000465 source[i]=QuantumScale*GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000466 break;
467 }
468 case GreenChannel:
469 {
cristyce70c172010-01-07 17:15:30 +0000470 source[i]=QuantumScale*GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000471 break;
472 }
473 case BlueChannel:
474 {
cristyce70c172010-01-07 17:15:30 +0000475 source[i]=QuantumScale*GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000476 break;
477 }
478 case OpacityChannel:
479 {
cristyce70c172010-01-07 17:15:30 +0000480 source[i]=QuantumScale*GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000481 break;
482 }
483 case IndexChannel:
484 {
485 source[i]=QuantumScale*indexes[x];
486 break;
487 }
488 case GrayChannels:
489 {
cristyce70c172010-01-07 17:15:30 +0000490 source[i]=QuantumScale*GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000491 break;
492 }
493 }
494 i++;
495 p++;
496 }
497 }
498 image_view=DestroyCacheView(image_view);
cristyb41ee102010-10-04 16:46:15 +0000499 fourier=(fftw_complex *) AcquireQuantumMemory((size_t) fourier_info->height,
cristy3ed852e2009-09-05 21:47:34 +0000500 fourier_info->center*sizeof(*fourier));
501 if (fourier == (fftw_complex *) NULL)
502 {
503 (void) ThrowMagickException(exception,GetMagickModule(),
504 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
505 source=(double *) RelinquishMagickMemory(source);
506 return(MagickFalse);
507 }
cristyb5d5f722009-11-04 03:03:49 +0000508#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000509 #pragma omp critical (MagickCore_ForwardFourierTransform)
510#endif
511 fftw_r2c_plan=fftw_plan_dft_r2c_2d(fourier_info->width,fourier_info->width,
512 source,fourier,FFTW_ESTIMATE);
513 fftw_execute(fftw_r2c_plan);
514 fftw_destroy_plan(fftw_r2c_plan);
515 source=(double *) RelinquishMagickMemory(source);
516 /*
517 Normalize Fourier transform.
518 */
519 n=(double) fourier_info->width*(double) fourier_info->width;
520 i=0L;
cristybb503372010-05-27 20:51:26 +0000521 for (y=0L; y < (ssize_t) fourier_info->height; y++)
522 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy56ed31c2010-03-22 00:46:21 +0000523 {
524#if defined(MAGICKCORE_HAVE_COMPLEX_H)
525 fourier[i]/=n;
526#else
527 fourier[i][0]/=n;
528 fourier[i][1]/=n;
529#endif
530 i++;
531 }
cristy3ed852e2009-09-05 21:47:34 +0000532 /*
533 Generate magnitude and phase (or real and imaginary).
534 */
535 i=0L;
536 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000537 for (y=0L; y < (ssize_t) fourier_info->height; y++)
538 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000539 {
540 magnitude[i]=cabs(fourier[i]);
541 phase[i]=carg(fourier[i]);
542 i++;
543 }
544 else
cristybb503372010-05-27 20:51:26 +0000545 for (y=0L; y < (ssize_t) fourier_info->height; y++)
546 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000547 {
548 magnitude[i]=creal(fourier[i]);
549 phase[i]=cimag(fourier[i]);
550 i++;
551 }
cristyb41ee102010-10-04 16:46:15 +0000552 fourier=(fftw_complex *) RelinquishMagickMemory(fourier);
cristy3ed852e2009-09-05 21:47:34 +0000553 return(MagickTrue);
554}
555
556static MagickBooleanType ForwardFourierTransformChannel(const Image *image,
557 const ChannelType channel,const MagickBooleanType modulus,
558 Image *fourier_image,ExceptionInfo *exception)
559{
560 double
561 *magnitude,
562 *phase;
563
564 fftw_complex
565 *fourier;
566
cristy9f3502b2010-03-21 02:39:26 +0000567 MagickBooleanType
568 status;
569
cristy56ed31c2010-03-22 00:46:21 +0000570 FourierInfo
571 fourier_info;
572
cristy3ed852e2009-09-05 21:47:34 +0000573 size_t
574 extent;
575
576 fourier_info.width=image->columns;
577 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
578 ((image->rows % 2) != 0))
579 {
580 extent=image->columns < image->rows ? image->rows : image->columns;
581 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
582 }
583 fourier_info.height=fourier_info.width;
cristybb503372010-05-27 20:51:26 +0000584 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2.0)+1L;
cristy3ed852e2009-09-05 21:47:34 +0000585 fourier_info.channel=channel;
586 fourier_info.modulus=modulus;
587 magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
588 fourier_info.center*sizeof(*magnitude));
589 if (magnitude == (double *) NULL)
590 {
591 (void) ThrowMagickException(exception,GetMagickModule(),
592 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
593 return(MagickFalse);
594 }
595 phase=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
596 fourier_info.center*sizeof(*phase));
597 if (phase == (double *) NULL)
598 {
599 (void) ThrowMagickException(exception,GetMagickModule(),
600 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
601 magnitude=(double *) RelinquishMagickMemory(magnitude);
602 return(MagickFalse);
603 }
cristyb41ee102010-10-04 16:46:15 +0000604 fourier=(fftw_complex *) AcquireQuantumMemory((size_t) fourier_info.height,
cristy3ed852e2009-09-05 21:47:34 +0000605 fourier_info.center*sizeof(*fourier));
606 if (fourier == (fftw_complex *) NULL)
607 {
608 (void) ThrowMagickException(exception,GetMagickModule(),
609 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
610 phase=(double *) RelinquishMagickMemory(phase);
611 magnitude=(double *) RelinquishMagickMemory(magnitude);
612 return(MagickFalse);
613 }
614 status=ForwardFourierTransform(&fourier_info,image,magnitude,phase,exception);
615 if (status != MagickFalse)
616 status=ForwardFourier(&fourier_info,fourier_image,magnitude,phase,
617 exception);
cristyb41ee102010-10-04 16:46:15 +0000618 fourier=(fftw_complex *) RelinquishMagickMemory(fourier);
cristy3ed852e2009-09-05 21:47:34 +0000619 phase=(double *) RelinquishMagickMemory(phase);
620 magnitude=(double *) RelinquishMagickMemory(magnitude);
621 return(status);
622}
623#endif
624
625MagickExport Image *ForwardFourierTransformImage(const Image *image,
626 const MagickBooleanType modulus,ExceptionInfo *exception)
627{
628 Image
629 *fourier_image;
630
631 fourier_image=NewImageList();
632#if !defined(MAGICKCORE_FFTW_DELEGATE)
633 (void) modulus;
634 (void) ThrowMagickException(exception,GetMagickModule(),
635 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","`%s' (FFTW)",
636 image->filename);
637#else
638 {
639 Image
640 *magnitude_image;
641
cristybb503372010-05-27 20:51:26 +0000642 size_t
cristy3ed852e2009-09-05 21:47:34 +0000643 extent,
644 width;
645
646 width=image->columns;
647 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
648 ((image->rows % 2) != 0))
649 {
650 extent=image->columns < image->rows ? image->rows : image->columns;
651 width=(extent & 0x01) == 1 ? extent+1UL : extent;
652 }
653 magnitude_image=CloneImage(image,width,width,MagickFalse,exception);
654 if (magnitude_image != (Image *) NULL)
655 {
656 Image
657 *phase_image;
658
659 magnitude_image->storage_class=DirectClass;
660 magnitude_image->depth=32UL;
661 phase_image=CloneImage(image,width,width,MagickFalse,exception);
662 if (phase_image == (Image *) NULL)
663 magnitude_image=DestroyImage(magnitude_image);
664 else
665 {
666 MagickBooleanType
667 is_gray,
668 status;
669
cristy3ed852e2009-09-05 21:47:34 +0000670 phase_image->storage_class=DirectClass;
671 phase_image->depth=32UL;
672 AppendImageToList(&fourier_image,magnitude_image);
673 AppendImageToList(&fourier_image,phase_image);
674 status=MagickTrue;
675 is_gray=IsGrayImage(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000676#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb34ef052010-10-07 00:12:05 +0000677 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +0000678#endif
cristy3ed852e2009-09-05 21:47:34 +0000679 {
cristyb34ef052010-10-07 00:12:05 +0000680#if defined(MAGICKCORE_OPENMP_SUPPORT)
681 #pragma omp section
682#endif
cristy3ed852e2009-09-05 21:47:34 +0000683 {
cristyb34ef052010-10-07 00:12:05 +0000684 MagickBooleanType
685 thread_status;
686
687 if (is_gray != MagickFalse)
688 thread_status=ForwardFourierTransformChannel(image,
689 GrayChannels,modulus,fourier_image,exception);
690 else
691 thread_status=ForwardFourierTransformChannel(image,
692 RedChannel,modulus,fourier_image,exception);
693 if (thread_status == MagickFalse)
694 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +0000695 }
cristyb34ef052010-10-07 00:12:05 +0000696#if defined(MAGICKCORE_OPENMP_SUPPORT)
697 #pragma omp section
698#endif
699 {
700 MagickBooleanType
701 thread_status;
702
703 thread_status=MagickTrue;
704 if (is_gray == MagickFalse)
705 thread_status=ForwardFourierTransformChannel(image,
706 GreenChannel,modulus,fourier_image,exception);
707 if (thread_status == MagickFalse)
708 status=thread_status;
709 }
710#if defined(MAGICKCORE_OPENMP_SUPPORT)
711 #pragma omp section
712#endif
713 {
714 MagickBooleanType
715 thread_status;
716
717 thread_status=MagickTrue;
718 if (is_gray == MagickFalse)
719 thread_status=ForwardFourierTransformChannel(image,
720 BlueChannel,modulus,fourier_image,exception);
721 if (thread_status == MagickFalse)
722 status=thread_status;
723 }
724#if defined(MAGICKCORE_OPENMP_SUPPORT)
725 #pragma omp section
726#endif
727 {
728 MagickBooleanType
729 thread_status;
730
731 thread_status=MagickTrue;
732 if (image->matte != MagickFalse)
733 thread_status=ForwardFourierTransformChannel(image,
734 OpacityChannel,modulus,fourier_image,exception);
735 if (thread_status == MagickFalse)
736 status=thread_status;
737 }
738#if defined(MAGICKCORE_OPENMP_SUPPORT)
739 #pragma omp section
740#endif
741 {
742 MagickBooleanType
743 thread_status;
744
745 thread_status=MagickTrue;
746 if (image->colorspace == CMYKColorspace)
747 thread_status=ForwardFourierTransformChannel(image,
748 IndexChannel,modulus,fourier_image,exception);
749 if (thread_status == MagickFalse)
750 status=thread_status;
751 }
cristy3ed852e2009-09-05 21:47:34 +0000752 }
753 if (status == MagickFalse)
754 fourier_image=DestroyImageList(fourier_image);
755 fftw_cleanup();
756 }
757 }
758 }
759#endif
760 return(fourier_image);
761}
762
763/*
764%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
765% %
766% %
767% %
768% 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 %
769% %
770% %
771% %
772%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
773%
774% InverseFourierTransformImage() implements the inverse discrete Fourier
775% transform (DFT) of the image either as a magnitude / phase or real /
776% imaginary image pair.
777%
778% The format of the InverseFourierTransformImage method is:
779%
cristyc9550792009-11-13 20:05:42 +0000780% Image *InverseFourierTransformImage(const Image *magnitude_image,
781% const Image *phase_image,const MagickBooleanType modulus,
782% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000783%
784% A description of each parameter follows:
785%
cristyc9550792009-11-13 20:05:42 +0000786% o magnitude_image: the magnitude or real image.
787%
788% o phase_image: the phase or imaginary image.
cristy3ed852e2009-09-05 21:47:34 +0000789%
790% o modulus: if true, return transform as a magnitude / phase pair
791% otherwise a real / imaginary image pair.
792%
793% o exception: return any errors or warnings in this structure.
794%
795*/
796
797#if defined(MAGICKCORE_FFTW_DELEGATE)
cristybb503372010-05-27 20:51:26 +0000798static MagickBooleanType InverseQuadrantSwap(const size_t width,
799 const size_t height,const double *source,double *destination)
cristy3ed852e2009-09-05 21:47:34 +0000800{
cristybb503372010-05-27 20:51:26 +0000801 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000802 center,
803 y;
804
cristybb503372010-05-27 20:51:26 +0000805 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000806 x;
807
808 /*
809 Swap quadrants.
810 */
cristybb503372010-05-27 20:51:26 +0000811 center=(ssize_t) floor((double) width/2.0)+1L;
812 for (y=1L; y < (ssize_t) height; y++)
813 for (x=0L; x < (ssize_t) (width/2L+1L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000814 destination[center*(height-y)-x+width/2L]=source[y*width+x];
cristybb503372010-05-27 20:51:26 +0000815 for (y=0L; y < (ssize_t) height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000816 destination[center*y]=source[y*width+width/2L];
817 for (x=0L; x < center; x++)
818 destination[x]=source[center-x-1L];
cristybb503372010-05-27 20:51:26 +0000819 return(RollFourier(center,height,0L,(ssize_t) height/-2L,destination));
cristy3ed852e2009-09-05 21:47:34 +0000820}
821
822static MagickBooleanType InverseFourier(FourierInfo *fourier_info,
cristyc9550792009-11-13 20:05:42 +0000823 const Image *magnitude_image,const Image *phase_image,fftw_complex *fourier,
824 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000825{
826 CacheView
827 *magnitude_view,
828 *phase_view;
829
830 double
831 *magnitude,
832 *phase,
833 *magnitude_source,
834 *phase_source;
835
cristybb503372010-05-27 20:51:26 +0000836 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000837 y;
838
839 MagickBooleanType
840 status;
841
842 register const IndexPacket
843 *indexes;
844
845 register const PixelPacket
846 *p;
847
cristybb503372010-05-27 20:51:26 +0000848 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000849 i,
850 x;
851
852 /*
853 Inverse fourier - read image and break down into a double array.
854 */
cristy3ed852e2009-09-05 21:47:34 +0000855 magnitude_source=(double *) AcquireQuantumMemory((size_t)
856 fourier_info->height,fourier_info->width*sizeof(*magnitude_source));
857 if (magnitude_source == (double *) NULL)
858 {
859 (void) ThrowMagickException(exception,GetMagickModule(),
cristyc9550792009-11-13 20:05:42 +0000860 ResourceLimitError,"MemoryAllocationFailed","`%s'",
861 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000862 return(MagickFalse);
863 }
864 phase_source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
865 fourier_info->height*sizeof(*phase_source));
866 if (phase_source == (double *) NULL)
867 {
868 (void) ThrowMagickException(exception,GetMagickModule(),
cristyc9550792009-11-13 20:05:42 +0000869 ResourceLimitError,"MemoryAllocationFailed","`%s'",
870 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000871 magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
872 return(MagickFalse);
873 }
874 i=0L;
875 magnitude_view=AcquireCacheView(magnitude_image);
cristybb503372010-05-27 20:51:26 +0000876 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000877 {
878 p=GetCacheViewVirtualPixels(magnitude_view,0L,y,fourier_info->width,1UL,
879 exception);
880 if (p == (const PixelPacket *) NULL)
881 break;
882 indexes=GetCacheViewAuthenticIndexQueue(magnitude_view);
cristybb503372010-05-27 20:51:26 +0000883 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000884 {
885 switch (fourier_info->channel)
886 {
887 case RedChannel:
888 default:
889 {
cristyce70c172010-01-07 17:15:30 +0000890 magnitude_source[i]=QuantumScale*GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000891 break;
892 }
893 case GreenChannel:
894 {
cristyce70c172010-01-07 17:15:30 +0000895 magnitude_source[i]=QuantumScale*GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000896 break;
897 }
898 case BlueChannel:
899 {
cristyce70c172010-01-07 17:15:30 +0000900 magnitude_source[i]=QuantumScale*GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000901 break;
902 }
903 case OpacityChannel:
904 {
cristyce70c172010-01-07 17:15:30 +0000905 magnitude_source[i]=QuantumScale*GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000906 break;
907 }
908 case IndexChannel:
909 {
910 magnitude_source[i]=QuantumScale*indexes[x];
911 break;
912 }
913 case GrayChannels:
914 {
cristyce70c172010-01-07 17:15:30 +0000915 magnitude_source[i]=QuantumScale*GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000916 break;
917 }
918 }
919 i++;
920 p++;
921 }
922 }
923 i=0L;
924 phase_view=AcquireCacheView(phase_image);
cristybb503372010-05-27 20:51:26 +0000925 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000926 {
927 p=GetCacheViewVirtualPixels(phase_view,0,y,fourier_info->width,1,
928 exception);
929 if (p == (const PixelPacket *) NULL)
930 break;
931 indexes=GetCacheViewAuthenticIndexQueue(phase_view);
cristybb503372010-05-27 20:51:26 +0000932 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000933 {
934 switch (fourier_info->channel)
935 {
936 case RedChannel:
937 default:
938 {
cristyce70c172010-01-07 17:15:30 +0000939 phase_source[i]=QuantumScale*GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000940 break;
941 }
942 case GreenChannel:
943 {
cristyce70c172010-01-07 17:15:30 +0000944 phase_source[i]=QuantumScale*GetGreenPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000945 break;
946 }
947 case BlueChannel:
948 {
cristyce70c172010-01-07 17:15:30 +0000949 phase_source[i]=QuantumScale*GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000950 break;
951 }
952 case OpacityChannel:
953 {
cristyce70c172010-01-07 17:15:30 +0000954 phase_source[i]=QuantumScale*GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000955 break;
956 }
957 case IndexChannel:
958 {
959 phase_source[i]=QuantumScale*indexes[x];
960 break;
961 }
962 case GrayChannels:
963 {
cristyce70c172010-01-07 17:15:30 +0000964 phase_source[i]=QuantumScale*GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +0000965 break;
966 }
967 }
968 i++;
969 p++;
970 }
971 }
972 if (fourier_info->modulus != MagickFalse)
973 {
974 i=0L;
cristybb503372010-05-27 20:51:26 +0000975 for (y=0L; y < (ssize_t) fourier_info->height; y++)
976 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000977 {
978 phase_source[i]-=0.5;
979 phase_source[i]*=(2.0*MagickPI);
980 i++;
981 }
982 }
983 magnitude_view=DestroyCacheView(magnitude_view);
984 phase_view=DestroyCacheView(phase_view);
985 magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
986 fourier_info->center*sizeof(*magnitude));
987 if (magnitude == (double *) NULL)
988 {
989 (void) ThrowMagickException(exception,GetMagickModule(),
cristyc9550792009-11-13 20:05:42 +0000990 ResourceLimitError,"MemoryAllocationFailed","`%s'",
991 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000992 magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
993 phase_source=(double *) RelinquishMagickMemory(phase_source);
994 return(MagickFalse);
995 }
996 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
997 magnitude_source,magnitude);
998 magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
999 phase=(double *) AcquireQuantumMemory((size_t) fourier_info->width,
1000 fourier_info->height*sizeof(*phase));
1001 if (phase == (double *) NULL)
1002 {
1003 (void) ThrowMagickException(exception,GetMagickModule(),
cristyc9550792009-11-13 20:05:42 +00001004 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1005 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001006 phase_source=(double *) RelinquishMagickMemory(phase_source);
1007 return(MagickFalse);
1008 }
1009 CorrectPhaseLHS(fourier_info->width,fourier_info->width,phase_source);
1010 if (status != MagickFalse)
1011 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
1012 phase_source,phase);
1013 phase_source=(double *) RelinquishMagickMemory(phase_source);
1014 /*
1015 Merge two sets.
1016 */
1017 i=0L;
1018 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +00001019 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1020 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001021 {
cristy56ed31c2010-03-22 00:46:21 +00001022#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy3ed852e2009-09-05 21:47:34 +00001023 fourier[i]=magnitude[i]*cos(phase[i])+I*magnitude[i]*sin(phase[i]);
cristy56ed31c2010-03-22 00:46:21 +00001024#else
1025 fourier[i][0]=magnitude[i]*cos(phase[i]);
1026 fourier[i][1]=magnitude[i]*sin(phase[i]);
1027#endif
cristy3ed852e2009-09-05 21:47:34 +00001028 i++;
1029 }
1030 else
cristybb503372010-05-27 20:51:26 +00001031 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1032 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001033 {
cristy56ed31c2010-03-22 00:46:21 +00001034#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy3ed852e2009-09-05 21:47:34 +00001035 fourier[i]=magnitude[i]+I*phase[i];
cristy56ed31c2010-03-22 00:46:21 +00001036#else
1037 fourier[i][0]=magnitude[i];
1038 fourier[i][1]=phase[i];
1039#endif
cristy3ed852e2009-09-05 21:47:34 +00001040 i++;
1041 }
1042 phase=(double *) RelinquishMagickMemory(phase);
1043 magnitude=(double *) RelinquishMagickMemory(magnitude);
1044 return(status);
1045}
1046
1047static MagickBooleanType InverseFourierTransform(FourierInfo *fourier_info,
1048 fftw_complex *fourier,Image *image,ExceptionInfo *exception)
1049{
1050 CacheView
1051 *image_view;
1052
1053 double
1054 *source;
1055
1056 fftw_plan
1057 fftw_c2r_plan;
1058
cristybb503372010-05-27 20:51:26 +00001059 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001060 y;
1061
1062 register IndexPacket
1063 *indexes;
1064
cristybb503372010-05-27 20:51:26 +00001065 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001066 i,
1067 x;
1068
1069 register PixelPacket
1070 *q;
1071
1072 source=(double *) AcquireQuantumMemory((size_t) fourier_info->width,
1073 fourier_info->height*sizeof(double));
1074 if (source == (double *) NULL)
1075 {
1076 (void) ThrowMagickException(exception,GetMagickModule(),
1077 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
1078 return(MagickFalse);
1079 }
cristyb5d5f722009-11-04 03:03:49 +00001080#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001081 #pragma omp critical (MagickCore_InverseFourierTransform)
1082#endif
cristydf079ac2010-09-10 01:45:44 +00001083 {
1084 fftw_c2r_plan=fftw_plan_dft_c2r_2d(fourier_info->width,fourier_info->height,
1085 fourier,source,FFTW_ESTIMATE);
1086 fftw_execute(fftw_c2r_plan);
1087 fftw_destroy_plan(fftw_c2r_plan);
1088 }
cristy3ed852e2009-09-05 21:47:34 +00001089 i=0L;
1090 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +00001091 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001092 {
cristy85812052010-09-14 17:56:15 +00001093 if (y >= (ssize_t) image->rows)
1094 break;
1095 q=GetCacheViewAuthenticPixels(image_view,0L,y,fourier_info->width >
1096 image->columns ? image->columns : fourier_info->width,1UL,exception);
cristy3ed852e2009-09-05 21:47:34 +00001097 if (q == (PixelPacket *) NULL)
1098 break;
1099 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00001100 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001101 {
1102 switch (fourier_info->channel)
1103 {
1104 case RedChannel:
1105 default:
1106 {
cristye85007d2010-06-06 22:51:36 +00001107 q->red=ClampToQuantum(QuantumRange*source[i]);
cristy3ed852e2009-09-05 21:47:34 +00001108 break;
1109 }
1110 case GreenChannel:
1111 {
cristye85007d2010-06-06 22:51:36 +00001112 q->green=ClampToQuantum(QuantumRange*source[i]);
cristy3ed852e2009-09-05 21:47:34 +00001113 break;
1114 }
1115 case BlueChannel:
1116 {
cristye85007d2010-06-06 22:51:36 +00001117 q->blue=ClampToQuantum(QuantumRange*source[i]);
cristy3ed852e2009-09-05 21:47:34 +00001118 break;
1119 }
1120 case OpacityChannel:
1121 {
cristye85007d2010-06-06 22:51:36 +00001122 q->opacity=ClampToQuantum(QuantumRange*source[i]);
cristy3ed852e2009-09-05 21:47:34 +00001123 break;
1124 }
1125 case IndexChannel:
1126 {
cristye85007d2010-06-06 22:51:36 +00001127 indexes[x]=ClampToQuantum(QuantumRange*source[i]);
cristy3ed852e2009-09-05 21:47:34 +00001128 break;
1129 }
1130 case GrayChannels:
1131 {
cristye85007d2010-06-06 22:51:36 +00001132 q->red=ClampToQuantum(QuantumRange*source[i]);
cristy3ed852e2009-09-05 21:47:34 +00001133 q->green=q->red;
1134 q->blue=q->red;
1135 break;
1136 }
1137 }
1138 i++;
1139 q++;
1140 }
1141 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1142 break;
1143 }
1144 image_view=DestroyCacheView(image_view);
1145 source=(double *) RelinquishMagickMemory(source);
1146 return(MagickTrue);
1147}
1148
cristyc9550792009-11-13 20:05:42 +00001149static MagickBooleanType InverseFourierTransformChannel(
1150 const Image *magnitude_image,const Image *phase_image,
cristy3ed852e2009-09-05 21:47:34 +00001151 const ChannelType channel,const MagickBooleanType modulus,
1152 Image *fourier_image,ExceptionInfo *exception)
1153{
1154 double
1155 *magnitude,
1156 *phase;
1157
1158 fftw_complex
1159 *fourier;
1160
1161 FourierInfo
1162 fourier_info;
1163
1164 MagickBooleanType
1165 status;
1166
1167 size_t
1168 extent;
1169
cristyc9550792009-11-13 20:05:42 +00001170 fourier_info.width=magnitude_image->columns;
1171 if ((magnitude_image->columns != magnitude_image->rows) ||
1172 ((magnitude_image->columns % 2) != 0) ||
1173 ((magnitude_image->rows % 2) != 0))
cristy3ed852e2009-09-05 21:47:34 +00001174 {
cristyc9550792009-11-13 20:05:42 +00001175 extent=magnitude_image->columns < magnitude_image->rows ?
1176 magnitude_image->rows : magnitude_image->columns;
cristy3ed852e2009-09-05 21:47:34 +00001177 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
1178 }
1179 fourier_info.height=fourier_info.width;
cristybb503372010-05-27 20:51:26 +00001180 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2.0)+1L;
cristy3ed852e2009-09-05 21:47:34 +00001181 fourier_info.channel=channel;
1182 fourier_info.modulus=modulus;
1183 magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
1184 fourier_info.center*sizeof(double));
1185 if (magnitude == (double *) NULL)
1186 {
1187 (void) ThrowMagickException(exception,GetMagickModule(),
cristyc9550792009-11-13 20:05:42 +00001188 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1189 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001190 return(MagickFalse);
1191 }
1192 phase=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
1193 fourier_info.center*sizeof(double));
1194 if (phase == (double *) NULL)
1195 {
1196 (void) ThrowMagickException(exception,GetMagickModule(),
cristyc9550792009-11-13 20:05:42 +00001197 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1198 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001199 magnitude=(double *) RelinquishMagickMemory(magnitude);
1200 return(MagickFalse);
1201 }
cristyb41ee102010-10-04 16:46:15 +00001202 fourier=(fftw_complex *) AcquireQuantumMemory((size_t) fourier_info.height,
cristy3ed852e2009-09-05 21:47:34 +00001203 fourier_info.center*sizeof(*fourier));
1204 if (fourier == (fftw_complex *) NULL)
1205 {
1206 (void) ThrowMagickException(exception,GetMagickModule(),
cristyc9550792009-11-13 20:05:42 +00001207 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1208 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001209 phase=(double *) RelinquishMagickMemory(phase);
1210 magnitude=(double *) RelinquishMagickMemory(magnitude);
1211 return(MagickFalse);
1212 }
cristyc9550792009-11-13 20:05:42 +00001213 status=InverseFourier(&fourier_info,magnitude_image,phase_image,fourier,
1214 exception);
cristy3ed852e2009-09-05 21:47:34 +00001215 if (status != MagickFalse)
1216 status=InverseFourierTransform(&fourier_info,fourier,fourier_image,
1217 exception);
cristyb41ee102010-10-04 16:46:15 +00001218 fourier=(fftw_complex *) RelinquishMagickMemory(fourier);
cristy3ed852e2009-09-05 21:47:34 +00001219 phase=(double *) RelinquishMagickMemory(phase);
1220 magnitude=(double *) RelinquishMagickMemory(magnitude);
1221 return(status);
1222}
1223#endif
1224
cristyc9550792009-11-13 20:05:42 +00001225MagickExport Image *InverseFourierTransformImage(const Image *magnitude_image,
1226 const Image *phase_image,const MagickBooleanType modulus,
1227 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001228{
1229 Image
1230 *fourier_image;
1231
cristyc9550792009-11-13 20:05:42 +00001232 assert(magnitude_image != (Image *) NULL);
1233 assert(magnitude_image->signature == MagickSignature);
1234 if (magnitude_image->debug != MagickFalse)
1235 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1236 magnitude_image->filename);
1237 if (phase_image == (Image *) NULL)
1238 {
1239 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
1240 "ImageSequenceRequired","`%s'",magnitude_image->filename);
cristy9372a152009-11-18 01:42:16 +00001241 return((Image *) NULL);
cristyc9550792009-11-13 20:05:42 +00001242 }
cristy3ed852e2009-09-05 21:47:34 +00001243#if !defined(MAGICKCORE_FFTW_DELEGATE)
1244 fourier_image=(Image *) NULL;
1245 (void) modulus;
1246 (void) ThrowMagickException(exception,GetMagickModule(),
1247 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","`%s' (FFTW)",
cristyc9550792009-11-13 20:05:42 +00001248 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001249#else
1250 {
cristyc9550792009-11-13 20:05:42 +00001251 fourier_image=CloneImage(magnitude_image,magnitude_image->columns,
1252 magnitude_image->rows,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00001253 if (fourier_image != (Image *) NULL)
1254 {
1255 MagickBooleanType
1256 is_gray,
1257 status;
1258
cristy3ed852e2009-09-05 21:47:34 +00001259 status=MagickTrue;
cristyc9550792009-11-13 20:05:42 +00001260 is_gray=IsGrayImage(magnitude_image,exception);
1261 if (is_gray != MagickFalse)
1262 is_gray=IsGrayImage(phase_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001263#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb34ef052010-10-07 00:12:05 +00001264 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +00001265#endif
cristy3ed852e2009-09-05 21:47:34 +00001266 {
cristyb34ef052010-10-07 00:12:05 +00001267#if defined(MAGICKCORE_OPENMP_SUPPORT)
1268 #pragma omp section
1269#endif
cristy3ed852e2009-09-05 21:47:34 +00001270 {
cristyb34ef052010-10-07 00:12:05 +00001271 MagickBooleanType
1272 thread_status;
1273
1274 if (is_gray != MagickFalse)
1275 thread_status=InverseFourierTransformChannel(magnitude_image,
1276 phase_image,GrayChannels,modulus,fourier_image,exception);
1277 else
cristyc9550792009-11-13 20:05:42 +00001278 thread_status=InverseFourierTransformChannel(magnitude_image,
1279 phase_image,RedChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001280 if (thread_status == MagickFalse)
1281 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +00001282 }
cristyb34ef052010-10-07 00:12:05 +00001283#if defined(MAGICKCORE_OPENMP_SUPPORT)
1284 #pragma omp section
1285#endif
1286 {
1287 MagickBooleanType
1288 thread_status;
1289
1290 thread_status=MagickTrue;
1291 if (is_gray == MagickFalse)
1292 thread_status=InverseFourierTransformChannel(magnitude_image,
1293 phase_image,GreenChannel,modulus,fourier_image,exception);
1294 if (thread_status == MagickFalse)
1295 status=thread_status;
1296 }
1297#if defined(MAGICKCORE_OPENMP_SUPPORT)
1298 #pragma omp section
1299#endif
1300 {
1301 MagickBooleanType
1302 thread_status;
1303
1304 thread_status=MagickTrue;
1305 if (is_gray == MagickFalse)
1306 thread_status=InverseFourierTransformChannel(magnitude_image,
1307 phase_image,BlueChannel,modulus,fourier_image,exception);
1308 if (thread_status == MagickFalse)
1309 status=thread_status;
1310 }
1311#if defined(MAGICKCORE_OPENMP_SUPPORT)
1312 #pragma omp section
1313#endif
1314 {
1315 MagickBooleanType
1316 thread_status;
1317
1318 thread_status=MagickTrue;
1319 if (magnitude_image->matte != MagickFalse)
1320 thread_status=InverseFourierTransformChannel(magnitude_image,
1321 phase_image,OpacityChannel,modulus,fourier_image,exception);
1322 if (thread_status == MagickFalse)
1323 status=thread_status;
1324 }
1325#if defined(MAGICKCORE_OPENMP_SUPPORT)
1326 #pragma omp section
1327#endif
1328 {
1329 MagickBooleanType
1330 thread_status;
1331
1332 thread_status=MagickTrue;
1333 if (magnitude_image->colorspace == CMYKColorspace)
1334 thread_status=InverseFourierTransformChannel(magnitude_image,
1335 phase_image,IndexChannel,modulus,fourier_image,exception);
1336 if (thread_status == MagickFalse)
1337 status=thread_status;
1338 }
cristy3ed852e2009-09-05 21:47:34 +00001339 }
1340 if (status == MagickFalse)
1341 fourier_image=DestroyImage(fourier_image);
1342 }
cristyb34ef052010-10-07 00:12:05 +00001343 fftw_cleanup();
cristy3ed852e2009-09-05 21:47:34 +00001344 }
1345#endif
1346 return(fourier_image);
1347}