blob: 6d77c7f8e0cda60b4a20be9964cebe8929752d96 [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 {
252 case ConjugateComplexOperator:
253 default:
254 {
255 Cr[i]=Ar[i];
256 Ci[i]=(-Bi[i]);
257 break;
258 }
259 case DivideComplexOperator:
260 {
261 double
262 gamma;
263
264 gamma=PerceptibleReciprocal(Br[i]*Br[i]+Bi[i]*Bi[i]);
265 Cr[i]=gamma*(Ar[i]*Br[i]+Ai[i]*Bi[i]);
266 Ci[i]=gamma*(Ai[i]*Br[i]-Ai[i]*Bi[i]);
267 break;
268 }
269 case MultiplyComplexOperator:
270 {
271 Cr[i]=(Ar[i]*Br[i]+Ai[i]*Bi[i]);
272 Ci[i]=(Ai[i]*Br[i]-Ai[i]*Bi[i]);
273 break;
274 }
275 }
276 }
cristy1042ed22013-10-05 17:38:54 +0000277 }
278 if (SyncCacheViewAuthenticPixels(Ci_view,exception) == MagickFalse)
279 status=MagickFalse;
280 if (SyncCacheViewAuthenticPixels(Cr_view,exception) == MagickFalse)
281 status=MagickFalse;
282 if (images->progress_monitor != (MagickProgressMonitor) NULL)
283 {
284 MagickBooleanType
285 proceed;
286
287#if defined(MAGICKCORE_OPENMP_SUPPORT)
288 #pragma omp critical (MagickCore_ComplexImages)
289#endif
290 proceed=SetImageProgress(images,ComplexImageTag,progress++,
291 images->rows);
292 if (proceed == MagickFalse)
293 status=MagickFalse;
294 }
295 }
296 Cr_view=DestroyCacheView(Cr_view);
297 Ci_view=DestroyCacheView(Ci_view);
298 Br_view=DestroyCacheView(Br_view);
299 Bi_view=DestroyCacheView(Bi_view);
300 Ar_view=DestroyCacheView(Ar_view);
301 Ai_view=DestroyCacheView(Ai_view);
302 if (status == MagickFalse)
303 complex_images=DestroyImageList(complex_images);
304 return(complex_images);
cristy790190d2013-10-04 00:51:51 +0000305}
306
307/*
308%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
309% %
310% %
311% %
cristy3ed852e2009-09-05 21:47:34 +0000312% 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 %
313% %
314% %
315% %
316%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
317%
318% ForwardFourierTransformImage() implements the discrete Fourier transform
319% (DFT) of the image either as a magnitude / phase or real / imaginary image
320% pair.
321%
322% The format of the ForwadFourierTransformImage method is:
323%
324% Image *ForwardFourierTransformImage(const Image *image,
325% const MagickBooleanType modulus,ExceptionInfo *exception)
326%
327% A description of each parameter follows:
328%
329% o image: the image.
330%
331% o modulus: if true, return as transform as a magnitude / phase pair
332% otherwise a real / imaginary image pair.
333%
334% o exception: return any errors or warnings in this structure.
335%
336*/
337
338#if defined(MAGICKCORE_FFTW_DELEGATE)
339
cristyc4ea4a42011-01-24 01:43:30 +0000340static MagickBooleanType RollFourier(const size_t width,const size_t height,
cristy699ae5b2013-07-03 13:41:29 +0000341 const ssize_t x_offset,const ssize_t y_offset,double *roll_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000342{
343 double
cristy699ae5b2013-07-03 13:41:29 +0000344 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000345
cristy7d4aa382013-06-30 01:59:39 +0000346 MemoryInfo
cristy699ae5b2013-07-03 13:41:29 +0000347 *source_info;
cristy7d4aa382013-06-30 01:59:39 +0000348
cristyc4ea4a42011-01-24 01:43:30 +0000349 register ssize_t
350 i,
351 x;
352
cristybb503372010-05-27 20:51:26 +0000353 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000354 u,
355 v,
356 y;
357
cristy3ed852e2009-09-05 21:47:34 +0000358 /*
cristy2da05352010-09-15 19:22:17 +0000359 Move zero frequency (DC, average color) from (0,0) to (width/2,height/2).
cristy3ed852e2009-09-05 21:47:34 +0000360 */
cristy699ae5b2013-07-03 13:41:29 +0000361 source_info=AcquireVirtualMemory(height,width*sizeof(*source_pixels));
362 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000363 return(MagickFalse);
cristy699ae5b2013-07-03 13:41:29 +0000364 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000365 i=0L;
cristybb503372010-05-27 20:51:26 +0000366 for (y=0L; y < (ssize_t) height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000367 {
368 if (y_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000369 v=((y+y_offset) < 0L) ? y+y_offset+(ssize_t) height : y+y_offset;
cristy3ed852e2009-09-05 21:47:34 +0000370 else
cristybb503372010-05-27 20:51:26 +0000371 v=((y+y_offset) > ((ssize_t) height-1L)) ? y+y_offset-(ssize_t) height :
cristy3ed852e2009-09-05 21:47:34 +0000372 y+y_offset;
cristybb503372010-05-27 20:51:26 +0000373 for (x=0L; x < (ssize_t) width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000374 {
375 if (x_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000376 u=((x+x_offset) < 0L) ? x+x_offset+(ssize_t) width : x+x_offset;
cristy3ed852e2009-09-05 21:47:34 +0000377 else
cristybb503372010-05-27 20:51:26 +0000378 u=((x+x_offset) > ((ssize_t) width-1L)) ? x+x_offset-(ssize_t) width :
cristy3ed852e2009-09-05 21:47:34 +0000379 x+x_offset;
cristy699ae5b2013-07-03 13:41:29 +0000380 source_pixels[v*width+u]=roll_pixels[i++];
cristyc4ea4a42011-01-24 01:43:30 +0000381 }
cristy3ed852e2009-09-05 21:47:34 +0000382 }
cristy699ae5b2013-07-03 13:41:29 +0000383 (void) CopyMagickMemory(roll_pixels,source_pixels,height*width*
384 sizeof(*source_pixels));
385 source_info=RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000386 return(MagickTrue);
387}
388
cristybb503372010-05-27 20:51:26 +0000389static MagickBooleanType ForwardQuadrantSwap(const size_t width,
cristy699ae5b2013-07-03 13:41:29 +0000390 const size_t height,double *source_pixels,double *forward_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000391{
cristy3ed852e2009-09-05 21:47:34 +0000392 MagickBooleanType
393 status;
394
cristybb503372010-05-27 20:51:26 +0000395 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000396 x;
397
cristyc4ea4a42011-01-24 01:43:30 +0000398 ssize_t
399 center,
400 y;
401
cristy3ed852e2009-09-05 21:47:34 +0000402 /*
403 Swap quadrants.
404 */
cristybb503372010-05-27 20:51:26 +0000405 center=(ssize_t) floor((double) width/2L)+1L;
cristy699ae5b2013-07-03 13:41:29 +0000406 status=RollFourier((size_t) center,height,0L,(ssize_t) height/2L,
407 source_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000408 if (status == MagickFalse)
409 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000410 for (y=0L; y < (ssize_t) height; y++)
411 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy3d04ed72013-08-18 17:51:01 +0000412 forward_pixels[y*width+x+width/2L]=source_pixels[y*center+x];
cristybb503372010-05-27 20:51:26 +0000413 for (y=1; y < (ssize_t) height; y++)
414 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy9b1d6c72013-08-18 17:53:56 +0000415 forward_pixels[(height-y)*width+width/2L-x-1L]=
cristy3d04ed72013-08-18 17:51:01 +0000416 source_pixels[y*center+x+1L];
cristybb503372010-05-27 20:51:26 +0000417 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy699ae5b2013-07-03 13:41:29 +0000418 forward_pixels[-x+width/2L-1L]=forward_pixels[x+width/2L+1L];
cristy3ed852e2009-09-05 21:47:34 +0000419 return(MagickTrue);
420}
421
cristyc4ea4a42011-01-24 01:43:30 +0000422static void CorrectPhaseLHS(const size_t width,const size_t height,
cristy699ae5b2013-07-03 13:41:29 +0000423 double *fourier_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000424{
cristybb503372010-05-27 20:51:26 +0000425 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000426 x;
427
cristy9d314ff2011-03-09 01:30:28 +0000428 ssize_t
429 y;
430
cristybb503372010-05-27 20:51:26 +0000431 for (y=0L; y < (ssize_t) height; y++)
432 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy699ae5b2013-07-03 13:41:29 +0000433 fourier_pixels[y*width+x]*=(-1.0);
cristy3ed852e2009-09-05 21:47:34 +0000434}
435
436static MagickBooleanType ForwardFourier(const FourierInfo *fourier_info,
437 Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
438{
439 CacheView
440 *magnitude_view,
441 *phase_view;
442
443 double
cristy7d4aa382013-06-30 01:59:39 +0000444 *magnitude_pixels,
445 *phase_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000446
447 Image
448 *magnitude_image,
449 *phase_image;
450
cristy3ed852e2009-09-05 21:47:34 +0000451 MagickBooleanType
452 status;
453
cristy7d4aa382013-06-30 01:59:39 +0000454 MemoryInfo
455 *magnitude_info,
456 *phase_info;
457
cristy4c08aed2011-07-01 19:47:50 +0000458 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000459 *q;
460
cristyf5742792012-11-27 18:31:26 +0000461 register ssize_t
462 x;
463
cristyc4ea4a42011-01-24 01:43:30 +0000464 ssize_t
465 i,
466 y;
467
cristy3ed852e2009-09-05 21:47:34 +0000468 magnitude_image=GetFirstImageInList(image);
469 phase_image=GetNextImageInList(image);
470 if (phase_image == (Image *) NULL)
471 {
472 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
cristyefe601c2013-01-05 17:51:12 +0000473 "TwoOrMoreImagesRequired","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000474 return(MagickFalse);
475 }
476 /*
477 Create "Fourier Transform" image from constituent arrays.
478 */
cristy7d4aa382013-06-30 01:59:39 +0000479 magnitude_info=AcquireVirtualMemory((size_t) fourier_info->height,
480 fourier_info->width*sizeof(*magnitude_pixels));
481 phase_info=AcquireVirtualMemory((size_t) fourier_info->height,
482 fourier_info->width*sizeof(*phase_pixels));
cristybb3c02e2013-07-02 00:43:10 +0000483 if ((magnitude_info == (MemoryInfo *) NULL) ||
484 (phase_info == (MemoryInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000485 {
cristy7d4aa382013-06-30 01:59:39 +0000486 if (phase_info != (MemoryInfo *) NULL)
487 phase_info=RelinquishVirtualMemory(phase_info);
488 if (magnitude_info != (MemoryInfo *) NULL)
489 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000490 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000491 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000492 return(MagickFalse);
493 }
cristy7d4aa382013-06-30 01:59:39 +0000494 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
495 (void) ResetMagickMemory(magnitude_pixels,0,fourier_info->height*
496 fourier_info->width*sizeof(*magnitude_pixels));
cristybb3c02e2013-07-02 00:43:10 +0000497 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
cristy7d4aa382013-06-30 01:59:39 +0000498 (void) ResetMagickMemory(phase_pixels,0,fourier_info->height*
499 fourier_info->width*sizeof(*phase_pixels));
cristy13c99c42013-08-18 13:53:30 +0000500 status=ForwardQuadrantSwap(fourier_info->width,fourier_info->height,
cristy7d4aa382013-06-30 01:59:39 +0000501 magnitude,magnitude_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000502 if (status != MagickFalse)
cristy13c99c42013-08-18 13:53:30 +0000503 status=ForwardQuadrantSwap(fourier_info->width,fourier_info->height,phase,
cristy7d4aa382013-06-30 01:59:39 +0000504 phase_pixels);
cristy13c99c42013-08-18 13:53:30 +0000505 CorrectPhaseLHS(fourier_info->width,fourier_info->height,phase_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000506 if (fourier_info->modulus != MagickFalse)
507 {
508 i=0L;
cristybb503372010-05-27 20:51:26 +0000509 for (y=0L; y < (ssize_t) fourier_info->height; y++)
510 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000511 {
cristy7d4aa382013-06-30 01:59:39 +0000512 phase_pixels[i]/=(2.0*MagickPI);
513 phase_pixels[i]+=0.5;
cristy3ed852e2009-09-05 21:47:34 +0000514 i++;
515 }
516 }
cristy46ff2672012-12-14 15:32:26 +0000517 magnitude_view=AcquireAuthenticCacheView(magnitude_image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000518 i=0L;
cristybb503372010-05-27 20:51:26 +0000519 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000520 {
521 q=GetCacheViewAuthenticPixels(magnitude_view,0L,y,fourier_info->height,1UL,
522 exception);
cristyacd2ed22011-08-30 01:44:23 +0000523 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000524 break;
cristybb503372010-05-27 20:51:26 +0000525 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000526 {
527 switch (fourier_info->channel)
528 {
cristyd3090f92011-10-18 00:05:15 +0000529 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000530 default:
531 {
cristy4c08aed2011-07-01 19:47:50 +0000532 SetPixelRed(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000533 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000534 break;
535 }
cristyd3090f92011-10-18 00:05:15 +0000536 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000537 {
cristy4c08aed2011-07-01 19:47:50 +0000538 SetPixelGreen(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000539 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000540 break;
541 }
cristyd3090f92011-10-18 00:05:15 +0000542 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000543 {
cristy4c08aed2011-07-01 19:47:50 +0000544 SetPixelBlue(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000545 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000546 break;
547 }
cristyd3090f92011-10-18 00:05:15 +0000548 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000549 {
cristy4c08aed2011-07-01 19:47:50 +0000550 SetPixelBlack(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 AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000555 {
cristy4c08aed2011-07-01 19:47:50 +0000556 SetPixelAlpha(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000557 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000558 break;
559 }
cristy3ed852e2009-09-05 21:47:34 +0000560 }
561 i++;
cristyed231572011-07-14 02:18:59 +0000562 q+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +0000563 }
564 status=SyncCacheViewAuthenticPixels(magnitude_view,exception);
565 if (status == MagickFalse)
566 break;
567 }
cristydb070952012-04-20 14:33:00 +0000568 magnitude_view=DestroyCacheView(magnitude_view);
cristy3ed852e2009-09-05 21:47:34 +0000569 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000570 phase_view=AcquireAuthenticCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +0000571 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000572 {
573 q=GetCacheViewAuthenticPixels(phase_view,0L,y,fourier_info->height,1UL,
574 exception);
cristyacd2ed22011-08-30 01:44:23 +0000575 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000576 break;
cristybb503372010-05-27 20:51:26 +0000577 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000578 {
579 switch (fourier_info->channel)
580 {
cristyd3090f92011-10-18 00:05:15 +0000581 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000582 default:
583 {
cristy4c08aed2011-07-01 19:47:50 +0000584 SetPixelRed(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000585 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000586 break;
587 }
cristyd3090f92011-10-18 00:05:15 +0000588 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000589 {
cristy4c08aed2011-07-01 19:47:50 +0000590 SetPixelGreen(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000591 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000592 break;
593 }
cristyd3090f92011-10-18 00:05:15 +0000594 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000595 {
cristy4c08aed2011-07-01 19:47:50 +0000596 SetPixelBlue(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000597 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000598 break;
599 }
cristyd3090f92011-10-18 00:05:15 +0000600 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000601 {
cristy4c08aed2011-07-01 19:47:50 +0000602 SetPixelBlack(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 AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000607 {
cristy4c08aed2011-07-01 19:47:50 +0000608 SetPixelAlpha(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000609 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000610 break;
611 }
cristy3ed852e2009-09-05 21:47:34 +0000612 }
613 i++;
cristyed231572011-07-14 02:18:59 +0000614 q+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +0000615 }
616 status=SyncCacheViewAuthenticPixels(phase_view,exception);
617 if (status == MagickFalse)
618 break;
619 }
620 phase_view=DestroyCacheView(phase_view);
cristy7d4aa382013-06-30 01:59:39 +0000621 phase_info=RelinquishVirtualMemory(phase_info);
622 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000623 return(status);
624}
625
626static MagickBooleanType ForwardFourierTransform(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +0000627 const Image *image,double *magnitude_pixels,double *phase_pixels,
628 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000629{
630 CacheView
631 *image_view;
632
cristy99dc0362013-09-15 18:32:54 +0000633 const char
634 *value;
635
cristy3ed852e2009-09-05 21:47:34 +0000636 double
cristybb3c02e2013-07-02 00:43:10 +0000637 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000638
639 fftw_complex
cristy699ae5b2013-07-03 13:41:29 +0000640 *forward_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000641
642 fftw_plan
643 fftw_r2c_plan;
644
cristy7d4aa382013-06-30 01:59:39 +0000645 MemoryInfo
cristy699ae5b2013-07-03 13:41:29 +0000646 *forward_info,
cristy7d4aa382013-06-30 01:59:39 +0000647 *source_info;
648
cristy4c08aed2011-07-01 19:47:50 +0000649 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000650 *p;
651
cristybb503372010-05-27 20:51:26 +0000652 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000653 i,
654 x;
655
cristyc4ea4a42011-01-24 01:43:30 +0000656 ssize_t
657 y;
658
cristy3ed852e2009-09-05 21:47:34 +0000659 /*
660 Generate the forward Fourier transform.
661 */
cristy7d4aa382013-06-30 01:59:39 +0000662 source_info=AcquireVirtualMemory((size_t) fourier_info->height,
cristybb3c02e2013-07-02 00:43:10 +0000663 fourier_info->width*sizeof(*source_pixels));
cristy7d4aa382013-06-30 01:59:39 +0000664 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000665 {
666 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000667 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000668 return(MagickFalse);
669 }
cristybb3c02e2013-07-02 00:43:10 +0000670 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
671 ResetMagickMemory(source_pixels,0,fourier_info->height*fourier_info->width*
672 sizeof(*source_pixels));
cristy3ed852e2009-09-05 21:47:34 +0000673 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000674 image_view=AcquireVirtualCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +0000675 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000676 {
677 p=GetCacheViewVirtualPixels(image_view,0L,y,fourier_info->width,1UL,
678 exception);
cristy4c08aed2011-07-01 19:47:50 +0000679 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000680 break;
cristybb503372010-05-27 20:51:26 +0000681 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000682 {
683 switch (fourier_info->channel)
684 {
cristyd3090f92011-10-18 00:05:15 +0000685 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000686 default:
687 {
cristybb3c02e2013-07-02 00:43:10 +0000688 source_pixels[i]=QuantumScale*GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000689 break;
690 }
cristyd3090f92011-10-18 00:05:15 +0000691 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000692 {
cristybb3c02e2013-07-02 00:43:10 +0000693 source_pixels[i]=QuantumScale*GetPixelGreen(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000694 break;
695 }
cristyd3090f92011-10-18 00:05:15 +0000696 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000697 {
cristybb3c02e2013-07-02 00:43:10 +0000698 source_pixels[i]=QuantumScale*GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000699 break;
700 }
cristyd3090f92011-10-18 00:05:15 +0000701 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000702 {
cristybb3c02e2013-07-02 00:43:10 +0000703 source_pixels[i]=QuantumScale*GetPixelBlack(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000704 break;
705 }
cristyd3090f92011-10-18 00:05:15 +0000706 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000707 {
cristybb3c02e2013-07-02 00:43:10 +0000708 source_pixels[i]=QuantumScale*GetPixelAlpha(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000709 break;
710 }
cristy3ed852e2009-09-05 21:47:34 +0000711 }
712 i++;
cristyed231572011-07-14 02:18:59 +0000713 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000714 }
715 }
716 image_view=DestroyCacheView(image_view);
cristy699ae5b2013-07-03 13:41:29 +0000717 forward_info=AcquireVirtualMemory((size_t) fourier_info->height,
718 fourier_info->center*sizeof(*forward_pixels));
719 if (forward_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000720 {
721 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000722 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristybb3c02e2013-07-02 00:43:10 +0000723 source_info=(MemoryInfo *) RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000724 return(MagickFalse);
725 }
cristy699ae5b2013-07-03 13:41:29 +0000726 forward_pixels=(fftw_complex *) GetVirtualMemoryBlob(forward_info);
cristyb5d5f722009-11-04 03:03:49 +0000727#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000728 #pragma omp critical (MagickCore_ForwardFourierTransform)
729#endif
cristy70841a12012-10-27 20:52:57 +0000730 fftw_r2c_plan=fftw_plan_dft_r2c_2d(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +0000731 source_pixels,forward_pixels,FFTW_ESTIMATE);
cristy3ed852e2009-09-05 21:47:34 +0000732 fftw_execute(fftw_r2c_plan);
733 fftw_destroy_plan(fftw_r2c_plan);
cristybb3c02e2013-07-02 00:43:10 +0000734 source_info=(MemoryInfo *) RelinquishVirtualMemory(source_info);
cristy99dc0362013-09-15 18:32:54 +0000735 value=GetImageArtifact(image,"fourier:normalize");
cristyf8ecbfd2013-09-15 19:11:14 +0000736 if ((value == (const char *) NULL) || (LocaleCompare(value,"forward") == 0))
cristy56ed31c2010-03-22 00:46:21 +0000737 {
cristy99dc0362013-09-15 18:32:54 +0000738 double
739 gamma;
740
741 /*
742 Normalize Fourier transform.
743 */
744 i=0L;
745 gamma=PerceptibleReciprocal((double) fourier_info->width*
746 fourier_info->height);
747 for (y=0L; y < (ssize_t) fourier_info->height; y++)
748 for (x=0L; x < (ssize_t) fourier_info->center; x++)
749 {
cristy56ed31c2010-03-22 00:46:21 +0000750#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy99dc0362013-09-15 18:32:54 +0000751 forward_pixels[i]*=gamma;
cristy56ed31c2010-03-22 00:46:21 +0000752#else
cristy99dc0362013-09-15 18:32:54 +0000753 forward_pixels[i][0]*=gamma;
754 forward_pixels[i][1]*=gamma;
cristy56ed31c2010-03-22 00:46:21 +0000755#endif
cristy99dc0362013-09-15 18:32:54 +0000756 i++;
757 }
cristy56ed31c2010-03-22 00:46:21 +0000758 }
cristy3ed852e2009-09-05 21:47:34 +0000759 /*
760 Generate magnitude and phase (or real and imaginary).
761 */
762 i=0L;
763 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000764 for (y=0L; y < (ssize_t) fourier_info->height; y++)
765 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000766 {
cristy699ae5b2013-07-03 13:41:29 +0000767 magnitude_pixels[i]=cabs(forward_pixels[i]);
768 phase_pixels[i]=carg(forward_pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000769 i++;
770 }
771 else
cristybb503372010-05-27 20:51:26 +0000772 for (y=0L; y < (ssize_t) fourier_info->height; y++)
773 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000774 {
cristy699ae5b2013-07-03 13:41:29 +0000775 magnitude_pixels[i]=creal(forward_pixels[i]);
776 phase_pixels[i]=cimag(forward_pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000777 i++;
778 }
cristy699ae5b2013-07-03 13:41:29 +0000779 forward_info=(MemoryInfo *) RelinquishVirtualMemory(forward_info);
cristy3ed852e2009-09-05 21:47:34 +0000780 return(MagickTrue);
781}
782
783static MagickBooleanType ForwardFourierTransformChannel(const Image *image,
cristyd3090f92011-10-18 00:05:15 +0000784 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +0000785 Image *fourier_image,ExceptionInfo *exception)
786{
787 double
cristyce9fe782013-07-03 00:59:41 +0000788 *magnitude_pixels,
789 *phase_pixels;
cristybb3c02e2013-07-02 00:43:10 +0000790
cristy56ed31c2010-03-22 00:46:21 +0000791 FourierInfo
792 fourier_info;
793
cristyc4ea4a42011-01-24 01:43:30 +0000794 MagickBooleanType
795 status;
796
cristyce9fe782013-07-03 00:59:41 +0000797 MemoryInfo
798 *magnitude_info,
799 *phase_info;
800
cristy3ed852e2009-09-05 21:47:34 +0000801 size_t
802 extent;
803
804 fourier_info.width=image->columns;
805 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
806 ((image->rows % 2) != 0))
807 {
808 extent=image->columns < image->rows ? image->rows : image->columns;
809 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
810 }
811 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +0000812 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +0000813 fourier_info.channel=channel;
814 fourier_info.modulus=modulus;
cristyce9fe782013-07-03 00:59:41 +0000815 magnitude_info=AcquireVirtualMemory((size_t) fourier_info.height,
816 fourier_info.center*sizeof(*magnitude_pixels));
817 phase_info=AcquireVirtualMemory((size_t) fourier_info.height,
818 fourier_info.center*sizeof(*phase_pixels));
819 if ((magnitude_info == (MemoryInfo *) NULL) ||
820 (phase_info == (MemoryInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000821 {
cristyce9fe782013-07-03 00:59:41 +0000822 if (phase_info != (MemoryInfo *) NULL)
823 phase_info=RelinquishVirtualMemory(phase_info);
824 if (magnitude_info == (MemoryInfo *) NULL)
825 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000826 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000827 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000828 return(MagickFalse);
829 }
cristyce9fe782013-07-03 00:59:41 +0000830 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
831 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
832 status=ForwardFourierTransform(&fourier_info,image,magnitude_pixels,
833 phase_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +0000834 if (status != MagickFalse)
cristyce9fe782013-07-03 00:59:41 +0000835 status=ForwardFourier(&fourier_info,fourier_image,magnitude_pixels,
836 phase_pixels,exception);
837 phase_info=RelinquishVirtualMemory(phase_info);
838 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000839 return(status);
840}
841#endif
842
843MagickExport Image *ForwardFourierTransformImage(const Image *image,
844 const MagickBooleanType modulus,ExceptionInfo *exception)
845{
846 Image
847 *fourier_image;
848
849 fourier_image=NewImageList();
850#if !defined(MAGICKCORE_FFTW_DELEGATE)
851 (void) modulus;
852 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000853 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristy3ed852e2009-09-05 21:47:34 +0000854 image->filename);
855#else
856 {
857 Image
858 *magnitude_image;
859
cristybb503372010-05-27 20:51:26 +0000860 size_t
cristy3ed852e2009-09-05 21:47:34 +0000861 extent,
862 width;
863
864 width=image->columns;
865 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
866 ((image->rows % 2) != 0))
867 {
868 extent=image->columns < image->rows ? image->rows : image->columns;
869 width=(extent & 0x01) == 1 ? extent+1UL : extent;
870 }
871 magnitude_image=CloneImage(image,width,width,MagickFalse,exception);
872 if (magnitude_image != (Image *) NULL)
873 {
874 Image
875 *phase_image;
876
877 magnitude_image->storage_class=DirectClass;
878 magnitude_image->depth=32UL;
879 phase_image=CloneImage(image,width,width,MagickFalse,exception);
880 if (phase_image == (Image *) NULL)
881 magnitude_image=DestroyImage(magnitude_image);
882 else
883 {
884 MagickBooleanType
885 is_gray,
886 status;
887
cristy3ed852e2009-09-05 21:47:34 +0000888 phase_image->storage_class=DirectClass;
889 phase_image->depth=32UL;
890 AppendImageToList(&fourier_image,magnitude_image);
891 AppendImageToList(&fourier_image,phase_image);
892 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000893 is_gray=IsImageGray(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000894#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy564a5692012-01-20 23:56:26 +0000895 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +0000896#endif
cristy3ed852e2009-09-05 21:47:34 +0000897 {
cristyb34ef052010-10-07 00:12:05 +0000898#if defined(MAGICKCORE_OPENMP_SUPPORT)
899 #pragma omp section
900#endif
cristy3ed852e2009-09-05 21:47:34 +0000901 {
cristyb34ef052010-10-07 00:12:05 +0000902 MagickBooleanType
903 thread_status;
904
905 if (is_gray != MagickFalse)
906 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000907 GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000908 else
909 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000910 RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000911 if (thread_status == MagickFalse)
912 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +0000913 }
cristyb34ef052010-10-07 00:12:05 +0000914#if defined(MAGICKCORE_OPENMP_SUPPORT)
915 #pragma omp section
916#endif
917 {
918 MagickBooleanType
919 thread_status;
920
921 thread_status=MagickTrue;
922 if (is_gray == MagickFalse)
923 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000924 GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000925 if (thread_status == MagickFalse)
926 status=thread_status;
927 }
928#if defined(MAGICKCORE_OPENMP_SUPPORT)
929 #pragma omp section
930#endif
931 {
932 MagickBooleanType
933 thread_status;
934
935 thread_status=MagickTrue;
936 if (is_gray == MagickFalse)
937 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000938 BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000939 if (thread_status == MagickFalse)
940 status=thread_status;
941 }
942#if defined(MAGICKCORE_OPENMP_SUPPORT)
943 #pragma omp section
944#endif
945 {
946 MagickBooleanType
947 thread_status;
948
949 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000950 if (image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +0000951 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000952 BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000953 if (thread_status == MagickFalse)
954 status=thread_status;
955 }
956#if defined(MAGICKCORE_OPENMP_SUPPORT)
957 #pragma omp section
958#endif
959 {
960 MagickBooleanType
961 thread_status;
962
963 thread_status=MagickTrue;
cristy8a46d822012-08-28 23:32:39 +0000964 if (image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +0000965 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000966 AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000967 if (thread_status == MagickFalse)
968 status=thread_status;
969 }
cristy3ed852e2009-09-05 21:47:34 +0000970 }
971 if (status == MagickFalse)
972 fourier_image=DestroyImageList(fourier_image);
973 fftw_cleanup();
974 }
975 }
976 }
977#endif
978 return(fourier_image);
979}
980
981/*
982%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
983% %
984% %
985% %
986% 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 %
987% %
988% %
989% %
990%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
991%
992% InverseFourierTransformImage() implements the inverse discrete Fourier
993% transform (DFT) of the image either as a magnitude / phase or real /
994% imaginary image pair.
995%
996% The format of the InverseFourierTransformImage method is:
997%
cristyc9550792009-11-13 20:05:42 +0000998% Image *InverseFourierTransformImage(const Image *magnitude_image,
999% const Image *phase_image,const MagickBooleanType modulus,
1000% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001001%
1002% A description of each parameter follows:
1003%
cristyc9550792009-11-13 20:05:42 +00001004% o magnitude_image: the magnitude or real image.
1005%
1006% o phase_image: the phase or imaginary image.
cristy3ed852e2009-09-05 21:47:34 +00001007%
1008% o modulus: if true, return transform as a magnitude / phase pair
1009% otherwise a real / imaginary image pair.
1010%
1011% o exception: return any errors or warnings in this structure.
1012%
1013*/
1014
1015#if defined(MAGICKCORE_FFTW_DELEGATE)
cristybb503372010-05-27 20:51:26 +00001016static MagickBooleanType InverseQuadrantSwap(const size_t width,
1017 const size_t height,const double *source,double *destination)
cristy3ed852e2009-09-05 21:47:34 +00001018{
cristyc4ea4a42011-01-24 01:43:30 +00001019 register ssize_t
1020 x;
1021
cristybb503372010-05-27 20:51:26 +00001022 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001023 center,
1024 y;
1025
cristy3ed852e2009-09-05 21:47:34 +00001026 /*
1027 Swap quadrants.
1028 */
cristy233fe582012-07-07 14:00:18 +00001029 center=(ssize_t) floor((double) width/2L)+1L;
cristybb503372010-05-27 20:51:26 +00001030 for (y=1L; y < (ssize_t) height; y++)
1031 for (x=0L; x < (ssize_t) (width/2L+1L); x++)
cristy9b1d6c72013-08-18 17:53:56 +00001032 destination[(height-y)*center-x+width/2L]=source[y*width+x];
cristybb503372010-05-27 20:51:26 +00001033 for (y=0L; y < (ssize_t) height; y++)
cristy3d04ed72013-08-18 17:51:01 +00001034 destination[y*center]=source[y*width+width/2L];
cristy3ed852e2009-09-05 21:47:34 +00001035 for (x=0L; x < center; x++)
1036 destination[x]=source[center-x-1L];
cristybb503372010-05-27 20:51:26 +00001037 return(RollFourier(center,height,0L,(ssize_t) height/-2L,destination));
cristy3ed852e2009-09-05 21:47:34 +00001038}
1039
1040static MagickBooleanType InverseFourier(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +00001041 const Image *magnitude_image,const Image *phase_image,
1042 fftw_complex *fourier_pixels,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001043{
1044 CacheView
1045 *magnitude_view,
1046 *phase_view;
1047
1048 double
cristy699ae5b2013-07-03 13:41:29 +00001049 *inverse_pixels,
cristy7d4aa382013-06-30 01:59:39 +00001050 *magnitude_pixels,
1051 *phase_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001052
cristy3ed852e2009-09-05 21:47:34 +00001053 MagickBooleanType
1054 status;
1055
cristy699ae5b2013-07-03 13:41:29 +00001056 MemoryInfo
1057 *inverse_info,
1058 *magnitude_info,
1059 *phase_info;
1060
cristy4c08aed2011-07-01 19:47:50 +00001061 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001062 *p;
1063
cristybb503372010-05-27 20:51:26 +00001064 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001065 i,
1066 x;
1067
cristyc4ea4a42011-01-24 01:43:30 +00001068 ssize_t
1069 y;
1070
cristy3ed852e2009-09-05 21:47:34 +00001071 /*
1072 Inverse fourier - read image and break down into a double array.
1073 */
cristy699ae5b2013-07-03 13:41:29 +00001074 magnitude_info=AcquireVirtualMemory((size_t)fourier_info->height,
1075 fourier_info->width*sizeof(*magnitude_pixels));
1076 phase_info=AcquireVirtualMemory((size_t) fourier_info->height,
cristybb3c02e2013-07-02 00:43:10 +00001077 fourier_info->width*sizeof(*phase_pixels));
cristy699ae5b2013-07-03 13:41:29 +00001078 inverse_info=AcquireVirtualMemory((size_t) fourier_info->height,
1079 fourier_info->center*sizeof(*inverse_pixels));
1080 if ((magnitude_info == (MemoryInfo *) NULL) ||
1081 (phase_info == (MemoryInfo *) NULL) ||
1082 (inverse_info == (MemoryInfo *) NULL))
cristybb3c02e2013-07-02 00:43:10 +00001083 {
cristy699ae5b2013-07-03 13:41:29 +00001084 if (magnitude_info != (MemoryInfo *) NULL)
1085 magnitude_info=RelinquishVirtualMemory(magnitude_info);
1086 if (phase_info != (MemoryInfo *) NULL)
1087 phase_info=RelinquishVirtualMemory(phase_info);
1088 if (inverse_info != (MemoryInfo *) NULL)
1089 inverse_info=RelinquishVirtualMemory(inverse_info);
cristybb3c02e2013-07-02 00:43:10 +00001090 (void) ThrowMagickException(exception,GetMagickModule(),
1091 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1092 magnitude_image->filename);
cristybb3c02e2013-07-02 00:43:10 +00001093 return(MagickFalse);
1094 }
cristy699ae5b2013-07-03 13:41:29 +00001095 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
1096 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
1097 inverse_pixels=(double *) GetVirtualMemoryBlob(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +00001098 i=0L;
cristy46ff2672012-12-14 15:32:26 +00001099 magnitude_view=AcquireVirtualCacheView(magnitude_image,exception);
cristybb503372010-05-27 20:51:26 +00001100 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001101 {
1102 p=GetCacheViewVirtualPixels(magnitude_view,0L,y,fourier_info->width,1UL,
1103 exception);
cristy4c08aed2011-07-01 19:47:50 +00001104 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001105 break;
cristybb503372010-05-27 20:51:26 +00001106 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001107 {
1108 switch (fourier_info->channel)
1109 {
cristyd3090f92011-10-18 00:05:15 +00001110 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001111 default:
1112 {
cristy7d4aa382013-06-30 01:59:39 +00001113 magnitude_pixels[i]=QuantumScale*GetPixelRed(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001114 break;
1115 }
cristyd3090f92011-10-18 00:05:15 +00001116 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001117 {
cristy7d4aa382013-06-30 01:59:39 +00001118 magnitude_pixels[i]=QuantumScale*GetPixelGreen(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001119 break;
1120 }
cristyd3090f92011-10-18 00:05:15 +00001121 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001122 {
cristy7d4aa382013-06-30 01:59:39 +00001123 magnitude_pixels[i]=QuantumScale*GetPixelBlue(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001124 break;
1125 }
cristyd3090f92011-10-18 00:05:15 +00001126 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001127 {
cristy7d4aa382013-06-30 01:59:39 +00001128 magnitude_pixels[i]=QuantumScale*GetPixelBlack(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001129 break;
1130 }
cristyd3090f92011-10-18 00:05:15 +00001131 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001132 {
cristy7d4aa382013-06-30 01:59:39 +00001133 magnitude_pixels[i]=QuantumScale*GetPixelAlpha(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001134 break;
1135 }
cristy3ed852e2009-09-05 21:47:34 +00001136 }
1137 i++;
cristyed231572011-07-14 02:18:59 +00001138 p+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +00001139 }
1140 }
cristy699ae5b2013-07-03 13:41:29 +00001141 magnitude_view=DestroyCacheView(magnitude_view);
1142 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
1143 magnitude_pixels,inverse_pixels);
1144 (void) CopyMagickMemory(magnitude_pixels,inverse_pixels,fourier_info->height*
1145 fourier_info->center*sizeof(*magnitude_pixels));
cristy3ed852e2009-09-05 21:47:34 +00001146 i=0L;
cristy46ff2672012-12-14 15:32:26 +00001147 phase_view=AcquireVirtualCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +00001148 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001149 {
1150 p=GetCacheViewVirtualPixels(phase_view,0,y,fourier_info->width,1,
1151 exception);
cristy4c08aed2011-07-01 19:47:50 +00001152 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001153 break;
cristybb503372010-05-27 20:51:26 +00001154 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001155 {
1156 switch (fourier_info->channel)
1157 {
cristyd3090f92011-10-18 00:05:15 +00001158 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001159 default:
1160 {
cristy7d4aa382013-06-30 01:59:39 +00001161 phase_pixels[i]=QuantumScale*GetPixelRed(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001162 break;
1163 }
cristyd3090f92011-10-18 00:05:15 +00001164 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001165 {
cristy7d4aa382013-06-30 01:59:39 +00001166 phase_pixels[i]=QuantumScale*GetPixelGreen(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001167 break;
1168 }
cristyd3090f92011-10-18 00:05:15 +00001169 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001170 {
cristy7d4aa382013-06-30 01:59:39 +00001171 phase_pixels[i]=QuantumScale*GetPixelBlue(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001172 break;
1173 }
cristyd3090f92011-10-18 00:05:15 +00001174 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001175 {
cristy7d4aa382013-06-30 01:59:39 +00001176 phase_pixels[i]=QuantumScale*GetPixelBlack(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001177 break;
1178 }
cristyd3090f92011-10-18 00:05:15 +00001179 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001180 {
cristy7d4aa382013-06-30 01:59:39 +00001181 phase_pixels[i]=QuantumScale*GetPixelAlpha(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001182 break;
1183 }
cristy3ed852e2009-09-05 21:47:34 +00001184 }
1185 i++;
cristyed231572011-07-14 02:18:59 +00001186 p+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +00001187 }
1188 }
1189 if (fourier_info->modulus != MagickFalse)
1190 {
1191 i=0L;
cristybb503372010-05-27 20:51:26 +00001192 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1193 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001194 {
cristy7d4aa382013-06-30 01:59:39 +00001195 phase_pixels[i]-=0.5;
1196 phase_pixels[i]*=(2.0*MagickPI);
cristy3ed852e2009-09-05 21:47:34 +00001197 i++;
1198 }
1199 }
cristy3ed852e2009-09-05 21:47:34 +00001200 phase_view=DestroyCacheView(phase_view);
cristy13c99c42013-08-18 13:53:30 +00001201 CorrectPhaseLHS(fourier_info->width,fourier_info->height,phase_pixels);
cristy3ed852e2009-09-05 21:47:34 +00001202 if (status != MagickFalse)
1203 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +00001204 phase_pixels,inverse_pixels);
1205 (void) CopyMagickMemory(phase_pixels,inverse_pixels,fourier_info->height*
1206 fourier_info->center*sizeof(*phase_pixels));
1207 inverse_info=RelinquishVirtualMemory(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +00001208 /*
1209 Merge two sets.
1210 */
1211 i=0L;
1212 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +00001213 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1214 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001215 {
cristy56ed31c2010-03-22 00:46:21 +00001216#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy699ae5b2013-07-03 13:41:29 +00001217 fourier_pixels[i]=magnitude_pixels[i]*cos(phase_pixels[i])+I*
1218 magnitude_pixels[i]*sin(phase_pixels[i]);
cristy56ed31c2010-03-22 00:46:21 +00001219#else
cristy699ae5b2013-07-03 13:41:29 +00001220 fourier_pixels[i][0]=magnitude_pixels[i]*cos(phase_pixels[i]);
1221 fourier_pixels[i][1]=magnitude_pixels[i]*sin(phase_pixels[i]);
cristy56ed31c2010-03-22 00:46:21 +00001222#endif
cristy3ed852e2009-09-05 21:47:34 +00001223 i++;
1224 }
1225 else
cristybb503372010-05-27 20:51:26 +00001226 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1227 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001228 {
cristy56ed31c2010-03-22 00:46:21 +00001229#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy699ae5b2013-07-03 13:41:29 +00001230 fourier_pixels[i]=magnitude_pixels[i]+I*phase_pixels[i];
cristy56ed31c2010-03-22 00:46:21 +00001231#else
cristy699ae5b2013-07-03 13:41:29 +00001232 fourier_pixels[i][0]=magnitude_pixels[i];
1233 fourier_pixels[i][1]=phase_pixels[i];
cristy56ed31c2010-03-22 00:46:21 +00001234#endif
cristy3ed852e2009-09-05 21:47:34 +00001235 i++;
1236 }
cristy699ae5b2013-07-03 13:41:29 +00001237 magnitude_info=RelinquishVirtualMemory(magnitude_info);
1238 phase_info=RelinquishVirtualMemory(phase_info);
cristy3ed852e2009-09-05 21:47:34 +00001239 return(status);
1240}
1241
1242static MagickBooleanType InverseFourierTransform(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +00001243 fftw_complex *fourier_pixels,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001244{
1245 CacheView
1246 *image_view;
1247
cristy99dc0362013-09-15 18:32:54 +00001248 const char
1249 *value;
1250
cristy3ed852e2009-09-05 21:47:34 +00001251 double
cristy699ae5b2013-07-03 13:41:29 +00001252 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001253
1254 fftw_plan
1255 fftw_c2r_plan;
1256
cristy699ae5b2013-07-03 13:41:29 +00001257 MemoryInfo
1258 *source_info;
1259
cristy4c08aed2011-07-01 19:47:50 +00001260 register Quantum
cristyc4ea4a42011-01-24 01:43:30 +00001261 *q;
1262
cristybb503372010-05-27 20:51:26 +00001263 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001264 i,
1265 x;
1266
cristyc4ea4a42011-01-24 01:43:30 +00001267 ssize_t
1268 y;
cristy3ed852e2009-09-05 21:47:34 +00001269
cristy699ae5b2013-07-03 13:41:29 +00001270 source_info=AcquireVirtualMemory((size_t) fourier_info->height,
1271 fourier_info->width*sizeof(*source_pixels));
1272 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001273 {
1274 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001275 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001276 return(MagickFalse);
1277 }
cristy699ae5b2013-07-03 13:41:29 +00001278 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
cristy99dc0362013-09-15 18:32:54 +00001279 value=GetImageArtifact(image,"fourier:normalize");
cristyf8ecbfd2013-09-15 19:11:14 +00001280 if (LocaleCompare(value,"inverse") == 0)
cristy99dc0362013-09-15 18:32:54 +00001281 {
1282 double
1283 gamma;
1284
1285 /*
1286 Normalize inverse transform.
1287 */
1288 i=0L;
1289 gamma=PerceptibleReciprocal((double) fourier_info->width*
1290 fourier_info->height);
1291 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1292 for (x=0L; x < (ssize_t) fourier_info->center; x++)
1293 {
1294#if defined(MAGICKCORE_HAVE_COMPLEX_H)
1295 fourier_pixels[i]*=gamma;
1296#else
1297 fourier_pixels[i][0]*=gamma;
1298 fourier_pixels[i][1]*=gamma;
1299#endif
1300 i++;
1301 }
1302 }
cristyb5d5f722009-11-04 03:03:49 +00001303#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001304 #pragma omp critical (MagickCore_InverseFourierTransform)
1305#endif
cristydf079ac2010-09-10 01:45:44 +00001306 {
1307 fftw_c2r_plan=fftw_plan_dft_c2r_2d(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +00001308 fourier_pixels,source_pixels,FFTW_ESTIMATE);
cristydf079ac2010-09-10 01:45:44 +00001309 fftw_execute(fftw_c2r_plan);
1310 fftw_destroy_plan(fftw_c2r_plan);
1311 }
cristy3ed852e2009-09-05 21:47:34 +00001312 i=0L;
cristy46ff2672012-12-14 15:32:26 +00001313 image_view=AcquireAuthenticCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +00001314 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001315 {
cristy85812052010-09-14 17:56:15 +00001316 if (y >= (ssize_t) image->rows)
1317 break;
1318 q=GetCacheViewAuthenticPixels(image_view,0L,y,fourier_info->width >
1319 image->columns ? image->columns : fourier_info->width,1UL,exception);
cristyacd2ed22011-08-30 01:44:23 +00001320 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001321 break;
cristybb503372010-05-27 20:51:26 +00001322 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001323 {
cristy233fe582012-07-07 14:00:18 +00001324 if (x < (ssize_t) image->columns)
1325 switch (fourier_info->channel)
cristy3ed852e2009-09-05 21:47:34 +00001326 {
cristy233fe582012-07-07 14:00:18 +00001327 case RedPixelChannel:
1328 default:
1329 {
cristy699ae5b2013-07-03 13:41:29 +00001330 SetPixelRed(image,ClampToQuantum(QuantumRange*source_pixels[i]),q);
cristy233fe582012-07-07 14:00:18 +00001331 break;
1332 }
1333 case GreenPixelChannel:
1334 {
cristy699ae5b2013-07-03 13:41:29 +00001335 SetPixelGreen(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1336 q);
cristy233fe582012-07-07 14:00:18 +00001337 break;
1338 }
1339 case BluePixelChannel:
1340 {
cristy699ae5b2013-07-03 13:41:29 +00001341 SetPixelBlue(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1342 q);
cristy233fe582012-07-07 14:00:18 +00001343 break;
1344 }
1345 case BlackPixelChannel:
1346 {
cristy699ae5b2013-07-03 13:41:29 +00001347 SetPixelBlack(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1348 q);
cristy233fe582012-07-07 14:00:18 +00001349 break;
1350 }
1351 case AlphaPixelChannel:
1352 {
cristy699ae5b2013-07-03 13:41:29 +00001353 SetPixelAlpha(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1354 q);
cristy233fe582012-07-07 14:00:18 +00001355 break;
1356 }
cristy3ed852e2009-09-05 21:47:34 +00001357 }
cristy3ed852e2009-09-05 21:47:34 +00001358 i++;
cristyed231572011-07-14 02:18:59 +00001359 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001360 }
1361 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1362 break;
1363 }
1364 image_view=DestroyCacheView(image_view);
cristy699ae5b2013-07-03 13:41:29 +00001365 source_info=RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +00001366 return(MagickTrue);
1367}
1368
cristyc9550792009-11-13 20:05:42 +00001369static MagickBooleanType InverseFourierTransformChannel(
1370 const Image *magnitude_image,const Image *phase_image,
cristyd3090f92011-10-18 00:05:15 +00001371 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +00001372 Image *fourier_image,ExceptionInfo *exception)
1373{
cristy3ed852e2009-09-05 21:47:34 +00001374 fftw_complex
cristy699ae5b2013-07-03 13:41:29 +00001375 *inverse_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001376
1377 FourierInfo
1378 fourier_info;
1379
1380 MagickBooleanType
1381 status;
1382
cristy699ae5b2013-07-03 13:41:29 +00001383 MemoryInfo
1384 *inverse_info;
1385
cristy3ed852e2009-09-05 21:47:34 +00001386 size_t
1387 extent;
1388
cristyc9550792009-11-13 20:05:42 +00001389 fourier_info.width=magnitude_image->columns;
1390 if ((magnitude_image->columns != magnitude_image->rows) ||
1391 ((magnitude_image->columns % 2) != 0) ||
1392 ((magnitude_image->rows % 2) != 0))
cristy3ed852e2009-09-05 21:47:34 +00001393 {
cristyc9550792009-11-13 20:05:42 +00001394 extent=magnitude_image->columns < magnitude_image->rows ?
1395 magnitude_image->rows : magnitude_image->columns;
cristy3ed852e2009-09-05 21:47:34 +00001396 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
1397 }
1398 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +00001399 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +00001400 fourier_info.channel=channel;
1401 fourier_info.modulus=modulus;
cristy699ae5b2013-07-03 13:41:29 +00001402 inverse_info=AcquireVirtualMemory((size_t) fourier_info.height,
1403 fourier_info.center*sizeof(*inverse_pixels));
1404 if (inverse_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001405 {
1406 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001407 ResourceLimitError,"MemoryAllocationFailed","`%s'",
cristyc9550792009-11-13 20:05:42 +00001408 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001409 return(MagickFalse);
1410 }
cristy699ae5b2013-07-03 13:41:29 +00001411 inverse_pixels=(fftw_complex *) GetVirtualMemoryBlob(inverse_info);
1412 status=InverseFourier(&fourier_info,magnitude_image,phase_image,
1413 inverse_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +00001414 if (status != MagickFalse)
cristy699ae5b2013-07-03 13:41:29 +00001415 status=InverseFourierTransform(&fourier_info,inverse_pixels,fourier_image,
cristy3ed852e2009-09-05 21:47:34 +00001416 exception);
cristy699ae5b2013-07-03 13:41:29 +00001417 inverse_info=RelinquishVirtualMemory(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +00001418 return(status);
1419}
1420#endif
1421
cristyc9550792009-11-13 20:05:42 +00001422MagickExport Image *InverseFourierTransformImage(const Image *magnitude_image,
1423 const Image *phase_image,const MagickBooleanType modulus,
1424 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001425{
1426 Image
1427 *fourier_image;
1428
cristyc9550792009-11-13 20:05:42 +00001429 assert(magnitude_image != (Image *) NULL);
1430 assert(magnitude_image->signature == MagickSignature);
1431 if (magnitude_image->debug != MagickFalse)
1432 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1433 magnitude_image->filename);
1434 if (phase_image == (Image *) NULL)
1435 {
1436 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
cristyefe601c2013-01-05 17:51:12 +00001437 "TwoOrMoreImagesRequired","`%s'",magnitude_image->filename);
cristy9372a152009-11-18 01:42:16 +00001438 return((Image *) NULL);
cristyc9550792009-11-13 20:05:42 +00001439 }
cristy3ed852e2009-09-05 21:47:34 +00001440#if !defined(MAGICKCORE_FFTW_DELEGATE)
1441 fourier_image=(Image *) NULL;
1442 (void) modulus;
1443 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00001444 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristyc9550792009-11-13 20:05:42 +00001445 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001446#else
1447 {
cristyc9550792009-11-13 20:05:42 +00001448 fourier_image=CloneImage(magnitude_image,magnitude_image->columns,
1449 magnitude_image->rows,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00001450 if (fourier_image != (Image *) NULL)
1451 {
1452 MagickBooleanType
1453 is_gray,
1454 status;
1455
cristy3ed852e2009-09-05 21:47:34 +00001456 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001457 is_gray=IsImageGray(magnitude_image,exception);
cristyc9550792009-11-13 20:05:42 +00001458 if (is_gray != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001459 is_gray=IsImageGray(phase_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001460#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb34ef052010-10-07 00:12:05 +00001461 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +00001462#endif
cristy3ed852e2009-09-05 21:47:34 +00001463 {
cristyb34ef052010-10-07 00:12:05 +00001464#if defined(MAGICKCORE_OPENMP_SUPPORT)
1465 #pragma omp section
1466#endif
cristy3ed852e2009-09-05 21:47:34 +00001467 {
cristyb34ef052010-10-07 00:12:05 +00001468 MagickBooleanType
1469 thread_status;
1470
1471 if (is_gray != MagickFalse)
1472 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001473 phase_image,GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001474 else
cristyc9550792009-11-13 20:05:42 +00001475 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001476 phase_image,RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001477 if (thread_status == MagickFalse)
1478 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +00001479 }
cristyb34ef052010-10-07 00:12:05 +00001480#if defined(MAGICKCORE_OPENMP_SUPPORT)
1481 #pragma omp section
1482#endif
1483 {
1484 MagickBooleanType
1485 thread_status;
1486
1487 thread_status=MagickTrue;
1488 if (is_gray == MagickFalse)
1489 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001490 phase_image,GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001491 if (thread_status == MagickFalse)
1492 status=thread_status;
1493 }
1494#if defined(MAGICKCORE_OPENMP_SUPPORT)
1495 #pragma omp section
1496#endif
1497 {
1498 MagickBooleanType
1499 thread_status;
1500
1501 thread_status=MagickTrue;
1502 if (is_gray == MagickFalse)
1503 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001504 phase_image,BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001505 if (thread_status == MagickFalse)
1506 status=thread_status;
1507 }
1508#if defined(MAGICKCORE_OPENMP_SUPPORT)
1509 #pragma omp section
1510#endif
1511 {
1512 MagickBooleanType
1513 thread_status;
1514
1515 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001516 if (magnitude_image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +00001517 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001518 phase_image,BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001519 if (thread_status == MagickFalse)
1520 status=thread_status;
1521 }
1522#if defined(MAGICKCORE_OPENMP_SUPPORT)
1523 #pragma omp section
1524#endif
1525 {
1526 MagickBooleanType
1527 thread_status;
1528
1529 thread_status=MagickTrue;
cristy8a46d822012-08-28 23:32:39 +00001530 if (magnitude_image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +00001531 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001532 phase_image,AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001533 if (thread_status == MagickFalse)
1534 status=thread_status;
1535 }
cristy3ed852e2009-09-05 21:47:34 +00001536 }
1537 if (status == MagickFalse)
1538 fourier_image=DestroyImage(fourier_image);
1539 }
cristyb34ef052010-10-07 00:12:05 +00001540 fftw_cleanup();
cristy3ed852e2009-09-05 21:47:34 +00001541 }
1542#endif
1543 return(fourier_image);
1544}