blob: de3f209d66e3142ecdeef64a917c8efe2e74c429 [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"
57#include "MagickCore/pixel-accessor.h"
cristy99dc0362013-09-15 18:32:54 +000058#include "MagickCore/pixel-private.h"
cristy4c08aed2011-07-01 19:47:50 +000059#include "MagickCore/property.h"
60#include "MagickCore/quantum-private.h"
cristyac245f82012-05-05 17:13:57 +000061#include "MagickCore/resource_.h"
cristy4c08aed2011-07-01 19:47:50 +000062#include "MagickCore/thread-private.h"
cristy3ed852e2009-09-05 21:47:34 +000063#if defined(MAGICKCORE_FFTW_DELEGATE)
cristy56ed31c2010-03-22 00:46:21 +000064#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy3ed852e2009-09-05 21:47:34 +000065#include <complex.h>
cristy56ed31c2010-03-22 00:46:21 +000066#endif
cristy3ed852e2009-09-05 21:47:34 +000067#include <fftw3.h>
cristy47b022b2011-01-18 22:29:21 +000068#if !defined(MAGICKCORE_HAVE_CABS)
69#define cabs(z) (sqrt(z[0]*z[0]+z[1]*z[1]))
70#endif
71#if !defined(MAGICKCORE_HAVE_CARG)
cristy4da3ba32011-02-07 16:58:11 +000072#define carg(z) (atan2(cimag(z),creal(z)))
cristy47b022b2011-01-18 22:29:21 +000073#endif
74#if !defined(MAGICKCORE_HAVE_CIMAG)
75#define cimag(z) (z[1])
76#endif
77#if !defined(MAGICKCORE_HAVE_CREAL)
78#define creal(z) (z[0])
79#endif
cristy3ed852e2009-09-05 21:47:34 +000080#endif
81
82/*
83 Typedef declarations.
84*/
85typedef struct _FourierInfo
86{
cristyd3090f92011-10-18 00:05:15 +000087 PixelChannel
cristy3ed852e2009-09-05 21:47:34 +000088 channel;
89
90 MagickBooleanType
91 modulus;
92
cristybb503372010-05-27 20:51:26 +000093 size_t
cristy3ed852e2009-09-05 21:47:34 +000094 width,
95 height;
96
cristybb503372010-05-27 20:51:26 +000097 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000098 center;
99} FourierInfo;
100
101/*
102%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
103% %
104% %
105% %
cristy790190d2013-10-04 00:51:51 +0000106% C o m p l e x I m a g e s %
107% %
108% %
109% %
110%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
111%
112% ComplexImages() performs complex mathematics on an image sequence.
113%
114% The format of the ComplexImages method is:
115%
116% MagickBooleanType ComplexImages(Image *images,
117% const ComplexOperator operator,ExceptionInfo *exception)
118%
119% A description of each parameter follows:
120%
121% o image: the image.
122%
123% o operator: A complex operator.
124%
125% o exception: return any errors or warnings in this structure.
126%
127*/
128MagickExport Image *ComplexImages(const Image *images,
129 const ComplexOperator operator,ExceptionInfo *exception)
130{
131#define ComplexImageTag "Complex/Image"
132
133 CacheView
134 *complex_view;
135
136 Image
137 *image;
138
139 MagickBooleanType
140 status;
141
142 MagickOffsetType
143 progress;
144
145 size_t
146 number_images;
147
148 ssize_t
149 y;
150
151 assert(images != (Image *) NULL);
152 assert(images->signature == MagickSignature);
153 if (images->debug != MagickFalse)
154 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
155 assert(exception != (ExceptionInfo *) NULL);
156 assert(exception->signature == MagickSignature);
cristy3787ea42013-10-05 08:01:00 +0000157 image=(Image *) NULL;
cristy790190d2013-10-04 00:51:51 +0000158 return(image);
159}
160
161/*
162%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
163% %
164% %
165% %
cristy3ed852e2009-09-05 21:47:34 +0000166% 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 %
167% %
168% %
169% %
170%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
171%
172% ForwardFourierTransformImage() implements the discrete Fourier transform
173% (DFT) of the image either as a magnitude / phase or real / imaginary image
174% pair.
175%
176% The format of the ForwadFourierTransformImage method is:
177%
178% Image *ForwardFourierTransformImage(const Image *image,
179% const MagickBooleanType modulus,ExceptionInfo *exception)
180%
181% A description of each parameter follows:
182%
183% o image: the image.
184%
185% o modulus: if true, return as transform as a magnitude / phase pair
186% otherwise a real / imaginary image pair.
187%
188% o exception: return any errors or warnings in this structure.
189%
190*/
191
192#if defined(MAGICKCORE_FFTW_DELEGATE)
193
cristyc4ea4a42011-01-24 01:43:30 +0000194static MagickBooleanType RollFourier(const size_t width,const size_t height,
cristy699ae5b2013-07-03 13:41:29 +0000195 const ssize_t x_offset,const ssize_t y_offset,double *roll_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000196{
197 double
cristy699ae5b2013-07-03 13:41:29 +0000198 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000199
cristy7d4aa382013-06-30 01:59:39 +0000200 MemoryInfo
cristy699ae5b2013-07-03 13:41:29 +0000201 *source_info;
cristy7d4aa382013-06-30 01:59:39 +0000202
cristyc4ea4a42011-01-24 01:43:30 +0000203 register ssize_t
204 i,
205 x;
206
cristybb503372010-05-27 20:51:26 +0000207 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000208 u,
209 v,
210 y;
211
cristy3ed852e2009-09-05 21:47:34 +0000212 /*
cristy2da05352010-09-15 19:22:17 +0000213 Move zero frequency (DC, average color) from (0,0) to (width/2,height/2).
cristy3ed852e2009-09-05 21:47:34 +0000214 */
cristy699ae5b2013-07-03 13:41:29 +0000215 source_info=AcquireVirtualMemory(height,width*sizeof(*source_pixels));
216 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000217 return(MagickFalse);
cristy699ae5b2013-07-03 13:41:29 +0000218 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000219 i=0L;
cristybb503372010-05-27 20:51:26 +0000220 for (y=0L; y < (ssize_t) height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000221 {
222 if (y_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000223 v=((y+y_offset) < 0L) ? y+y_offset+(ssize_t) height : y+y_offset;
cristy3ed852e2009-09-05 21:47:34 +0000224 else
cristybb503372010-05-27 20:51:26 +0000225 v=((y+y_offset) > ((ssize_t) height-1L)) ? y+y_offset-(ssize_t) height :
cristy3ed852e2009-09-05 21:47:34 +0000226 y+y_offset;
cristybb503372010-05-27 20:51:26 +0000227 for (x=0L; x < (ssize_t) width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000228 {
229 if (x_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000230 u=((x+x_offset) < 0L) ? x+x_offset+(ssize_t) width : x+x_offset;
cristy3ed852e2009-09-05 21:47:34 +0000231 else
cristybb503372010-05-27 20:51:26 +0000232 u=((x+x_offset) > ((ssize_t) width-1L)) ? x+x_offset-(ssize_t) width :
cristy3ed852e2009-09-05 21:47:34 +0000233 x+x_offset;
cristy699ae5b2013-07-03 13:41:29 +0000234 source_pixels[v*width+u]=roll_pixels[i++];
cristyc4ea4a42011-01-24 01:43:30 +0000235 }
cristy3ed852e2009-09-05 21:47:34 +0000236 }
cristy699ae5b2013-07-03 13:41:29 +0000237 (void) CopyMagickMemory(roll_pixels,source_pixels,height*width*
238 sizeof(*source_pixels));
239 source_info=RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000240 return(MagickTrue);
241}
242
cristybb503372010-05-27 20:51:26 +0000243static MagickBooleanType ForwardQuadrantSwap(const size_t width,
cristy699ae5b2013-07-03 13:41:29 +0000244 const size_t height,double *source_pixels,double *forward_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000245{
cristy3ed852e2009-09-05 21:47:34 +0000246 MagickBooleanType
247 status;
248
cristybb503372010-05-27 20:51:26 +0000249 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000250 x;
251
cristyc4ea4a42011-01-24 01:43:30 +0000252 ssize_t
253 center,
254 y;
255
cristy3ed852e2009-09-05 21:47:34 +0000256 /*
257 Swap quadrants.
258 */
cristybb503372010-05-27 20:51:26 +0000259 center=(ssize_t) floor((double) width/2L)+1L;
cristy699ae5b2013-07-03 13:41:29 +0000260 status=RollFourier((size_t) center,height,0L,(ssize_t) height/2L,
261 source_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000262 if (status == MagickFalse)
263 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000264 for (y=0L; y < (ssize_t) height; y++)
265 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy3d04ed72013-08-18 17:51:01 +0000266 forward_pixels[y*width+x+width/2L]=source_pixels[y*center+x];
cristybb503372010-05-27 20:51:26 +0000267 for (y=1; y < (ssize_t) height; y++)
268 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy9b1d6c72013-08-18 17:53:56 +0000269 forward_pixels[(height-y)*width+width/2L-x-1L]=
cristy3d04ed72013-08-18 17:51:01 +0000270 source_pixels[y*center+x+1L];
cristybb503372010-05-27 20:51:26 +0000271 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy699ae5b2013-07-03 13:41:29 +0000272 forward_pixels[-x+width/2L-1L]=forward_pixels[x+width/2L+1L];
cristy3ed852e2009-09-05 21:47:34 +0000273 return(MagickTrue);
274}
275
cristyc4ea4a42011-01-24 01:43:30 +0000276static void CorrectPhaseLHS(const size_t width,const size_t height,
cristy699ae5b2013-07-03 13:41:29 +0000277 double *fourier_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000278{
cristybb503372010-05-27 20:51:26 +0000279 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000280 x;
281
cristy9d314ff2011-03-09 01:30:28 +0000282 ssize_t
283 y;
284
cristybb503372010-05-27 20:51:26 +0000285 for (y=0L; y < (ssize_t) height; y++)
286 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy699ae5b2013-07-03 13:41:29 +0000287 fourier_pixels[y*width+x]*=(-1.0);
cristy3ed852e2009-09-05 21:47:34 +0000288}
289
290static MagickBooleanType ForwardFourier(const FourierInfo *fourier_info,
291 Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
292{
293 CacheView
294 *magnitude_view,
295 *phase_view;
296
297 double
cristy7d4aa382013-06-30 01:59:39 +0000298 *magnitude_pixels,
299 *phase_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000300
301 Image
302 *magnitude_image,
303 *phase_image;
304
cristy3ed852e2009-09-05 21:47:34 +0000305 MagickBooleanType
306 status;
307
cristy7d4aa382013-06-30 01:59:39 +0000308 MemoryInfo
309 *magnitude_info,
310 *phase_info;
311
cristy4c08aed2011-07-01 19:47:50 +0000312 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000313 *q;
314
cristyf5742792012-11-27 18:31:26 +0000315 register ssize_t
316 x;
317
cristyc4ea4a42011-01-24 01:43:30 +0000318 ssize_t
319 i,
320 y;
321
cristy3ed852e2009-09-05 21:47:34 +0000322 magnitude_image=GetFirstImageInList(image);
323 phase_image=GetNextImageInList(image);
324 if (phase_image == (Image *) NULL)
325 {
326 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
cristyefe601c2013-01-05 17:51:12 +0000327 "TwoOrMoreImagesRequired","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000328 return(MagickFalse);
329 }
330 /*
331 Create "Fourier Transform" image from constituent arrays.
332 */
cristy7d4aa382013-06-30 01:59:39 +0000333 magnitude_info=AcquireVirtualMemory((size_t) fourier_info->height,
334 fourier_info->width*sizeof(*magnitude_pixels));
335 phase_info=AcquireVirtualMemory((size_t) fourier_info->height,
336 fourier_info->width*sizeof(*phase_pixels));
cristybb3c02e2013-07-02 00:43:10 +0000337 if ((magnitude_info == (MemoryInfo *) NULL) ||
338 (phase_info == (MemoryInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000339 {
cristy7d4aa382013-06-30 01:59:39 +0000340 if (phase_info != (MemoryInfo *) NULL)
341 phase_info=RelinquishVirtualMemory(phase_info);
342 if (magnitude_info != (MemoryInfo *) NULL)
343 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000344 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000345 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000346 return(MagickFalse);
347 }
cristy7d4aa382013-06-30 01:59:39 +0000348 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
349 (void) ResetMagickMemory(magnitude_pixels,0,fourier_info->height*
350 fourier_info->width*sizeof(*magnitude_pixels));
cristybb3c02e2013-07-02 00:43:10 +0000351 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
cristy7d4aa382013-06-30 01:59:39 +0000352 (void) ResetMagickMemory(phase_pixels,0,fourier_info->height*
353 fourier_info->width*sizeof(*phase_pixels));
cristy13c99c42013-08-18 13:53:30 +0000354 status=ForwardQuadrantSwap(fourier_info->width,fourier_info->height,
cristy7d4aa382013-06-30 01:59:39 +0000355 magnitude,magnitude_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000356 if (status != MagickFalse)
cristy13c99c42013-08-18 13:53:30 +0000357 status=ForwardQuadrantSwap(fourier_info->width,fourier_info->height,phase,
cristy7d4aa382013-06-30 01:59:39 +0000358 phase_pixels);
cristy13c99c42013-08-18 13:53:30 +0000359 CorrectPhaseLHS(fourier_info->width,fourier_info->height,phase_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000360 if (fourier_info->modulus != MagickFalse)
361 {
362 i=0L;
cristybb503372010-05-27 20:51:26 +0000363 for (y=0L; y < (ssize_t) fourier_info->height; y++)
364 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000365 {
cristy7d4aa382013-06-30 01:59:39 +0000366 phase_pixels[i]/=(2.0*MagickPI);
367 phase_pixels[i]+=0.5;
cristy3ed852e2009-09-05 21:47:34 +0000368 i++;
369 }
370 }
cristy46ff2672012-12-14 15:32:26 +0000371 magnitude_view=AcquireAuthenticCacheView(magnitude_image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000372 i=0L;
cristybb503372010-05-27 20:51:26 +0000373 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000374 {
375 q=GetCacheViewAuthenticPixels(magnitude_view,0L,y,fourier_info->height,1UL,
376 exception);
cristyacd2ed22011-08-30 01:44:23 +0000377 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000378 break;
cristybb503372010-05-27 20:51:26 +0000379 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000380 {
381 switch (fourier_info->channel)
382 {
cristyd3090f92011-10-18 00:05:15 +0000383 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000384 default:
385 {
cristy4c08aed2011-07-01 19:47:50 +0000386 SetPixelRed(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000387 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000388 break;
389 }
cristyd3090f92011-10-18 00:05:15 +0000390 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000391 {
cristy4c08aed2011-07-01 19:47:50 +0000392 SetPixelGreen(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000393 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000394 break;
395 }
cristyd3090f92011-10-18 00:05:15 +0000396 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000397 {
cristy4c08aed2011-07-01 19:47:50 +0000398 SetPixelBlue(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000399 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000400 break;
401 }
cristyd3090f92011-10-18 00:05:15 +0000402 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000403 {
cristy4c08aed2011-07-01 19:47:50 +0000404 SetPixelBlack(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000405 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000406 break;
407 }
cristyd3090f92011-10-18 00:05:15 +0000408 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000409 {
cristy4c08aed2011-07-01 19:47:50 +0000410 SetPixelAlpha(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000411 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000412 break;
413 }
cristy3ed852e2009-09-05 21:47:34 +0000414 }
415 i++;
cristyed231572011-07-14 02:18:59 +0000416 q+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +0000417 }
418 status=SyncCacheViewAuthenticPixels(magnitude_view,exception);
419 if (status == MagickFalse)
420 break;
421 }
cristydb070952012-04-20 14:33:00 +0000422 magnitude_view=DestroyCacheView(magnitude_view);
cristy3ed852e2009-09-05 21:47:34 +0000423 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000424 phase_view=AcquireAuthenticCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +0000425 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000426 {
427 q=GetCacheViewAuthenticPixels(phase_view,0L,y,fourier_info->height,1UL,
428 exception);
cristyacd2ed22011-08-30 01:44:23 +0000429 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000430 break;
cristybb503372010-05-27 20:51:26 +0000431 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000432 {
433 switch (fourier_info->channel)
434 {
cristyd3090f92011-10-18 00:05:15 +0000435 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000436 default:
437 {
cristy4c08aed2011-07-01 19:47:50 +0000438 SetPixelRed(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000439 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000440 break;
441 }
cristyd3090f92011-10-18 00:05:15 +0000442 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000443 {
cristy4c08aed2011-07-01 19:47:50 +0000444 SetPixelGreen(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000445 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000446 break;
447 }
cristyd3090f92011-10-18 00:05:15 +0000448 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000449 {
cristy4c08aed2011-07-01 19:47:50 +0000450 SetPixelBlue(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000451 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000452 break;
453 }
cristyd3090f92011-10-18 00:05:15 +0000454 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000455 {
cristy4c08aed2011-07-01 19:47:50 +0000456 SetPixelBlack(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000457 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000458 break;
459 }
cristyd3090f92011-10-18 00:05:15 +0000460 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000461 {
cristy4c08aed2011-07-01 19:47:50 +0000462 SetPixelAlpha(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000463 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000464 break;
465 }
cristy3ed852e2009-09-05 21:47:34 +0000466 }
467 i++;
cristyed231572011-07-14 02:18:59 +0000468 q+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +0000469 }
470 status=SyncCacheViewAuthenticPixels(phase_view,exception);
471 if (status == MagickFalse)
472 break;
473 }
474 phase_view=DestroyCacheView(phase_view);
cristy7d4aa382013-06-30 01:59:39 +0000475 phase_info=RelinquishVirtualMemory(phase_info);
476 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000477 return(status);
478}
479
480static MagickBooleanType ForwardFourierTransform(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +0000481 const Image *image,double *magnitude_pixels,double *phase_pixels,
482 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000483{
484 CacheView
485 *image_view;
486
cristy99dc0362013-09-15 18:32:54 +0000487 const char
488 *value;
489
cristy3ed852e2009-09-05 21:47:34 +0000490 double
cristybb3c02e2013-07-02 00:43:10 +0000491 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000492
493 fftw_complex
cristy699ae5b2013-07-03 13:41:29 +0000494 *forward_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000495
496 fftw_plan
497 fftw_r2c_plan;
498
cristy7d4aa382013-06-30 01:59:39 +0000499 MemoryInfo
cristy699ae5b2013-07-03 13:41:29 +0000500 *forward_info,
cristy7d4aa382013-06-30 01:59:39 +0000501 *source_info;
502
cristy4c08aed2011-07-01 19:47:50 +0000503 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000504 *p;
505
cristybb503372010-05-27 20:51:26 +0000506 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000507 i,
508 x;
509
cristyc4ea4a42011-01-24 01:43:30 +0000510 ssize_t
511 y;
512
cristy3ed852e2009-09-05 21:47:34 +0000513 /*
514 Generate the forward Fourier transform.
515 */
cristy7d4aa382013-06-30 01:59:39 +0000516 source_info=AcquireVirtualMemory((size_t) fourier_info->height,
cristybb3c02e2013-07-02 00:43:10 +0000517 fourier_info->width*sizeof(*source_pixels));
cristy7d4aa382013-06-30 01:59:39 +0000518 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000519 {
520 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000521 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000522 return(MagickFalse);
523 }
cristybb3c02e2013-07-02 00:43:10 +0000524 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
525 ResetMagickMemory(source_pixels,0,fourier_info->height*fourier_info->width*
526 sizeof(*source_pixels));
cristy3ed852e2009-09-05 21:47:34 +0000527 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000528 image_view=AcquireVirtualCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +0000529 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000530 {
531 p=GetCacheViewVirtualPixels(image_view,0L,y,fourier_info->width,1UL,
532 exception);
cristy4c08aed2011-07-01 19:47:50 +0000533 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000534 break;
cristybb503372010-05-27 20:51:26 +0000535 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000536 {
537 switch (fourier_info->channel)
538 {
cristyd3090f92011-10-18 00:05:15 +0000539 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000540 default:
541 {
cristybb3c02e2013-07-02 00:43:10 +0000542 source_pixels[i]=QuantumScale*GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000543 break;
544 }
cristyd3090f92011-10-18 00:05:15 +0000545 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000546 {
cristybb3c02e2013-07-02 00:43:10 +0000547 source_pixels[i]=QuantumScale*GetPixelGreen(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000548 break;
549 }
cristyd3090f92011-10-18 00:05:15 +0000550 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000551 {
cristybb3c02e2013-07-02 00:43:10 +0000552 source_pixels[i]=QuantumScale*GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000553 break;
554 }
cristyd3090f92011-10-18 00:05:15 +0000555 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000556 {
cristybb3c02e2013-07-02 00:43:10 +0000557 source_pixels[i]=QuantumScale*GetPixelBlack(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000558 break;
559 }
cristyd3090f92011-10-18 00:05:15 +0000560 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000561 {
cristybb3c02e2013-07-02 00:43:10 +0000562 source_pixels[i]=QuantumScale*GetPixelAlpha(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000563 break;
564 }
cristy3ed852e2009-09-05 21:47:34 +0000565 }
566 i++;
cristyed231572011-07-14 02:18:59 +0000567 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000568 }
569 }
570 image_view=DestroyCacheView(image_view);
cristy699ae5b2013-07-03 13:41:29 +0000571 forward_info=AcquireVirtualMemory((size_t) fourier_info->height,
572 fourier_info->center*sizeof(*forward_pixels));
573 if (forward_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000574 {
575 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000576 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristybb3c02e2013-07-02 00:43:10 +0000577 source_info=(MemoryInfo *) RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000578 return(MagickFalse);
579 }
cristy699ae5b2013-07-03 13:41:29 +0000580 forward_pixels=(fftw_complex *) GetVirtualMemoryBlob(forward_info);
cristyb5d5f722009-11-04 03:03:49 +0000581#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000582 #pragma omp critical (MagickCore_ForwardFourierTransform)
583#endif
cristy70841a12012-10-27 20:52:57 +0000584 fftw_r2c_plan=fftw_plan_dft_r2c_2d(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +0000585 source_pixels,forward_pixels,FFTW_ESTIMATE);
cristy3ed852e2009-09-05 21:47:34 +0000586 fftw_execute(fftw_r2c_plan);
587 fftw_destroy_plan(fftw_r2c_plan);
cristybb3c02e2013-07-02 00:43:10 +0000588 source_info=(MemoryInfo *) RelinquishVirtualMemory(source_info);
cristy99dc0362013-09-15 18:32:54 +0000589 value=GetImageArtifact(image,"fourier:normalize");
cristyf8ecbfd2013-09-15 19:11:14 +0000590 if ((value == (const char *) NULL) || (LocaleCompare(value,"forward") == 0))
cristy56ed31c2010-03-22 00:46:21 +0000591 {
cristy99dc0362013-09-15 18:32:54 +0000592 double
593 gamma;
594
595 /*
596 Normalize Fourier transform.
597 */
598 i=0L;
599 gamma=PerceptibleReciprocal((double) fourier_info->width*
600 fourier_info->height);
601 for (y=0L; y < (ssize_t) fourier_info->height; y++)
602 for (x=0L; x < (ssize_t) fourier_info->center; x++)
603 {
cristy56ed31c2010-03-22 00:46:21 +0000604#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy99dc0362013-09-15 18:32:54 +0000605 forward_pixels[i]*=gamma;
cristy56ed31c2010-03-22 00:46:21 +0000606#else
cristy99dc0362013-09-15 18:32:54 +0000607 forward_pixels[i][0]*=gamma;
608 forward_pixels[i][1]*=gamma;
cristy56ed31c2010-03-22 00:46:21 +0000609#endif
cristy99dc0362013-09-15 18:32:54 +0000610 i++;
611 }
cristy56ed31c2010-03-22 00:46:21 +0000612 }
cristy3ed852e2009-09-05 21:47:34 +0000613 /*
614 Generate magnitude and phase (or real and imaginary).
615 */
616 i=0L;
617 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000618 for (y=0L; y < (ssize_t) fourier_info->height; y++)
619 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000620 {
cristy699ae5b2013-07-03 13:41:29 +0000621 magnitude_pixels[i]=cabs(forward_pixels[i]);
622 phase_pixels[i]=carg(forward_pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000623 i++;
624 }
625 else
cristybb503372010-05-27 20:51:26 +0000626 for (y=0L; y < (ssize_t) fourier_info->height; y++)
627 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000628 {
cristy699ae5b2013-07-03 13:41:29 +0000629 magnitude_pixels[i]=creal(forward_pixels[i]);
630 phase_pixels[i]=cimag(forward_pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000631 i++;
632 }
cristy699ae5b2013-07-03 13:41:29 +0000633 forward_info=(MemoryInfo *) RelinquishVirtualMemory(forward_info);
cristy3ed852e2009-09-05 21:47:34 +0000634 return(MagickTrue);
635}
636
637static MagickBooleanType ForwardFourierTransformChannel(const Image *image,
cristyd3090f92011-10-18 00:05:15 +0000638 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +0000639 Image *fourier_image,ExceptionInfo *exception)
640{
641 double
cristyce9fe782013-07-03 00:59:41 +0000642 *magnitude_pixels,
643 *phase_pixels;
cristybb3c02e2013-07-02 00:43:10 +0000644
cristy56ed31c2010-03-22 00:46:21 +0000645 FourierInfo
646 fourier_info;
647
cristyc4ea4a42011-01-24 01:43:30 +0000648 MagickBooleanType
649 status;
650
cristyce9fe782013-07-03 00:59:41 +0000651 MemoryInfo
652 *magnitude_info,
653 *phase_info;
654
cristy3ed852e2009-09-05 21:47:34 +0000655 size_t
656 extent;
657
658 fourier_info.width=image->columns;
659 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
660 ((image->rows % 2) != 0))
661 {
662 extent=image->columns < image->rows ? image->rows : image->columns;
663 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
664 }
665 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +0000666 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +0000667 fourier_info.channel=channel;
668 fourier_info.modulus=modulus;
cristyce9fe782013-07-03 00:59:41 +0000669 magnitude_info=AcquireVirtualMemory((size_t) fourier_info.height,
670 fourier_info.center*sizeof(*magnitude_pixels));
671 phase_info=AcquireVirtualMemory((size_t) fourier_info.height,
672 fourier_info.center*sizeof(*phase_pixels));
673 if ((magnitude_info == (MemoryInfo *) NULL) ||
674 (phase_info == (MemoryInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000675 {
cristyce9fe782013-07-03 00:59:41 +0000676 if (phase_info != (MemoryInfo *) NULL)
677 phase_info=RelinquishVirtualMemory(phase_info);
678 if (magnitude_info == (MemoryInfo *) NULL)
679 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000680 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000681 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000682 return(MagickFalse);
683 }
cristyce9fe782013-07-03 00:59:41 +0000684 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
685 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
686 status=ForwardFourierTransform(&fourier_info,image,magnitude_pixels,
687 phase_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +0000688 if (status != MagickFalse)
cristyce9fe782013-07-03 00:59:41 +0000689 status=ForwardFourier(&fourier_info,fourier_image,magnitude_pixels,
690 phase_pixels,exception);
691 phase_info=RelinquishVirtualMemory(phase_info);
692 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000693 return(status);
694}
695#endif
696
697MagickExport Image *ForwardFourierTransformImage(const Image *image,
698 const MagickBooleanType modulus,ExceptionInfo *exception)
699{
700 Image
701 *fourier_image;
702
703 fourier_image=NewImageList();
704#if !defined(MAGICKCORE_FFTW_DELEGATE)
705 (void) modulus;
706 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000707 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristy3ed852e2009-09-05 21:47:34 +0000708 image->filename);
709#else
710 {
711 Image
712 *magnitude_image;
713
cristybb503372010-05-27 20:51:26 +0000714 size_t
cristy3ed852e2009-09-05 21:47:34 +0000715 extent,
716 width;
717
718 width=image->columns;
719 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
720 ((image->rows % 2) != 0))
721 {
722 extent=image->columns < image->rows ? image->rows : image->columns;
723 width=(extent & 0x01) == 1 ? extent+1UL : extent;
724 }
725 magnitude_image=CloneImage(image,width,width,MagickFalse,exception);
726 if (magnitude_image != (Image *) NULL)
727 {
728 Image
729 *phase_image;
730
731 magnitude_image->storage_class=DirectClass;
732 magnitude_image->depth=32UL;
733 phase_image=CloneImage(image,width,width,MagickFalse,exception);
734 if (phase_image == (Image *) NULL)
735 magnitude_image=DestroyImage(magnitude_image);
736 else
737 {
738 MagickBooleanType
739 is_gray,
740 status;
741
cristy3ed852e2009-09-05 21:47:34 +0000742 phase_image->storage_class=DirectClass;
743 phase_image->depth=32UL;
744 AppendImageToList(&fourier_image,magnitude_image);
745 AppendImageToList(&fourier_image,phase_image);
746 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000747 is_gray=IsImageGray(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000748#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy564a5692012-01-20 23:56:26 +0000749 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +0000750#endif
cristy3ed852e2009-09-05 21:47:34 +0000751 {
cristyb34ef052010-10-07 00:12:05 +0000752#if defined(MAGICKCORE_OPENMP_SUPPORT)
753 #pragma omp section
754#endif
cristy3ed852e2009-09-05 21:47:34 +0000755 {
cristyb34ef052010-10-07 00:12:05 +0000756 MagickBooleanType
757 thread_status;
758
759 if (is_gray != MagickFalse)
760 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000761 GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000762 else
763 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000764 RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000765 if (thread_status == MagickFalse)
766 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +0000767 }
cristyb34ef052010-10-07 00:12:05 +0000768#if defined(MAGICKCORE_OPENMP_SUPPORT)
769 #pragma omp section
770#endif
771 {
772 MagickBooleanType
773 thread_status;
774
775 thread_status=MagickTrue;
776 if (is_gray == MagickFalse)
777 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000778 GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000779 if (thread_status == MagickFalse)
780 status=thread_status;
781 }
782#if defined(MAGICKCORE_OPENMP_SUPPORT)
783 #pragma omp section
784#endif
785 {
786 MagickBooleanType
787 thread_status;
788
789 thread_status=MagickTrue;
790 if (is_gray == MagickFalse)
791 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000792 BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000793 if (thread_status == MagickFalse)
794 status=thread_status;
795 }
796#if defined(MAGICKCORE_OPENMP_SUPPORT)
797 #pragma omp section
798#endif
799 {
800 MagickBooleanType
801 thread_status;
802
803 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000804 if (image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +0000805 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000806 BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000807 if (thread_status == MagickFalse)
808 status=thread_status;
809 }
810#if defined(MAGICKCORE_OPENMP_SUPPORT)
811 #pragma omp section
812#endif
813 {
814 MagickBooleanType
815 thread_status;
816
817 thread_status=MagickTrue;
cristy8a46d822012-08-28 23:32:39 +0000818 if (image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +0000819 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000820 AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000821 if (thread_status == MagickFalse)
822 status=thread_status;
823 }
cristy3ed852e2009-09-05 21:47:34 +0000824 }
825 if (status == MagickFalse)
826 fourier_image=DestroyImageList(fourier_image);
827 fftw_cleanup();
828 }
829 }
830 }
831#endif
832 return(fourier_image);
833}
834
835/*
836%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
837% %
838% %
839% %
840% 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 %
841% %
842% %
843% %
844%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
845%
846% InverseFourierTransformImage() implements the inverse discrete Fourier
847% transform (DFT) of the image either as a magnitude / phase or real /
848% imaginary image pair.
849%
850% The format of the InverseFourierTransformImage method is:
851%
cristyc9550792009-11-13 20:05:42 +0000852% Image *InverseFourierTransformImage(const Image *magnitude_image,
853% const Image *phase_image,const MagickBooleanType modulus,
854% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000855%
856% A description of each parameter follows:
857%
cristyc9550792009-11-13 20:05:42 +0000858% o magnitude_image: the magnitude or real image.
859%
860% o phase_image: the phase or imaginary image.
cristy3ed852e2009-09-05 21:47:34 +0000861%
862% o modulus: if true, return transform as a magnitude / phase pair
863% otherwise a real / imaginary image pair.
864%
865% o exception: return any errors or warnings in this structure.
866%
867*/
868
869#if defined(MAGICKCORE_FFTW_DELEGATE)
cristybb503372010-05-27 20:51:26 +0000870static MagickBooleanType InverseQuadrantSwap(const size_t width,
871 const size_t height,const double *source,double *destination)
cristy3ed852e2009-09-05 21:47:34 +0000872{
cristyc4ea4a42011-01-24 01:43:30 +0000873 register ssize_t
874 x;
875
cristybb503372010-05-27 20:51:26 +0000876 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000877 center,
878 y;
879
cristy3ed852e2009-09-05 21:47:34 +0000880 /*
881 Swap quadrants.
882 */
cristy233fe582012-07-07 14:00:18 +0000883 center=(ssize_t) floor((double) width/2L)+1L;
cristybb503372010-05-27 20:51:26 +0000884 for (y=1L; y < (ssize_t) height; y++)
885 for (x=0L; x < (ssize_t) (width/2L+1L); x++)
cristy9b1d6c72013-08-18 17:53:56 +0000886 destination[(height-y)*center-x+width/2L]=source[y*width+x];
cristybb503372010-05-27 20:51:26 +0000887 for (y=0L; y < (ssize_t) height; y++)
cristy3d04ed72013-08-18 17:51:01 +0000888 destination[y*center]=source[y*width+width/2L];
cristy3ed852e2009-09-05 21:47:34 +0000889 for (x=0L; x < center; x++)
890 destination[x]=source[center-x-1L];
cristybb503372010-05-27 20:51:26 +0000891 return(RollFourier(center,height,0L,(ssize_t) height/-2L,destination));
cristy3ed852e2009-09-05 21:47:34 +0000892}
893
894static MagickBooleanType InverseFourier(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +0000895 const Image *magnitude_image,const Image *phase_image,
896 fftw_complex *fourier_pixels,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000897{
898 CacheView
899 *magnitude_view,
900 *phase_view;
901
902 double
cristy699ae5b2013-07-03 13:41:29 +0000903 *inverse_pixels,
cristy7d4aa382013-06-30 01:59:39 +0000904 *magnitude_pixels,
905 *phase_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000906
cristy3ed852e2009-09-05 21:47:34 +0000907 MagickBooleanType
908 status;
909
cristy699ae5b2013-07-03 13:41:29 +0000910 MemoryInfo
911 *inverse_info,
912 *magnitude_info,
913 *phase_info;
914
cristy4c08aed2011-07-01 19:47:50 +0000915 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000916 *p;
917
cristybb503372010-05-27 20:51:26 +0000918 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000919 i,
920 x;
921
cristyc4ea4a42011-01-24 01:43:30 +0000922 ssize_t
923 y;
924
cristy3ed852e2009-09-05 21:47:34 +0000925 /*
926 Inverse fourier - read image and break down into a double array.
927 */
cristy699ae5b2013-07-03 13:41:29 +0000928 magnitude_info=AcquireVirtualMemory((size_t)fourier_info->height,
929 fourier_info->width*sizeof(*magnitude_pixels));
930 phase_info=AcquireVirtualMemory((size_t) fourier_info->height,
cristybb3c02e2013-07-02 00:43:10 +0000931 fourier_info->width*sizeof(*phase_pixels));
cristy699ae5b2013-07-03 13:41:29 +0000932 inverse_info=AcquireVirtualMemory((size_t) fourier_info->height,
933 fourier_info->center*sizeof(*inverse_pixels));
934 if ((magnitude_info == (MemoryInfo *) NULL) ||
935 (phase_info == (MemoryInfo *) NULL) ||
936 (inverse_info == (MemoryInfo *) NULL))
cristybb3c02e2013-07-02 00:43:10 +0000937 {
cristy699ae5b2013-07-03 13:41:29 +0000938 if (magnitude_info != (MemoryInfo *) NULL)
939 magnitude_info=RelinquishVirtualMemory(magnitude_info);
940 if (phase_info != (MemoryInfo *) NULL)
941 phase_info=RelinquishVirtualMemory(phase_info);
942 if (inverse_info != (MemoryInfo *) NULL)
943 inverse_info=RelinquishVirtualMemory(inverse_info);
cristybb3c02e2013-07-02 00:43:10 +0000944 (void) ThrowMagickException(exception,GetMagickModule(),
945 ResourceLimitError,"MemoryAllocationFailed","`%s'",
946 magnitude_image->filename);
cristybb3c02e2013-07-02 00:43:10 +0000947 return(MagickFalse);
948 }
cristy699ae5b2013-07-03 13:41:29 +0000949 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
950 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
951 inverse_pixels=(double *) GetVirtualMemoryBlob(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +0000952 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000953 magnitude_view=AcquireVirtualCacheView(magnitude_image,exception);
cristybb503372010-05-27 20:51:26 +0000954 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000955 {
956 p=GetCacheViewVirtualPixels(magnitude_view,0L,y,fourier_info->width,1UL,
957 exception);
cristy4c08aed2011-07-01 19:47:50 +0000958 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000959 break;
cristybb503372010-05-27 20:51:26 +0000960 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000961 {
962 switch (fourier_info->channel)
963 {
cristyd3090f92011-10-18 00:05:15 +0000964 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000965 default:
966 {
cristy7d4aa382013-06-30 01:59:39 +0000967 magnitude_pixels[i]=QuantumScale*GetPixelRed(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000968 break;
969 }
cristyd3090f92011-10-18 00:05:15 +0000970 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000971 {
cristy7d4aa382013-06-30 01:59:39 +0000972 magnitude_pixels[i]=QuantumScale*GetPixelGreen(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000973 break;
974 }
cristyd3090f92011-10-18 00:05:15 +0000975 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000976 {
cristy7d4aa382013-06-30 01:59:39 +0000977 magnitude_pixels[i]=QuantumScale*GetPixelBlue(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000978 break;
979 }
cristyd3090f92011-10-18 00:05:15 +0000980 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000981 {
cristy7d4aa382013-06-30 01:59:39 +0000982 magnitude_pixels[i]=QuantumScale*GetPixelBlack(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000983 break;
984 }
cristyd3090f92011-10-18 00:05:15 +0000985 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000986 {
cristy7d4aa382013-06-30 01:59:39 +0000987 magnitude_pixels[i]=QuantumScale*GetPixelAlpha(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000988 break;
989 }
cristy3ed852e2009-09-05 21:47:34 +0000990 }
991 i++;
cristyed231572011-07-14 02:18:59 +0000992 p+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +0000993 }
994 }
cristy699ae5b2013-07-03 13:41:29 +0000995 magnitude_view=DestroyCacheView(magnitude_view);
996 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
997 magnitude_pixels,inverse_pixels);
998 (void) CopyMagickMemory(magnitude_pixels,inverse_pixels,fourier_info->height*
999 fourier_info->center*sizeof(*magnitude_pixels));
cristy3ed852e2009-09-05 21:47:34 +00001000 i=0L;
cristy46ff2672012-12-14 15:32:26 +00001001 phase_view=AcquireVirtualCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +00001002 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001003 {
1004 p=GetCacheViewVirtualPixels(phase_view,0,y,fourier_info->width,1,
1005 exception);
cristy4c08aed2011-07-01 19:47:50 +00001006 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001007 break;
cristybb503372010-05-27 20:51:26 +00001008 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001009 {
1010 switch (fourier_info->channel)
1011 {
cristyd3090f92011-10-18 00:05:15 +00001012 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001013 default:
1014 {
cristy7d4aa382013-06-30 01:59:39 +00001015 phase_pixels[i]=QuantumScale*GetPixelRed(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001016 break;
1017 }
cristyd3090f92011-10-18 00:05:15 +00001018 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001019 {
cristy7d4aa382013-06-30 01:59:39 +00001020 phase_pixels[i]=QuantumScale*GetPixelGreen(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001021 break;
1022 }
cristyd3090f92011-10-18 00:05:15 +00001023 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001024 {
cristy7d4aa382013-06-30 01:59:39 +00001025 phase_pixels[i]=QuantumScale*GetPixelBlue(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001026 break;
1027 }
cristyd3090f92011-10-18 00:05:15 +00001028 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001029 {
cristy7d4aa382013-06-30 01:59:39 +00001030 phase_pixels[i]=QuantumScale*GetPixelBlack(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001031 break;
1032 }
cristyd3090f92011-10-18 00:05:15 +00001033 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001034 {
cristy7d4aa382013-06-30 01:59:39 +00001035 phase_pixels[i]=QuantumScale*GetPixelAlpha(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001036 break;
1037 }
cristy3ed852e2009-09-05 21:47:34 +00001038 }
1039 i++;
cristyed231572011-07-14 02:18:59 +00001040 p+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +00001041 }
1042 }
1043 if (fourier_info->modulus != MagickFalse)
1044 {
1045 i=0L;
cristybb503372010-05-27 20:51:26 +00001046 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1047 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001048 {
cristy7d4aa382013-06-30 01:59:39 +00001049 phase_pixels[i]-=0.5;
1050 phase_pixels[i]*=(2.0*MagickPI);
cristy3ed852e2009-09-05 21:47:34 +00001051 i++;
1052 }
1053 }
cristy3ed852e2009-09-05 21:47:34 +00001054 phase_view=DestroyCacheView(phase_view);
cristy13c99c42013-08-18 13:53:30 +00001055 CorrectPhaseLHS(fourier_info->width,fourier_info->height,phase_pixels);
cristy3ed852e2009-09-05 21:47:34 +00001056 if (status != MagickFalse)
1057 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +00001058 phase_pixels,inverse_pixels);
1059 (void) CopyMagickMemory(phase_pixels,inverse_pixels,fourier_info->height*
1060 fourier_info->center*sizeof(*phase_pixels));
1061 inverse_info=RelinquishVirtualMemory(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +00001062 /*
1063 Merge two sets.
1064 */
1065 i=0L;
1066 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +00001067 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1068 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001069 {
cristy56ed31c2010-03-22 00:46:21 +00001070#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy699ae5b2013-07-03 13:41:29 +00001071 fourier_pixels[i]=magnitude_pixels[i]*cos(phase_pixels[i])+I*
1072 magnitude_pixels[i]*sin(phase_pixels[i]);
cristy56ed31c2010-03-22 00:46:21 +00001073#else
cristy699ae5b2013-07-03 13:41:29 +00001074 fourier_pixels[i][0]=magnitude_pixels[i]*cos(phase_pixels[i]);
1075 fourier_pixels[i][1]=magnitude_pixels[i]*sin(phase_pixels[i]);
cristy56ed31c2010-03-22 00:46:21 +00001076#endif
cristy3ed852e2009-09-05 21:47:34 +00001077 i++;
1078 }
1079 else
cristybb503372010-05-27 20:51:26 +00001080 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1081 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001082 {
cristy56ed31c2010-03-22 00:46:21 +00001083#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy699ae5b2013-07-03 13:41:29 +00001084 fourier_pixels[i]=magnitude_pixels[i]+I*phase_pixels[i];
cristy56ed31c2010-03-22 00:46:21 +00001085#else
cristy699ae5b2013-07-03 13:41:29 +00001086 fourier_pixels[i][0]=magnitude_pixels[i];
1087 fourier_pixels[i][1]=phase_pixels[i];
cristy56ed31c2010-03-22 00:46:21 +00001088#endif
cristy3ed852e2009-09-05 21:47:34 +00001089 i++;
1090 }
cristy699ae5b2013-07-03 13:41:29 +00001091 magnitude_info=RelinquishVirtualMemory(magnitude_info);
1092 phase_info=RelinquishVirtualMemory(phase_info);
cristy3ed852e2009-09-05 21:47:34 +00001093 return(status);
1094}
1095
1096static MagickBooleanType InverseFourierTransform(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +00001097 fftw_complex *fourier_pixels,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001098{
1099 CacheView
1100 *image_view;
1101
cristy99dc0362013-09-15 18:32:54 +00001102 const char
1103 *value;
1104
cristy3ed852e2009-09-05 21:47:34 +00001105 double
cristy699ae5b2013-07-03 13:41:29 +00001106 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001107
1108 fftw_plan
1109 fftw_c2r_plan;
1110
cristy699ae5b2013-07-03 13:41:29 +00001111 MemoryInfo
1112 *source_info;
1113
cristy4c08aed2011-07-01 19:47:50 +00001114 register Quantum
cristyc4ea4a42011-01-24 01:43:30 +00001115 *q;
1116
cristybb503372010-05-27 20:51:26 +00001117 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001118 i,
1119 x;
1120
cristyc4ea4a42011-01-24 01:43:30 +00001121 ssize_t
1122 y;
cristy3ed852e2009-09-05 21:47:34 +00001123
cristy699ae5b2013-07-03 13:41:29 +00001124 source_info=AcquireVirtualMemory((size_t) fourier_info->height,
1125 fourier_info->width*sizeof(*source_pixels));
1126 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001127 {
1128 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001129 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001130 return(MagickFalse);
1131 }
cristy699ae5b2013-07-03 13:41:29 +00001132 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
cristy99dc0362013-09-15 18:32:54 +00001133 value=GetImageArtifact(image,"fourier:normalize");
cristyf8ecbfd2013-09-15 19:11:14 +00001134 if (LocaleCompare(value,"inverse") == 0)
cristy99dc0362013-09-15 18:32:54 +00001135 {
1136 double
1137 gamma;
1138
1139 /*
1140 Normalize inverse transform.
1141 */
1142 i=0L;
1143 gamma=PerceptibleReciprocal((double) fourier_info->width*
1144 fourier_info->height);
1145 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1146 for (x=0L; x < (ssize_t) fourier_info->center; x++)
1147 {
1148#if defined(MAGICKCORE_HAVE_COMPLEX_H)
1149 fourier_pixels[i]*=gamma;
1150#else
1151 fourier_pixels[i][0]*=gamma;
1152 fourier_pixels[i][1]*=gamma;
1153#endif
1154 i++;
1155 }
1156 }
cristyb5d5f722009-11-04 03:03:49 +00001157#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001158 #pragma omp critical (MagickCore_InverseFourierTransform)
1159#endif
cristydf079ac2010-09-10 01:45:44 +00001160 {
1161 fftw_c2r_plan=fftw_plan_dft_c2r_2d(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +00001162 fourier_pixels,source_pixels,FFTW_ESTIMATE);
cristydf079ac2010-09-10 01:45:44 +00001163 fftw_execute(fftw_c2r_plan);
1164 fftw_destroy_plan(fftw_c2r_plan);
1165 }
cristy3ed852e2009-09-05 21:47:34 +00001166 i=0L;
cristy46ff2672012-12-14 15:32:26 +00001167 image_view=AcquireAuthenticCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +00001168 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001169 {
cristy85812052010-09-14 17:56:15 +00001170 if (y >= (ssize_t) image->rows)
1171 break;
1172 q=GetCacheViewAuthenticPixels(image_view,0L,y,fourier_info->width >
1173 image->columns ? image->columns : fourier_info->width,1UL,exception);
cristyacd2ed22011-08-30 01:44:23 +00001174 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001175 break;
cristybb503372010-05-27 20:51:26 +00001176 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001177 {
cristy233fe582012-07-07 14:00:18 +00001178 if (x < (ssize_t) image->columns)
1179 switch (fourier_info->channel)
cristy3ed852e2009-09-05 21:47:34 +00001180 {
cristy233fe582012-07-07 14:00:18 +00001181 case RedPixelChannel:
1182 default:
1183 {
cristy699ae5b2013-07-03 13:41:29 +00001184 SetPixelRed(image,ClampToQuantum(QuantumRange*source_pixels[i]),q);
cristy233fe582012-07-07 14:00:18 +00001185 break;
1186 }
1187 case GreenPixelChannel:
1188 {
cristy699ae5b2013-07-03 13:41:29 +00001189 SetPixelGreen(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1190 q);
cristy233fe582012-07-07 14:00:18 +00001191 break;
1192 }
1193 case BluePixelChannel:
1194 {
cristy699ae5b2013-07-03 13:41:29 +00001195 SetPixelBlue(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1196 q);
cristy233fe582012-07-07 14:00:18 +00001197 break;
1198 }
1199 case BlackPixelChannel:
1200 {
cristy699ae5b2013-07-03 13:41:29 +00001201 SetPixelBlack(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1202 q);
cristy233fe582012-07-07 14:00:18 +00001203 break;
1204 }
1205 case AlphaPixelChannel:
1206 {
cristy699ae5b2013-07-03 13:41:29 +00001207 SetPixelAlpha(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1208 q);
cristy233fe582012-07-07 14:00:18 +00001209 break;
1210 }
cristy3ed852e2009-09-05 21:47:34 +00001211 }
cristy3ed852e2009-09-05 21:47:34 +00001212 i++;
cristyed231572011-07-14 02:18:59 +00001213 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001214 }
1215 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1216 break;
1217 }
1218 image_view=DestroyCacheView(image_view);
cristy699ae5b2013-07-03 13:41:29 +00001219 source_info=RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +00001220 return(MagickTrue);
1221}
1222
cristyc9550792009-11-13 20:05:42 +00001223static MagickBooleanType InverseFourierTransformChannel(
1224 const Image *magnitude_image,const Image *phase_image,
cristyd3090f92011-10-18 00:05:15 +00001225 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +00001226 Image *fourier_image,ExceptionInfo *exception)
1227{
cristy3ed852e2009-09-05 21:47:34 +00001228 fftw_complex
cristy699ae5b2013-07-03 13:41:29 +00001229 *inverse_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001230
1231 FourierInfo
1232 fourier_info;
1233
1234 MagickBooleanType
1235 status;
1236
cristy699ae5b2013-07-03 13:41:29 +00001237 MemoryInfo
1238 *inverse_info;
1239
cristy3ed852e2009-09-05 21:47:34 +00001240 size_t
1241 extent;
1242
cristyc9550792009-11-13 20:05:42 +00001243 fourier_info.width=magnitude_image->columns;
1244 if ((magnitude_image->columns != magnitude_image->rows) ||
1245 ((magnitude_image->columns % 2) != 0) ||
1246 ((magnitude_image->rows % 2) != 0))
cristy3ed852e2009-09-05 21:47:34 +00001247 {
cristyc9550792009-11-13 20:05:42 +00001248 extent=magnitude_image->columns < magnitude_image->rows ?
1249 magnitude_image->rows : magnitude_image->columns;
cristy3ed852e2009-09-05 21:47:34 +00001250 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
1251 }
1252 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +00001253 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +00001254 fourier_info.channel=channel;
1255 fourier_info.modulus=modulus;
cristy699ae5b2013-07-03 13:41:29 +00001256 inverse_info=AcquireVirtualMemory((size_t) fourier_info.height,
1257 fourier_info.center*sizeof(*inverse_pixels));
1258 if (inverse_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001259 {
1260 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001261 ResourceLimitError,"MemoryAllocationFailed","`%s'",
cristyc9550792009-11-13 20:05:42 +00001262 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001263 return(MagickFalse);
1264 }
cristy699ae5b2013-07-03 13:41:29 +00001265 inverse_pixels=(fftw_complex *) GetVirtualMemoryBlob(inverse_info);
1266 status=InverseFourier(&fourier_info,magnitude_image,phase_image,
1267 inverse_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +00001268 if (status != MagickFalse)
cristy699ae5b2013-07-03 13:41:29 +00001269 status=InverseFourierTransform(&fourier_info,inverse_pixels,fourier_image,
cristy3ed852e2009-09-05 21:47:34 +00001270 exception);
cristy699ae5b2013-07-03 13:41:29 +00001271 inverse_info=RelinquishVirtualMemory(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +00001272 return(status);
1273}
1274#endif
1275
cristyc9550792009-11-13 20:05:42 +00001276MagickExport Image *InverseFourierTransformImage(const Image *magnitude_image,
1277 const Image *phase_image,const MagickBooleanType modulus,
1278 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001279{
1280 Image
1281 *fourier_image;
1282
cristyc9550792009-11-13 20:05:42 +00001283 assert(magnitude_image != (Image *) NULL);
1284 assert(magnitude_image->signature == MagickSignature);
1285 if (magnitude_image->debug != MagickFalse)
1286 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1287 magnitude_image->filename);
1288 if (phase_image == (Image *) NULL)
1289 {
1290 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
cristyefe601c2013-01-05 17:51:12 +00001291 "TwoOrMoreImagesRequired","`%s'",magnitude_image->filename);
cristy9372a152009-11-18 01:42:16 +00001292 return((Image *) NULL);
cristyc9550792009-11-13 20:05:42 +00001293 }
cristy3ed852e2009-09-05 21:47:34 +00001294#if !defined(MAGICKCORE_FFTW_DELEGATE)
1295 fourier_image=(Image *) NULL;
1296 (void) modulus;
1297 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00001298 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristyc9550792009-11-13 20:05:42 +00001299 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001300#else
1301 {
cristyc9550792009-11-13 20:05:42 +00001302 fourier_image=CloneImage(magnitude_image,magnitude_image->columns,
1303 magnitude_image->rows,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00001304 if (fourier_image != (Image *) NULL)
1305 {
1306 MagickBooleanType
1307 is_gray,
1308 status;
1309
cristy3ed852e2009-09-05 21:47:34 +00001310 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001311 is_gray=IsImageGray(magnitude_image,exception);
cristyc9550792009-11-13 20:05:42 +00001312 if (is_gray != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001313 is_gray=IsImageGray(phase_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001314#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb34ef052010-10-07 00:12:05 +00001315 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +00001316#endif
cristy3ed852e2009-09-05 21:47:34 +00001317 {
cristyb34ef052010-10-07 00:12:05 +00001318#if defined(MAGICKCORE_OPENMP_SUPPORT)
1319 #pragma omp section
1320#endif
cristy3ed852e2009-09-05 21:47:34 +00001321 {
cristyb34ef052010-10-07 00:12:05 +00001322 MagickBooleanType
1323 thread_status;
1324
1325 if (is_gray != MagickFalse)
1326 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001327 phase_image,GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001328 else
cristyc9550792009-11-13 20:05:42 +00001329 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001330 phase_image,RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001331 if (thread_status == MagickFalse)
1332 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +00001333 }
cristyb34ef052010-10-07 00:12:05 +00001334#if defined(MAGICKCORE_OPENMP_SUPPORT)
1335 #pragma omp section
1336#endif
1337 {
1338 MagickBooleanType
1339 thread_status;
1340
1341 thread_status=MagickTrue;
1342 if (is_gray == MagickFalse)
1343 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001344 phase_image,GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001345 if (thread_status == MagickFalse)
1346 status=thread_status;
1347 }
1348#if defined(MAGICKCORE_OPENMP_SUPPORT)
1349 #pragma omp section
1350#endif
1351 {
1352 MagickBooleanType
1353 thread_status;
1354
1355 thread_status=MagickTrue;
1356 if (is_gray == MagickFalse)
1357 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001358 phase_image,BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001359 if (thread_status == MagickFalse)
1360 status=thread_status;
1361 }
1362#if defined(MAGICKCORE_OPENMP_SUPPORT)
1363 #pragma omp section
1364#endif
1365 {
1366 MagickBooleanType
1367 thread_status;
1368
1369 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001370 if (magnitude_image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +00001371 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001372 phase_image,BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001373 if (thread_status == MagickFalse)
1374 status=thread_status;
1375 }
1376#if defined(MAGICKCORE_OPENMP_SUPPORT)
1377 #pragma omp section
1378#endif
1379 {
1380 MagickBooleanType
1381 thread_status;
1382
1383 thread_status=MagickTrue;
cristy8a46d822012-08-28 23:32:39 +00001384 if (magnitude_image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +00001385 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001386 phase_image,AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001387 if (thread_status == MagickFalse)
1388 status=thread_status;
1389 }
cristy3ed852e2009-09-05 21:47:34 +00001390 }
1391 if (status == MagickFalse)
1392 fourier_image=DestroyImage(fourier_image);
1393 }
cristyb34ef052010-10-07 00:12:05 +00001394 fftw_cleanup();
cristy3ed852e2009-09-05 21:47:34 +00001395 }
1396#endif
1397 return(fourier_image);
1398}