blob: 6e18dcb5a848d7faa172758e7743a9a8f04fa127 [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 {
245 }
246 if (SyncCacheViewAuthenticPixels(Ci_view,exception) == MagickFalse)
247 status=MagickFalse;
248 if (SyncCacheViewAuthenticPixels(Cr_view,exception) == MagickFalse)
249 status=MagickFalse;
250 if (images->progress_monitor != (MagickProgressMonitor) NULL)
251 {
252 MagickBooleanType
253 proceed;
254
255#if defined(MAGICKCORE_OPENMP_SUPPORT)
256 #pragma omp critical (MagickCore_ComplexImages)
257#endif
258 proceed=SetImageProgress(images,ComplexImageTag,progress++,
259 images->rows);
260 if (proceed == MagickFalse)
261 status=MagickFalse;
262 }
263 }
264 Cr_view=DestroyCacheView(Cr_view);
265 Ci_view=DestroyCacheView(Ci_view);
266 Br_view=DestroyCacheView(Br_view);
267 Bi_view=DestroyCacheView(Bi_view);
268 Ar_view=DestroyCacheView(Ar_view);
269 Ai_view=DestroyCacheView(Ai_view);
270 if (status == MagickFalse)
271 complex_images=DestroyImageList(complex_images);
272 return(complex_images);
cristy790190d2013-10-04 00:51:51 +0000273}
274
275/*
276%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
277% %
278% %
279% %
cristy3ed852e2009-09-05 21:47:34 +0000280% 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 %
281% %
282% %
283% %
284%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
285%
286% ForwardFourierTransformImage() implements the discrete Fourier transform
287% (DFT) of the image either as a magnitude / phase or real / imaginary image
288% pair.
289%
290% The format of the ForwadFourierTransformImage method is:
291%
292% Image *ForwardFourierTransformImage(const Image *image,
293% const MagickBooleanType modulus,ExceptionInfo *exception)
294%
295% A description of each parameter follows:
296%
297% o image: the image.
298%
299% o modulus: if true, return as transform as a magnitude / phase pair
300% otherwise a real / imaginary image pair.
301%
302% o exception: return any errors or warnings in this structure.
303%
304*/
305
306#if defined(MAGICKCORE_FFTW_DELEGATE)
307
cristyc4ea4a42011-01-24 01:43:30 +0000308static MagickBooleanType RollFourier(const size_t width,const size_t height,
cristy699ae5b2013-07-03 13:41:29 +0000309 const ssize_t x_offset,const ssize_t y_offset,double *roll_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000310{
311 double
cristy699ae5b2013-07-03 13:41:29 +0000312 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000313
cristy7d4aa382013-06-30 01:59:39 +0000314 MemoryInfo
cristy699ae5b2013-07-03 13:41:29 +0000315 *source_info;
cristy7d4aa382013-06-30 01:59:39 +0000316
cristyc4ea4a42011-01-24 01:43:30 +0000317 register ssize_t
318 i,
319 x;
320
cristybb503372010-05-27 20:51:26 +0000321 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000322 u,
323 v,
324 y;
325
cristy3ed852e2009-09-05 21:47:34 +0000326 /*
cristy2da05352010-09-15 19:22:17 +0000327 Move zero frequency (DC, average color) from (0,0) to (width/2,height/2).
cristy3ed852e2009-09-05 21:47:34 +0000328 */
cristy699ae5b2013-07-03 13:41:29 +0000329 source_info=AcquireVirtualMemory(height,width*sizeof(*source_pixels));
330 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000331 return(MagickFalse);
cristy699ae5b2013-07-03 13:41:29 +0000332 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000333 i=0L;
cristybb503372010-05-27 20:51:26 +0000334 for (y=0L; y < (ssize_t) height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000335 {
336 if (y_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000337 v=((y+y_offset) < 0L) ? y+y_offset+(ssize_t) height : y+y_offset;
cristy3ed852e2009-09-05 21:47:34 +0000338 else
cristybb503372010-05-27 20:51:26 +0000339 v=((y+y_offset) > ((ssize_t) height-1L)) ? y+y_offset-(ssize_t) height :
cristy3ed852e2009-09-05 21:47:34 +0000340 y+y_offset;
cristybb503372010-05-27 20:51:26 +0000341 for (x=0L; x < (ssize_t) width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000342 {
343 if (x_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000344 u=((x+x_offset) < 0L) ? x+x_offset+(ssize_t) width : x+x_offset;
cristy3ed852e2009-09-05 21:47:34 +0000345 else
cristybb503372010-05-27 20:51:26 +0000346 u=((x+x_offset) > ((ssize_t) width-1L)) ? x+x_offset-(ssize_t) width :
cristy3ed852e2009-09-05 21:47:34 +0000347 x+x_offset;
cristy699ae5b2013-07-03 13:41:29 +0000348 source_pixels[v*width+u]=roll_pixels[i++];
cristyc4ea4a42011-01-24 01:43:30 +0000349 }
cristy3ed852e2009-09-05 21:47:34 +0000350 }
cristy699ae5b2013-07-03 13:41:29 +0000351 (void) CopyMagickMemory(roll_pixels,source_pixels,height*width*
352 sizeof(*source_pixels));
353 source_info=RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000354 return(MagickTrue);
355}
356
cristybb503372010-05-27 20:51:26 +0000357static MagickBooleanType ForwardQuadrantSwap(const size_t width,
cristy699ae5b2013-07-03 13:41:29 +0000358 const size_t height,double *source_pixels,double *forward_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000359{
cristy3ed852e2009-09-05 21:47:34 +0000360 MagickBooleanType
361 status;
362
cristybb503372010-05-27 20:51:26 +0000363 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000364 x;
365
cristyc4ea4a42011-01-24 01:43:30 +0000366 ssize_t
367 center,
368 y;
369
cristy3ed852e2009-09-05 21:47:34 +0000370 /*
371 Swap quadrants.
372 */
cristybb503372010-05-27 20:51:26 +0000373 center=(ssize_t) floor((double) width/2L)+1L;
cristy699ae5b2013-07-03 13:41:29 +0000374 status=RollFourier((size_t) center,height,0L,(ssize_t) height/2L,
375 source_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000376 if (status == MagickFalse)
377 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000378 for (y=0L; y < (ssize_t) height; y++)
379 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy3d04ed72013-08-18 17:51:01 +0000380 forward_pixels[y*width+x+width/2L]=source_pixels[y*center+x];
cristybb503372010-05-27 20:51:26 +0000381 for (y=1; y < (ssize_t) height; y++)
382 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy9b1d6c72013-08-18 17:53:56 +0000383 forward_pixels[(height-y)*width+width/2L-x-1L]=
cristy3d04ed72013-08-18 17:51:01 +0000384 source_pixels[y*center+x+1L];
cristybb503372010-05-27 20:51:26 +0000385 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy699ae5b2013-07-03 13:41:29 +0000386 forward_pixels[-x+width/2L-1L]=forward_pixels[x+width/2L+1L];
cristy3ed852e2009-09-05 21:47:34 +0000387 return(MagickTrue);
388}
389
cristyc4ea4a42011-01-24 01:43:30 +0000390static void CorrectPhaseLHS(const size_t width,const size_t height,
cristy699ae5b2013-07-03 13:41:29 +0000391 double *fourier_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000392{
cristybb503372010-05-27 20:51:26 +0000393 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000394 x;
395
cristy9d314ff2011-03-09 01:30:28 +0000396 ssize_t
397 y;
398
cristybb503372010-05-27 20:51:26 +0000399 for (y=0L; y < (ssize_t) height; y++)
400 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy699ae5b2013-07-03 13:41:29 +0000401 fourier_pixels[y*width+x]*=(-1.0);
cristy3ed852e2009-09-05 21:47:34 +0000402}
403
404static MagickBooleanType ForwardFourier(const FourierInfo *fourier_info,
405 Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
406{
407 CacheView
408 *magnitude_view,
409 *phase_view;
410
411 double
cristy7d4aa382013-06-30 01:59:39 +0000412 *magnitude_pixels,
413 *phase_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000414
415 Image
416 *magnitude_image,
417 *phase_image;
418
cristy3ed852e2009-09-05 21:47:34 +0000419 MagickBooleanType
420 status;
421
cristy7d4aa382013-06-30 01:59:39 +0000422 MemoryInfo
423 *magnitude_info,
424 *phase_info;
425
cristy4c08aed2011-07-01 19:47:50 +0000426 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000427 *q;
428
cristyf5742792012-11-27 18:31:26 +0000429 register ssize_t
430 x;
431
cristyc4ea4a42011-01-24 01:43:30 +0000432 ssize_t
433 i,
434 y;
435
cristy3ed852e2009-09-05 21:47:34 +0000436 magnitude_image=GetFirstImageInList(image);
437 phase_image=GetNextImageInList(image);
438 if (phase_image == (Image *) NULL)
439 {
440 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
cristyefe601c2013-01-05 17:51:12 +0000441 "TwoOrMoreImagesRequired","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000442 return(MagickFalse);
443 }
444 /*
445 Create "Fourier Transform" image from constituent arrays.
446 */
cristy7d4aa382013-06-30 01:59:39 +0000447 magnitude_info=AcquireVirtualMemory((size_t) fourier_info->height,
448 fourier_info->width*sizeof(*magnitude_pixels));
449 phase_info=AcquireVirtualMemory((size_t) fourier_info->height,
450 fourier_info->width*sizeof(*phase_pixels));
cristybb3c02e2013-07-02 00:43:10 +0000451 if ((magnitude_info == (MemoryInfo *) NULL) ||
452 (phase_info == (MemoryInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000453 {
cristy7d4aa382013-06-30 01:59:39 +0000454 if (phase_info != (MemoryInfo *) NULL)
455 phase_info=RelinquishVirtualMemory(phase_info);
456 if (magnitude_info != (MemoryInfo *) NULL)
457 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000458 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000459 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000460 return(MagickFalse);
461 }
cristy7d4aa382013-06-30 01:59:39 +0000462 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
463 (void) ResetMagickMemory(magnitude_pixels,0,fourier_info->height*
464 fourier_info->width*sizeof(*magnitude_pixels));
cristybb3c02e2013-07-02 00:43:10 +0000465 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
cristy7d4aa382013-06-30 01:59:39 +0000466 (void) ResetMagickMemory(phase_pixels,0,fourier_info->height*
467 fourier_info->width*sizeof(*phase_pixels));
cristy13c99c42013-08-18 13:53:30 +0000468 status=ForwardQuadrantSwap(fourier_info->width,fourier_info->height,
cristy7d4aa382013-06-30 01:59:39 +0000469 magnitude,magnitude_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000470 if (status != MagickFalse)
cristy13c99c42013-08-18 13:53:30 +0000471 status=ForwardQuadrantSwap(fourier_info->width,fourier_info->height,phase,
cristy7d4aa382013-06-30 01:59:39 +0000472 phase_pixels);
cristy13c99c42013-08-18 13:53:30 +0000473 CorrectPhaseLHS(fourier_info->width,fourier_info->height,phase_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000474 if (fourier_info->modulus != MagickFalse)
475 {
476 i=0L;
cristybb503372010-05-27 20:51:26 +0000477 for (y=0L; y < (ssize_t) fourier_info->height; y++)
478 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000479 {
cristy7d4aa382013-06-30 01:59:39 +0000480 phase_pixels[i]/=(2.0*MagickPI);
481 phase_pixels[i]+=0.5;
cristy3ed852e2009-09-05 21:47:34 +0000482 i++;
483 }
484 }
cristy46ff2672012-12-14 15:32:26 +0000485 magnitude_view=AcquireAuthenticCacheView(magnitude_image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000486 i=0L;
cristybb503372010-05-27 20:51:26 +0000487 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000488 {
489 q=GetCacheViewAuthenticPixels(magnitude_view,0L,y,fourier_info->height,1UL,
490 exception);
cristyacd2ed22011-08-30 01:44:23 +0000491 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000492 break;
cristybb503372010-05-27 20:51:26 +0000493 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000494 {
495 switch (fourier_info->channel)
496 {
cristyd3090f92011-10-18 00:05:15 +0000497 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000498 default:
499 {
cristy4c08aed2011-07-01 19:47:50 +0000500 SetPixelRed(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000501 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000502 break;
503 }
cristyd3090f92011-10-18 00:05:15 +0000504 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000505 {
cristy4c08aed2011-07-01 19:47:50 +0000506 SetPixelGreen(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000507 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000508 break;
509 }
cristyd3090f92011-10-18 00:05:15 +0000510 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000511 {
cristy4c08aed2011-07-01 19:47:50 +0000512 SetPixelBlue(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000513 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000514 break;
515 }
cristyd3090f92011-10-18 00:05:15 +0000516 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000517 {
cristy4c08aed2011-07-01 19:47:50 +0000518 SetPixelBlack(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000519 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000520 break;
521 }
cristyd3090f92011-10-18 00:05:15 +0000522 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000523 {
cristy4c08aed2011-07-01 19:47:50 +0000524 SetPixelAlpha(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000525 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000526 break;
527 }
cristy3ed852e2009-09-05 21:47:34 +0000528 }
529 i++;
cristyed231572011-07-14 02:18:59 +0000530 q+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +0000531 }
532 status=SyncCacheViewAuthenticPixels(magnitude_view,exception);
533 if (status == MagickFalse)
534 break;
535 }
cristydb070952012-04-20 14:33:00 +0000536 magnitude_view=DestroyCacheView(magnitude_view);
cristy3ed852e2009-09-05 21:47:34 +0000537 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000538 phase_view=AcquireAuthenticCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +0000539 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000540 {
541 q=GetCacheViewAuthenticPixels(phase_view,0L,y,fourier_info->height,1UL,
542 exception);
cristyacd2ed22011-08-30 01:44:23 +0000543 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000544 break;
cristybb503372010-05-27 20:51:26 +0000545 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000546 {
547 switch (fourier_info->channel)
548 {
cristyd3090f92011-10-18 00:05:15 +0000549 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000550 default:
551 {
cristy4c08aed2011-07-01 19:47:50 +0000552 SetPixelRed(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000553 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000554 break;
555 }
cristyd3090f92011-10-18 00:05:15 +0000556 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000557 {
cristy4c08aed2011-07-01 19:47:50 +0000558 SetPixelGreen(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000559 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000560 break;
561 }
cristyd3090f92011-10-18 00:05:15 +0000562 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000563 {
cristy4c08aed2011-07-01 19:47:50 +0000564 SetPixelBlue(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000565 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000566 break;
567 }
cristyd3090f92011-10-18 00:05:15 +0000568 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000569 {
cristy4c08aed2011-07-01 19:47:50 +0000570 SetPixelBlack(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000571 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000572 break;
573 }
cristyd3090f92011-10-18 00:05:15 +0000574 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000575 {
cristy4c08aed2011-07-01 19:47:50 +0000576 SetPixelAlpha(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000577 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000578 break;
579 }
cristy3ed852e2009-09-05 21:47:34 +0000580 }
581 i++;
cristyed231572011-07-14 02:18:59 +0000582 q+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +0000583 }
584 status=SyncCacheViewAuthenticPixels(phase_view,exception);
585 if (status == MagickFalse)
586 break;
587 }
588 phase_view=DestroyCacheView(phase_view);
cristy7d4aa382013-06-30 01:59:39 +0000589 phase_info=RelinquishVirtualMemory(phase_info);
590 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000591 return(status);
592}
593
594static MagickBooleanType ForwardFourierTransform(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +0000595 const Image *image,double *magnitude_pixels,double *phase_pixels,
596 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000597{
598 CacheView
599 *image_view;
600
cristy99dc0362013-09-15 18:32:54 +0000601 const char
602 *value;
603
cristy3ed852e2009-09-05 21:47:34 +0000604 double
cristybb3c02e2013-07-02 00:43:10 +0000605 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000606
607 fftw_complex
cristy699ae5b2013-07-03 13:41:29 +0000608 *forward_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000609
610 fftw_plan
611 fftw_r2c_plan;
612
cristy7d4aa382013-06-30 01:59:39 +0000613 MemoryInfo
cristy699ae5b2013-07-03 13:41:29 +0000614 *forward_info,
cristy7d4aa382013-06-30 01:59:39 +0000615 *source_info;
616
cristy4c08aed2011-07-01 19:47:50 +0000617 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000618 *p;
619
cristybb503372010-05-27 20:51:26 +0000620 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000621 i,
622 x;
623
cristyc4ea4a42011-01-24 01:43:30 +0000624 ssize_t
625 y;
626
cristy3ed852e2009-09-05 21:47:34 +0000627 /*
628 Generate the forward Fourier transform.
629 */
cristy7d4aa382013-06-30 01:59:39 +0000630 source_info=AcquireVirtualMemory((size_t) fourier_info->height,
cristybb3c02e2013-07-02 00:43:10 +0000631 fourier_info->width*sizeof(*source_pixels));
cristy7d4aa382013-06-30 01:59:39 +0000632 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000633 {
634 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000635 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000636 return(MagickFalse);
637 }
cristybb3c02e2013-07-02 00:43:10 +0000638 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
639 ResetMagickMemory(source_pixels,0,fourier_info->height*fourier_info->width*
640 sizeof(*source_pixels));
cristy3ed852e2009-09-05 21:47:34 +0000641 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000642 image_view=AcquireVirtualCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +0000643 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000644 {
645 p=GetCacheViewVirtualPixels(image_view,0L,y,fourier_info->width,1UL,
646 exception);
cristy4c08aed2011-07-01 19:47:50 +0000647 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000648 break;
cristybb503372010-05-27 20:51:26 +0000649 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000650 {
651 switch (fourier_info->channel)
652 {
cristyd3090f92011-10-18 00:05:15 +0000653 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000654 default:
655 {
cristybb3c02e2013-07-02 00:43:10 +0000656 source_pixels[i]=QuantumScale*GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000657 break;
658 }
cristyd3090f92011-10-18 00:05:15 +0000659 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000660 {
cristybb3c02e2013-07-02 00:43:10 +0000661 source_pixels[i]=QuantumScale*GetPixelGreen(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000662 break;
663 }
cristyd3090f92011-10-18 00:05:15 +0000664 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000665 {
cristybb3c02e2013-07-02 00:43:10 +0000666 source_pixels[i]=QuantumScale*GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000667 break;
668 }
cristyd3090f92011-10-18 00:05:15 +0000669 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000670 {
cristybb3c02e2013-07-02 00:43:10 +0000671 source_pixels[i]=QuantumScale*GetPixelBlack(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000672 break;
673 }
cristyd3090f92011-10-18 00:05:15 +0000674 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000675 {
cristybb3c02e2013-07-02 00:43:10 +0000676 source_pixels[i]=QuantumScale*GetPixelAlpha(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000677 break;
678 }
cristy3ed852e2009-09-05 21:47:34 +0000679 }
680 i++;
cristyed231572011-07-14 02:18:59 +0000681 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000682 }
683 }
684 image_view=DestroyCacheView(image_view);
cristy699ae5b2013-07-03 13:41:29 +0000685 forward_info=AcquireVirtualMemory((size_t) fourier_info->height,
686 fourier_info->center*sizeof(*forward_pixels));
687 if (forward_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000688 {
689 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000690 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristybb3c02e2013-07-02 00:43:10 +0000691 source_info=(MemoryInfo *) RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000692 return(MagickFalse);
693 }
cristy699ae5b2013-07-03 13:41:29 +0000694 forward_pixels=(fftw_complex *) GetVirtualMemoryBlob(forward_info);
cristyb5d5f722009-11-04 03:03:49 +0000695#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000696 #pragma omp critical (MagickCore_ForwardFourierTransform)
697#endif
cristy70841a12012-10-27 20:52:57 +0000698 fftw_r2c_plan=fftw_plan_dft_r2c_2d(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +0000699 source_pixels,forward_pixels,FFTW_ESTIMATE);
cristy3ed852e2009-09-05 21:47:34 +0000700 fftw_execute(fftw_r2c_plan);
701 fftw_destroy_plan(fftw_r2c_plan);
cristybb3c02e2013-07-02 00:43:10 +0000702 source_info=(MemoryInfo *) RelinquishVirtualMemory(source_info);
cristy99dc0362013-09-15 18:32:54 +0000703 value=GetImageArtifact(image,"fourier:normalize");
cristyf8ecbfd2013-09-15 19:11:14 +0000704 if ((value == (const char *) NULL) || (LocaleCompare(value,"forward") == 0))
cristy56ed31c2010-03-22 00:46:21 +0000705 {
cristy99dc0362013-09-15 18:32:54 +0000706 double
707 gamma;
708
709 /*
710 Normalize Fourier transform.
711 */
712 i=0L;
713 gamma=PerceptibleReciprocal((double) fourier_info->width*
714 fourier_info->height);
715 for (y=0L; y < (ssize_t) fourier_info->height; y++)
716 for (x=0L; x < (ssize_t) fourier_info->center; x++)
717 {
cristy56ed31c2010-03-22 00:46:21 +0000718#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy99dc0362013-09-15 18:32:54 +0000719 forward_pixels[i]*=gamma;
cristy56ed31c2010-03-22 00:46:21 +0000720#else
cristy99dc0362013-09-15 18:32:54 +0000721 forward_pixels[i][0]*=gamma;
722 forward_pixels[i][1]*=gamma;
cristy56ed31c2010-03-22 00:46:21 +0000723#endif
cristy99dc0362013-09-15 18:32:54 +0000724 i++;
725 }
cristy56ed31c2010-03-22 00:46:21 +0000726 }
cristy3ed852e2009-09-05 21:47:34 +0000727 /*
728 Generate magnitude and phase (or real and imaginary).
729 */
730 i=0L;
731 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000732 for (y=0L; y < (ssize_t) fourier_info->height; y++)
733 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000734 {
cristy699ae5b2013-07-03 13:41:29 +0000735 magnitude_pixels[i]=cabs(forward_pixels[i]);
736 phase_pixels[i]=carg(forward_pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000737 i++;
738 }
739 else
cristybb503372010-05-27 20:51:26 +0000740 for (y=0L; y < (ssize_t) fourier_info->height; y++)
741 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000742 {
cristy699ae5b2013-07-03 13:41:29 +0000743 magnitude_pixels[i]=creal(forward_pixels[i]);
744 phase_pixels[i]=cimag(forward_pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000745 i++;
746 }
cristy699ae5b2013-07-03 13:41:29 +0000747 forward_info=(MemoryInfo *) RelinquishVirtualMemory(forward_info);
cristy3ed852e2009-09-05 21:47:34 +0000748 return(MagickTrue);
749}
750
751static MagickBooleanType ForwardFourierTransformChannel(const Image *image,
cristyd3090f92011-10-18 00:05:15 +0000752 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +0000753 Image *fourier_image,ExceptionInfo *exception)
754{
755 double
cristyce9fe782013-07-03 00:59:41 +0000756 *magnitude_pixels,
757 *phase_pixels;
cristybb3c02e2013-07-02 00:43:10 +0000758
cristy56ed31c2010-03-22 00:46:21 +0000759 FourierInfo
760 fourier_info;
761
cristyc4ea4a42011-01-24 01:43:30 +0000762 MagickBooleanType
763 status;
764
cristyce9fe782013-07-03 00:59:41 +0000765 MemoryInfo
766 *magnitude_info,
767 *phase_info;
768
cristy3ed852e2009-09-05 21:47:34 +0000769 size_t
770 extent;
771
772 fourier_info.width=image->columns;
773 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
774 ((image->rows % 2) != 0))
775 {
776 extent=image->columns < image->rows ? image->rows : image->columns;
777 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
778 }
779 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +0000780 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +0000781 fourier_info.channel=channel;
782 fourier_info.modulus=modulus;
cristyce9fe782013-07-03 00:59:41 +0000783 magnitude_info=AcquireVirtualMemory((size_t) fourier_info.height,
784 fourier_info.center*sizeof(*magnitude_pixels));
785 phase_info=AcquireVirtualMemory((size_t) fourier_info.height,
786 fourier_info.center*sizeof(*phase_pixels));
787 if ((magnitude_info == (MemoryInfo *) NULL) ||
788 (phase_info == (MemoryInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000789 {
cristyce9fe782013-07-03 00:59:41 +0000790 if (phase_info != (MemoryInfo *) NULL)
791 phase_info=RelinquishVirtualMemory(phase_info);
792 if (magnitude_info == (MemoryInfo *) NULL)
793 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000794 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000795 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000796 return(MagickFalse);
797 }
cristyce9fe782013-07-03 00:59:41 +0000798 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
799 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
800 status=ForwardFourierTransform(&fourier_info,image,magnitude_pixels,
801 phase_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +0000802 if (status != MagickFalse)
cristyce9fe782013-07-03 00:59:41 +0000803 status=ForwardFourier(&fourier_info,fourier_image,magnitude_pixels,
804 phase_pixels,exception);
805 phase_info=RelinquishVirtualMemory(phase_info);
806 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000807 return(status);
808}
809#endif
810
811MagickExport Image *ForwardFourierTransformImage(const Image *image,
812 const MagickBooleanType modulus,ExceptionInfo *exception)
813{
814 Image
815 *fourier_image;
816
817 fourier_image=NewImageList();
818#if !defined(MAGICKCORE_FFTW_DELEGATE)
819 (void) modulus;
820 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000821 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristy3ed852e2009-09-05 21:47:34 +0000822 image->filename);
823#else
824 {
825 Image
826 *magnitude_image;
827
cristybb503372010-05-27 20:51:26 +0000828 size_t
cristy3ed852e2009-09-05 21:47:34 +0000829 extent,
830 width;
831
832 width=image->columns;
833 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
834 ((image->rows % 2) != 0))
835 {
836 extent=image->columns < image->rows ? image->rows : image->columns;
837 width=(extent & 0x01) == 1 ? extent+1UL : extent;
838 }
839 magnitude_image=CloneImage(image,width,width,MagickFalse,exception);
840 if (magnitude_image != (Image *) NULL)
841 {
842 Image
843 *phase_image;
844
845 magnitude_image->storage_class=DirectClass;
846 magnitude_image->depth=32UL;
847 phase_image=CloneImage(image,width,width,MagickFalse,exception);
848 if (phase_image == (Image *) NULL)
849 magnitude_image=DestroyImage(magnitude_image);
850 else
851 {
852 MagickBooleanType
853 is_gray,
854 status;
855
cristy3ed852e2009-09-05 21:47:34 +0000856 phase_image->storage_class=DirectClass;
857 phase_image->depth=32UL;
858 AppendImageToList(&fourier_image,magnitude_image);
859 AppendImageToList(&fourier_image,phase_image);
860 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000861 is_gray=IsImageGray(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000862#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy564a5692012-01-20 23:56:26 +0000863 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +0000864#endif
cristy3ed852e2009-09-05 21:47:34 +0000865 {
cristyb34ef052010-10-07 00:12:05 +0000866#if defined(MAGICKCORE_OPENMP_SUPPORT)
867 #pragma omp section
868#endif
cristy3ed852e2009-09-05 21:47:34 +0000869 {
cristyb34ef052010-10-07 00:12:05 +0000870 MagickBooleanType
871 thread_status;
872
873 if (is_gray != MagickFalse)
874 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000875 GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000876 else
877 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000878 RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000879 if (thread_status == MagickFalse)
880 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +0000881 }
cristyb34ef052010-10-07 00:12:05 +0000882#if defined(MAGICKCORE_OPENMP_SUPPORT)
883 #pragma omp section
884#endif
885 {
886 MagickBooleanType
887 thread_status;
888
889 thread_status=MagickTrue;
890 if (is_gray == MagickFalse)
891 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000892 GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000893 if (thread_status == MagickFalse)
894 status=thread_status;
895 }
896#if defined(MAGICKCORE_OPENMP_SUPPORT)
897 #pragma omp section
898#endif
899 {
900 MagickBooleanType
901 thread_status;
902
903 thread_status=MagickTrue;
904 if (is_gray == MagickFalse)
905 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000906 BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000907 if (thread_status == MagickFalse)
908 status=thread_status;
909 }
910#if defined(MAGICKCORE_OPENMP_SUPPORT)
911 #pragma omp section
912#endif
913 {
914 MagickBooleanType
915 thread_status;
916
917 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000918 if (image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +0000919 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000920 BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000921 if (thread_status == MagickFalse)
922 status=thread_status;
923 }
924#if defined(MAGICKCORE_OPENMP_SUPPORT)
925 #pragma omp section
926#endif
927 {
928 MagickBooleanType
929 thread_status;
930
931 thread_status=MagickTrue;
cristy8a46d822012-08-28 23:32:39 +0000932 if (image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +0000933 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000934 AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000935 if (thread_status == MagickFalse)
936 status=thread_status;
937 }
cristy3ed852e2009-09-05 21:47:34 +0000938 }
939 if (status == MagickFalse)
940 fourier_image=DestroyImageList(fourier_image);
941 fftw_cleanup();
942 }
943 }
944 }
945#endif
946 return(fourier_image);
947}
948
949/*
950%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
951% %
952% %
953% %
954% 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 %
955% %
956% %
957% %
958%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
959%
960% InverseFourierTransformImage() implements the inverse discrete Fourier
961% transform (DFT) of the image either as a magnitude / phase or real /
962% imaginary image pair.
963%
964% The format of the InverseFourierTransformImage method is:
965%
cristyc9550792009-11-13 20:05:42 +0000966% Image *InverseFourierTransformImage(const Image *magnitude_image,
967% const Image *phase_image,const MagickBooleanType modulus,
968% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000969%
970% A description of each parameter follows:
971%
cristyc9550792009-11-13 20:05:42 +0000972% o magnitude_image: the magnitude or real image.
973%
974% o phase_image: the phase or imaginary image.
cristy3ed852e2009-09-05 21:47:34 +0000975%
976% o modulus: if true, return transform as a magnitude / phase pair
977% otherwise a real / imaginary image pair.
978%
979% o exception: return any errors or warnings in this structure.
980%
981*/
982
983#if defined(MAGICKCORE_FFTW_DELEGATE)
cristybb503372010-05-27 20:51:26 +0000984static MagickBooleanType InverseQuadrantSwap(const size_t width,
985 const size_t height,const double *source,double *destination)
cristy3ed852e2009-09-05 21:47:34 +0000986{
cristyc4ea4a42011-01-24 01:43:30 +0000987 register ssize_t
988 x;
989
cristybb503372010-05-27 20:51:26 +0000990 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000991 center,
992 y;
993
cristy3ed852e2009-09-05 21:47:34 +0000994 /*
995 Swap quadrants.
996 */
cristy233fe582012-07-07 14:00:18 +0000997 center=(ssize_t) floor((double) width/2L)+1L;
cristybb503372010-05-27 20:51:26 +0000998 for (y=1L; y < (ssize_t) height; y++)
999 for (x=0L; x < (ssize_t) (width/2L+1L); x++)
cristy9b1d6c72013-08-18 17:53:56 +00001000 destination[(height-y)*center-x+width/2L]=source[y*width+x];
cristybb503372010-05-27 20:51:26 +00001001 for (y=0L; y < (ssize_t) height; y++)
cristy3d04ed72013-08-18 17:51:01 +00001002 destination[y*center]=source[y*width+width/2L];
cristy3ed852e2009-09-05 21:47:34 +00001003 for (x=0L; x < center; x++)
1004 destination[x]=source[center-x-1L];
cristybb503372010-05-27 20:51:26 +00001005 return(RollFourier(center,height,0L,(ssize_t) height/-2L,destination));
cristy3ed852e2009-09-05 21:47:34 +00001006}
1007
1008static MagickBooleanType InverseFourier(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +00001009 const Image *magnitude_image,const Image *phase_image,
1010 fftw_complex *fourier_pixels,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001011{
1012 CacheView
1013 *magnitude_view,
1014 *phase_view;
1015
1016 double
cristy699ae5b2013-07-03 13:41:29 +00001017 *inverse_pixels,
cristy7d4aa382013-06-30 01:59:39 +00001018 *magnitude_pixels,
1019 *phase_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001020
cristy3ed852e2009-09-05 21:47:34 +00001021 MagickBooleanType
1022 status;
1023
cristy699ae5b2013-07-03 13:41:29 +00001024 MemoryInfo
1025 *inverse_info,
1026 *magnitude_info,
1027 *phase_info;
1028
cristy4c08aed2011-07-01 19:47:50 +00001029 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001030 *p;
1031
cristybb503372010-05-27 20:51:26 +00001032 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001033 i,
1034 x;
1035
cristyc4ea4a42011-01-24 01:43:30 +00001036 ssize_t
1037 y;
1038
cristy3ed852e2009-09-05 21:47:34 +00001039 /*
1040 Inverse fourier - read image and break down into a double array.
1041 */
cristy699ae5b2013-07-03 13:41:29 +00001042 magnitude_info=AcquireVirtualMemory((size_t)fourier_info->height,
1043 fourier_info->width*sizeof(*magnitude_pixels));
1044 phase_info=AcquireVirtualMemory((size_t) fourier_info->height,
cristybb3c02e2013-07-02 00:43:10 +00001045 fourier_info->width*sizeof(*phase_pixels));
cristy699ae5b2013-07-03 13:41:29 +00001046 inverse_info=AcquireVirtualMemory((size_t) fourier_info->height,
1047 fourier_info->center*sizeof(*inverse_pixels));
1048 if ((magnitude_info == (MemoryInfo *) NULL) ||
1049 (phase_info == (MemoryInfo *) NULL) ||
1050 (inverse_info == (MemoryInfo *) NULL))
cristybb3c02e2013-07-02 00:43:10 +00001051 {
cristy699ae5b2013-07-03 13:41:29 +00001052 if (magnitude_info != (MemoryInfo *) NULL)
1053 magnitude_info=RelinquishVirtualMemory(magnitude_info);
1054 if (phase_info != (MemoryInfo *) NULL)
1055 phase_info=RelinquishVirtualMemory(phase_info);
1056 if (inverse_info != (MemoryInfo *) NULL)
1057 inverse_info=RelinquishVirtualMemory(inverse_info);
cristybb3c02e2013-07-02 00:43:10 +00001058 (void) ThrowMagickException(exception,GetMagickModule(),
1059 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1060 magnitude_image->filename);
cristybb3c02e2013-07-02 00:43:10 +00001061 return(MagickFalse);
1062 }
cristy699ae5b2013-07-03 13:41:29 +00001063 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
1064 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
1065 inverse_pixels=(double *) GetVirtualMemoryBlob(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +00001066 i=0L;
cristy46ff2672012-12-14 15:32:26 +00001067 magnitude_view=AcquireVirtualCacheView(magnitude_image,exception);
cristybb503372010-05-27 20:51:26 +00001068 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001069 {
1070 p=GetCacheViewVirtualPixels(magnitude_view,0L,y,fourier_info->width,1UL,
1071 exception);
cristy4c08aed2011-07-01 19:47:50 +00001072 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001073 break;
cristybb503372010-05-27 20:51:26 +00001074 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001075 {
1076 switch (fourier_info->channel)
1077 {
cristyd3090f92011-10-18 00:05:15 +00001078 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001079 default:
1080 {
cristy7d4aa382013-06-30 01:59:39 +00001081 magnitude_pixels[i]=QuantumScale*GetPixelRed(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001082 break;
1083 }
cristyd3090f92011-10-18 00:05:15 +00001084 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001085 {
cristy7d4aa382013-06-30 01:59:39 +00001086 magnitude_pixels[i]=QuantumScale*GetPixelGreen(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001087 break;
1088 }
cristyd3090f92011-10-18 00:05:15 +00001089 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001090 {
cristy7d4aa382013-06-30 01:59:39 +00001091 magnitude_pixels[i]=QuantumScale*GetPixelBlue(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001092 break;
1093 }
cristyd3090f92011-10-18 00:05:15 +00001094 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001095 {
cristy7d4aa382013-06-30 01:59:39 +00001096 magnitude_pixels[i]=QuantumScale*GetPixelBlack(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001097 break;
1098 }
cristyd3090f92011-10-18 00:05:15 +00001099 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001100 {
cristy7d4aa382013-06-30 01:59:39 +00001101 magnitude_pixels[i]=QuantumScale*GetPixelAlpha(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001102 break;
1103 }
cristy3ed852e2009-09-05 21:47:34 +00001104 }
1105 i++;
cristyed231572011-07-14 02:18:59 +00001106 p+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +00001107 }
1108 }
cristy699ae5b2013-07-03 13:41:29 +00001109 magnitude_view=DestroyCacheView(magnitude_view);
1110 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
1111 magnitude_pixels,inverse_pixels);
1112 (void) CopyMagickMemory(magnitude_pixels,inverse_pixels,fourier_info->height*
1113 fourier_info->center*sizeof(*magnitude_pixels));
cristy3ed852e2009-09-05 21:47:34 +00001114 i=0L;
cristy46ff2672012-12-14 15:32:26 +00001115 phase_view=AcquireVirtualCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +00001116 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001117 {
1118 p=GetCacheViewVirtualPixels(phase_view,0,y,fourier_info->width,1,
1119 exception);
cristy4c08aed2011-07-01 19:47:50 +00001120 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001121 break;
cristybb503372010-05-27 20:51:26 +00001122 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001123 {
1124 switch (fourier_info->channel)
1125 {
cristyd3090f92011-10-18 00:05:15 +00001126 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001127 default:
1128 {
cristy7d4aa382013-06-30 01:59:39 +00001129 phase_pixels[i]=QuantumScale*GetPixelRed(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001130 break;
1131 }
cristyd3090f92011-10-18 00:05:15 +00001132 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001133 {
cristy7d4aa382013-06-30 01:59:39 +00001134 phase_pixels[i]=QuantumScale*GetPixelGreen(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001135 break;
1136 }
cristyd3090f92011-10-18 00:05:15 +00001137 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001138 {
cristy7d4aa382013-06-30 01:59:39 +00001139 phase_pixels[i]=QuantumScale*GetPixelBlue(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001140 break;
1141 }
cristyd3090f92011-10-18 00:05:15 +00001142 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001143 {
cristy7d4aa382013-06-30 01:59:39 +00001144 phase_pixels[i]=QuantumScale*GetPixelBlack(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001145 break;
1146 }
cristyd3090f92011-10-18 00:05:15 +00001147 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001148 {
cristy7d4aa382013-06-30 01:59:39 +00001149 phase_pixels[i]=QuantumScale*GetPixelAlpha(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001150 break;
1151 }
cristy3ed852e2009-09-05 21:47:34 +00001152 }
1153 i++;
cristyed231572011-07-14 02:18:59 +00001154 p+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +00001155 }
1156 }
1157 if (fourier_info->modulus != MagickFalse)
1158 {
1159 i=0L;
cristybb503372010-05-27 20:51:26 +00001160 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1161 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001162 {
cristy7d4aa382013-06-30 01:59:39 +00001163 phase_pixels[i]-=0.5;
1164 phase_pixels[i]*=(2.0*MagickPI);
cristy3ed852e2009-09-05 21:47:34 +00001165 i++;
1166 }
1167 }
cristy3ed852e2009-09-05 21:47:34 +00001168 phase_view=DestroyCacheView(phase_view);
cristy13c99c42013-08-18 13:53:30 +00001169 CorrectPhaseLHS(fourier_info->width,fourier_info->height,phase_pixels);
cristy3ed852e2009-09-05 21:47:34 +00001170 if (status != MagickFalse)
1171 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +00001172 phase_pixels,inverse_pixels);
1173 (void) CopyMagickMemory(phase_pixels,inverse_pixels,fourier_info->height*
1174 fourier_info->center*sizeof(*phase_pixels));
1175 inverse_info=RelinquishVirtualMemory(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +00001176 /*
1177 Merge two sets.
1178 */
1179 i=0L;
1180 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +00001181 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1182 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001183 {
cristy56ed31c2010-03-22 00:46:21 +00001184#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy699ae5b2013-07-03 13:41:29 +00001185 fourier_pixels[i]=magnitude_pixels[i]*cos(phase_pixels[i])+I*
1186 magnitude_pixels[i]*sin(phase_pixels[i]);
cristy56ed31c2010-03-22 00:46:21 +00001187#else
cristy699ae5b2013-07-03 13:41:29 +00001188 fourier_pixels[i][0]=magnitude_pixels[i]*cos(phase_pixels[i]);
1189 fourier_pixels[i][1]=magnitude_pixels[i]*sin(phase_pixels[i]);
cristy56ed31c2010-03-22 00:46:21 +00001190#endif
cristy3ed852e2009-09-05 21:47:34 +00001191 i++;
1192 }
1193 else
cristybb503372010-05-27 20:51:26 +00001194 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1195 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001196 {
cristy56ed31c2010-03-22 00:46:21 +00001197#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy699ae5b2013-07-03 13:41:29 +00001198 fourier_pixels[i]=magnitude_pixels[i]+I*phase_pixels[i];
cristy56ed31c2010-03-22 00:46:21 +00001199#else
cristy699ae5b2013-07-03 13:41:29 +00001200 fourier_pixels[i][0]=magnitude_pixels[i];
1201 fourier_pixels[i][1]=phase_pixels[i];
cristy56ed31c2010-03-22 00:46:21 +00001202#endif
cristy3ed852e2009-09-05 21:47:34 +00001203 i++;
1204 }
cristy699ae5b2013-07-03 13:41:29 +00001205 magnitude_info=RelinquishVirtualMemory(magnitude_info);
1206 phase_info=RelinquishVirtualMemory(phase_info);
cristy3ed852e2009-09-05 21:47:34 +00001207 return(status);
1208}
1209
1210static MagickBooleanType InverseFourierTransform(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +00001211 fftw_complex *fourier_pixels,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001212{
1213 CacheView
1214 *image_view;
1215
cristy99dc0362013-09-15 18:32:54 +00001216 const char
1217 *value;
1218
cristy3ed852e2009-09-05 21:47:34 +00001219 double
cristy699ae5b2013-07-03 13:41:29 +00001220 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001221
1222 fftw_plan
1223 fftw_c2r_plan;
1224
cristy699ae5b2013-07-03 13:41:29 +00001225 MemoryInfo
1226 *source_info;
1227
cristy4c08aed2011-07-01 19:47:50 +00001228 register Quantum
cristyc4ea4a42011-01-24 01:43:30 +00001229 *q;
1230
cristybb503372010-05-27 20:51:26 +00001231 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001232 i,
1233 x;
1234
cristyc4ea4a42011-01-24 01:43:30 +00001235 ssize_t
1236 y;
cristy3ed852e2009-09-05 21:47:34 +00001237
cristy699ae5b2013-07-03 13:41:29 +00001238 source_info=AcquireVirtualMemory((size_t) fourier_info->height,
1239 fourier_info->width*sizeof(*source_pixels));
1240 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001241 {
1242 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001243 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001244 return(MagickFalse);
1245 }
cristy699ae5b2013-07-03 13:41:29 +00001246 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
cristy99dc0362013-09-15 18:32:54 +00001247 value=GetImageArtifact(image,"fourier:normalize");
cristyf8ecbfd2013-09-15 19:11:14 +00001248 if (LocaleCompare(value,"inverse") == 0)
cristy99dc0362013-09-15 18:32:54 +00001249 {
1250 double
1251 gamma;
1252
1253 /*
1254 Normalize inverse transform.
1255 */
1256 i=0L;
1257 gamma=PerceptibleReciprocal((double) fourier_info->width*
1258 fourier_info->height);
1259 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1260 for (x=0L; x < (ssize_t) fourier_info->center; x++)
1261 {
1262#if defined(MAGICKCORE_HAVE_COMPLEX_H)
1263 fourier_pixels[i]*=gamma;
1264#else
1265 fourier_pixels[i][0]*=gamma;
1266 fourier_pixels[i][1]*=gamma;
1267#endif
1268 i++;
1269 }
1270 }
cristyb5d5f722009-11-04 03:03:49 +00001271#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001272 #pragma omp critical (MagickCore_InverseFourierTransform)
1273#endif
cristydf079ac2010-09-10 01:45:44 +00001274 {
1275 fftw_c2r_plan=fftw_plan_dft_c2r_2d(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +00001276 fourier_pixels,source_pixels,FFTW_ESTIMATE);
cristydf079ac2010-09-10 01:45:44 +00001277 fftw_execute(fftw_c2r_plan);
1278 fftw_destroy_plan(fftw_c2r_plan);
1279 }
cristy3ed852e2009-09-05 21:47:34 +00001280 i=0L;
cristy46ff2672012-12-14 15:32:26 +00001281 image_view=AcquireAuthenticCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +00001282 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001283 {
cristy85812052010-09-14 17:56:15 +00001284 if (y >= (ssize_t) image->rows)
1285 break;
1286 q=GetCacheViewAuthenticPixels(image_view,0L,y,fourier_info->width >
1287 image->columns ? image->columns : fourier_info->width,1UL,exception);
cristyacd2ed22011-08-30 01:44:23 +00001288 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001289 break;
cristybb503372010-05-27 20:51:26 +00001290 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001291 {
cristy233fe582012-07-07 14:00:18 +00001292 if (x < (ssize_t) image->columns)
1293 switch (fourier_info->channel)
cristy3ed852e2009-09-05 21:47:34 +00001294 {
cristy233fe582012-07-07 14:00:18 +00001295 case RedPixelChannel:
1296 default:
1297 {
cristy699ae5b2013-07-03 13:41:29 +00001298 SetPixelRed(image,ClampToQuantum(QuantumRange*source_pixels[i]),q);
cristy233fe582012-07-07 14:00:18 +00001299 break;
1300 }
1301 case GreenPixelChannel:
1302 {
cristy699ae5b2013-07-03 13:41:29 +00001303 SetPixelGreen(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1304 q);
cristy233fe582012-07-07 14:00:18 +00001305 break;
1306 }
1307 case BluePixelChannel:
1308 {
cristy699ae5b2013-07-03 13:41:29 +00001309 SetPixelBlue(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1310 q);
cristy233fe582012-07-07 14:00:18 +00001311 break;
1312 }
1313 case BlackPixelChannel:
1314 {
cristy699ae5b2013-07-03 13:41:29 +00001315 SetPixelBlack(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1316 q);
cristy233fe582012-07-07 14:00:18 +00001317 break;
1318 }
1319 case AlphaPixelChannel:
1320 {
cristy699ae5b2013-07-03 13:41:29 +00001321 SetPixelAlpha(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1322 q);
cristy233fe582012-07-07 14:00:18 +00001323 break;
1324 }
cristy3ed852e2009-09-05 21:47:34 +00001325 }
cristy3ed852e2009-09-05 21:47:34 +00001326 i++;
cristyed231572011-07-14 02:18:59 +00001327 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001328 }
1329 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1330 break;
1331 }
1332 image_view=DestroyCacheView(image_view);
cristy699ae5b2013-07-03 13:41:29 +00001333 source_info=RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +00001334 return(MagickTrue);
1335}
1336
cristyc9550792009-11-13 20:05:42 +00001337static MagickBooleanType InverseFourierTransformChannel(
1338 const Image *magnitude_image,const Image *phase_image,
cristyd3090f92011-10-18 00:05:15 +00001339 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +00001340 Image *fourier_image,ExceptionInfo *exception)
1341{
cristy3ed852e2009-09-05 21:47:34 +00001342 fftw_complex
cristy699ae5b2013-07-03 13:41:29 +00001343 *inverse_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001344
1345 FourierInfo
1346 fourier_info;
1347
1348 MagickBooleanType
1349 status;
1350
cristy699ae5b2013-07-03 13:41:29 +00001351 MemoryInfo
1352 *inverse_info;
1353
cristy3ed852e2009-09-05 21:47:34 +00001354 size_t
1355 extent;
1356
cristyc9550792009-11-13 20:05:42 +00001357 fourier_info.width=magnitude_image->columns;
1358 if ((magnitude_image->columns != magnitude_image->rows) ||
1359 ((magnitude_image->columns % 2) != 0) ||
1360 ((magnitude_image->rows % 2) != 0))
cristy3ed852e2009-09-05 21:47:34 +00001361 {
cristyc9550792009-11-13 20:05:42 +00001362 extent=magnitude_image->columns < magnitude_image->rows ?
1363 magnitude_image->rows : magnitude_image->columns;
cristy3ed852e2009-09-05 21:47:34 +00001364 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
1365 }
1366 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +00001367 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +00001368 fourier_info.channel=channel;
1369 fourier_info.modulus=modulus;
cristy699ae5b2013-07-03 13:41:29 +00001370 inverse_info=AcquireVirtualMemory((size_t) fourier_info.height,
1371 fourier_info.center*sizeof(*inverse_pixels));
1372 if (inverse_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001373 {
1374 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001375 ResourceLimitError,"MemoryAllocationFailed","`%s'",
cristyc9550792009-11-13 20:05:42 +00001376 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001377 return(MagickFalse);
1378 }
cristy699ae5b2013-07-03 13:41:29 +00001379 inverse_pixels=(fftw_complex *) GetVirtualMemoryBlob(inverse_info);
1380 status=InverseFourier(&fourier_info,magnitude_image,phase_image,
1381 inverse_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +00001382 if (status != MagickFalse)
cristy699ae5b2013-07-03 13:41:29 +00001383 status=InverseFourierTransform(&fourier_info,inverse_pixels,fourier_image,
cristy3ed852e2009-09-05 21:47:34 +00001384 exception);
cristy699ae5b2013-07-03 13:41:29 +00001385 inverse_info=RelinquishVirtualMemory(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +00001386 return(status);
1387}
1388#endif
1389
cristyc9550792009-11-13 20:05:42 +00001390MagickExport Image *InverseFourierTransformImage(const Image *magnitude_image,
1391 const Image *phase_image,const MagickBooleanType modulus,
1392 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001393{
1394 Image
1395 *fourier_image;
1396
cristyc9550792009-11-13 20:05:42 +00001397 assert(magnitude_image != (Image *) NULL);
1398 assert(magnitude_image->signature == MagickSignature);
1399 if (magnitude_image->debug != MagickFalse)
1400 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1401 magnitude_image->filename);
1402 if (phase_image == (Image *) NULL)
1403 {
1404 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
cristyefe601c2013-01-05 17:51:12 +00001405 "TwoOrMoreImagesRequired","`%s'",magnitude_image->filename);
cristy9372a152009-11-18 01:42:16 +00001406 return((Image *) NULL);
cristyc9550792009-11-13 20:05:42 +00001407 }
cristy3ed852e2009-09-05 21:47:34 +00001408#if !defined(MAGICKCORE_FFTW_DELEGATE)
1409 fourier_image=(Image *) NULL;
1410 (void) modulus;
1411 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00001412 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristyc9550792009-11-13 20:05:42 +00001413 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001414#else
1415 {
cristyc9550792009-11-13 20:05:42 +00001416 fourier_image=CloneImage(magnitude_image,magnitude_image->columns,
1417 magnitude_image->rows,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00001418 if (fourier_image != (Image *) NULL)
1419 {
1420 MagickBooleanType
1421 is_gray,
1422 status;
1423
cristy3ed852e2009-09-05 21:47:34 +00001424 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001425 is_gray=IsImageGray(magnitude_image,exception);
cristyc9550792009-11-13 20:05:42 +00001426 if (is_gray != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001427 is_gray=IsImageGray(phase_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001428#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb34ef052010-10-07 00:12:05 +00001429 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +00001430#endif
cristy3ed852e2009-09-05 21:47:34 +00001431 {
cristyb34ef052010-10-07 00:12:05 +00001432#if defined(MAGICKCORE_OPENMP_SUPPORT)
1433 #pragma omp section
1434#endif
cristy3ed852e2009-09-05 21:47:34 +00001435 {
cristyb34ef052010-10-07 00:12:05 +00001436 MagickBooleanType
1437 thread_status;
1438
1439 if (is_gray != MagickFalse)
1440 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001441 phase_image,GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001442 else
cristyc9550792009-11-13 20:05:42 +00001443 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001444 phase_image,RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001445 if (thread_status == MagickFalse)
1446 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +00001447 }
cristyb34ef052010-10-07 00:12:05 +00001448#if defined(MAGICKCORE_OPENMP_SUPPORT)
1449 #pragma omp section
1450#endif
1451 {
1452 MagickBooleanType
1453 thread_status;
1454
1455 thread_status=MagickTrue;
1456 if (is_gray == MagickFalse)
1457 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001458 phase_image,GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001459 if (thread_status == MagickFalse)
1460 status=thread_status;
1461 }
1462#if defined(MAGICKCORE_OPENMP_SUPPORT)
1463 #pragma omp section
1464#endif
1465 {
1466 MagickBooleanType
1467 thread_status;
1468
1469 thread_status=MagickTrue;
1470 if (is_gray == MagickFalse)
1471 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001472 phase_image,BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001473 if (thread_status == MagickFalse)
1474 status=thread_status;
1475 }
1476#if defined(MAGICKCORE_OPENMP_SUPPORT)
1477 #pragma omp section
1478#endif
1479 {
1480 MagickBooleanType
1481 thread_status;
1482
1483 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001484 if (magnitude_image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +00001485 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001486 phase_image,BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001487 if (thread_status == MagickFalse)
1488 status=thread_status;
1489 }
1490#if defined(MAGICKCORE_OPENMP_SUPPORT)
1491 #pragma omp section
1492#endif
1493 {
1494 MagickBooleanType
1495 thread_status;
1496
1497 thread_status=MagickTrue;
cristy8a46d822012-08-28 23:32:39 +00001498 if (magnitude_image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +00001499 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001500 phase_image,AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001501 if (thread_status == MagickFalse)
1502 status=thread_status;
1503 }
cristy3ed852e2009-09-05 21:47:34 +00001504 }
1505 if (status == MagickFalse)
1506 fourier_image=DestroyImage(fourier_image);
1507 }
cristyb34ef052010-10-07 00:12:05 +00001508 fftw_cleanup();
cristy3ed852e2009-09-05 21:47:34 +00001509 }
1510#endif
1511 return(fourier_image);
1512}