blob: 9ad798a41159ba044fceaaca65b3f48dabfa6524 [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,
cristy34919ed2013-10-06 15:42:40 +0000151 *Cr_image,
152 *image;
cristy790190d2013-10-04 00:51:51 +0000153
154 MagickBooleanType
155 status;
156
157 MagickOffsetType
158 progress;
159
cristy790190d2013-10-04 00:51:51 +0000160 ssize_t
161 y;
162
163 assert(images != (Image *) NULL);
164 assert(images->signature == MagickSignature);
165 if (images->debug != MagickFalse)
166 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
167 assert(exception != (ExceptionInfo *) NULL);
168 assert(exception->signature == MagickSignature);
cristy1042ed22013-10-05 17:38:54 +0000169 if (images->next == (Image *) NULL)
170 {
171 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
172 "ImageSequenceRequired","`%s'",images->filename);
173 return((Image *) NULL);
174 }
cristy34919ed2013-10-06 15:42:40 +0000175 image=CloneImage(images,images->columns,images->rows,MagickTrue,exception);
176 if (image == (Image *) NULL)
cristy1042ed22013-10-05 17:38:54 +0000177 return((Image *) NULL);
cristy34919ed2013-10-06 15:42:40 +0000178 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
179 {
180 image=DestroyImageList(image);
181 return(image);
182 }
183 complex_images=NewImageList();
184 AppendImageToList(&complex_images,image);
185 image=CloneImage(images,images->columns,images->rows,MagickTrue,exception);
186 if (image == (Image *) NULL)
cristy1042ed22013-10-05 17:38:54 +0000187 {
188 complex_images=DestroyImageList(complex_images);
189 return(complex_images);
190 }
cristy34919ed2013-10-06 15:42:40 +0000191 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
192 {
193 complex_images=DestroyImageList(complex_images);
194 return(complex_images);
195 }
196 AppendImageToList(&complex_images,image);
cristy1042ed22013-10-05 17:38:54 +0000197 /*
198 Apply complex mathematics to image pixels.
199 */
200 Ar_image=images;
201 Ai_image=images->next;
202 if ((images->next->next == (Image *) NULL) ||
203 (images->next->next->next == (Image *) NULL))
204 {
205 Br_image=images;
206 Bi_image=images->next;
207 }
208 else
209 {
210 Br_image=images->next->next;
211 Bi_image=images->next->next->next;
212 }
213 Cr_image=complex_images;
214 Ci_image=complex_images->next;
215 Ar_view=AcquireVirtualCacheView(Ar_image,exception);
216 Ai_view=AcquireVirtualCacheView(Ai_image,exception);
217 Br_view=AcquireVirtualCacheView(Br_image,exception);
218 Bi_view=AcquireVirtualCacheView(Bi_image,exception);
219 Cr_view=AcquireAuthenticCacheView(Cr_image,exception);
220 Ci_view=AcquireAuthenticCacheView(Ci_image,exception);
221 status=MagickTrue;
222 progress=0;
223#if defined(MAGICKCORE_OPENMP_SUPPORT)
224 #pragma omp parallel for schedule(static,4) shared(progress,status) \
225 magick_threads(images,complex_images,images->rows,1L)
226#endif
227 for (y=0; y < (ssize_t) images->rows; y++)
228 {
229 register const Quantum
230 *restrict Ai,
231 *restrict Ar,
232 *restrict Bi,
233 *restrict Br;
234
235 register Quantum
236 *restrict Ci,
237 *restrict Cr;
238
239 register ssize_t
240 x;
241
cristy34919ed2013-10-06 15:42:40 +0000242 if (status == MagickFalse)
243 continue;
cristy1042ed22013-10-05 17:38:54 +0000244 Ar=GetCacheViewVirtualPixels(Ar_view,0,y,images->columns,1,exception);
245 Ai=GetCacheViewVirtualPixels(Ai_view,0,y,images->columns,1,exception);
246 Br=GetCacheViewVirtualPixels(Br_view,0,y,images->columns,1,exception);
247 Bi=GetCacheViewVirtualPixels(Bi_view,0,y,images->columns,1,exception);
248 Cr=QueueCacheViewAuthenticPixels(Cr_view,0,y,images->columns,1,exception);
249 Ci=QueueCacheViewAuthenticPixels(Ci_view,0,y,images->columns,1,exception);
250 if ((Ar == (const Quantum *) NULL) || (Ai == (const Quantum *) NULL) ||
251 (Br == (const Quantum *) NULL) || (Bi == (const Quantum *) NULL) ||
252 (Cr == (Quantum *) NULL) || (Ci == (Quantum *) NULL))
253 {
254 status=MagickFalse;
255 continue;
256 }
257 for (x=0; x < (ssize_t) images->columns; x++)
258 {
cristy9f654472013-10-05 19:44:06 +0000259 register ssize_t
260 i;
261
262 for (i=0; i < (ssize_t) GetPixelChannels(images); i++)
263 {
264 switch (operator)
265 {
cristy19f78862013-10-05 22:21:46 +0000266 case AddComplexOperator:
267 {
268 Cr[i]=Ar[i]+Br[i];
269 Ci[i]=Ai[i]+Bi[i];
270 break;
271 }
cristy9f654472013-10-05 19:44:06 +0000272 case ConjugateComplexOperator:
273 default:
274 {
275 Cr[i]=Ar[i];
276 Ci[i]=(-Bi[i]);
277 break;
278 }
279 case DivideComplexOperator:
280 {
281 double
282 gamma;
283
284 gamma=PerceptibleReciprocal(Br[i]*Br[i]+Bi[i]*Bi[i]);
285 Cr[i]=gamma*(Ar[i]*Br[i]+Ai[i]*Bi[i]);
286 Ci[i]=gamma*(Ai[i]*Br[i]-Ai[i]*Bi[i]);
287 break;
288 }
289 case MultiplyComplexOperator:
290 {
291 Cr[i]=(Ar[i]*Br[i]+Ai[i]*Bi[i]);
292 Ci[i]=(Ai[i]*Br[i]-Ai[i]*Bi[i]);
293 break;
294 }
cristy19f78862013-10-05 22:21:46 +0000295 case SubtractComplexOperator:
296 {
297 Cr[i]=Ar[i]-Br[i];
298 Ci[i]=Ai[i]-Bi[i];
299 break;
300 }
cristy9f654472013-10-05 19:44:06 +0000301 }
cristy19f78862013-10-05 22:21:46 +0000302 Ar+=GetPixelChannels(Ar_image);
303 Ai+=GetPixelChannels(Ai_image);
304 Br+=GetPixelChannels(Br_image);
305 Bi+=GetPixelChannels(Bi_image);
306 Cr+=GetPixelChannels(Cr_image);
307 Ci+=GetPixelChannels(Ci_image);
cristy9f654472013-10-05 19:44:06 +0000308 }
cristy1042ed22013-10-05 17:38:54 +0000309 }
310 if (SyncCacheViewAuthenticPixels(Ci_view,exception) == MagickFalse)
311 status=MagickFalse;
312 if (SyncCacheViewAuthenticPixels(Cr_view,exception) == MagickFalse)
313 status=MagickFalse;
314 if (images->progress_monitor != (MagickProgressMonitor) NULL)
315 {
316 MagickBooleanType
317 proceed;
318
319#if defined(MAGICKCORE_OPENMP_SUPPORT)
320 #pragma omp critical (MagickCore_ComplexImages)
321#endif
322 proceed=SetImageProgress(images,ComplexImageTag,progress++,
323 images->rows);
324 if (proceed == MagickFalse)
325 status=MagickFalse;
326 }
327 }
328 Cr_view=DestroyCacheView(Cr_view);
329 Ci_view=DestroyCacheView(Ci_view);
330 Br_view=DestroyCacheView(Br_view);
331 Bi_view=DestroyCacheView(Bi_view);
332 Ar_view=DestroyCacheView(Ar_view);
333 Ai_view=DestroyCacheView(Ai_view);
334 if (status == MagickFalse)
335 complex_images=DestroyImageList(complex_images);
336 return(complex_images);
cristy790190d2013-10-04 00:51:51 +0000337}
338
339/*
340%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
341% %
342% %
343% %
cristy3ed852e2009-09-05 21:47:34 +0000344% 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 %
345% %
346% %
347% %
348%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
349%
350% ForwardFourierTransformImage() implements the discrete Fourier transform
351% (DFT) of the image either as a magnitude / phase or real / imaginary image
352% pair.
353%
354% The format of the ForwadFourierTransformImage method is:
355%
356% Image *ForwardFourierTransformImage(const Image *image,
357% const MagickBooleanType modulus,ExceptionInfo *exception)
358%
359% A description of each parameter follows:
360%
361% o image: the image.
362%
363% o modulus: if true, return as transform as a magnitude / phase pair
364% otherwise a real / imaginary image pair.
365%
366% o exception: return any errors or warnings in this structure.
367%
368*/
369
370#if defined(MAGICKCORE_FFTW_DELEGATE)
371
cristyc4ea4a42011-01-24 01:43:30 +0000372static MagickBooleanType RollFourier(const size_t width,const size_t height,
cristy699ae5b2013-07-03 13:41:29 +0000373 const ssize_t x_offset,const ssize_t y_offset,double *roll_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000374{
375 double
cristy699ae5b2013-07-03 13:41:29 +0000376 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000377
cristy7d4aa382013-06-30 01:59:39 +0000378 MemoryInfo
cristy699ae5b2013-07-03 13:41:29 +0000379 *source_info;
cristy7d4aa382013-06-30 01:59:39 +0000380
cristyc4ea4a42011-01-24 01:43:30 +0000381 register ssize_t
382 i,
383 x;
384
cristybb503372010-05-27 20:51:26 +0000385 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000386 u,
387 v,
388 y;
389
cristy3ed852e2009-09-05 21:47:34 +0000390 /*
cristy2da05352010-09-15 19:22:17 +0000391 Move zero frequency (DC, average color) from (0,0) to (width/2,height/2).
cristy3ed852e2009-09-05 21:47:34 +0000392 */
cristy699ae5b2013-07-03 13:41:29 +0000393 source_info=AcquireVirtualMemory(height,width*sizeof(*source_pixels));
394 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000395 return(MagickFalse);
cristy699ae5b2013-07-03 13:41:29 +0000396 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000397 i=0L;
cristybb503372010-05-27 20:51:26 +0000398 for (y=0L; y < (ssize_t) height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000399 {
400 if (y_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000401 v=((y+y_offset) < 0L) ? y+y_offset+(ssize_t) height : y+y_offset;
cristy3ed852e2009-09-05 21:47:34 +0000402 else
cristybb503372010-05-27 20:51:26 +0000403 v=((y+y_offset) > ((ssize_t) height-1L)) ? y+y_offset-(ssize_t) height :
cristy3ed852e2009-09-05 21:47:34 +0000404 y+y_offset;
cristybb503372010-05-27 20:51:26 +0000405 for (x=0L; x < (ssize_t) width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000406 {
407 if (x_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000408 u=((x+x_offset) < 0L) ? x+x_offset+(ssize_t) width : x+x_offset;
cristy3ed852e2009-09-05 21:47:34 +0000409 else
cristybb503372010-05-27 20:51:26 +0000410 u=((x+x_offset) > ((ssize_t) width-1L)) ? x+x_offset-(ssize_t) width :
cristy3ed852e2009-09-05 21:47:34 +0000411 x+x_offset;
cristy699ae5b2013-07-03 13:41:29 +0000412 source_pixels[v*width+u]=roll_pixels[i++];
cristyc4ea4a42011-01-24 01:43:30 +0000413 }
cristy3ed852e2009-09-05 21:47:34 +0000414 }
cristy699ae5b2013-07-03 13:41:29 +0000415 (void) CopyMagickMemory(roll_pixels,source_pixels,height*width*
416 sizeof(*source_pixels));
417 source_info=RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000418 return(MagickTrue);
419}
420
cristybb503372010-05-27 20:51:26 +0000421static MagickBooleanType ForwardQuadrantSwap(const size_t width,
cristy699ae5b2013-07-03 13:41:29 +0000422 const size_t height,double *source_pixels,double *forward_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000423{
cristy3ed852e2009-09-05 21:47:34 +0000424 MagickBooleanType
425 status;
426
cristybb503372010-05-27 20:51:26 +0000427 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000428 x;
429
cristyc4ea4a42011-01-24 01:43:30 +0000430 ssize_t
431 center,
432 y;
433
cristy3ed852e2009-09-05 21:47:34 +0000434 /*
435 Swap quadrants.
436 */
cristybb503372010-05-27 20:51:26 +0000437 center=(ssize_t) floor((double) width/2L)+1L;
cristy699ae5b2013-07-03 13:41:29 +0000438 status=RollFourier((size_t) center,height,0L,(ssize_t) height/2L,
439 source_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000440 if (status == MagickFalse)
441 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000442 for (y=0L; y < (ssize_t) height; y++)
443 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy3d04ed72013-08-18 17:51:01 +0000444 forward_pixels[y*width+x+width/2L]=source_pixels[y*center+x];
cristybb503372010-05-27 20:51:26 +0000445 for (y=1; y < (ssize_t) height; y++)
446 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy9b1d6c72013-08-18 17:53:56 +0000447 forward_pixels[(height-y)*width+width/2L-x-1L]=
cristy3d04ed72013-08-18 17:51:01 +0000448 source_pixels[y*center+x+1L];
cristybb503372010-05-27 20:51:26 +0000449 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy699ae5b2013-07-03 13:41:29 +0000450 forward_pixels[-x+width/2L-1L]=forward_pixels[x+width/2L+1L];
cristy3ed852e2009-09-05 21:47:34 +0000451 return(MagickTrue);
452}
453
cristyc4ea4a42011-01-24 01:43:30 +0000454static void CorrectPhaseLHS(const size_t width,const size_t height,
cristy699ae5b2013-07-03 13:41:29 +0000455 double *fourier_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000456{
cristybb503372010-05-27 20:51:26 +0000457 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000458 x;
459
cristy9d314ff2011-03-09 01:30:28 +0000460 ssize_t
461 y;
462
cristybb503372010-05-27 20:51:26 +0000463 for (y=0L; y < (ssize_t) height; y++)
464 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy699ae5b2013-07-03 13:41:29 +0000465 fourier_pixels[y*width+x]*=(-1.0);
cristy3ed852e2009-09-05 21:47:34 +0000466}
467
468static MagickBooleanType ForwardFourier(const FourierInfo *fourier_info,
469 Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
470{
471 CacheView
472 *magnitude_view,
473 *phase_view;
474
475 double
cristy7d4aa382013-06-30 01:59:39 +0000476 *magnitude_pixels,
477 *phase_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000478
479 Image
480 *magnitude_image,
481 *phase_image;
482
cristy3ed852e2009-09-05 21:47:34 +0000483 MagickBooleanType
484 status;
485
cristy7d4aa382013-06-30 01:59:39 +0000486 MemoryInfo
487 *magnitude_info,
488 *phase_info;
489
cristy4c08aed2011-07-01 19:47:50 +0000490 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000491 *q;
492
cristyf5742792012-11-27 18:31:26 +0000493 register ssize_t
494 x;
495
cristyc4ea4a42011-01-24 01:43:30 +0000496 ssize_t
497 i,
498 y;
499
cristy3ed852e2009-09-05 21:47:34 +0000500 magnitude_image=GetFirstImageInList(image);
501 phase_image=GetNextImageInList(image);
502 if (phase_image == (Image *) NULL)
503 {
504 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
cristyefe601c2013-01-05 17:51:12 +0000505 "TwoOrMoreImagesRequired","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000506 return(MagickFalse);
507 }
508 /*
509 Create "Fourier Transform" image from constituent arrays.
510 */
cristy7d4aa382013-06-30 01:59:39 +0000511 magnitude_info=AcquireVirtualMemory((size_t) fourier_info->height,
512 fourier_info->width*sizeof(*magnitude_pixels));
513 phase_info=AcquireVirtualMemory((size_t) fourier_info->height,
514 fourier_info->width*sizeof(*phase_pixels));
cristybb3c02e2013-07-02 00:43:10 +0000515 if ((magnitude_info == (MemoryInfo *) NULL) ||
516 (phase_info == (MemoryInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000517 {
cristy7d4aa382013-06-30 01:59:39 +0000518 if (phase_info != (MemoryInfo *) NULL)
519 phase_info=RelinquishVirtualMemory(phase_info);
520 if (magnitude_info != (MemoryInfo *) NULL)
521 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000522 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000523 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000524 return(MagickFalse);
525 }
cristy7d4aa382013-06-30 01:59:39 +0000526 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
527 (void) ResetMagickMemory(magnitude_pixels,0,fourier_info->height*
528 fourier_info->width*sizeof(*magnitude_pixels));
cristybb3c02e2013-07-02 00:43:10 +0000529 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
cristy7d4aa382013-06-30 01:59:39 +0000530 (void) ResetMagickMemory(phase_pixels,0,fourier_info->height*
531 fourier_info->width*sizeof(*phase_pixels));
cristy13c99c42013-08-18 13:53:30 +0000532 status=ForwardQuadrantSwap(fourier_info->width,fourier_info->height,
cristy7d4aa382013-06-30 01:59:39 +0000533 magnitude,magnitude_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000534 if (status != MagickFalse)
cristy13c99c42013-08-18 13:53:30 +0000535 status=ForwardQuadrantSwap(fourier_info->width,fourier_info->height,phase,
cristy7d4aa382013-06-30 01:59:39 +0000536 phase_pixels);
cristy13c99c42013-08-18 13:53:30 +0000537 CorrectPhaseLHS(fourier_info->width,fourier_info->height,phase_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000538 if (fourier_info->modulus != MagickFalse)
539 {
540 i=0L;
cristybb503372010-05-27 20:51:26 +0000541 for (y=0L; y < (ssize_t) fourier_info->height; y++)
542 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000543 {
cristy7d4aa382013-06-30 01:59:39 +0000544 phase_pixels[i]/=(2.0*MagickPI);
545 phase_pixels[i]+=0.5;
cristy3ed852e2009-09-05 21:47:34 +0000546 i++;
547 }
548 }
cristy46ff2672012-12-14 15:32:26 +0000549 magnitude_view=AcquireAuthenticCacheView(magnitude_image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000550 i=0L;
cristybb503372010-05-27 20:51:26 +0000551 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000552 {
553 q=GetCacheViewAuthenticPixels(magnitude_view,0L,y,fourier_info->height,1UL,
554 exception);
cristyacd2ed22011-08-30 01:44:23 +0000555 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000556 break;
cristybb503372010-05-27 20:51:26 +0000557 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000558 {
559 switch (fourier_info->channel)
560 {
cristyd3090f92011-10-18 00:05:15 +0000561 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000562 default:
563 {
cristy4c08aed2011-07-01 19:47:50 +0000564 SetPixelRed(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000565 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000566 break;
567 }
cristyd3090f92011-10-18 00:05:15 +0000568 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000569 {
cristy4c08aed2011-07-01 19:47:50 +0000570 SetPixelGreen(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000571 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000572 break;
573 }
cristyd3090f92011-10-18 00:05:15 +0000574 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000575 {
cristy4c08aed2011-07-01 19:47:50 +0000576 SetPixelBlue(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000577 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000578 break;
579 }
cristyd3090f92011-10-18 00:05:15 +0000580 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000581 {
cristy4c08aed2011-07-01 19:47:50 +0000582 SetPixelBlack(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000583 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000584 break;
585 }
cristyd3090f92011-10-18 00:05:15 +0000586 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000587 {
cristy4c08aed2011-07-01 19:47:50 +0000588 SetPixelAlpha(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000589 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000590 break;
591 }
cristy3ed852e2009-09-05 21:47:34 +0000592 }
593 i++;
cristyed231572011-07-14 02:18:59 +0000594 q+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +0000595 }
596 status=SyncCacheViewAuthenticPixels(magnitude_view,exception);
597 if (status == MagickFalse)
598 break;
599 }
cristydb070952012-04-20 14:33:00 +0000600 magnitude_view=DestroyCacheView(magnitude_view);
cristy3ed852e2009-09-05 21:47:34 +0000601 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000602 phase_view=AcquireAuthenticCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +0000603 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000604 {
605 q=GetCacheViewAuthenticPixels(phase_view,0L,y,fourier_info->height,1UL,
606 exception);
cristyacd2ed22011-08-30 01:44:23 +0000607 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000608 break;
cristybb503372010-05-27 20:51:26 +0000609 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000610 {
611 switch (fourier_info->channel)
612 {
cristyd3090f92011-10-18 00:05:15 +0000613 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000614 default:
615 {
cristy4c08aed2011-07-01 19:47:50 +0000616 SetPixelRed(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000617 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000618 break;
619 }
cristyd3090f92011-10-18 00:05:15 +0000620 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000621 {
cristy4c08aed2011-07-01 19:47:50 +0000622 SetPixelGreen(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000623 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000624 break;
625 }
cristyd3090f92011-10-18 00:05:15 +0000626 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000627 {
cristy4c08aed2011-07-01 19:47:50 +0000628 SetPixelBlue(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000629 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000630 break;
631 }
cristyd3090f92011-10-18 00:05:15 +0000632 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000633 {
cristy4c08aed2011-07-01 19:47:50 +0000634 SetPixelBlack(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000635 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000636 break;
637 }
cristyd3090f92011-10-18 00:05:15 +0000638 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000639 {
cristy4c08aed2011-07-01 19:47:50 +0000640 SetPixelAlpha(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000641 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000642 break;
643 }
cristy3ed852e2009-09-05 21:47:34 +0000644 }
645 i++;
cristyed231572011-07-14 02:18:59 +0000646 q+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +0000647 }
648 status=SyncCacheViewAuthenticPixels(phase_view,exception);
649 if (status == MagickFalse)
650 break;
651 }
652 phase_view=DestroyCacheView(phase_view);
cristy7d4aa382013-06-30 01:59:39 +0000653 phase_info=RelinquishVirtualMemory(phase_info);
654 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000655 return(status);
656}
657
658static MagickBooleanType ForwardFourierTransform(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +0000659 const Image *image,double *magnitude_pixels,double *phase_pixels,
660 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000661{
662 CacheView
663 *image_view;
664
cristy99dc0362013-09-15 18:32:54 +0000665 const char
666 *value;
667
cristy3ed852e2009-09-05 21:47:34 +0000668 double
cristybb3c02e2013-07-02 00:43:10 +0000669 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000670
671 fftw_complex
cristy699ae5b2013-07-03 13:41:29 +0000672 *forward_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000673
674 fftw_plan
675 fftw_r2c_plan;
676
cristy7d4aa382013-06-30 01:59:39 +0000677 MemoryInfo
cristy699ae5b2013-07-03 13:41:29 +0000678 *forward_info,
cristy7d4aa382013-06-30 01:59:39 +0000679 *source_info;
680
cristy4c08aed2011-07-01 19:47:50 +0000681 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000682 *p;
683
cristybb503372010-05-27 20:51:26 +0000684 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000685 i,
686 x;
687
cristyc4ea4a42011-01-24 01:43:30 +0000688 ssize_t
689 y;
690
cristy3ed852e2009-09-05 21:47:34 +0000691 /*
692 Generate the forward Fourier transform.
693 */
cristy7d4aa382013-06-30 01:59:39 +0000694 source_info=AcquireVirtualMemory((size_t) fourier_info->height,
cristybb3c02e2013-07-02 00:43:10 +0000695 fourier_info->width*sizeof(*source_pixels));
cristy7d4aa382013-06-30 01:59:39 +0000696 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000697 {
698 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000699 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000700 return(MagickFalse);
701 }
cristybb3c02e2013-07-02 00:43:10 +0000702 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
703 ResetMagickMemory(source_pixels,0,fourier_info->height*fourier_info->width*
704 sizeof(*source_pixels));
cristy3ed852e2009-09-05 21:47:34 +0000705 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000706 image_view=AcquireVirtualCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +0000707 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000708 {
709 p=GetCacheViewVirtualPixels(image_view,0L,y,fourier_info->width,1UL,
710 exception);
cristy4c08aed2011-07-01 19:47:50 +0000711 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000712 break;
cristybb503372010-05-27 20:51:26 +0000713 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000714 {
715 switch (fourier_info->channel)
716 {
cristyd3090f92011-10-18 00:05:15 +0000717 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000718 default:
719 {
cristybb3c02e2013-07-02 00:43:10 +0000720 source_pixels[i]=QuantumScale*GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000721 break;
722 }
cristyd3090f92011-10-18 00:05:15 +0000723 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000724 {
cristybb3c02e2013-07-02 00:43:10 +0000725 source_pixels[i]=QuantumScale*GetPixelGreen(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000726 break;
727 }
cristyd3090f92011-10-18 00:05:15 +0000728 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000729 {
cristybb3c02e2013-07-02 00:43:10 +0000730 source_pixels[i]=QuantumScale*GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000731 break;
732 }
cristyd3090f92011-10-18 00:05:15 +0000733 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000734 {
cristybb3c02e2013-07-02 00:43:10 +0000735 source_pixels[i]=QuantumScale*GetPixelBlack(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000736 break;
737 }
cristyd3090f92011-10-18 00:05:15 +0000738 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000739 {
cristybb3c02e2013-07-02 00:43:10 +0000740 source_pixels[i]=QuantumScale*GetPixelAlpha(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000741 break;
742 }
cristy3ed852e2009-09-05 21:47:34 +0000743 }
744 i++;
cristyed231572011-07-14 02:18:59 +0000745 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000746 }
747 }
748 image_view=DestroyCacheView(image_view);
cristy699ae5b2013-07-03 13:41:29 +0000749 forward_info=AcquireVirtualMemory((size_t) fourier_info->height,
750 fourier_info->center*sizeof(*forward_pixels));
751 if (forward_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000752 {
753 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000754 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristybb3c02e2013-07-02 00:43:10 +0000755 source_info=(MemoryInfo *) RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000756 return(MagickFalse);
757 }
cristy699ae5b2013-07-03 13:41:29 +0000758 forward_pixels=(fftw_complex *) GetVirtualMemoryBlob(forward_info);
cristyb5d5f722009-11-04 03:03:49 +0000759#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000760 #pragma omp critical (MagickCore_ForwardFourierTransform)
761#endif
cristy70841a12012-10-27 20:52:57 +0000762 fftw_r2c_plan=fftw_plan_dft_r2c_2d(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +0000763 source_pixels,forward_pixels,FFTW_ESTIMATE);
cristy3ed852e2009-09-05 21:47:34 +0000764 fftw_execute(fftw_r2c_plan);
765 fftw_destroy_plan(fftw_r2c_plan);
cristybb3c02e2013-07-02 00:43:10 +0000766 source_info=(MemoryInfo *) RelinquishVirtualMemory(source_info);
cristy99dc0362013-09-15 18:32:54 +0000767 value=GetImageArtifact(image,"fourier:normalize");
cristyf8ecbfd2013-09-15 19:11:14 +0000768 if ((value == (const char *) NULL) || (LocaleCompare(value,"forward") == 0))
cristy56ed31c2010-03-22 00:46:21 +0000769 {
cristy99dc0362013-09-15 18:32:54 +0000770 double
771 gamma;
772
773 /*
774 Normalize Fourier transform.
775 */
776 i=0L;
777 gamma=PerceptibleReciprocal((double) fourier_info->width*
778 fourier_info->height);
779 for (y=0L; y < (ssize_t) fourier_info->height; y++)
780 for (x=0L; x < (ssize_t) fourier_info->center; x++)
781 {
cristy56ed31c2010-03-22 00:46:21 +0000782#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy99dc0362013-09-15 18:32:54 +0000783 forward_pixels[i]*=gamma;
cristy56ed31c2010-03-22 00:46:21 +0000784#else
cristy99dc0362013-09-15 18:32:54 +0000785 forward_pixels[i][0]*=gamma;
786 forward_pixels[i][1]*=gamma;
cristy56ed31c2010-03-22 00:46:21 +0000787#endif
cristy99dc0362013-09-15 18:32:54 +0000788 i++;
789 }
cristy56ed31c2010-03-22 00:46:21 +0000790 }
cristy3ed852e2009-09-05 21:47:34 +0000791 /*
792 Generate magnitude and phase (or real and imaginary).
793 */
794 i=0L;
795 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000796 for (y=0L; y < (ssize_t) fourier_info->height; y++)
797 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000798 {
cristy699ae5b2013-07-03 13:41:29 +0000799 magnitude_pixels[i]=cabs(forward_pixels[i]);
800 phase_pixels[i]=carg(forward_pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000801 i++;
802 }
803 else
cristybb503372010-05-27 20:51:26 +0000804 for (y=0L; y < (ssize_t) fourier_info->height; y++)
805 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000806 {
cristy699ae5b2013-07-03 13:41:29 +0000807 magnitude_pixels[i]=creal(forward_pixels[i]);
808 phase_pixels[i]=cimag(forward_pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000809 i++;
810 }
cristy699ae5b2013-07-03 13:41:29 +0000811 forward_info=(MemoryInfo *) RelinquishVirtualMemory(forward_info);
cristy3ed852e2009-09-05 21:47:34 +0000812 return(MagickTrue);
813}
814
815static MagickBooleanType ForwardFourierTransformChannel(const Image *image,
cristyd3090f92011-10-18 00:05:15 +0000816 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +0000817 Image *fourier_image,ExceptionInfo *exception)
818{
819 double
cristyce9fe782013-07-03 00:59:41 +0000820 *magnitude_pixels,
821 *phase_pixels;
cristybb3c02e2013-07-02 00:43:10 +0000822
cristy56ed31c2010-03-22 00:46:21 +0000823 FourierInfo
824 fourier_info;
825
cristyc4ea4a42011-01-24 01:43:30 +0000826 MagickBooleanType
827 status;
828
cristyce9fe782013-07-03 00:59:41 +0000829 MemoryInfo
830 *magnitude_info,
831 *phase_info;
832
cristy3ed852e2009-09-05 21:47:34 +0000833 size_t
834 extent;
835
836 fourier_info.width=image->columns;
837 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
838 ((image->rows % 2) != 0))
839 {
840 extent=image->columns < image->rows ? image->rows : image->columns;
841 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
842 }
843 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +0000844 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +0000845 fourier_info.channel=channel;
846 fourier_info.modulus=modulus;
cristyce9fe782013-07-03 00:59:41 +0000847 magnitude_info=AcquireVirtualMemory((size_t) fourier_info.height,
848 fourier_info.center*sizeof(*magnitude_pixels));
849 phase_info=AcquireVirtualMemory((size_t) fourier_info.height,
850 fourier_info.center*sizeof(*phase_pixels));
851 if ((magnitude_info == (MemoryInfo *) NULL) ||
852 (phase_info == (MemoryInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000853 {
cristyce9fe782013-07-03 00:59:41 +0000854 if (phase_info != (MemoryInfo *) NULL)
855 phase_info=RelinquishVirtualMemory(phase_info);
856 if (magnitude_info == (MemoryInfo *) NULL)
857 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000858 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000859 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000860 return(MagickFalse);
861 }
cristyce9fe782013-07-03 00:59:41 +0000862 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
863 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
864 status=ForwardFourierTransform(&fourier_info,image,magnitude_pixels,
865 phase_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +0000866 if (status != MagickFalse)
cristyce9fe782013-07-03 00:59:41 +0000867 status=ForwardFourier(&fourier_info,fourier_image,magnitude_pixels,
868 phase_pixels,exception);
869 phase_info=RelinquishVirtualMemory(phase_info);
870 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000871 return(status);
872}
873#endif
874
875MagickExport Image *ForwardFourierTransformImage(const Image *image,
876 const MagickBooleanType modulus,ExceptionInfo *exception)
877{
878 Image
879 *fourier_image;
880
881 fourier_image=NewImageList();
882#if !defined(MAGICKCORE_FFTW_DELEGATE)
883 (void) modulus;
884 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000885 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristy3ed852e2009-09-05 21:47:34 +0000886 image->filename);
887#else
888 {
889 Image
890 *magnitude_image;
891
cristybb503372010-05-27 20:51:26 +0000892 size_t
cristy3ed852e2009-09-05 21:47:34 +0000893 extent,
894 width;
895
896 width=image->columns;
897 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
898 ((image->rows % 2) != 0))
899 {
900 extent=image->columns < image->rows ? image->rows : image->columns;
901 width=(extent & 0x01) == 1 ? extent+1UL : extent;
902 }
903 magnitude_image=CloneImage(image,width,width,MagickFalse,exception);
904 if (magnitude_image != (Image *) NULL)
905 {
906 Image
907 *phase_image;
908
909 magnitude_image->storage_class=DirectClass;
910 magnitude_image->depth=32UL;
911 phase_image=CloneImage(image,width,width,MagickFalse,exception);
912 if (phase_image == (Image *) NULL)
913 magnitude_image=DestroyImage(magnitude_image);
914 else
915 {
916 MagickBooleanType
917 is_gray,
918 status;
919
cristy3ed852e2009-09-05 21:47:34 +0000920 phase_image->storage_class=DirectClass;
921 phase_image->depth=32UL;
922 AppendImageToList(&fourier_image,magnitude_image);
923 AppendImageToList(&fourier_image,phase_image);
924 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000925 is_gray=IsImageGray(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000926#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy564a5692012-01-20 23:56:26 +0000927 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +0000928#endif
cristy3ed852e2009-09-05 21:47:34 +0000929 {
cristyb34ef052010-10-07 00:12:05 +0000930#if defined(MAGICKCORE_OPENMP_SUPPORT)
931 #pragma omp section
932#endif
cristy3ed852e2009-09-05 21:47:34 +0000933 {
cristyb34ef052010-10-07 00:12:05 +0000934 MagickBooleanType
935 thread_status;
936
937 if (is_gray != MagickFalse)
938 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000939 GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000940 else
941 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000942 RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000943 if (thread_status == MagickFalse)
944 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +0000945 }
cristyb34ef052010-10-07 00:12:05 +0000946#if defined(MAGICKCORE_OPENMP_SUPPORT)
947 #pragma omp section
948#endif
949 {
950 MagickBooleanType
951 thread_status;
952
953 thread_status=MagickTrue;
954 if (is_gray == MagickFalse)
955 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000956 GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000957 if (thread_status == MagickFalse)
958 status=thread_status;
959 }
960#if defined(MAGICKCORE_OPENMP_SUPPORT)
961 #pragma omp section
962#endif
963 {
964 MagickBooleanType
965 thread_status;
966
967 thread_status=MagickTrue;
968 if (is_gray == MagickFalse)
969 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000970 BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000971 if (thread_status == MagickFalse)
972 status=thread_status;
973 }
974#if defined(MAGICKCORE_OPENMP_SUPPORT)
975 #pragma omp section
976#endif
977 {
978 MagickBooleanType
979 thread_status;
980
981 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000982 if (image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +0000983 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000984 BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000985 if (thread_status == MagickFalse)
986 status=thread_status;
987 }
988#if defined(MAGICKCORE_OPENMP_SUPPORT)
989 #pragma omp section
990#endif
991 {
992 MagickBooleanType
993 thread_status;
994
995 thread_status=MagickTrue;
cristy8a46d822012-08-28 23:32:39 +0000996 if (image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +0000997 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000998 AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000999 if (thread_status == MagickFalse)
1000 status=thread_status;
1001 }
cristy3ed852e2009-09-05 21:47:34 +00001002 }
1003 if (status == MagickFalse)
1004 fourier_image=DestroyImageList(fourier_image);
1005 fftw_cleanup();
1006 }
1007 }
1008 }
1009#endif
1010 return(fourier_image);
1011}
1012
1013/*
1014%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1015% %
1016% %
1017% %
1018% 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 %
1019% %
1020% %
1021% %
1022%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1023%
1024% InverseFourierTransformImage() implements the inverse discrete Fourier
1025% transform (DFT) of the image either as a magnitude / phase or real /
1026% imaginary image pair.
1027%
1028% The format of the InverseFourierTransformImage method is:
1029%
cristyc9550792009-11-13 20:05:42 +00001030% Image *InverseFourierTransformImage(const Image *magnitude_image,
1031% const Image *phase_image,const MagickBooleanType modulus,
1032% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001033%
1034% A description of each parameter follows:
1035%
cristyc9550792009-11-13 20:05:42 +00001036% o magnitude_image: the magnitude or real image.
1037%
1038% o phase_image: the phase or imaginary image.
cristy3ed852e2009-09-05 21:47:34 +00001039%
1040% o modulus: if true, return transform as a magnitude / phase pair
1041% otherwise a real / imaginary image pair.
1042%
1043% o exception: return any errors or warnings in this structure.
1044%
1045*/
1046
1047#if defined(MAGICKCORE_FFTW_DELEGATE)
cristybb503372010-05-27 20:51:26 +00001048static MagickBooleanType InverseQuadrantSwap(const size_t width,
1049 const size_t height,const double *source,double *destination)
cristy3ed852e2009-09-05 21:47:34 +00001050{
cristyc4ea4a42011-01-24 01:43:30 +00001051 register ssize_t
1052 x;
1053
cristybb503372010-05-27 20:51:26 +00001054 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001055 center,
1056 y;
1057
cristy3ed852e2009-09-05 21:47:34 +00001058 /*
1059 Swap quadrants.
1060 */
cristy233fe582012-07-07 14:00:18 +00001061 center=(ssize_t) floor((double) width/2L)+1L;
cristybb503372010-05-27 20:51:26 +00001062 for (y=1L; y < (ssize_t) height; y++)
1063 for (x=0L; x < (ssize_t) (width/2L+1L); x++)
cristy9b1d6c72013-08-18 17:53:56 +00001064 destination[(height-y)*center-x+width/2L]=source[y*width+x];
cristybb503372010-05-27 20:51:26 +00001065 for (y=0L; y < (ssize_t) height; y++)
cristy3d04ed72013-08-18 17:51:01 +00001066 destination[y*center]=source[y*width+width/2L];
cristy3ed852e2009-09-05 21:47:34 +00001067 for (x=0L; x < center; x++)
1068 destination[x]=source[center-x-1L];
cristybb503372010-05-27 20:51:26 +00001069 return(RollFourier(center,height,0L,(ssize_t) height/-2L,destination));
cristy3ed852e2009-09-05 21:47:34 +00001070}
1071
1072static MagickBooleanType InverseFourier(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +00001073 const Image *magnitude_image,const Image *phase_image,
1074 fftw_complex *fourier_pixels,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001075{
1076 CacheView
1077 *magnitude_view,
1078 *phase_view;
1079
1080 double
cristy699ae5b2013-07-03 13:41:29 +00001081 *inverse_pixels,
cristy7d4aa382013-06-30 01:59:39 +00001082 *magnitude_pixels,
1083 *phase_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001084
cristy3ed852e2009-09-05 21:47:34 +00001085 MagickBooleanType
1086 status;
1087
cristy699ae5b2013-07-03 13:41:29 +00001088 MemoryInfo
1089 *inverse_info,
1090 *magnitude_info,
1091 *phase_info;
1092
cristy4c08aed2011-07-01 19:47:50 +00001093 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001094 *p;
1095
cristybb503372010-05-27 20:51:26 +00001096 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001097 i,
1098 x;
1099
cristyc4ea4a42011-01-24 01:43:30 +00001100 ssize_t
1101 y;
1102
cristy3ed852e2009-09-05 21:47:34 +00001103 /*
1104 Inverse fourier - read image and break down into a double array.
1105 */
cristy699ae5b2013-07-03 13:41:29 +00001106 magnitude_info=AcquireVirtualMemory((size_t)fourier_info->height,
1107 fourier_info->width*sizeof(*magnitude_pixels));
1108 phase_info=AcquireVirtualMemory((size_t) fourier_info->height,
cristybb3c02e2013-07-02 00:43:10 +00001109 fourier_info->width*sizeof(*phase_pixels));
cristy699ae5b2013-07-03 13:41:29 +00001110 inverse_info=AcquireVirtualMemory((size_t) fourier_info->height,
1111 fourier_info->center*sizeof(*inverse_pixels));
1112 if ((magnitude_info == (MemoryInfo *) NULL) ||
1113 (phase_info == (MemoryInfo *) NULL) ||
1114 (inverse_info == (MemoryInfo *) NULL))
cristybb3c02e2013-07-02 00:43:10 +00001115 {
cristy699ae5b2013-07-03 13:41:29 +00001116 if (magnitude_info != (MemoryInfo *) NULL)
1117 magnitude_info=RelinquishVirtualMemory(magnitude_info);
1118 if (phase_info != (MemoryInfo *) NULL)
1119 phase_info=RelinquishVirtualMemory(phase_info);
1120 if (inverse_info != (MemoryInfo *) NULL)
1121 inverse_info=RelinquishVirtualMemory(inverse_info);
cristybb3c02e2013-07-02 00:43:10 +00001122 (void) ThrowMagickException(exception,GetMagickModule(),
1123 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1124 magnitude_image->filename);
cristybb3c02e2013-07-02 00:43:10 +00001125 return(MagickFalse);
1126 }
cristy699ae5b2013-07-03 13:41:29 +00001127 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
1128 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
1129 inverse_pixels=(double *) GetVirtualMemoryBlob(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +00001130 i=0L;
cristy46ff2672012-12-14 15:32:26 +00001131 magnitude_view=AcquireVirtualCacheView(magnitude_image,exception);
cristybb503372010-05-27 20:51:26 +00001132 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001133 {
1134 p=GetCacheViewVirtualPixels(magnitude_view,0L,y,fourier_info->width,1UL,
1135 exception);
cristy4c08aed2011-07-01 19:47:50 +00001136 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001137 break;
cristybb503372010-05-27 20:51:26 +00001138 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001139 {
1140 switch (fourier_info->channel)
1141 {
cristyd3090f92011-10-18 00:05:15 +00001142 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001143 default:
1144 {
cristy7d4aa382013-06-30 01:59:39 +00001145 magnitude_pixels[i]=QuantumScale*GetPixelRed(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001146 break;
1147 }
cristyd3090f92011-10-18 00:05:15 +00001148 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001149 {
cristy7d4aa382013-06-30 01:59:39 +00001150 magnitude_pixels[i]=QuantumScale*GetPixelGreen(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001151 break;
1152 }
cristyd3090f92011-10-18 00:05:15 +00001153 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001154 {
cristy7d4aa382013-06-30 01:59:39 +00001155 magnitude_pixels[i]=QuantumScale*GetPixelBlue(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001156 break;
1157 }
cristyd3090f92011-10-18 00:05:15 +00001158 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001159 {
cristy7d4aa382013-06-30 01:59:39 +00001160 magnitude_pixels[i]=QuantumScale*GetPixelBlack(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001161 break;
1162 }
cristyd3090f92011-10-18 00:05:15 +00001163 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001164 {
cristy7d4aa382013-06-30 01:59:39 +00001165 magnitude_pixels[i]=QuantumScale*GetPixelAlpha(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001166 break;
1167 }
cristy3ed852e2009-09-05 21:47:34 +00001168 }
1169 i++;
cristyed231572011-07-14 02:18:59 +00001170 p+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +00001171 }
1172 }
cristy699ae5b2013-07-03 13:41:29 +00001173 magnitude_view=DestroyCacheView(magnitude_view);
1174 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
1175 magnitude_pixels,inverse_pixels);
1176 (void) CopyMagickMemory(magnitude_pixels,inverse_pixels,fourier_info->height*
1177 fourier_info->center*sizeof(*magnitude_pixels));
cristy3ed852e2009-09-05 21:47:34 +00001178 i=0L;
cristy46ff2672012-12-14 15:32:26 +00001179 phase_view=AcquireVirtualCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +00001180 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001181 {
1182 p=GetCacheViewVirtualPixels(phase_view,0,y,fourier_info->width,1,
1183 exception);
cristy4c08aed2011-07-01 19:47:50 +00001184 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001185 break;
cristybb503372010-05-27 20:51:26 +00001186 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001187 {
1188 switch (fourier_info->channel)
1189 {
cristyd3090f92011-10-18 00:05:15 +00001190 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001191 default:
1192 {
cristy7d4aa382013-06-30 01:59:39 +00001193 phase_pixels[i]=QuantumScale*GetPixelRed(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001194 break;
1195 }
cristyd3090f92011-10-18 00:05:15 +00001196 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001197 {
cristy7d4aa382013-06-30 01:59:39 +00001198 phase_pixels[i]=QuantumScale*GetPixelGreen(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001199 break;
1200 }
cristyd3090f92011-10-18 00:05:15 +00001201 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001202 {
cristy7d4aa382013-06-30 01:59:39 +00001203 phase_pixels[i]=QuantumScale*GetPixelBlue(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001204 break;
1205 }
cristyd3090f92011-10-18 00:05:15 +00001206 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001207 {
cristy7d4aa382013-06-30 01:59:39 +00001208 phase_pixels[i]=QuantumScale*GetPixelBlack(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001209 break;
1210 }
cristyd3090f92011-10-18 00:05:15 +00001211 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001212 {
cristy7d4aa382013-06-30 01:59:39 +00001213 phase_pixels[i]=QuantumScale*GetPixelAlpha(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001214 break;
1215 }
cristy3ed852e2009-09-05 21:47:34 +00001216 }
1217 i++;
cristyed231572011-07-14 02:18:59 +00001218 p+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +00001219 }
1220 }
1221 if (fourier_info->modulus != MagickFalse)
1222 {
1223 i=0L;
cristybb503372010-05-27 20:51:26 +00001224 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1225 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001226 {
cristy7d4aa382013-06-30 01:59:39 +00001227 phase_pixels[i]-=0.5;
1228 phase_pixels[i]*=(2.0*MagickPI);
cristy3ed852e2009-09-05 21:47:34 +00001229 i++;
1230 }
1231 }
cristy3ed852e2009-09-05 21:47:34 +00001232 phase_view=DestroyCacheView(phase_view);
cristy13c99c42013-08-18 13:53:30 +00001233 CorrectPhaseLHS(fourier_info->width,fourier_info->height,phase_pixels);
cristy3ed852e2009-09-05 21:47:34 +00001234 if (status != MagickFalse)
1235 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +00001236 phase_pixels,inverse_pixels);
1237 (void) CopyMagickMemory(phase_pixels,inverse_pixels,fourier_info->height*
1238 fourier_info->center*sizeof(*phase_pixels));
1239 inverse_info=RelinquishVirtualMemory(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +00001240 /*
1241 Merge two sets.
1242 */
1243 i=0L;
1244 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +00001245 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1246 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001247 {
cristy56ed31c2010-03-22 00:46:21 +00001248#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy699ae5b2013-07-03 13:41:29 +00001249 fourier_pixels[i]=magnitude_pixels[i]*cos(phase_pixels[i])+I*
1250 magnitude_pixels[i]*sin(phase_pixels[i]);
cristy56ed31c2010-03-22 00:46:21 +00001251#else
cristy699ae5b2013-07-03 13:41:29 +00001252 fourier_pixels[i][0]=magnitude_pixels[i]*cos(phase_pixels[i]);
1253 fourier_pixels[i][1]=magnitude_pixels[i]*sin(phase_pixels[i]);
cristy56ed31c2010-03-22 00:46:21 +00001254#endif
cristy3ed852e2009-09-05 21:47:34 +00001255 i++;
1256 }
1257 else
cristybb503372010-05-27 20:51:26 +00001258 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1259 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001260 {
cristy56ed31c2010-03-22 00:46:21 +00001261#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy699ae5b2013-07-03 13:41:29 +00001262 fourier_pixels[i]=magnitude_pixels[i]+I*phase_pixels[i];
cristy56ed31c2010-03-22 00:46:21 +00001263#else
cristy699ae5b2013-07-03 13:41:29 +00001264 fourier_pixels[i][0]=magnitude_pixels[i];
1265 fourier_pixels[i][1]=phase_pixels[i];
cristy56ed31c2010-03-22 00:46:21 +00001266#endif
cristy3ed852e2009-09-05 21:47:34 +00001267 i++;
1268 }
cristy699ae5b2013-07-03 13:41:29 +00001269 magnitude_info=RelinquishVirtualMemory(magnitude_info);
1270 phase_info=RelinquishVirtualMemory(phase_info);
cristy3ed852e2009-09-05 21:47:34 +00001271 return(status);
1272}
1273
1274static MagickBooleanType InverseFourierTransform(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +00001275 fftw_complex *fourier_pixels,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001276{
1277 CacheView
1278 *image_view;
1279
cristy99dc0362013-09-15 18:32:54 +00001280 const char
1281 *value;
1282
cristy3ed852e2009-09-05 21:47:34 +00001283 double
cristy699ae5b2013-07-03 13:41:29 +00001284 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001285
1286 fftw_plan
1287 fftw_c2r_plan;
1288
cristy699ae5b2013-07-03 13:41:29 +00001289 MemoryInfo
1290 *source_info;
1291
cristy4c08aed2011-07-01 19:47:50 +00001292 register Quantum
cristyc4ea4a42011-01-24 01:43:30 +00001293 *q;
1294
cristybb503372010-05-27 20:51:26 +00001295 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001296 i,
1297 x;
1298
cristyc4ea4a42011-01-24 01:43:30 +00001299 ssize_t
1300 y;
cristy3ed852e2009-09-05 21:47:34 +00001301
cristy699ae5b2013-07-03 13:41:29 +00001302 source_info=AcquireVirtualMemory((size_t) fourier_info->height,
1303 fourier_info->width*sizeof(*source_pixels));
1304 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001305 {
1306 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001307 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001308 return(MagickFalse);
1309 }
cristy699ae5b2013-07-03 13:41:29 +00001310 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
cristy99dc0362013-09-15 18:32:54 +00001311 value=GetImageArtifact(image,"fourier:normalize");
cristyf8ecbfd2013-09-15 19:11:14 +00001312 if (LocaleCompare(value,"inverse") == 0)
cristy99dc0362013-09-15 18:32:54 +00001313 {
1314 double
1315 gamma;
1316
1317 /*
1318 Normalize inverse transform.
1319 */
1320 i=0L;
1321 gamma=PerceptibleReciprocal((double) fourier_info->width*
1322 fourier_info->height);
1323 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1324 for (x=0L; x < (ssize_t) fourier_info->center; x++)
1325 {
1326#if defined(MAGICKCORE_HAVE_COMPLEX_H)
1327 fourier_pixels[i]*=gamma;
1328#else
1329 fourier_pixels[i][0]*=gamma;
1330 fourier_pixels[i][1]*=gamma;
1331#endif
1332 i++;
1333 }
1334 }
cristyb5d5f722009-11-04 03:03:49 +00001335#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001336 #pragma omp critical (MagickCore_InverseFourierTransform)
1337#endif
cristydf079ac2010-09-10 01:45:44 +00001338 {
1339 fftw_c2r_plan=fftw_plan_dft_c2r_2d(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +00001340 fourier_pixels,source_pixels,FFTW_ESTIMATE);
cristydf079ac2010-09-10 01:45:44 +00001341 fftw_execute(fftw_c2r_plan);
1342 fftw_destroy_plan(fftw_c2r_plan);
1343 }
cristy3ed852e2009-09-05 21:47:34 +00001344 i=0L;
cristy46ff2672012-12-14 15:32:26 +00001345 image_view=AcquireAuthenticCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +00001346 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001347 {
cristy85812052010-09-14 17:56:15 +00001348 if (y >= (ssize_t) image->rows)
1349 break;
1350 q=GetCacheViewAuthenticPixels(image_view,0L,y,fourier_info->width >
1351 image->columns ? image->columns : fourier_info->width,1UL,exception);
cristyacd2ed22011-08-30 01:44:23 +00001352 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001353 break;
cristybb503372010-05-27 20:51:26 +00001354 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001355 {
cristy233fe582012-07-07 14:00:18 +00001356 if (x < (ssize_t) image->columns)
1357 switch (fourier_info->channel)
cristy3ed852e2009-09-05 21:47:34 +00001358 {
cristy233fe582012-07-07 14:00:18 +00001359 case RedPixelChannel:
1360 default:
1361 {
cristy699ae5b2013-07-03 13:41:29 +00001362 SetPixelRed(image,ClampToQuantum(QuantumRange*source_pixels[i]),q);
cristy233fe582012-07-07 14:00:18 +00001363 break;
1364 }
1365 case GreenPixelChannel:
1366 {
cristy699ae5b2013-07-03 13:41:29 +00001367 SetPixelGreen(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1368 q);
cristy233fe582012-07-07 14:00:18 +00001369 break;
1370 }
1371 case BluePixelChannel:
1372 {
cristy699ae5b2013-07-03 13:41:29 +00001373 SetPixelBlue(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1374 q);
cristy233fe582012-07-07 14:00:18 +00001375 break;
1376 }
1377 case BlackPixelChannel:
1378 {
cristy699ae5b2013-07-03 13:41:29 +00001379 SetPixelBlack(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1380 q);
cristy233fe582012-07-07 14:00:18 +00001381 break;
1382 }
1383 case AlphaPixelChannel:
1384 {
cristy699ae5b2013-07-03 13:41:29 +00001385 SetPixelAlpha(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1386 q);
cristy233fe582012-07-07 14:00:18 +00001387 break;
1388 }
cristy3ed852e2009-09-05 21:47:34 +00001389 }
cristy3ed852e2009-09-05 21:47:34 +00001390 i++;
cristyed231572011-07-14 02:18:59 +00001391 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001392 }
1393 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1394 break;
1395 }
1396 image_view=DestroyCacheView(image_view);
cristy699ae5b2013-07-03 13:41:29 +00001397 source_info=RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +00001398 return(MagickTrue);
1399}
1400
cristyc9550792009-11-13 20:05:42 +00001401static MagickBooleanType InverseFourierTransformChannel(
1402 const Image *magnitude_image,const Image *phase_image,
cristyd3090f92011-10-18 00:05:15 +00001403 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +00001404 Image *fourier_image,ExceptionInfo *exception)
1405{
cristy3ed852e2009-09-05 21:47:34 +00001406 fftw_complex
cristy699ae5b2013-07-03 13:41:29 +00001407 *inverse_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001408
1409 FourierInfo
1410 fourier_info;
1411
1412 MagickBooleanType
1413 status;
1414
cristy699ae5b2013-07-03 13:41:29 +00001415 MemoryInfo
1416 *inverse_info;
1417
cristy3ed852e2009-09-05 21:47:34 +00001418 size_t
1419 extent;
1420
cristyc9550792009-11-13 20:05:42 +00001421 fourier_info.width=magnitude_image->columns;
1422 if ((magnitude_image->columns != magnitude_image->rows) ||
1423 ((magnitude_image->columns % 2) != 0) ||
1424 ((magnitude_image->rows % 2) != 0))
cristy3ed852e2009-09-05 21:47:34 +00001425 {
cristyc9550792009-11-13 20:05:42 +00001426 extent=magnitude_image->columns < magnitude_image->rows ?
1427 magnitude_image->rows : magnitude_image->columns;
cristy3ed852e2009-09-05 21:47:34 +00001428 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
1429 }
1430 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +00001431 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +00001432 fourier_info.channel=channel;
1433 fourier_info.modulus=modulus;
cristy699ae5b2013-07-03 13:41:29 +00001434 inverse_info=AcquireVirtualMemory((size_t) fourier_info.height,
1435 fourier_info.center*sizeof(*inverse_pixels));
1436 if (inverse_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001437 {
1438 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001439 ResourceLimitError,"MemoryAllocationFailed","`%s'",
cristyc9550792009-11-13 20:05:42 +00001440 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001441 return(MagickFalse);
1442 }
cristy699ae5b2013-07-03 13:41:29 +00001443 inverse_pixels=(fftw_complex *) GetVirtualMemoryBlob(inverse_info);
1444 status=InverseFourier(&fourier_info,magnitude_image,phase_image,
1445 inverse_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +00001446 if (status != MagickFalse)
cristy699ae5b2013-07-03 13:41:29 +00001447 status=InverseFourierTransform(&fourier_info,inverse_pixels,fourier_image,
cristy3ed852e2009-09-05 21:47:34 +00001448 exception);
cristy699ae5b2013-07-03 13:41:29 +00001449 inverse_info=RelinquishVirtualMemory(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +00001450 return(status);
1451}
1452#endif
1453
cristyc9550792009-11-13 20:05:42 +00001454MagickExport Image *InverseFourierTransformImage(const Image *magnitude_image,
1455 const Image *phase_image,const MagickBooleanType modulus,
1456 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001457{
1458 Image
1459 *fourier_image;
1460
cristyc9550792009-11-13 20:05:42 +00001461 assert(magnitude_image != (Image *) NULL);
1462 assert(magnitude_image->signature == MagickSignature);
1463 if (magnitude_image->debug != MagickFalse)
1464 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1465 magnitude_image->filename);
1466 if (phase_image == (Image *) NULL)
1467 {
1468 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
cristyefe601c2013-01-05 17:51:12 +00001469 "TwoOrMoreImagesRequired","`%s'",magnitude_image->filename);
cristy9372a152009-11-18 01:42:16 +00001470 return((Image *) NULL);
cristyc9550792009-11-13 20:05:42 +00001471 }
cristy3ed852e2009-09-05 21:47:34 +00001472#if !defined(MAGICKCORE_FFTW_DELEGATE)
1473 fourier_image=(Image *) NULL;
1474 (void) modulus;
1475 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00001476 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristyc9550792009-11-13 20:05:42 +00001477 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001478#else
1479 {
cristyc9550792009-11-13 20:05:42 +00001480 fourier_image=CloneImage(magnitude_image,magnitude_image->columns,
1481 magnitude_image->rows,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00001482 if (fourier_image != (Image *) NULL)
1483 {
1484 MagickBooleanType
1485 is_gray,
1486 status;
1487
cristy3ed852e2009-09-05 21:47:34 +00001488 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001489 is_gray=IsImageGray(magnitude_image,exception);
cristyc9550792009-11-13 20:05:42 +00001490 if (is_gray != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001491 is_gray=IsImageGray(phase_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001492#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb34ef052010-10-07 00:12:05 +00001493 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +00001494#endif
cristy3ed852e2009-09-05 21:47:34 +00001495 {
cristyb34ef052010-10-07 00:12:05 +00001496#if defined(MAGICKCORE_OPENMP_SUPPORT)
1497 #pragma omp section
1498#endif
cristy3ed852e2009-09-05 21:47:34 +00001499 {
cristyb34ef052010-10-07 00:12:05 +00001500 MagickBooleanType
1501 thread_status;
1502
1503 if (is_gray != MagickFalse)
1504 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001505 phase_image,GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001506 else
cristyc9550792009-11-13 20:05:42 +00001507 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001508 phase_image,RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001509 if (thread_status == MagickFalse)
1510 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +00001511 }
cristyb34ef052010-10-07 00:12:05 +00001512#if defined(MAGICKCORE_OPENMP_SUPPORT)
1513 #pragma omp section
1514#endif
1515 {
1516 MagickBooleanType
1517 thread_status;
1518
1519 thread_status=MagickTrue;
1520 if (is_gray == MagickFalse)
1521 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001522 phase_image,GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001523 if (thread_status == MagickFalse)
1524 status=thread_status;
1525 }
1526#if defined(MAGICKCORE_OPENMP_SUPPORT)
1527 #pragma omp section
1528#endif
1529 {
1530 MagickBooleanType
1531 thread_status;
1532
1533 thread_status=MagickTrue;
1534 if (is_gray == MagickFalse)
1535 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001536 phase_image,BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001537 if (thread_status == MagickFalse)
1538 status=thread_status;
1539 }
1540#if defined(MAGICKCORE_OPENMP_SUPPORT)
1541 #pragma omp section
1542#endif
1543 {
1544 MagickBooleanType
1545 thread_status;
1546
1547 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001548 if (magnitude_image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +00001549 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001550 phase_image,BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001551 if (thread_status == MagickFalse)
1552 status=thread_status;
1553 }
1554#if defined(MAGICKCORE_OPENMP_SUPPORT)
1555 #pragma omp section
1556#endif
1557 {
1558 MagickBooleanType
1559 thread_status;
1560
1561 thread_status=MagickTrue;
cristy8a46d822012-08-28 23:32:39 +00001562 if (magnitude_image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +00001563 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001564 phase_image,AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001565 if (thread_status == MagickFalse)
1566 status=thread_status;
1567 }
cristy3ed852e2009-09-05 21:47:34 +00001568 }
1569 if (status == MagickFalse)
1570 fourier_image=DestroyImage(fourier_image);
1571 }
cristyb34ef052010-10-07 00:12:05 +00001572 fftw_cleanup();
cristy3ed852e2009-09-05 21:47:34 +00001573 }
1574#endif
1575 return(fourier_image);
1576}