blob: 6404376886c50fe0de975678c7ab2c9e0a21ef89 [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% %
cristy45ef08f2012-12-07 13:13:34 +000022% Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000023% dedicated to making software imaging solutions freely available. %
24% %
25% You may not use this file except in compliance with the License. You may %
26% obtain a copy of the License at %
27% %
28% http://www.imagemagick.org/script/license.php %
29% %
30% Unless required by applicable law or agreed to in writing, software %
31% distributed under the License is distributed on an "AS IS" BASIS, %
32% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
33% See the License for the specific language governing permissions and %
34% limitations under the License. %
35% %
36%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
37%
38%
39%
40*/
41
42/*
43 Include declarations.
44*/
cristy4c08aed2011-07-01 19:47:50 +000045#include "MagickCore/studio.h"
cristy99dc0362013-09-15 18:32:54 +000046#include "MagickCore/artifact.h"
cristy4c08aed2011-07-01 19:47:50 +000047#include "MagickCore/attribute.h"
cristy7d4aa382013-06-30 01:59:39 +000048#include "MagickCore/blob.h"
cristy4c08aed2011-07-01 19:47:50 +000049#include "MagickCore/cache.h"
50#include "MagickCore/image.h"
51#include "MagickCore/image-private.h"
52#include "MagickCore/list.h"
53#include "MagickCore/fourier.h"
54#include "MagickCore/log.h"
55#include "MagickCore/memory_.h"
56#include "MagickCore/monitor.h"
cristy1042ed22013-10-05 17:38:54 +000057#include "MagickCore/monitor-private.h"
cristy4c08aed2011-07-01 19:47:50 +000058#include "MagickCore/pixel-accessor.h"
cristy99dc0362013-09-15 18:32:54 +000059#include "MagickCore/pixel-private.h"
cristy4c08aed2011-07-01 19:47:50 +000060#include "MagickCore/property.h"
61#include "MagickCore/quantum-private.h"
cristyac245f82012-05-05 17:13:57 +000062#include "MagickCore/resource_.h"
cristy4c08aed2011-07-01 19:47:50 +000063#include "MagickCore/thread-private.h"
cristy3ed852e2009-09-05 21:47:34 +000064#if defined(MAGICKCORE_FFTW_DELEGATE)
cristy56ed31c2010-03-22 00:46:21 +000065#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy3ed852e2009-09-05 21:47:34 +000066#include <complex.h>
cristy56ed31c2010-03-22 00:46:21 +000067#endif
cristy3ed852e2009-09-05 21:47:34 +000068#include <fftw3.h>
cristy47b022b2011-01-18 22:29:21 +000069#if !defined(MAGICKCORE_HAVE_CABS)
70#define cabs(z) (sqrt(z[0]*z[0]+z[1]*z[1]))
71#endif
72#if !defined(MAGICKCORE_HAVE_CARG)
cristy4da3ba32011-02-07 16:58:11 +000073#define carg(z) (atan2(cimag(z),creal(z)))
cristy47b022b2011-01-18 22:29:21 +000074#endif
75#if !defined(MAGICKCORE_HAVE_CIMAG)
76#define cimag(z) (z[1])
77#endif
78#if !defined(MAGICKCORE_HAVE_CREAL)
79#define creal(z) (z[0])
80#endif
cristy3ed852e2009-09-05 21:47:34 +000081#endif
82
83/*
84 Typedef declarations.
85*/
86typedef struct _FourierInfo
87{
cristyd3090f92011-10-18 00:05:15 +000088 PixelChannel
cristy3ed852e2009-09-05 21:47:34 +000089 channel;
90
91 MagickBooleanType
92 modulus;
93
cristybb503372010-05-27 20:51:26 +000094 size_t
cristy3ed852e2009-09-05 21:47:34 +000095 width,
96 height;
97
cristybb503372010-05-27 20:51:26 +000098 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000099 center;
100} FourierInfo;
101
102/*
103%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
104% %
105% %
106% %
cristy790190d2013-10-04 00:51:51 +0000107% C o m p l e x I m a g e s %
108% %
109% %
110% %
111%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
112%
113% ComplexImages() performs complex mathematics on an image sequence.
114%
115% The format of the ComplexImages method is:
116%
117% MagickBooleanType ComplexImages(Image *images,
118% const ComplexOperator operator,ExceptionInfo *exception)
119%
120% A description of each parameter follows:
121%
122% o image: the image.
123%
124% o operator: A complex operator.
125%
126% o exception: return any errors or warnings in this structure.
127%
128*/
129MagickExport Image *ComplexImages(const Image *images,
130 const ComplexOperator operator,ExceptionInfo *exception)
131{
132#define ComplexImageTag "Complex/Image"
133
134 CacheView
cristy1042ed22013-10-05 17:38:54 +0000135 *Ai_view,
136 *Ar_view,
137 *Bi_view,
138 *Br_view,
139 *Ci_view,
140 *Cr_view;
141
142 const Image
143 *Ai_image,
144 *Ar_image,
145 *Bi_image,
146 *Br_image;
cristy790190d2013-10-04 00:51:51 +0000147
148 Image
cristy1042ed22013-10-05 17:38:54 +0000149 *Ci_image,
150 *complex_images,
151 *Cr_image;
cristy790190d2013-10-04 00:51:51 +0000152
153 MagickBooleanType
154 status;
155
156 MagickOffsetType
157 progress;
158
cristy790190d2013-10-04 00:51:51 +0000159 ssize_t
160 y;
161
162 assert(images != (Image *) NULL);
163 assert(images->signature == MagickSignature);
164 if (images->debug != MagickFalse)
165 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
166 assert(exception != (ExceptionInfo *) NULL);
167 assert(exception->signature == MagickSignature);
cristy1042ed22013-10-05 17:38:54 +0000168 if (images->next == (Image *) NULL)
169 {
170 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
171 "ImageSequenceRequired","`%s'",images->filename);
172 return((Image *) NULL);
173 }
174 complex_images=CloneImage(images,images->columns,images->rows,MagickTrue,
175 exception);
176 if (complex_images == (Image *) NULL)
177 return((Image *) NULL);
178 complex_images->next=CloneImage(images,images->columns,images->rows,
179 MagickTrue,exception);
180 if (complex_images->next == (Image *) NULL)
181 {
182 complex_images=DestroyImageList(complex_images);
183 return(complex_images);
184 }
185 /*
186 Apply complex mathematics to image pixels.
187 */
188 Ar_image=images;
189 Ai_image=images->next;
190 if ((images->next->next == (Image *) NULL) ||
191 (images->next->next->next == (Image *) NULL))
192 {
193 Br_image=images;
194 Bi_image=images->next;
195 }
196 else
197 {
198 Br_image=images->next->next;
199 Bi_image=images->next->next->next;
200 }
201 Cr_image=complex_images;
202 Ci_image=complex_images->next;
203 Ar_view=AcquireVirtualCacheView(Ar_image,exception);
204 Ai_view=AcquireVirtualCacheView(Ai_image,exception);
205 Br_view=AcquireVirtualCacheView(Br_image,exception);
206 Bi_view=AcquireVirtualCacheView(Bi_image,exception);
207 Cr_view=AcquireAuthenticCacheView(Cr_image,exception);
208 Ci_view=AcquireAuthenticCacheView(Ci_image,exception);
209 status=MagickTrue;
210 progress=0;
211#if defined(MAGICKCORE_OPENMP_SUPPORT)
212 #pragma omp parallel for schedule(static,4) shared(progress,status) \
213 magick_threads(images,complex_images,images->rows,1L)
214#endif
215 for (y=0; y < (ssize_t) images->rows; y++)
216 {
217 register const Quantum
218 *restrict Ai,
219 *restrict Ar,
220 *restrict Bi,
221 *restrict Br;
222
223 register Quantum
224 *restrict Ci,
225 *restrict Cr;
226
227 register ssize_t
228 x;
229
230 Ar=GetCacheViewVirtualPixels(Ar_view,0,y,images->columns,1,exception);
231 Ai=GetCacheViewVirtualPixels(Ai_view,0,y,images->columns,1,exception);
232 Br=GetCacheViewVirtualPixels(Br_view,0,y,images->columns,1,exception);
233 Bi=GetCacheViewVirtualPixels(Bi_view,0,y,images->columns,1,exception);
234 Cr=QueueCacheViewAuthenticPixels(Cr_view,0,y,images->columns,1,exception);
235 Ci=QueueCacheViewAuthenticPixels(Ci_view,0,y,images->columns,1,exception);
236 if ((Ar == (const Quantum *) NULL) || (Ai == (const Quantum *) NULL) ||
237 (Br == (const Quantum *) NULL) || (Bi == (const Quantum *) NULL) ||
238 (Cr == (Quantum *) NULL) || (Ci == (Quantum *) NULL))
239 {
240 status=MagickFalse;
241 continue;
242 }
243 for (x=0; x < (ssize_t) images->columns; x++)
244 {
cristy9f654472013-10-05 19:44:06 +0000245 register ssize_t
246 i;
247
248 for (i=0; i < (ssize_t) GetPixelChannels(images); i++)
249 {
250 switch (operator)
251 {
cristy19f78862013-10-05 22:21:46 +0000252 case AddComplexOperator:
253 {
254 Cr[i]=Ar[i]+Br[i];
255 Ci[i]=Ai[i]+Bi[i];
256 break;
257 }
cristy9f654472013-10-05 19:44:06 +0000258 case ConjugateComplexOperator:
259 default:
260 {
261 Cr[i]=Ar[i];
262 Ci[i]=(-Bi[i]);
263 break;
264 }
265 case DivideComplexOperator:
266 {
267 double
268 gamma;
269
270 gamma=PerceptibleReciprocal(Br[i]*Br[i]+Bi[i]*Bi[i]);
271 Cr[i]=gamma*(Ar[i]*Br[i]+Ai[i]*Bi[i]);
272 Ci[i]=gamma*(Ai[i]*Br[i]-Ai[i]*Bi[i]);
273 break;
274 }
275 case MultiplyComplexOperator:
276 {
277 Cr[i]=(Ar[i]*Br[i]+Ai[i]*Bi[i]);
278 Ci[i]=(Ai[i]*Br[i]-Ai[i]*Bi[i]);
279 break;
280 }
cristy19f78862013-10-05 22:21:46 +0000281 case SubtractComplexOperator:
282 {
283 Cr[i]=Ar[i]-Br[i];
284 Ci[i]=Ai[i]-Bi[i];
285 break;
286 }
cristy9f654472013-10-05 19:44:06 +0000287 }
cristy19f78862013-10-05 22:21:46 +0000288 Ar+=GetPixelChannels(Ar_image);
289 Ai+=GetPixelChannels(Ai_image);
290 Br+=GetPixelChannels(Br_image);
291 Bi+=GetPixelChannels(Bi_image);
292 Cr+=GetPixelChannels(Cr_image);
293 Ci+=GetPixelChannels(Ci_image);
cristy9f654472013-10-05 19:44:06 +0000294 }
cristy1042ed22013-10-05 17:38:54 +0000295 }
296 if (SyncCacheViewAuthenticPixels(Ci_view,exception) == MagickFalse)
297 status=MagickFalse;
298 if (SyncCacheViewAuthenticPixels(Cr_view,exception) == MagickFalse)
299 status=MagickFalse;
300 if (images->progress_monitor != (MagickProgressMonitor) NULL)
301 {
302 MagickBooleanType
303 proceed;
304
305#if defined(MAGICKCORE_OPENMP_SUPPORT)
306 #pragma omp critical (MagickCore_ComplexImages)
307#endif
308 proceed=SetImageProgress(images,ComplexImageTag,progress++,
309 images->rows);
310 if (proceed == MagickFalse)
311 status=MagickFalse;
312 }
313 }
314 Cr_view=DestroyCacheView(Cr_view);
315 Ci_view=DestroyCacheView(Ci_view);
316 Br_view=DestroyCacheView(Br_view);
317 Bi_view=DestroyCacheView(Bi_view);
318 Ar_view=DestroyCacheView(Ar_view);
319 Ai_view=DestroyCacheView(Ai_view);
320 if (status == MagickFalse)
321 complex_images=DestroyImageList(complex_images);
322 return(complex_images);
cristy790190d2013-10-04 00:51:51 +0000323}
324
325/*
326%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
327% %
328% %
329% %
cristy3ed852e2009-09-05 21:47:34 +0000330% 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 %
331% %
332% %
333% %
334%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
335%
336% ForwardFourierTransformImage() implements the discrete Fourier transform
337% (DFT) of the image either as a magnitude / phase or real / imaginary image
338% pair.
339%
340% The format of the ForwadFourierTransformImage method is:
341%
342% Image *ForwardFourierTransformImage(const Image *image,
343% const MagickBooleanType modulus,ExceptionInfo *exception)
344%
345% A description of each parameter follows:
346%
347% o image: the image.
348%
349% o modulus: if true, return as transform as a magnitude / phase pair
350% otherwise a real / imaginary image pair.
351%
352% o exception: return any errors or warnings in this structure.
353%
354*/
355
356#if defined(MAGICKCORE_FFTW_DELEGATE)
357
cristyc4ea4a42011-01-24 01:43:30 +0000358static MagickBooleanType RollFourier(const size_t width,const size_t height,
cristy699ae5b2013-07-03 13:41:29 +0000359 const ssize_t x_offset,const ssize_t y_offset,double *roll_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000360{
361 double
cristy699ae5b2013-07-03 13:41:29 +0000362 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000363
cristy7d4aa382013-06-30 01:59:39 +0000364 MemoryInfo
cristy699ae5b2013-07-03 13:41:29 +0000365 *source_info;
cristy7d4aa382013-06-30 01:59:39 +0000366
cristyc4ea4a42011-01-24 01:43:30 +0000367 register ssize_t
368 i,
369 x;
370
cristybb503372010-05-27 20:51:26 +0000371 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000372 u,
373 v,
374 y;
375
cristy3ed852e2009-09-05 21:47:34 +0000376 /*
cristy2da05352010-09-15 19:22:17 +0000377 Move zero frequency (DC, average color) from (0,0) to (width/2,height/2).
cristy3ed852e2009-09-05 21:47:34 +0000378 */
cristy699ae5b2013-07-03 13:41:29 +0000379 source_info=AcquireVirtualMemory(height,width*sizeof(*source_pixels));
380 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000381 return(MagickFalse);
cristy699ae5b2013-07-03 13:41:29 +0000382 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000383 i=0L;
cristybb503372010-05-27 20:51:26 +0000384 for (y=0L; y < (ssize_t) height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000385 {
386 if (y_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000387 v=((y+y_offset) < 0L) ? y+y_offset+(ssize_t) height : y+y_offset;
cristy3ed852e2009-09-05 21:47:34 +0000388 else
cristybb503372010-05-27 20:51:26 +0000389 v=((y+y_offset) > ((ssize_t) height-1L)) ? y+y_offset-(ssize_t) height :
cristy3ed852e2009-09-05 21:47:34 +0000390 y+y_offset;
cristybb503372010-05-27 20:51:26 +0000391 for (x=0L; x < (ssize_t) width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000392 {
393 if (x_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000394 u=((x+x_offset) < 0L) ? x+x_offset+(ssize_t) width : x+x_offset;
cristy3ed852e2009-09-05 21:47:34 +0000395 else
cristybb503372010-05-27 20:51:26 +0000396 u=((x+x_offset) > ((ssize_t) width-1L)) ? x+x_offset-(ssize_t) width :
cristy3ed852e2009-09-05 21:47:34 +0000397 x+x_offset;
cristy699ae5b2013-07-03 13:41:29 +0000398 source_pixels[v*width+u]=roll_pixels[i++];
cristyc4ea4a42011-01-24 01:43:30 +0000399 }
cristy3ed852e2009-09-05 21:47:34 +0000400 }
cristy699ae5b2013-07-03 13:41:29 +0000401 (void) CopyMagickMemory(roll_pixels,source_pixels,height*width*
402 sizeof(*source_pixels));
403 source_info=RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000404 return(MagickTrue);
405}
406
cristybb503372010-05-27 20:51:26 +0000407static MagickBooleanType ForwardQuadrantSwap(const size_t width,
cristy699ae5b2013-07-03 13:41:29 +0000408 const size_t height,double *source_pixels,double *forward_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000409{
cristy3ed852e2009-09-05 21:47:34 +0000410 MagickBooleanType
411 status;
412
cristybb503372010-05-27 20:51:26 +0000413 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000414 x;
415
cristyc4ea4a42011-01-24 01:43:30 +0000416 ssize_t
417 center,
418 y;
419
cristy3ed852e2009-09-05 21:47:34 +0000420 /*
421 Swap quadrants.
422 */
cristybb503372010-05-27 20:51:26 +0000423 center=(ssize_t) floor((double) width/2L)+1L;
cristy699ae5b2013-07-03 13:41:29 +0000424 status=RollFourier((size_t) center,height,0L,(ssize_t) height/2L,
425 source_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000426 if (status == MagickFalse)
427 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000428 for (y=0L; y < (ssize_t) height; y++)
429 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy3d04ed72013-08-18 17:51:01 +0000430 forward_pixels[y*width+x+width/2L]=source_pixels[y*center+x];
cristybb503372010-05-27 20:51:26 +0000431 for (y=1; y < (ssize_t) height; y++)
432 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy9b1d6c72013-08-18 17:53:56 +0000433 forward_pixels[(height-y)*width+width/2L-x-1L]=
cristy3d04ed72013-08-18 17:51:01 +0000434 source_pixels[y*center+x+1L];
cristybb503372010-05-27 20:51:26 +0000435 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy699ae5b2013-07-03 13:41:29 +0000436 forward_pixels[-x+width/2L-1L]=forward_pixels[x+width/2L+1L];
cristy3ed852e2009-09-05 21:47:34 +0000437 return(MagickTrue);
438}
439
cristyc4ea4a42011-01-24 01:43:30 +0000440static void CorrectPhaseLHS(const size_t width,const size_t height,
cristy699ae5b2013-07-03 13:41:29 +0000441 double *fourier_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000442{
cristybb503372010-05-27 20:51:26 +0000443 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000444 x;
445
cristy9d314ff2011-03-09 01:30:28 +0000446 ssize_t
447 y;
448
cristybb503372010-05-27 20:51:26 +0000449 for (y=0L; y < (ssize_t) height; y++)
450 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy699ae5b2013-07-03 13:41:29 +0000451 fourier_pixels[y*width+x]*=(-1.0);
cristy3ed852e2009-09-05 21:47:34 +0000452}
453
454static MagickBooleanType ForwardFourier(const FourierInfo *fourier_info,
455 Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
456{
457 CacheView
458 *magnitude_view,
459 *phase_view;
460
461 double
cristy7d4aa382013-06-30 01:59:39 +0000462 *magnitude_pixels,
463 *phase_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000464
465 Image
466 *magnitude_image,
467 *phase_image;
468
cristy3ed852e2009-09-05 21:47:34 +0000469 MagickBooleanType
470 status;
471
cristy7d4aa382013-06-30 01:59:39 +0000472 MemoryInfo
473 *magnitude_info,
474 *phase_info;
475
cristy4c08aed2011-07-01 19:47:50 +0000476 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000477 *q;
478
cristyf5742792012-11-27 18:31:26 +0000479 register ssize_t
480 x;
481
cristyc4ea4a42011-01-24 01:43:30 +0000482 ssize_t
483 i,
484 y;
485
cristy3ed852e2009-09-05 21:47:34 +0000486 magnitude_image=GetFirstImageInList(image);
487 phase_image=GetNextImageInList(image);
488 if (phase_image == (Image *) NULL)
489 {
490 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
cristyefe601c2013-01-05 17:51:12 +0000491 "TwoOrMoreImagesRequired","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000492 return(MagickFalse);
493 }
494 /*
495 Create "Fourier Transform" image from constituent arrays.
496 */
cristy7d4aa382013-06-30 01:59:39 +0000497 magnitude_info=AcquireVirtualMemory((size_t) fourier_info->height,
498 fourier_info->width*sizeof(*magnitude_pixels));
499 phase_info=AcquireVirtualMemory((size_t) fourier_info->height,
500 fourier_info->width*sizeof(*phase_pixels));
cristybb3c02e2013-07-02 00:43:10 +0000501 if ((magnitude_info == (MemoryInfo *) NULL) ||
502 (phase_info == (MemoryInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000503 {
cristy7d4aa382013-06-30 01:59:39 +0000504 if (phase_info != (MemoryInfo *) NULL)
505 phase_info=RelinquishVirtualMemory(phase_info);
506 if (magnitude_info != (MemoryInfo *) NULL)
507 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000508 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000509 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000510 return(MagickFalse);
511 }
cristy7d4aa382013-06-30 01:59:39 +0000512 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
513 (void) ResetMagickMemory(magnitude_pixels,0,fourier_info->height*
514 fourier_info->width*sizeof(*magnitude_pixels));
cristybb3c02e2013-07-02 00:43:10 +0000515 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
cristy7d4aa382013-06-30 01:59:39 +0000516 (void) ResetMagickMemory(phase_pixels,0,fourier_info->height*
517 fourier_info->width*sizeof(*phase_pixels));
cristy13c99c42013-08-18 13:53:30 +0000518 status=ForwardQuadrantSwap(fourier_info->width,fourier_info->height,
cristy7d4aa382013-06-30 01:59:39 +0000519 magnitude,magnitude_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000520 if (status != MagickFalse)
cristy13c99c42013-08-18 13:53:30 +0000521 status=ForwardQuadrantSwap(fourier_info->width,fourier_info->height,phase,
cristy7d4aa382013-06-30 01:59:39 +0000522 phase_pixels);
cristy13c99c42013-08-18 13:53:30 +0000523 CorrectPhaseLHS(fourier_info->width,fourier_info->height,phase_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000524 if (fourier_info->modulus != MagickFalse)
525 {
526 i=0L;
cristybb503372010-05-27 20:51:26 +0000527 for (y=0L; y < (ssize_t) fourier_info->height; y++)
528 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000529 {
cristy7d4aa382013-06-30 01:59:39 +0000530 phase_pixels[i]/=(2.0*MagickPI);
531 phase_pixels[i]+=0.5;
cristy3ed852e2009-09-05 21:47:34 +0000532 i++;
533 }
534 }
cristy46ff2672012-12-14 15:32:26 +0000535 magnitude_view=AcquireAuthenticCacheView(magnitude_image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000536 i=0L;
cristybb503372010-05-27 20:51:26 +0000537 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000538 {
539 q=GetCacheViewAuthenticPixels(magnitude_view,0L,y,fourier_info->height,1UL,
540 exception);
cristyacd2ed22011-08-30 01:44:23 +0000541 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000542 break;
cristybb503372010-05-27 20:51:26 +0000543 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000544 {
545 switch (fourier_info->channel)
546 {
cristyd3090f92011-10-18 00:05:15 +0000547 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000548 default:
549 {
cristy4c08aed2011-07-01 19:47:50 +0000550 SetPixelRed(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000551 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000552 break;
553 }
cristyd3090f92011-10-18 00:05:15 +0000554 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000555 {
cristy4c08aed2011-07-01 19:47:50 +0000556 SetPixelGreen(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000557 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000558 break;
559 }
cristyd3090f92011-10-18 00:05:15 +0000560 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000561 {
cristy4c08aed2011-07-01 19:47:50 +0000562 SetPixelBlue(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000563 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000564 break;
565 }
cristyd3090f92011-10-18 00:05:15 +0000566 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000567 {
cristy4c08aed2011-07-01 19:47:50 +0000568 SetPixelBlack(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000569 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000570 break;
571 }
cristyd3090f92011-10-18 00:05:15 +0000572 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000573 {
cristy4c08aed2011-07-01 19:47:50 +0000574 SetPixelAlpha(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000575 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000576 break;
577 }
cristy3ed852e2009-09-05 21:47:34 +0000578 }
579 i++;
cristyed231572011-07-14 02:18:59 +0000580 q+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +0000581 }
582 status=SyncCacheViewAuthenticPixels(magnitude_view,exception);
583 if (status == MagickFalse)
584 break;
585 }
cristydb070952012-04-20 14:33:00 +0000586 magnitude_view=DestroyCacheView(magnitude_view);
cristy3ed852e2009-09-05 21:47:34 +0000587 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000588 phase_view=AcquireAuthenticCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +0000589 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000590 {
591 q=GetCacheViewAuthenticPixels(phase_view,0L,y,fourier_info->height,1UL,
592 exception);
cristyacd2ed22011-08-30 01:44:23 +0000593 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000594 break;
cristybb503372010-05-27 20:51:26 +0000595 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000596 {
597 switch (fourier_info->channel)
598 {
cristyd3090f92011-10-18 00:05:15 +0000599 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000600 default:
601 {
cristy4c08aed2011-07-01 19:47:50 +0000602 SetPixelRed(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000603 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000604 break;
605 }
cristyd3090f92011-10-18 00:05:15 +0000606 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000607 {
cristy4c08aed2011-07-01 19:47:50 +0000608 SetPixelGreen(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000609 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000610 break;
611 }
cristyd3090f92011-10-18 00:05:15 +0000612 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000613 {
cristy4c08aed2011-07-01 19:47:50 +0000614 SetPixelBlue(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000615 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000616 break;
617 }
cristyd3090f92011-10-18 00:05:15 +0000618 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000619 {
cristy4c08aed2011-07-01 19:47:50 +0000620 SetPixelBlack(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000621 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000622 break;
623 }
cristyd3090f92011-10-18 00:05:15 +0000624 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000625 {
cristy4c08aed2011-07-01 19:47:50 +0000626 SetPixelAlpha(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000627 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000628 break;
629 }
cristy3ed852e2009-09-05 21:47:34 +0000630 }
631 i++;
cristyed231572011-07-14 02:18:59 +0000632 q+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +0000633 }
634 status=SyncCacheViewAuthenticPixels(phase_view,exception);
635 if (status == MagickFalse)
636 break;
637 }
638 phase_view=DestroyCacheView(phase_view);
cristy7d4aa382013-06-30 01:59:39 +0000639 phase_info=RelinquishVirtualMemory(phase_info);
640 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000641 return(status);
642}
643
644static MagickBooleanType ForwardFourierTransform(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +0000645 const Image *image,double *magnitude_pixels,double *phase_pixels,
646 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000647{
648 CacheView
649 *image_view;
650
cristy99dc0362013-09-15 18:32:54 +0000651 const char
652 *value;
653
cristy3ed852e2009-09-05 21:47:34 +0000654 double
cristybb3c02e2013-07-02 00:43:10 +0000655 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000656
657 fftw_complex
cristy699ae5b2013-07-03 13:41:29 +0000658 *forward_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000659
660 fftw_plan
661 fftw_r2c_plan;
662
cristy7d4aa382013-06-30 01:59:39 +0000663 MemoryInfo
cristy699ae5b2013-07-03 13:41:29 +0000664 *forward_info,
cristy7d4aa382013-06-30 01:59:39 +0000665 *source_info;
666
cristy4c08aed2011-07-01 19:47:50 +0000667 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000668 *p;
669
cristybb503372010-05-27 20:51:26 +0000670 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000671 i,
672 x;
673
cristyc4ea4a42011-01-24 01:43:30 +0000674 ssize_t
675 y;
676
cristy3ed852e2009-09-05 21:47:34 +0000677 /*
678 Generate the forward Fourier transform.
679 */
cristy7d4aa382013-06-30 01:59:39 +0000680 source_info=AcquireVirtualMemory((size_t) fourier_info->height,
cristybb3c02e2013-07-02 00:43:10 +0000681 fourier_info->width*sizeof(*source_pixels));
cristy7d4aa382013-06-30 01:59:39 +0000682 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000683 {
684 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000685 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000686 return(MagickFalse);
687 }
cristybb3c02e2013-07-02 00:43:10 +0000688 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
689 ResetMagickMemory(source_pixels,0,fourier_info->height*fourier_info->width*
690 sizeof(*source_pixels));
cristy3ed852e2009-09-05 21:47:34 +0000691 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000692 image_view=AcquireVirtualCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +0000693 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000694 {
695 p=GetCacheViewVirtualPixels(image_view,0L,y,fourier_info->width,1UL,
696 exception);
cristy4c08aed2011-07-01 19:47:50 +0000697 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000698 break;
cristybb503372010-05-27 20:51:26 +0000699 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000700 {
701 switch (fourier_info->channel)
702 {
cristyd3090f92011-10-18 00:05:15 +0000703 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000704 default:
705 {
cristybb3c02e2013-07-02 00:43:10 +0000706 source_pixels[i]=QuantumScale*GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000707 break;
708 }
cristyd3090f92011-10-18 00:05:15 +0000709 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000710 {
cristybb3c02e2013-07-02 00:43:10 +0000711 source_pixels[i]=QuantumScale*GetPixelGreen(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000712 break;
713 }
cristyd3090f92011-10-18 00:05:15 +0000714 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000715 {
cristybb3c02e2013-07-02 00:43:10 +0000716 source_pixels[i]=QuantumScale*GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000717 break;
718 }
cristyd3090f92011-10-18 00:05:15 +0000719 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000720 {
cristybb3c02e2013-07-02 00:43:10 +0000721 source_pixels[i]=QuantumScale*GetPixelBlack(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000722 break;
723 }
cristyd3090f92011-10-18 00:05:15 +0000724 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000725 {
cristybb3c02e2013-07-02 00:43:10 +0000726 source_pixels[i]=QuantumScale*GetPixelAlpha(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000727 break;
728 }
cristy3ed852e2009-09-05 21:47:34 +0000729 }
730 i++;
cristyed231572011-07-14 02:18:59 +0000731 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000732 }
733 }
734 image_view=DestroyCacheView(image_view);
cristy699ae5b2013-07-03 13:41:29 +0000735 forward_info=AcquireVirtualMemory((size_t) fourier_info->height,
736 fourier_info->center*sizeof(*forward_pixels));
737 if (forward_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000738 {
739 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000740 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristybb3c02e2013-07-02 00:43:10 +0000741 source_info=(MemoryInfo *) RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000742 return(MagickFalse);
743 }
cristy699ae5b2013-07-03 13:41:29 +0000744 forward_pixels=(fftw_complex *) GetVirtualMemoryBlob(forward_info);
cristyb5d5f722009-11-04 03:03:49 +0000745#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000746 #pragma omp critical (MagickCore_ForwardFourierTransform)
747#endif
cristy70841a12012-10-27 20:52:57 +0000748 fftw_r2c_plan=fftw_plan_dft_r2c_2d(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +0000749 source_pixels,forward_pixels,FFTW_ESTIMATE);
cristy3ed852e2009-09-05 21:47:34 +0000750 fftw_execute(fftw_r2c_plan);
751 fftw_destroy_plan(fftw_r2c_plan);
cristybb3c02e2013-07-02 00:43:10 +0000752 source_info=(MemoryInfo *) RelinquishVirtualMemory(source_info);
cristy99dc0362013-09-15 18:32:54 +0000753 value=GetImageArtifact(image,"fourier:normalize");
cristyf8ecbfd2013-09-15 19:11:14 +0000754 if ((value == (const char *) NULL) || (LocaleCompare(value,"forward") == 0))
cristy56ed31c2010-03-22 00:46:21 +0000755 {
cristy99dc0362013-09-15 18:32:54 +0000756 double
757 gamma;
758
759 /*
760 Normalize Fourier transform.
761 */
762 i=0L;
763 gamma=PerceptibleReciprocal((double) fourier_info->width*
764 fourier_info->height);
765 for (y=0L; y < (ssize_t) fourier_info->height; y++)
766 for (x=0L; x < (ssize_t) fourier_info->center; x++)
767 {
cristy56ed31c2010-03-22 00:46:21 +0000768#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy99dc0362013-09-15 18:32:54 +0000769 forward_pixels[i]*=gamma;
cristy56ed31c2010-03-22 00:46:21 +0000770#else
cristy99dc0362013-09-15 18:32:54 +0000771 forward_pixels[i][0]*=gamma;
772 forward_pixels[i][1]*=gamma;
cristy56ed31c2010-03-22 00:46:21 +0000773#endif
cristy99dc0362013-09-15 18:32:54 +0000774 i++;
775 }
cristy56ed31c2010-03-22 00:46:21 +0000776 }
cristy3ed852e2009-09-05 21:47:34 +0000777 /*
778 Generate magnitude and phase (or real and imaginary).
779 */
780 i=0L;
781 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000782 for (y=0L; y < (ssize_t) fourier_info->height; y++)
783 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000784 {
cristy699ae5b2013-07-03 13:41:29 +0000785 magnitude_pixels[i]=cabs(forward_pixels[i]);
786 phase_pixels[i]=carg(forward_pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000787 i++;
788 }
789 else
cristybb503372010-05-27 20:51:26 +0000790 for (y=0L; y < (ssize_t) fourier_info->height; y++)
791 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000792 {
cristy699ae5b2013-07-03 13:41:29 +0000793 magnitude_pixels[i]=creal(forward_pixels[i]);
794 phase_pixels[i]=cimag(forward_pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000795 i++;
796 }
cristy699ae5b2013-07-03 13:41:29 +0000797 forward_info=(MemoryInfo *) RelinquishVirtualMemory(forward_info);
cristy3ed852e2009-09-05 21:47:34 +0000798 return(MagickTrue);
799}
800
801static MagickBooleanType ForwardFourierTransformChannel(const Image *image,
cristyd3090f92011-10-18 00:05:15 +0000802 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +0000803 Image *fourier_image,ExceptionInfo *exception)
804{
805 double
cristyce9fe782013-07-03 00:59:41 +0000806 *magnitude_pixels,
807 *phase_pixels;
cristybb3c02e2013-07-02 00:43:10 +0000808
cristy56ed31c2010-03-22 00:46:21 +0000809 FourierInfo
810 fourier_info;
811
cristyc4ea4a42011-01-24 01:43:30 +0000812 MagickBooleanType
813 status;
814
cristyce9fe782013-07-03 00:59:41 +0000815 MemoryInfo
816 *magnitude_info,
817 *phase_info;
818
cristy3ed852e2009-09-05 21:47:34 +0000819 size_t
820 extent;
821
822 fourier_info.width=image->columns;
823 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
824 ((image->rows % 2) != 0))
825 {
826 extent=image->columns < image->rows ? image->rows : image->columns;
827 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
828 }
829 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +0000830 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +0000831 fourier_info.channel=channel;
832 fourier_info.modulus=modulus;
cristyce9fe782013-07-03 00:59:41 +0000833 magnitude_info=AcquireVirtualMemory((size_t) fourier_info.height,
834 fourier_info.center*sizeof(*magnitude_pixels));
835 phase_info=AcquireVirtualMemory((size_t) fourier_info.height,
836 fourier_info.center*sizeof(*phase_pixels));
837 if ((magnitude_info == (MemoryInfo *) NULL) ||
838 (phase_info == (MemoryInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000839 {
cristyce9fe782013-07-03 00:59:41 +0000840 if (phase_info != (MemoryInfo *) NULL)
841 phase_info=RelinquishVirtualMemory(phase_info);
842 if (magnitude_info == (MemoryInfo *) NULL)
843 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000844 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000845 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000846 return(MagickFalse);
847 }
cristyce9fe782013-07-03 00:59:41 +0000848 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
849 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
850 status=ForwardFourierTransform(&fourier_info,image,magnitude_pixels,
851 phase_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +0000852 if (status != MagickFalse)
cristyce9fe782013-07-03 00:59:41 +0000853 status=ForwardFourier(&fourier_info,fourier_image,magnitude_pixels,
854 phase_pixels,exception);
855 phase_info=RelinquishVirtualMemory(phase_info);
856 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000857 return(status);
858}
859#endif
860
861MagickExport Image *ForwardFourierTransformImage(const Image *image,
862 const MagickBooleanType modulus,ExceptionInfo *exception)
863{
864 Image
865 *fourier_image;
866
867 fourier_image=NewImageList();
868#if !defined(MAGICKCORE_FFTW_DELEGATE)
869 (void) modulus;
870 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000871 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristy3ed852e2009-09-05 21:47:34 +0000872 image->filename);
873#else
874 {
875 Image
876 *magnitude_image;
877
cristybb503372010-05-27 20:51:26 +0000878 size_t
cristy3ed852e2009-09-05 21:47:34 +0000879 extent,
880 width;
881
882 width=image->columns;
883 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
884 ((image->rows % 2) != 0))
885 {
886 extent=image->columns < image->rows ? image->rows : image->columns;
887 width=(extent & 0x01) == 1 ? extent+1UL : extent;
888 }
889 magnitude_image=CloneImage(image,width,width,MagickFalse,exception);
890 if (magnitude_image != (Image *) NULL)
891 {
892 Image
893 *phase_image;
894
895 magnitude_image->storage_class=DirectClass;
896 magnitude_image->depth=32UL;
897 phase_image=CloneImage(image,width,width,MagickFalse,exception);
898 if (phase_image == (Image *) NULL)
899 magnitude_image=DestroyImage(magnitude_image);
900 else
901 {
902 MagickBooleanType
903 is_gray,
904 status;
905
cristy3ed852e2009-09-05 21:47:34 +0000906 phase_image->storage_class=DirectClass;
907 phase_image->depth=32UL;
908 AppendImageToList(&fourier_image,magnitude_image);
909 AppendImageToList(&fourier_image,phase_image);
910 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000911 is_gray=IsImageGray(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000912#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy564a5692012-01-20 23:56:26 +0000913 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +0000914#endif
cristy3ed852e2009-09-05 21:47:34 +0000915 {
cristyb34ef052010-10-07 00:12:05 +0000916#if defined(MAGICKCORE_OPENMP_SUPPORT)
917 #pragma omp section
918#endif
cristy3ed852e2009-09-05 21:47:34 +0000919 {
cristyb34ef052010-10-07 00:12:05 +0000920 MagickBooleanType
921 thread_status;
922
923 if (is_gray != MagickFalse)
924 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000925 GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000926 else
927 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000928 RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000929 if (thread_status == MagickFalse)
930 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +0000931 }
cristyb34ef052010-10-07 00:12:05 +0000932#if defined(MAGICKCORE_OPENMP_SUPPORT)
933 #pragma omp section
934#endif
935 {
936 MagickBooleanType
937 thread_status;
938
939 thread_status=MagickTrue;
940 if (is_gray == MagickFalse)
941 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000942 GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000943 if (thread_status == MagickFalse)
944 status=thread_status;
945 }
946#if defined(MAGICKCORE_OPENMP_SUPPORT)
947 #pragma omp section
948#endif
949 {
950 MagickBooleanType
951 thread_status;
952
953 thread_status=MagickTrue;
954 if (is_gray == MagickFalse)
955 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000956 BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000957 if (thread_status == MagickFalse)
958 status=thread_status;
959 }
960#if defined(MAGICKCORE_OPENMP_SUPPORT)
961 #pragma omp section
962#endif
963 {
964 MagickBooleanType
965 thread_status;
966
967 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000968 if (image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +0000969 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000970 BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000971 if (thread_status == MagickFalse)
972 status=thread_status;
973 }
974#if defined(MAGICKCORE_OPENMP_SUPPORT)
975 #pragma omp section
976#endif
977 {
978 MagickBooleanType
979 thread_status;
980
981 thread_status=MagickTrue;
cristy8a46d822012-08-28 23:32:39 +0000982 if (image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +0000983 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000984 AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000985 if (thread_status == MagickFalse)
986 status=thread_status;
987 }
cristy3ed852e2009-09-05 21:47:34 +0000988 }
989 if (status == MagickFalse)
990 fourier_image=DestroyImageList(fourier_image);
991 fftw_cleanup();
992 }
993 }
994 }
995#endif
996 return(fourier_image);
997}
998
999/*
1000%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1001% %
1002% %
1003% %
1004% 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 %
1005% %
1006% %
1007% %
1008%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1009%
1010% InverseFourierTransformImage() implements the inverse discrete Fourier
1011% transform (DFT) of the image either as a magnitude / phase or real /
1012% imaginary image pair.
1013%
1014% The format of the InverseFourierTransformImage method is:
1015%
cristyc9550792009-11-13 20:05:42 +00001016% Image *InverseFourierTransformImage(const Image *magnitude_image,
1017% const Image *phase_image,const MagickBooleanType modulus,
1018% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001019%
1020% A description of each parameter follows:
1021%
cristyc9550792009-11-13 20:05:42 +00001022% o magnitude_image: the magnitude or real image.
1023%
1024% o phase_image: the phase or imaginary image.
cristy3ed852e2009-09-05 21:47:34 +00001025%
1026% o modulus: if true, return transform as a magnitude / phase pair
1027% otherwise a real / imaginary image pair.
1028%
1029% o exception: return any errors or warnings in this structure.
1030%
1031*/
1032
1033#if defined(MAGICKCORE_FFTW_DELEGATE)
cristybb503372010-05-27 20:51:26 +00001034static MagickBooleanType InverseQuadrantSwap(const size_t width,
1035 const size_t height,const double *source,double *destination)
cristy3ed852e2009-09-05 21:47:34 +00001036{
cristyc4ea4a42011-01-24 01:43:30 +00001037 register ssize_t
1038 x;
1039
cristybb503372010-05-27 20:51:26 +00001040 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001041 center,
1042 y;
1043
cristy3ed852e2009-09-05 21:47:34 +00001044 /*
1045 Swap quadrants.
1046 */
cristy233fe582012-07-07 14:00:18 +00001047 center=(ssize_t) floor((double) width/2L)+1L;
cristybb503372010-05-27 20:51:26 +00001048 for (y=1L; y < (ssize_t) height; y++)
1049 for (x=0L; x < (ssize_t) (width/2L+1L); x++)
cristy9b1d6c72013-08-18 17:53:56 +00001050 destination[(height-y)*center-x+width/2L]=source[y*width+x];
cristybb503372010-05-27 20:51:26 +00001051 for (y=0L; y < (ssize_t) height; y++)
cristy3d04ed72013-08-18 17:51:01 +00001052 destination[y*center]=source[y*width+width/2L];
cristy3ed852e2009-09-05 21:47:34 +00001053 for (x=0L; x < center; x++)
1054 destination[x]=source[center-x-1L];
cristybb503372010-05-27 20:51:26 +00001055 return(RollFourier(center,height,0L,(ssize_t) height/-2L,destination));
cristy3ed852e2009-09-05 21:47:34 +00001056}
1057
1058static MagickBooleanType InverseFourier(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +00001059 const Image *magnitude_image,const Image *phase_image,
1060 fftw_complex *fourier_pixels,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001061{
1062 CacheView
1063 *magnitude_view,
1064 *phase_view;
1065
1066 double
cristy699ae5b2013-07-03 13:41:29 +00001067 *inverse_pixels,
cristy7d4aa382013-06-30 01:59:39 +00001068 *magnitude_pixels,
1069 *phase_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001070
cristy3ed852e2009-09-05 21:47:34 +00001071 MagickBooleanType
1072 status;
1073
cristy699ae5b2013-07-03 13:41:29 +00001074 MemoryInfo
1075 *inverse_info,
1076 *magnitude_info,
1077 *phase_info;
1078
cristy4c08aed2011-07-01 19:47:50 +00001079 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001080 *p;
1081
cristybb503372010-05-27 20:51:26 +00001082 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001083 i,
1084 x;
1085
cristyc4ea4a42011-01-24 01:43:30 +00001086 ssize_t
1087 y;
1088
cristy3ed852e2009-09-05 21:47:34 +00001089 /*
1090 Inverse fourier - read image and break down into a double array.
1091 */
cristy699ae5b2013-07-03 13:41:29 +00001092 magnitude_info=AcquireVirtualMemory((size_t)fourier_info->height,
1093 fourier_info->width*sizeof(*magnitude_pixels));
1094 phase_info=AcquireVirtualMemory((size_t) fourier_info->height,
cristybb3c02e2013-07-02 00:43:10 +00001095 fourier_info->width*sizeof(*phase_pixels));
cristy699ae5b2013-07-03 13:41:29 +00001096 inverse_info=AcquireVirtualMemory((size_t) fourier_info->height,
1097 fourier_info->center*sizeof(*inverse_pixels));
1098 if ((magnitude_info == (MemoryInfo *) NULL) ||
1099 (phase_info == (MemoryInfo *) NULL) ||
1100 (inverse_info == (MemoryInfo *) NULL))
cristybb3c02e2013-07-02 00:43:10 +00001101 {
cristy699ae5b2013-07-03 13:41:29 +00001102 if (magnitude_info != (MemoryInfo *) NULL)
1103 magnitude_info=RelinquishVirtualMemory(magnitude_info);
1104 if (phase_info != (MemoryInfo *) NULL)
1105 phase_info=RelinquishVirtualMemory(phase_info);
1106 if (inverse_info != (MemoryInfo *) NULL)
1107 inverse_info=RelinquishVirtualMemory(inverse_info);
cristybb3c02e2013-07-02 00:43:10 +00001108 (void) ThrowMagickException(exception,GetMagickModule(),
1109 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1110 magnitude_image->filename);
cristybb3c02e2013-07-02 00:43:10 +00001111 return(MagickFalse);
1112 }
cristy699ae5b2013-07-03 13:41:29 +00001113 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
1114 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
1115 inverse_pixels=(double *) GetVirtualMemoryBlob(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +00001116 i=0L;
cristy46ff2672012-12-14 15:32:26 +00001117 magnitude_view=AcquireVirtualCacheView(magnitude_image,exception);
cristybb503372010-05-27 20:51:26 +00001118 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001119 {
1120 p=GetCacheViewVirtualPixels(magnitude_view,0L,y,fourier_info->width,1UL,
1121 exception);
cristy4c08aed2011-07-01 19:47:50 +00001122 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001123 break;
cristybb503372010-05-27 20:51:26 +00001124 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001125 {
1126 switch (fourier_info->channel)
1127 {
cristyd3090f92011-10-18 00:05:15 +00001128 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001129 default:
1130 {
cristy7d4aa382013-06-30 01:59:39 +00001131 magnitude_pixels[i]=QuantumScale*GetPixelRed(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001132 break;
1133 }
cristyd3090f92011-10-18 00:05:15 +00001134 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001135 {
cristy7d4aa382013-06-30 01:59:39 +00001136 magnitude_pixels[i]=QuantumScale*GetPixelGreen(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001137 break;
1138 }
cristyd3090f92011-10-18 00:05:15 +00001139 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001140 {
cristy7d4aa382013-06-30 01:59:39 +00001141 magnitude_pixels[i]=QuantumScale*GetPixelBlue(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001142 break;
1143 }
cristyd3090f92011-10-18 00:05:15 +00001144 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001145 {
cristy7d4aa382013-06-30 01:59:39 +00001146 magnitude_pixels[i]=QuantumScale*GetPixelBlack(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001147 break;
1148 }
cristyd3090f92011-10-18 00:05:15 +00001149 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001150 {
cristy7d4aa382013-06-30 01:59:39 +00001151 magnitude_pixels[i]=QuantumScale*GetPixelAlpha(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001152 break;
1153 }
cristy3ed852e2009-09-05 21:47:34 +00001154 }
1155 i++;
cristyed231572011-07-14 02:18:59 +00001156 p+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +00001157 }
1158 }
cristy699ae5b2013-07-03 13:41:29 +00001159 magnitude_view=DestroyCacheView(magnitude_view);
1160 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
1161 magnitude_pixels,inverse_pixels);
1162 (void) CopyMagickMemory(magnitude_pixels,inverse_pixels,fourier_info->height*
1163 fourier_info->center*sizeof(*magnitude_pixels));
cristy3ed852e2009-09-05 21:47:34 +00001164 i=0L;
cristy46ff2672012-12-14 15:32:26 +00001165 phase_view=AcquireVirtualCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +00001166 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001167 {
1168 p=GetCacheViewVirtualPixels(phase_view,0,y,fourier_info->width,1,
1169 exception);
cristy4c08aed2011-07-01 19:47:50 +00001170 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001171 break;
cristybb503372010-05-27 20:51:26 +00001172 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001173 {
1174 switch (fourier_info->channel)
1175 {
cristyd3090f92011-10-18 00:05:15 +00001176 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001177 default:
1178 {
cristy7d4aa382013-06-30 01:59:39 +00001179 phase_pixels[i]=QuantumScale*GetPixelRed(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001180 break;
1181 }
cristyd3090f92011-10-18 00:05:15 +00001182 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001183 {
cristy7d4aa382013-06-30 01:59:39 +00001184 phase_pixels[i]=QuantumScale*GetPixelGreen(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001185 break;
1186 }
cristyd3090f92011-10-18 00:05:15 +00001187 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001188 {
cristy7d4aa382013-06-30 01:59:39 +00001189 phase_pixels[i]=QuantumScale*GetPixelBlue(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001190 break;
1191 }
cristyd3090f92011-10-18 00:05:15 +00001192 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001193 {
cristy7d4aa382013-06-30 01:59:39 +00001194 phase_pixels[i]=QuantumScale*GetPixelBlack(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001195 break;
1196 }
cristyd3090f92011-10-18 00:05:15 +00001197 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001198 {
cristy7d4aa382013-06-30 01:59:39 +00001199 phase_pixels[i]=QuantumScale*GetPixelAlpha(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001200 break;
1201 }
cristy3ed852e2009-09-05 21:47:34 +00001202 }
1203 i++;
cristyed231572011-07-14 02:18:59 +00001204 p+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +00001205 }
1206 }
1207 if (fourier_info->modulus != MagickFalse)
1208 {
1209 i=0L;
cristybb503372010-05-27 20:51:26 +00001210 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1211 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001212 {
cristy7d4aa382013-06-30 01:59:39 +00001213 phase_pixels[i]-=0.5;
1214 phase_pixels[i]*=(2.0*MagickPI);
cristy3ed852e2009-09-05 21:47:34 +00001215 i++;
1216 }
1217 }
cristy3ed852e2009-09-05 21:47:34 +00001218 phase_view=DestroyCacheView(phase_view);
cristy13c99c42013-08-18 13:53:30 +00001219 CorrectPhaseLHS(fourier_info->width,fourier_info->height,phase_pixels);
cristy3ed852e2009-09-05 21:47:34 +00001220 if (status != MagickFalse)
1221 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +00001222 phase_pixels,inverse_pixels);
1223 (void) CopyMagickMemory(phase_pixels,inverse_pixels,fourier_info->height*
1224 fourier_info->center*sizeof(*phase_pixels));
1225 inverse_info=RelinquishVirtualMemory(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +00001226 /*
1227 Merge two sets.
1228 */
1229 i=0L;
1230 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +00001231 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1232 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001233 {
cristy56ed31c2010-03-22 00:46:21 +00001234#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy699ae5b2013-07-03 13:41:29 +00001235 fourier_pixels[i]=magnitude_pixels[i]*cos(phase_pixels[i])+I*
1236 magnitude_pixels[i]*sin(phase_pixels[i]);
cristy56ed31c2010-03-22 00:46:21 +00001237#else
cristy699ae5b2013-07-03 13:41:29 +00001238 fourier_pixels[i][0]=magnitude_pixels[i]*cos(phase_pixels[i]);
1239 fourier_pixels[i][1]=magnitude_pixels[i]*sin(phase_pixels[i]);
cristy56ed31c2010-03-22 00:46:21 +00001240#endif
cristy3ed852e2009-09-05 21:47:34 +00001241 i++;
1242 }
1243 else
cristybb503372010-05-27 20:51:26 +00001244 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1245 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001246 {
cristy56ed31c2010-03-22 00:46:21 +00001247#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy699ae5b2013-07-03 13:41:29 +00001248 fourier_pixels[i]=magnitude_pixels[i]+I*phase_pixels[i];
cristy56ed31c2010-03-22 00:46:21 +00001249#else
cristy699ae5b2013-07-03 13:41:29 +00001250 fourier_pixels[i][0]=magnitude_pixels[i];
1251 fourier_pixels[i][1]=phase_pixels[i];
cristy56ed31c2010-03-22 00:46:21 +00001252#endif
cristy3ed852e2009-09-05 21:47:34 +00001253 i++;
1254 }
cristy699ae5b2013-07-03 13:41:29 +00001255 magnitude_info=RelinquishVirtualMemory(magnitude_info);
1256 phase_info=RelinquishVirtualMemory(phase_info);
cristy3ed852e2009-09-05 21:47:34 +00001257 return(status);
1258}
1259
1260static MagickBooleanType InverseFourierTransform(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +00001261 fftw_complex *fourier_pixels,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001262{
1263 CacheView
1264 *image_view;
1265
cristy99dc0362013-09-15 18:32:54 +00001266 const char
1267 *value;
1268
cristy3ed852e2009-09-05 21:47:34 +00001269 double
cristy699ae5b2013-07-03 13:41:29 +00001270 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001271
1272 fftw_plan
1273 fftw_c2r_plan;
1274
cristy699ae5b2013-07-03 13:41:29 +00001275 MemoryInfo
1276 *source_info;
1277
cristy4c08aed2011-07-01 19:47:50 +00001278 register Quantum
cristyc4ea4a42011-01-24 01:43:30 +00001279 *q;
1280
cristybb503372010-05-27 20:51:26 +00001281 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001282 i,
1283 x;
1284
cristyc4ea4a42011-01-24 01:43:30 +00001285 ssize_t
1286 y;
cristy3ed852e2009-09-05 21:47:34 +00001287
cristy699ae5b2013-07-03 13:41:29 +00001288 source_info=AcquireVirtualMemory((size_t) fourier_info->height,
1289 fourier_info->width*sizeof(*source_pixels));
1290 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001291 {
1292 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001293 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001294 return(MagickFalse);
1295 }
cristy699ae5b2013-07-03 13:41:29 +00001296 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
cristy99dc0362013-09-15 18:32:54 +00001297 value=GetImageArtifact(image,"fourier:normalize");
cristyf8ecbfd2013-09-15 19:11:14 +00001298 if (LocaleCompare(value,"inverse") == 0)
cristy99dc0362013-09-15 18:32:54 +00001299 {
1300 double
1301 gamma;
1302
1303 /*
1304 Normalize inverse transform.
1305 */
1306 i=0L;
1307 gamma=PerceptibleReciprocal((double) fourier_info->width*
1308 fourier_info->height);
1309 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1310 for (x=0L; x < (ssize_t) fourier_info->center; x++)
1311 {
1312#if defined(MAGICKCORE_HAVE_COMPLEX_H)
1313 fourier_pixels[i]*=gamma;
1314#else
1315 fourier_pixels[i][0]*=gamma;
1316 fourier_pixels[i][1]*=gamma;
1317#endif
1318 i++;
1319 }
1320 }
cristyb5d5f722009-11-04 03:03:49 +00001321#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001322 #pragma omp critical (MagickCore_InverseFourierTransform)
1323#endif
cristydf079ac2010-09-10 01:45:44 +00001324 {
1325 fftw_c2r_plan=fftw_plan_dft_c2r_2d(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +00001326 fourier_pixels,source_pixels,FFTW_ESTIMATE);
cristydf079ac2010-09-10 01:45:44 +00001327 fftw_execute(fftw_c2r_plan);
1328 fftw_destroy_plan(fftw_c2r_plan);
1329 }
cristy3ed852e2009-09-05 21:47:34 +00001330 i=0L;
cristy46ff2672012-12-14 15:32:26 +00001331 image_view=AcquireAuthenticCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +00001332 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001333 {
cristy85812052010-09-14 17:56:15 +00001334 if (y >= (ssize_t) image->rows)
1335 break;
1336 q=GetCacheViewAuthenticPixels(image_view,0L,y,fourier_info->width >
1337 image->columns ? image->columns : fourier_info->width,1UL,exception);
cristyacd2ed22011-08-30 01:44:23 +00001338 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001339 break;
cristybb503372010-05-27 20:51:26 +00001340 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001341 {
cristy233fe582012-07-07 14:00:18 +00001342 if (x < (ssize_t) image->columns)
1343 switch (fourier_info->channel)
cristy3ed852e2009-09-05 21:47:34 +00001344 {
cristy233fe582012-07-07 14:00:18 +00001345 case RedPixelChannel:
1346 default:
1347 {
cristy699ae5b2013-07-03 13:41:29 +00001348 SetPixelRed(image,ClampToQuantum(QuantumRange*source_pixels[i]),q);
cristy233fe582012-07-07 14:00:18 +00001349 break;
1350 }
1351 case GreenPixelChannel:
1352 {
cristy699ae5b2013-07-03 13:41:29 +00001353 SetPixelGreen(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1354 q);
cristy233fe582012-07-07 14:00:18 +00001355 break;
1356 }
1357 case BluePixelChannel:
1358 {
cristy699ae5b2013-07-03 13:41:29 +00001359 SetPixelBlue(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1360 q);
cristy233fe582012-07-07 14:00:18 +00001361 break;
1362 }
1363 case BlackPixelChannel:
1364 {
cristy699ae5b2013-07-03 13:41:29 +00001365 SetPixelBlack(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1366 q);
cristy233fe582012-07-07 14:00:18 +00001367 break;
1368 }
1369 case AlphaPixelChannel:
1370 {
cristy699ae5b2013-07-03 13:41:29 +00001371 SetPixelAlpha(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1372 q);
cristy233fe582012-07-07 14:00:18 +00001373 break;
1374 }
cristy3ed852e2009-09-05 21:47:34 +00001375 }
cristy3ed852e2009-09-05 21:47:34 +00001376 i++;
cristyed231572011-07-14 02:18:59 +00001377 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001378 }
1379 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1380 break;
1381 }
1382 image_view=DestroyCacheView(image_view);
cristy699ae5b2013-07-03 13:41:29 +00001383 source_info=RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +00001384 return(MagickTrue);
1385}
1386
cristyc9550792009-11-13 20:05:42 +00001387static MagickBooleanType InverseFourierTransformChannel(
1388 const Image *magnitude_image,const Image *phase_image,
cristyd3090f92011-10-18 00:05:15 +00001389 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +00001390 Image *fourier_image,ExceptionInfo *exception)
1391{
cristy3ed852e2009-09-05 21:47:34 +00001392 fftw_complex
cristy699ae5b2013-07-03 13:41:29 +00001393 *inverse_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001394
1395 FourierInfo
1396 fourier_info;
1397
1398 MagickBooleanType
1399 status;
1400
cristy699ae5b2013-07-03 13:41:29 +00001401 MemoryInfo
1402 *inverse_info;
1403
cristy3ed852e2009-09-05 21:47:34 +00001404 size_t
1405 extent;
1406
cristyc9550792009-11-13 20:05:42 +00001407 fourier_info.width=magnitude_image->columns;
1408 if ((magnitude_image->columns != magnitude_image->rows) ||
1409 ((magnitude_image->columns % 2) != 0) ||
1410 ((magnitude_image->rows % 2) != 0))
cristy3ed852e2009-09-05 21:47:34 +00001411 {
cristyc9550792009-11-13 20:05:42 +00001412 extent=magnitude_image->columns < magnitude_image->rows ?
1413 magnitude_image->rows : magnitude_image->columns;
cristy3ed852e2009-09-05 21:47:34 +00001414 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
1415 }
1416 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +00001417 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +00001418 fourier_info.channel=channel;
1419 fourier_info.modulus=modulus;
cristy699ae5b2013-07-03 13:41:29 +00001420 inverse_info=AcquireVirtualMemory((size_t) fourier_info.height,
1421 fourier_info.center*sizeof(*inverse_pixels));
1422 if (inverse_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001423 {
1424 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001425 ResourceLimitError,"MemoryAllocationFailed","`%s'",
cristyc9550792009-11-13 20:05:42 +00001426 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001427 return(MagickFalse);
1428 }
cristy699ae5b2013-07-03 13:41:29 +00001429 inverse_pixels=(fftw_complex *) GetVirtualMemoryBlob(inverse_info);
1430 status=InverseFourier(&fourier_info,magnitude_image,phase_image,
1431 inverse_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +00001432 if (status != MagickFalse)
cristy699ae5b2013-07-03 13:41:29 +00001433 status=InverseFourierTransform(&fourier_info,inverse_pixels,fourier_image,
cristy3ed852e2009-09-05 21:47:34 +00001434 exception);
cristy699ae5b2013-07-03 13:41:29 +00001435 inverse_info=RelinquishVirtualMemory(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +00001436 return(status);
1437}
1438#endif
1439
cristyc9550792009-11-13 20:05:42 +00001440MagickExport Image *InverseFourierTransformImage(const Image *magnitude_image,
1441 const Image *phase_image,const MagickBooleanType modulus,
1442 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001443{
1444 Image
1445 *fourier_image;
1446
cristyc9550792009-11-13 20:05:42 +00001447 assert(magnitude_image != (Image *) NULL);
1448 assert(magnitude_image->signature == MagickSignature);
1449 if (magnitude_image->debug != MagickFalse)
1450 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1451 magnitude_image->filename);
1452 if (phase_image == (Image *) NULL)
1453 {
1454 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
cristyefe601c2013-01-05 17:51:12 +00001455 "TwoOrMoreImagesRequired","`%s'",magnitude_image->filename);
cristy9372a152009-11-18 01:42:16 +00001456 return((Image *) NULL);
cristyc9550792009-11-13 20:05:42 +00001457 }
cristy3ed852e2009-09-05 21:47:34 +00001458#if !defined(MAGICKCORE_FFTW_DELEGATE)
1459 fourier_image=(Image *) NULL;
1460 (void) modulus;
1461 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00001462 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristyc9550792009-11-13 20:05:42 +00001463 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001464#else
1465 {
cristyc9550792009-11-13 20:05:42 +00001466 fourier_image=CloneImage(magnitude_image,magnitude_image->columns,
1467 magnitude_image->rows,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00001468 if (fourier_image != (Image *) NULL)
1469 {
1470 MagickBooleanType
1471 is_gray,
1472 status;
1473
cristy3ed852e2009-09-05 21:47:34 +00001474 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001475 is_gray=IsImageGray(magnitude_image,exception);
cristyc9550792009-11-13 20:05:42 +00001476 if (is_gray != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001477 is_gray=IsImageGray(phase_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001478#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb34ef052010-10-07 00:12:05 +00001479 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +00001480#endif
cristy3ed852e2009-09-05 21:47:34 +00001481 {
cristyb34ef052010-10-07 00:12:05 +00001482#if defined(MAGICKCORE_OPENMP_SUPPORT)
1483 #pragma omp section
1484#endif
cristy3ed852e2009-09-05 21:47:34 +00001485 {
cristyb34ef052010-10-07 00:12:05 +00001486 MagickBooleanType
1487 thread_status;
1488
1489 if (is_gray != MagickFalse)
1490 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001491 phase_image,GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001492 else
cristyc9550792009-11-13 20:05:42 +00001493 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001494 phase_image,RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001495 if (thread_status == MagickFalse)
1496 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +00001497 }
cristyb34ef052010-10-07 00:12:05 +00001498#if defined(MAGICKCORE_OPENMP_SUPPORT)
1499 #pragma omp section
1500#endif
1501 {
1502 MagickBooleanType
1503 thread_status;
1504
1505 thread_status=MagickTrue;
1506 if (is_gray == MagickFalse)
1507 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001508 phase_image,GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001509 if (thread_status == MagickFalse)
1510 status=thread_status;
1511 }
1512#if defined(MAGICKCORE_OPENMP_SUPPORT)
1513 #pragma omp section
1514#endif
1515 {
1516 MagickBooleanType
1517 thread_status;
1518
1519 thread_status=MagickTrue;
1520 if (is_gray == MagickFalse)
1521 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001522 phase_image,BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001523 if (thread_status == MagickFalse)
1524 status=thread_status;
1525 }
1526#if defined(MAGICKCORE_OPENMP_SUPPORT)
1527 #pragma omp section
1528#endif
1529 {
1530 MagickBooleanType
1531 thread_status;
1532
1533 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001534 if (magnitude_image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +00001535 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001536 phase_image,BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001537 if (thread_status == MagickFalse)
1538 status=thread_status;
1539 }
1540#if defined(MAGICKCORE_OPENMP_SUPPORT)
1541 #pragma omp section
1542#endif
1543 {
1544 MagickBooleanType
1545 thread_status;
1546
1547 thread_status=MagickTrue;
cristy8a46d822012-08-28 23:32:39 +00001548 if (magnitude_image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +00001549 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001550 phase_image,AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001551 if (thread_status == MagickFalse)
1552 status=thread_status;
1553 }
cristy3ed852e2009-09-05 21:47:34 +00001554 }
1555 if (status == MagickFalse)
1556 fourier_image=DestroyImage(fourier_image);
1557 }
cristyb34ef052010-10-07 00:12:05 +00001558 fftw_cleanup();
cristy3ed852e2009-09-05 21:47:34 +00001559 }
1560#endif
1561 return(fourier_image);
1562}