blob: 5e22249fe1b32b0bf19961b52f2e24ef658baf00 [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);
157 return(image);
158}
159
160/*
161%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
162% %
163% %
164% %
cristy3ed852e2009-09-05 21:47:34 +0000165% 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 %
166% %
167% %
168% %
169%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
170%
171% ForwardFourierTransformImage() implements the discrete Fourier transform
172% (DFT) of the image either as a magnitude / phase or real / imaginary image
173% pair.
174%
175% The format of the ForwadFourierTransformImage method is:
176%
177% Image *ForwardFourierTransformImage(const Image *image,
178% const MagickBooleanType modulus,ExceptionInfo *exception)
179%
180% A description of each parameter follows:
181%
182% o image: the image.
183%
184% o modulus: if true, return as transform as a magnitude / phase pair
185% otherwise a real / imaginary image pair.
186%
187% o exception: return any errors or warnings in this structure.
188%
189*/
190
191#if defined(MAGICKCORE_FFTW_DELEGATE)
192
cristyc4ea4a42011-01-24 01:43:30 +0000193static MagickBooleanType RollFourier(const size_t width,const size_t height,
cristy699ae5b2013-07-03 13:41:29 +0000194 const ssize_t x_offset,const ssize_t y_offset,double *roll_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000195{
196 double
cristy699ae5b2013-07-03 13:41:29 +0000197 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000198
cristy7d4aa382013-06-30 01:59:39 +0000199 MemoryInfo
cristy699ae5b2013-07-03 13:41:29 +0000200 *source_info;
cristy7d4aa382013-06-30 01:59:39 +0000201
cristyc4ea4a42011-01-24 01:43:30 +0000202 register ssize_t
203 i,
204 x;
205
cristybb503372010-05-27 20:51:26 +0000206 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000207 u,
208 v,
209 y;
210
cristy3ed852e2009-09-05 21:47:34 +0000211 /*
cristy2da05352010-09-15 19:22:17 +0000212 Move zero frequency (DC, average color) from (0,0) to (width/2,height/2).
cristy3ed852e2009-09-05 21:47:34 +0000213 */
cristy699ae5b2013-07-03 13:41:29 +0000214 source_info=AcquireVirtualMemory(height,width*sizeof(*source_pixels));
215 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000216 return(MagickFalse);
cristy699ae5b2013-07-03 13:41:29 +0000217 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000218 i=0L;
cristybb503372010-05-27 20:51:26 +0000219 for (y=0L; y < (ssize_t) height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000220 {
221 if (y_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000222 v=((y+y_offset) < 0L) ? y+y_offset+(ssize_t) height : y+y_offset;
cristy3ed852e2009-09-05 21:47:34 +0000223 else
cristybb503372010-05-27 20:51:26 +0000224 v=((y+y_offset) > ((ssize_t) height-1L)) ? y+y_offset-(ssize_t) height :
cristy3ed852e2009-09-05 21:47:34 +0000225 y+y_offset;
cristybb503372010-05-27 20:51:26 +0000226 for (x=0L; x < (ssize_t) width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000227 {
228 if (x_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000229 u=((x+x_offset) < 0L) ? x+x_offset+(ssize_t) width : x+x_offset;
cristy3ed852e2009-09-05 21:47:34 +0000230 else
cristybb503372010-05-27 20:51:26 +0000231 u=((x+x_offset) > ((ssize_t) width-1L)) ? x+x_offset-(ssize_t) width :
cristy3ed852e2009-09-05 21:47:34 +0000232 x+x_offset;
cristy699ae5b2013-07-03 13:41:29 +0000233 source_pixels[v*width+u]=roll_pixels[i++];
cristyc4ea4a42011-01-24 01:43:30 +0000234 }
cristy3ed852e2009-09-05 21:47:34 +0000235 }
cristy699ae5b2013-07-03 13:41:29 +0000236 (void) CopyMagickMemory(roll_pixels,source_pixels,height*width*
237 sizeof(*source_pixels));
238 source_info=RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000239 return(MagickTrue);
240}
241
cristybb503372010-05-27 20:51:26 +0000242static MagickBooleanType ForwardQuadrantSwap(const size_t width,
cristy699ae5b2013-07-03 13:41:29 +0000243 const size_t height,double *source_pixels,double *forward_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000244{
cristy3ed852e2009-09-05 21:47:34 +0000245 MagickBooleanType
246 status;
247
cristybb503372010-05-27 20:51:26 +0000248 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000249 x;
250
cristyc4ea4a42011-01-24 01:43:30 +0000251 ssize_t
252 center,
253 y;
254
cristy3ed852e2009-09-05 21:47:34 +0000255 /*
256 Swap quadrants.
257 */
cristybb503372010-05-27 20:51:26 +0000258 center=(ssize_t) floor((double) width/2L)+1L;
cristy699ae5b2013-07-03 13:41:29 +0000259 status=RollFourier((size_t) center,height,0L,(ssize_t) height/2L,
260 source_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000261 if (status == MagickFalse)
262 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000263 for (y=0L; y < (ssize_t) height; y++)
264 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy3d04ed72013-08-18 17:51:01 +0000265 forward_pixels[y*width+x+width/2L]=source_pixels[y*center+x];
cristybb503372010-05-27 20:51:26 +0000266 for (y=1; y < (ssize_t) height; y++)
267 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy9b1d6c72013-08-18 17:53:56 +0000268 forward_pixels[(height-y)*width+width/2L-x-1L]=
cristy3d04ed72013-08-18 17:51:01 +0000269 source_pixels[y*center+x+1L];
cristybb503372010-05-27 20:51:26 +0000270 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy699ae5b2013-07-03 13:41:29 +0000271 forward_pixels[-x+width/2L-1L]=forward_pixels[x+width/2L+1L];
cristy3ed852e2009-09-05 21:47:34 +0000272 return(MagickTrue);
273}
274
cristyc4ea4a42011-01-24 01:43:30 +0000275static void CorrectPhaseLHS(const size_t width,const size_t height,
cristy699ae5b2013-07-03 13:41:29 +0000276 double *fourier_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000277{
cristybb503372010-05-27 20:51:26 +0000278 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000279 x;
280
cristy9d314ff2011-03-09 01:30:28 +0000281 ssize_t
282 y;
283
cristybb503372010-05-27 20:51:26 +0000284 for (y=0L; y < (ssize_t) height; y++)
285 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy699ae5b2013-07-03 13:41:29 +0000286 fourier_pixels[y*width+x]*=(-1.0);
cristy3ed852e2009-09-05 21:47:34 +0000287}
288
289static MagickBooleanType ForwardFourier(const FourierInfo *fourier_info,
290 Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
291{
292 CacheView
293 *magnitude_view,
294 *phase_view;
295
296 double
cristy7d4aa382013-06-30 01:59:39 +0000297 *magnitude_pixels,
298 *phase_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000299
300 Image
301 *magnitude_image,
302 *phase_image;
303
cristy3ed852e2009-09-05 21:47:34 +0000304 MagickBooleanType
305 status;
306
cristy7d4aa382013-06-30 01:59:39 +0000307 MemoryInfo
308 *magnitude_info,
309 *phase_info;
310
cristy4c08aed2011-07-01 19:47:50 +0000311 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000312 *q;
313
cristyf5742792012-11-27 18:31:26 +0000314 register ssize_t
315 x;
316
cristyc4ea4a42011-01-24 01:43:30 +0000317 ssize_t
318 i,
319 y;
320
cristy3ed852e2009-09-05 21:47:34 +0000321 magnitude_image=GetFirstImageInList(image);
322 phase_image=GetNextImageInList(image);
323 if (phase_image == (Image *) NULL)
324 {
325 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
cristyefe601c2013-01-05 17:51:12 +0000326 "TwoOrMoreImagesRequired","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000327 return(MagickFalse);
328 }
329 /*
330 Create "Fourier Transform" image from constituent arrays.
331 */
cristy7d4aa382013-06-30 01:59:39 +0000332 magnitude_info=AcquireVirtualMemory((size_t) fourier_info->height,
333 fourier_info->width*sizeof(*magnitude_pixels));
334 phase_info=AcquireVirtualMemory((size_t) fourier_info->height,
335 fourier_info->width*sizeof(*phase_pixels));
cristybb3c02e2013-07-02 00:43:10 +0000336 if ((magnitude_info == (MemoryInfo *) NULL) ||
337 (phase_info == (MemoryInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000338 {
cristy7d4aa382013-06-30 01:59:39 +0000339 if (phase_info != (MemoryInfo *) NULL)
340 phase_info=RelinquishVirtualMemory(phase_info);
341 if (magnitude_info != (MemoryInfo *) NULL)
342 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000343 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000344 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000345 return(MagickFalse);
346 }
cristy7d4aa382013-06-30 01:59:39 +0000347 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
348 (void) ResetMagickMemory(magnitude_pixels,0,fourier_info->height*
349 fourier_info->width*sizeof(*magnitude_pixels));
cristybb3c02e2013-07-02 00:43:10 +0000350 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
cristy7d4aa382013-06-30 01:59:39 +0000351 (void) ResetMagickMemory(phase_pixels,0,fourier_info->height*
352 fourier_info->width*sizeof(*phase_pixels));
cristy13c99c42013-08-18 13:53:30 +0000353 status=ForwardQuadrantSwap(fourier_info->width,fourier_info->height,
cristy7d4aa382013-06-30 01:59:39 +0000354 magnitude,magnitude_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000355 if (status != MagickFalse)
cristy13c99c42013-08-18 13:53:30 +0000356 status=ForwardQuadrantSwap(fourier_info->width,fourier_info->height,phase,
cristy7d4aa382013-06-30 01:59:39 +0000357 phase_pixels);
cristy13c99c42013-08-18 13:53:30 +0000358 CorrectPhaseLHS(fourier_info->width,fourier_info->height,phase_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000359 if (fourier_info->modulus != MagickFalse)
360 {
361 i=0L;
cristybb503372010-05-27 20:51:26 +0000362 for (y=0L; y < (ssize_t) fourier_info->height; y++)
363 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000364 {
cristy7d4aa382013-06-30 01:59:39 +0000365 phase_pixels[i]/=(2.0*MagickPI);
366 phase_pixels[i]+=0.5;
cristy3ed852e2009-09-05 21:47:34 +0000367 i++;
368 }
369 }
cristy46ff2672012-12-14 15:32:26 +0000370 magnitude_view=AcquireAuthenticCacheView(magnitude_image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000371 i=0L;
cristybb503372010-05-27 20:51:26 +0000372 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000373 {
374 q=GetCacheViewAuthenticPixels(magnitude_view,0L,y,fourier_info->height,1UL,
375 exception);
cristyacd2ed22011-08-30 01:44:23 +0000376 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000377 break;
cristybb503372010-05-27 20:51:26 +0000378 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000379 {
380 switch (fourier_info->channel)
381 {
cristyd3090f92011-10-18 00:05:15 +0000382 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000383 default:
384 {
cristy4c08aed2011-07-01 19:47:50 +0000385 SetPixelRed(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000386 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000387 break;
388 }
cristyd3090f92011-10-18 00:05:15 +0000389 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000390 {
cristy4c08aed2011-07-01 19:47:50 +0000391 SetPixelGreen(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000392 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000393 break;
394 }
cristyd3090f92011-10-18 00:05:15 +0000395 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000396 {
cristy4c08aed2011-07-01 19:47:50 +0000397 SetPixelBlue(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000398 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000399 break;
400 }
cristyd3090f92011-10-18 00:05:15 +0000401 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000402 {
cristy4c08aed2011-07-01 19:47:50 +0000403 SetPixelBlack(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000404 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000405 break;
406 }
cristyd3090f92011-10-18 00:05:15 +0000407 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000408 {
cristy4c08aed2011-07-01 19:47:50 +0000409 SetPixelAlpha(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000410 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000411 break;
412 }
cristy3ed852e2009-09-05 21:47:34 +0000413 }
414 i++;
cristyed231572011-07-14 02:18:59 +0000415 q+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +0000416 }
417 status=SyncCacheViewAuthenticPixels(magnitude_view,exception);
418 if (status == MagickFalse)
419 break;
420 }
cristydb070952012-04-20 14:33:00 +0000421 magnitude_view=DestroyCacheView(magnitude_view);
cristy3ed852e2009-09-05 21:47:34 +0000422 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000423 phase_view=AcquireAuthenticCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +0000424 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000425 {
426 q=GetCacheViewAuthenticPixels(phase_view,0L,y,fourier_info->height,1UL,
427 exception);
cristyacd2ed22011-08-30 01:44:23 +0000428 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000429 break;
cristybb503372010-05-27 20:51:26 +0000430 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000431 {
432 switch (fourier_info->channel)
433 {
cristyd3090f92011-10-18 00:05:15 +0000434 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000435 default:
436 {
cristy4c08aed2011-07-01 19:47:50 +0000437 SetPixelRed(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000438 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000439 break;
440 }
cristyd3090f92011-10-18 00:05:15 +0000441 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000442 {
cristy4c08aed2011-07-01 19:47:50 +0000443 SetPixelGreen(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000444 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000445 break;
446 }
cristyd3090f92011-10-18 00:05:15 +0000447 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000448 {
cristy4c08aed2011-07-01 19:47:50 +0000449 SetPixelBlue(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000450 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000451 break;
452 }
cristyd3090f92011-10-18 00:05:15 +0000453 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000454 {
cristy4c08aed2011-07-01 19:47:50 +0000455 SetPixelBlack(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000456 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000457 break;
458 }
cristyd3090f92011-10-18 00:05:15 +0000459 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000460 {
cristy4c08aed2011-07-01 19:47:50 +0000461 SetPixelAlpha(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000462 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000463 break;
464 }
cristy3ed852e2009-09-05 21:47:34 +0000465 }
466 i++;
cristyed231572011-07-14 02:18:59 +0000467 q+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +0000468 }
469 status=SyncCacheViewAuthenticPixels(phase_view,exception);
470 if (status == MagickFalse)
471 break;
472 }
473 phase_view=DestroyCacheView(phase_view);
cristy7d4aa382013-06-30 01:59:39 +0000474 phase_info=RelinquishVirtualMemory(phase_info);
475 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000476 return(status);
477}
478
479static MagickBooleanType ForwardFourierTransform(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +0000480 const Image *image,double *magnitude_pixels,double *phase_pixels,
481 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000482{
483 CacheView
484 *image_view;
485
cristy99dc0362013-09-15 18:32:54 +0000486 const char
487 *value;
488
cristy3ed852e2009-09-05 21:47:34 +0000489 double
cristybb3c02e2013-07-02 00:43:10 +0000490 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000491
492 fftw_complex
cristy699ae5b2013-07-03 13:41:29 +0000493 *forward_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000494
495 fftw_plan
496 fftw_r2c_plan;
497
cristy7d4aa382013-06-30 01:59:39 +0000498 MemoryInfo
cristy699ae5b2013-07-03 13:41:29 +0000499 *forward_info,
cristy7d4aa382013-06-30 01:59:39 +0000500 *source_info;
501
cristy4c08aed2011-07-01 19:47:50 +0000502 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000503 *p;
504
cristybb503372010-05-27 20:51:26 +0000505 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000506 i,
507 x;
508
cristyc4ea4a42011-01-24 01:43:30 +0000509 ssize_t
510 y;
511
cristy3ed852e2009-09-05 21:47:34 +0000512 /*
513 Generate the forward Fourier transform.
514 */
cristy7d4aa382013-06-30 01:59:39 +0000515 source_info=AcquireVirtualMemory((size_t) fourier_info->height,
cristybb3c02e2013-07-02 00:43:10 +0000516 fourier_info->width*sizeof(*source_pixels));
cristy7d4aa382013-06-30 01:59:39 +0000517 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000518 {
519 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000520 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000521 return(MagickFalse);
522 }
cristybb3c02e2013-07-02 00:43:10 +0000523 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
524 ResetMagickMemory(source_pixels,0,fourier_info->height*fourier_info->width*
525 sizeof(*source_pixels));
cristy3ed852e2009-09-05 21:47:34 +0000526 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000527 image_view=AcquireVirtualCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +0000528 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000529 {
530 p=GetCacheViewVirtualPixels(image_view,0L,y,fourier_info->width,1UL,
531 exception);
cristy4c08aed2011-07-01 19:47:50 +0000532 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000533 break;
cristybb503372010-05-27 20:51:26 +0000534 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000535 {
536 switch (fourier_info->channel)
537 {
cristyd3090f92011-10-18 00:05:15 +0000538 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000539 default:
540 {
cristybb3c02e2013-07-02 00:43:10 +0000541 source_pixels[i]=QuantumScale*GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000542 break;
543 }
cristyd3090f92011-10-18 00:05:15 +0000544 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000545 {
cristybb3c02e2013-07-02 00:43:10 +0000546 source_pixels[i]=QuantumScale*GetPixelGreen(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000547 break;
548 }
cristyd3090f92011-10-18 00:05:15 +0000549 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000550 {
cristybb3c02e2013-07-02 00:43:10 +0000551 source_pixels[i]=QuantumScale*GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000552 break;
553 }
cristyd3090f92011-10-18 00:05:15 +0000554 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000555 {
cristybb3c02e2013-07-02 00:43:10 +0000556 source_pixels[i]=QuantumScale*GetPixelBlack(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000557 break;
558 }
cristyd3090f92011-10-18 00:05:15 +0000559 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000560 {
cristybb3c02e2013-07-02 00:43:10 +0000561 source_pixels[i]=QuantumScale*GetPixelAlpha(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000562 break;
563 }
cristy3ed852e2009-09-05 21:47:34 +0000564 }
565 i++;
cristyed231572011-07-14 02:18:59 +0000566 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000567 }
568 }
569 image_view=DestroyCacheView(image_view);
cristy699ae5b2013-07-03 13:41:29 +0000570 forward_info=AcquireVirtualMemory((size_t) fourier_info->height,
571 fourier_info->center*sizeof(*forward_pixels));
572 if (forward_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000573 {
574 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000575 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristybb3c02e2013-07-02 00:43:10 +0000576 source_info=(MemoryInfo *) RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000577 return(MagickFalse);
578 }
cristy699ae5b2013-07-03 13:41:29 +0000579 forward_pixels=(fftw_complex *) GetVirtualMemoryBlob(forward_info);
cristyb5d5f722009-11-04 03:03:49 +0000580#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000581 #pragma omp critical (MagickCore_ForwardFourierTransform)
582#endif
cristy70841a12012-10-27 20:52:57 +0000583 fftw_r2c_plan=fftw_plan_dft_r2c_2d(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +0000584 source_pixels,forward_pixels,FFTW_ESTIMATE);
cristy3ed852e2009-09-05 21:47:34 +0000585 fftw_execute(fftw_r2c_plan);
586 fftw_destroy_plan(fftw_r2c_plan);
cristybb3c02e2013-07-02 00:43:10 +0000587 source_info=(MemoryInfo *) RelinquishVirtualMemory(source_info);
cristy99dc0362013-09-15 18:32:54 +0000588 value=GetImageArtifact(image,"fourier:normalize");
cristyf8ecbfd2013-09-15 19:11:14 +0000589 if ((value == (const char *) NULL) || (LocaleCompare(value,"forward") == 0))
cristy56ed31c2010-03-22 00:46:21 +0000590 {
cristy99dc0362013-09-15 18:32:54 +0000591 double
592 gamma;
593
594 /*
595 Normalize Fourier transform.
596 */
597 i=0L;
598 gamma=PerceptibleReciprocal((double) fourier_info->width*
599 fourier_info->height);
600 for (y=0L; y < (ssize_t) fourier_info->height; y++)
601 for (x=0L; x < (ssize_t) fourier_info->center; x++)
602 {
cristy56ed31c2010-03-22 00:46:21 +0000603#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy99dc0362013-09-15 18:32:54 +0000604 forward_pixels[i]*=gamma;
cristy56ed31c2010-03-22 00:46:21 +0000605#else
cristy99dc0362013-09-15 18:32:54 +0000606 forward_pixels[i][0]*=gamma;
607 forward_pixels[i][1]*=gamma;
cristy56ed31c2010-03-22 00:46:21 +0000608#endif
cristy99dc0362013-09-15 18:32:54 +0000609 i++;
610 }
cristy56ed31c2010-03-22 00:46:21 +0000611 }
cristy3ed852e2009-09-05 21:47:34 +0000612 /*
613 Generate magnitude and phase (or real and imaginary).
614 */
615 i=0L;
616 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000617 for (y=0L; y < (ssize_t) fourier_info->height; y++)
618 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000619 {
cristy699ae5b2013-07-03 13:41:29 +0000620 magnitude_pixels[i]=cabs(forward_pixels[i]);
621 phase_pixels[i]=carg(forward_pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000622 i++;
623 }
624 else
cristybb503372010-05-27 20:51:26 +0000625 for (y=0L; y < (ssize_t) fourier_info->height; y++)
626 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000627 {
cristy699ae5b2013-07-03 13:41:29 +0000628 magnitude_pixels[i]=creal(forward_pixels[i]);
629 phase_pixels[i]=cimag(forward_pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000630 i++;
631 }
cristy699ae5b2013-07-03 13:41:29 +0000632 forward_info=(MemoryInfo *) RelinquishVirtualMemory(forward_info);
cristy3ed852e2009-09-05 21:47:34 +0000633 return(MagickTrue);
634}
635
636static MagickBooleanType ForwardFourierTransformChannel(const Image *image,
cristyd3090f92011-10-18 00:05:15 +0000637 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +0000638 Image *fourier_image,ExceptionInfo *exception)
639{
640 double
cristyce9fe782013-07-03 00:59:41 +0000641 *magnitude_pixels,
642 *phase_pixels;
cristybb3c02e2013-07-02 00:43:10 +0000643
cristy56ed31c2010-03-22 00:46:21 +0000644 FourierInfo
645 fourier_info;
646
cristyc4ea4a42011-01-24 01:43:30 +0000647 MagickBooleanType
648 status;
649
cristyce9fe782013-07-03 00:59:41 +0000650 MemoryInfo
651 *magnitude_info,
652 *phase_info;
653
cristy3ed852e2009-09-05 21:47:34 +0000654 size_t
655 extent;
656
657 fourier_info.width=image->columns;
658 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
659 ((image->rows % 2) != 0))
660 {
661 extent=image->columns < image->rows ? image->rows : image->columns;
662 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
663 }
664 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +0000665 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +0000666 fourier_info.channel=channel;
667 fourier_info.modulus=modulus;
cristyce9fe782013-07-03 00:59:41 +0000668 magnitude_info=AcquireVirtualMemory((size_t) fourier_info.height,
669 fourier_info.center*sizeof(*magnitude_pixels));
670 phase_info=AcquireVirtualMemory((size_t) fourier_info.height,
671 fourier_info.center*sizeof(*phase_pixels));
672 if ((magnitude_info == (MemoryInfo *) NULL) ||
673 (phase_info == (MemoryInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000674 {
cristyce9fe782013-07-03 00:59:41 +0000675 if (phase_info != (MemoryInfo *) NULL)
676 phase_info=RelinquishVirtualMemory(phase_info);
677 if (magnitude_info == (MemoryInfo *) NULL)
678 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000679 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000680 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000681 return(MagickFalse);
682 }
cristyce9fe782013-07-03 00:59:41 +0000683 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
684 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
685 status=ForwardFourierTransform(&fourier_info,image,magnitude_pixels,
686 phase_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +0000687 if (status != MagickFalse)
cristyce9fe782013-07-03 00:59:41 +0000688 status=ForwardFourier(&fourier_info,fourier_image,magnitude_pixels,
689 phase_pixels,exception);
690 phase_info=RelinquishVirtualMemory(phase_info);
691 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000692 return(status);
693}
694#endif
695
696MagickExport Image *ForwardFourierTransformImage(const Image *image,
697 const MagickBooleanType modulus,ExceptionInfo *exception)
698{
699 Image
700 *fourier_image;
701
702 fourier_image=NewImageList();
703#if !defined(MAGICKCORE_FFTW_DELEGATE)
704 (void) modulus;
705 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000706 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristy3ed852e2009-09-05 21:47:34 +0000707 image->filename);
708#else
709 {
710 Image
711 *magnitude_image;
712
cristybb503372010-05-27 20:51:26 +0000713 size_t
cristy3ed852e2009-09-05 21:47:34 +0000714 extent,
715 width;
716
717 width=image->columns;
718 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
719 ((image->rows % 2) != 0))
720 {
721 extent=image->columns < image->rows ? image->rows : image->columns;
722 width=(extent & 0x01) == 1 ? extent+1UL : extent;
723 }
724 magnitude_image=CloneImage(image,width,width,MagickFalse,exception);
725 if (magnitude_image != (Image *) NULL)
726 {
727 Image
728 *phase_image;
729
730 magnitude_image->storage_class=DirectClass;
731 magnitude_image->depth=32UL;
732 phase_image=CloneImage(image,width,width,MagickFalse,exception);
733 if (phase_image == (Image *) NULL)
734 magnitude_image=DestroyImage(magnitude_image);
735 else
736 {
737 MagickBooleanType
738 is_gray,
739 status;
740
cristy3ed852e2009-09-05 21:47:34 +0000741 phase_image->storage_class=DirectClass;
742 phase_image->depth=32UL;
743 AppendImageToList(&fourier_image,magnitude_image);
744 AppendImageToList(&fourier_image,phase_image);
745 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000746 is_gray=IsImageGray(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000747#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy564a5692012-01-20 23:56:26 +0000748 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +0000749#endif
cristy3ed852e2009-09-05 21:47:34 +0000750 {
cristyb34ef052010-10-07 00:12:05 +0000751#if defined(MAGICKCORE_OPENMP_SUPPORT)
752 #pragma omp section
753#endif
cristy3ed852e2009-09-05 21:47:34 +0000754 {
cristyb34ef052010-10-07 00:12:05 +0000755 MagickBooleanType
756 thread_status;
757
758 if (is_gray != MagickFalse)
759 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000760 GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000761 else
762 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000763 RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000764 if (thread_status == MagickFalse)
765 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +0000766 }
cristyb34ef052010-10-07 00:12:05 +0000767#if defined(MAGICKCORE_OPENMP_SUPPORT)
768 #pragma omp section
769#endif
770 {
771 MagickBooleanType
772 thread_status;
773
774 thread_status=MagickTrue;
775 if (is_gray == MagickFalse)
776 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000777 GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000778 if (thread_status == MagickFalse)
779 status=thread_status;
780 }
781#if defined(MAGICKCORE_OPENMP_SUPPORT)
782 #pragma omp section
783#endif
784 {
785 MagickBooleanType
786 thread_status;
787
788 thread_status=MagickTrue;
789 if (is_gray == MagickFalse)
790 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000791 BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000792 if (thread_status == MagickFalse)
793 status=thread_status;
794 }
795#if defined(MAGICKCORE_OPENMP_SUPPORT)
796 #pragma omp section
797#endif
798 {
799 MagickBooleanType
800 thread_status;
801
802 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000803 if (image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +0000804 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000805 BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000806 if (thread_status == MagickFalse)
807 status=thread_status;
808 }
809#if defined(MAGICKCORE_OPENMP_SUPPORT)
810 #pragma omp section
811#endif
812 {
813 MagickBooleanType
814 thread_status;
815
816 thread_status=MagickTrue;
cristy8a46d822012-08-28 23:32:39 +0000817 if (image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +0000818 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000819 AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000820 if (thread_status == MagickFalse)
821 status=thread_status;
822 }
cristy3ed852e2009-09-05 21:47:34 +0000823 }
824 if (status == MagickFalse)
825 fourier_image=DestroyImageList(fourier_image);
826 fftw_cleanup();
827 }
828 }
829 }
830#endif
831 return(fourier_image);
832}
833
834/*
835%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
836% %
837% %
838% %
839% 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 %
840% %
841% %
842% %
843%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
844%
845% InverseFourierTransformImage() implements the inverse discrete Fourier
846% transform (DFT) of the image either as a magnitude / phase or real /
847% imaginary image pair.
848%
849% The format of the InverseFourierTransformImage method is:
850%
cristyc9550792009-11-13 20:05:42 +0000851% Image *InverseFourierTransformImage(const Image *magnitude_image,
852% const Image *phase_image,const MagickBooleanType modulus,
853% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000854%
855% A description of each parameter follows:
856%
cristyc9550792009-11-13 20:05:42 +0000857% o magnitude_image: the magnitude or real image.
858%
859% o phase_image: the phase or imaginary image.
cristy3ed852e2009-09-05 21:47:34 +0000860%
861% o modulus: if true, return transform as a magnitude / phase pair
862% otherwise a real / imaginary image pair.
863%
864% o exception: return any errors or warnings in this structure.
865%
866*/
867
868#if defined(MAGICKCORE_FFTW_DELEGATE)
cristybb503372010-05-27 20:51:26 +0000869static MagickBooleanType InverseQuadrantSwap(const size_t width,
870 const size_t height,const double *source,double *destination)
cristy3ed852e2009-09-05 21:47:34 +0000871{
cristyc4ea4a42011-01-24 01:43:30 +0000872 register ssize_t
873 x;
874
cristybb503372010-05-27 20:51:26 +0000875 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000876 center,
877 y;
878
cristy3ed852e2009-09-05 21:47:34 +0000879 /*
880 Swap quadrants.
881 */
cristy233fe582012-07-07 14:00:18 +0000882 center=(ssize_t) floor((double) width/2L)+1L;
cristybb503372010-05-27 20:51:26 +0000883 for (y=1L; y < (ssize_t) height; y++)
884 for (x=0L; x < (ssize_t) (width/2L+1L); x++)
cristy9b1d6c72013-08-18 17:53:56 +0000885 destination[(height-y)*center-x+width/2L]=source[y*width+x];
cristybb503372010-05-27 20:51:26 +0000886 for (y=0L; y < (ssize_t) height; y++)
cristy3d04ed72013-08-18 17:51:01 +0000887 destination[y*center]=source[y*width+width/2L];
cristy3ed852e2009-09-05 21:47:34 +0000888 for (x=0L; x < center; x++)
889 destination[x]=source[center-x-1L];
cristybb503372010-05-27 20:51:26 +0000890 return(RollFourier(center,height,0L,(ssize_t) height/-2L,destination));
cristy3ed852e2009-09-05 21:47:34 +0000891}
892
893static MagickBooleanType InverseFourier(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +0000894 const Image *magnitude_image,const Image *phase_image,
895 fftw_complex *fourier_pixels,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000896{
897 CacheView
898 *magnitude_view,
899 *phase_view;
900
901 double
cristy699ae5b2013-07-03 13:41:29 +0000902 *inverse_pixels,
cristy7d4aa382013-06-30 01:59:39 +0000903 *magnitude_pixels,
904 *phase_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000905
cristy3ed852e2009-09-05 21:47:34 +0000906 MagickBooleanType
907 status;
908
cristy699ae5b2013-07-03 13:41:29 +0000909 MemoryInfo
910 *inverse_info,
911 *magnitude_info,
912 *phase_info;
913
cristy4c08aed2011-07-01 19:47:50 +0000914 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000915 *p;
916
cristybb503372010-05-27 20:51:26 +0000917 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000918 i,
919 x;
920
cristyc4ea4a42011-01-24 01:43:30 +0000921 ssize_t
922 y;
923
cristy3ed852e2009-09-05 21:47:34 +0000924 /*
925 Inverse fourier - read image and break down into a double array.
926 */
cristy699ae5b2013-07-03 13:41:29 +0000927 magnitude_info=AcquireVirtualMemory((size_t)fourier_info->height,
928 fourier_info->width*sizeof(*magnitude_pixels));
929 phase_info=AcquireVirtualMemory((size_t) fourier_info->height,
cristybb3c02e2013-07-02 00:43:10 +0000930 fourier_info->width*sizeof(*phase_pixels));
cristy699ae5b2013-07-03 13:41:29 +0000931 inverse_info=AcquireVirtualMemory((size_t) fourier_info->height,
932 fourier_info->center*sizeof(*inverse_pixels));
933 if ((magnitude_info == (MemoryInfo *) NULL) ||
934 (phase_info == (MemoryInfo *) NULL) ||
935 (inverse_info == (MemoryInfo *) NULL))
cristybb3c02e2013-07-02 00:43:10 +0000936 {
cristy699ae5b2013-07-03 13:41:29 +0000937 if (magnitude_info != (MemoryInfo *) NULL)
938 magnitude_info=RelinquishVirtualMemory(magnitude_info);
939 if (phase_info != (MemoryInfo *) NULL)
940 phase_info=RelinquishVirtualMemory(phase_info);
941 if (inverse_info != (MemoryInfo *) NULL)
942 inverse_info=RelinquishVirtualMemory(inverse_info);
cristybb3c02e2013-07-02 00:43:10 +0000943 (void) ThrowMagickException(exception,GetMagickModule(),
944 ResourceLimitError,"MemoryAllocationFailed","`%s'",
945 magnitude_image->filename);
cristybb3c02e2013-07-02 00:43:10 +0000946 return(MagickFalse);
947 }
cristy699ae5b2013-07-03 13:41:29 +0000948 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
949 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
950 inverse_pixels=(double *) GetVirtualMemoryBlob(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +0000951 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000952 magnitude_view=AcquireVirtualCacheView(magnitude_image,exception);
cristybb503372010-05-27 20:51:26 +0000953 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000954 {
955 p=GetCacheViewVirtualPixels(magnitude_view,0L,y,fourier_info->width,1UL,
956 exception);
cristy4c08aed2011-07-01 19:47:50 +0000957 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000958 break;
cristybb503372010-05-27 20:51:26 +0000959 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000960 {
961 switch (fourier_info->channel)
962 {
cristyd3090f92011-10-18 00:05:15 +0000963 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000964 default:
965 {
cristy7d4aa382013-06-30 01:59:39 +0000966 magnitude_pixels[i]=QuantumScale*GetPixelRed(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000967 break;
968 }
cristyd3090f92011-10-18 00:05:15 +0000969 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000970 {
cristy7d4aa382013-06-30 01:59:39 +0000971 magnitude_pixels[i]=QuantumScale*GetPixelGreen(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000972 break;
973 }
cristyd3090f92011-10-18 00:05:15 +0000974 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000975 {
cristy7d4aa382013-06-30 01:59:39 +0000976 magnitude_pixels[i]=QuantumScale*GetPixelBlue(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000977 break;
978 }
cristyd3090f92011-10-18 00:05:15 +0000979 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000980 {
cristy7d4aa382013-06-30 01:59:39 +0000981 magnitude_pixels[i]=QuantumScale*GetPixelBlack(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000982 break;
983 }
cristyd3090f92011-10-18 00:05:15 +0000984 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000985 {
cristy7d4aa382013-06-30 01:59:39 +0000986 magnitude_pixels[i]=QuantumScale*GetPixelAlpha(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000987 break;
988 }
cristy3ed852e2009-09-05 21:47:34 +0000989 }
990 i++;
cristyed231572011-07-14 02:18:59 +0000991 p+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +0000992 }
993 }
cristy699ae5b2013-07-03 13:41:29 +0000994 magnitude_view=DestroyCacheView(magnitude_view);
995 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
996 magnitude_pixels,inverse_pixels);
997 (void) CopyMagickMemory(magnitude_pixels,inverse_pixels,fourier_info->height*
998 fourier_info->center*sizeof(*magnitude_pixels));
cristy3ed852e2009-09-05 21:47:34 +0000999 i=0L;
cristy46ff2672012-12-14 15:32:26 +00001000 phase_view=AcquireVirtualCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +00001001 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001002 {
1003 p=GetCacheViewVirtualPixels(phase_view,0,y,fourier_info->width,1,
1004 exception);
cristy4c08aed2011-07-01 19:47:50 +00001005 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001006 break;
cristybb503372010-05-27 20:51:26 +00001007 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001008 {
1009 switch (fourier_info->channel)
1010 {
cristyd3090f92011-10-18 00:05:15 +00001011 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001012 default:
1013 {
cristy7d4aa382013-06-30 01:59:39 +00001014 phase_pixels[i]=QuantumScale*GetPixelRed(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001015 break;
1016 }
cristyd3090f92011-10-18 00:05:15 +00001017 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001018 {
cristy7d4aa382013-06-30 01:59:39 +00001019 phase_pixels[i]=QuantumScale*GetPixelGreen(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001020 break;
1021 }
cristyd3090f92011-10-18 00:05:15 +00001022 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001023 {
cristy7d4aa382013-06-30 01:59:39 +00001024 phase_pixels[i]=QuantumScale*GetPixelBlue(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001025 break;
1026 }
cristyd3090f92011-10-18 00:05:15 +00001027 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001028 {
cristy7d4aa382013-06-30 01:59:39 +00001029 phase_pixels[i]=QuantumScale*GetPixelBlack(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001030 break;
1031 }
cristyd3090f92011-10-18 00:05:15 +00001032 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001033 {
cristy7d4aa382013-06-30 01:59:39 +00001034 phase_pixels[i]=QuantumScale*GetPixelAlpha(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001035 break;
1036 }
cristy3ed852e2009-09-05 21:47:34 +00001037 }
1038 i++;
cristyed231572011-07-14 02:18:59 +00001039 p+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +00001040 }
1041 }
1042 if (fourier_info->modulus != MagickFalse)
1043 {
1044 i=0L;
cristybb503372010-05-27 20:51:26 +00001045 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1046 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001047 {
cristy7d4aa382013-06-30 01:59:39 +00001048 phase_pixels[i]-=0.5;
1049 phase_pixels[i]*=(2.0*MagickPI);
cristy3ed852e2009-09-05 21:47:34 +00001050 i++;
1051 }
1052 }
cristy3ed852e2009-09-05 21:47:34 +00001053 phase_view=DestroyCacheView(phase_view);
cristy13c99c42013-08-18 13:53:30 +00001054 CorrectPhaseLHS(fourier_info->width,fourier_info->height,phase_pixels);
cristy3ed852e2009-09-05 21:47:34 +00001055 if (status != MagickFalse)
1056 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +00001057 phase_pixels,inverse_pixels);
1058 (void) CopyMagickMemory(phase_pixels,inverse_pixels,fourier_info->height*
1059 fourier_info->center*sizeof(*phase_pixels));
1060 inverse_info=RelinquishVirtualMemory(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +00001061 /*
1062 Merge two sets.
1063 */
1064 i=0L;
1065 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +00001066 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1067 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001068 {
cristy56ed31c2010-03-22 00:46:21 +00001069#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy699ae5b2013-07-03 13:41:29 +00001070 fourier_pixels[i]=magnitude_pixels[i]*cos(phase_pixels[i])+I*
1071 magnitude_pixels[i]*sin(phase_pixels[i]);
cristy56ed31c2010-03-22 00:46:21 +00001072#else
cristy699ae5b2013-07-03 13:41:29 +00001073 fourier_pixels[i][0]=magnitude_pixels[i]*cos(phase_pixels[i]);
1074 fourier_pixels[i][1]=magnitude_pixels[i]*sin(phase_pixels[i]);
cristy56ed31c2010-03-22 00:46:21 +00001075#endif
cristy3ed852e2009-09-05 21:47:34 +00001076 i++;
1077 }
1078 else
cristybb503372010-05-27 20:51:26 +00001079 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1080 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001081 {
cristy56ed31c2010-03-22 00:46:21 +00001082#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy699ae5b2013-07-03 13:41:29 +00001083 fourier_pixels[i]=magnitude_pixels[i]+I*phase_pixels[i];
cristy56ed31c2010-03-22 00:46:21 +00001084#else
cristy699ae5b2013-07-03 13:41:29 +00001085 fourier_pixels[i][0]=magnitude_pixels[i];
1086 fourier_pixels[i][1]=phase_pixels[i];
cristy56ed31c2010-03-22 00:46:21 +00001087#endif
cristy3ed852e2009-09-05 21:47:34 +00001088 i++;
1089 }
cristy699ae5b2013-07-03 13:41:29 +00001090 magnitude_info=RelinquishVirtualMemory(magnitude_info);
1091 phase_info=RelinquishVirtualMemory(phase_info);
cristy3ed852e2009-09-05 21:47:34 +00001092 return(status);
1093}
1094
1095static MagickBooleanType InverseFourierTransform(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +00001096 fftw_complex *fourier_pixels,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001097{
1098 CacheView
1099 *image_view;
1100
cristy99dc0362013-09-15 18:32:54 +00001101 const char
1102 *value;
1103
cristy3ed852e2009-09-05 21:47:34 +00001104 double
cristy699ae5b2013-07-03 13:41:29 +00001105 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001106
1107 fftw_plan
1108 fftw_c2r_plan;
1109
cristy699ae5b2013-07-03 13:41:29 +00001110 MemoryInfo
1111 *source_info;
1112
cristy4c08aed2011-07-01 19:47:50 +00001113 register Quantum
cristyc4ea4a42011-01-24 01:43:30 +00001114 *q;
1115
cristybb503372010-05-27 20:51:26 +00001116 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001117 i,
1118 x;
1119
cristyc4ea4a42011-01-24 01:43:30 +00001120 ssize_t
1121 y;
cristy3ed852e2009-09-05 21:47:34 +00001122
cristy699ae5b2013-07-03 13:41:29 +00001123 source_info=AcquireVirtualMemory((size_t) fourier_info->height,
1124 fourier_info->width*sizeof(*source_pixels));
1125 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001126 {
1127 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001128 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001129 return(MagickFalse);
1130 }
cristy699ae5b2013-07-03 13:41:29 +00001131 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
cristy99dc0362013-09-15 18:32:54 +00001132 value=GetImageArtifact(image,"fourier:normalize");
cristyf8ecbfd2013-09-15 19:11:14 +00001133 if (LocaleCompare(value,"inverse") == 0)
cristy99dc0362013-09-15 18:32:54 +00001134 {
1135 double
1136 gamma;
1137
1138 /*
1139 Normalize inverse transform.
1140 */
1141 i=0L;
1142 gamma=PerceptibleReciprocal((double) fourier_info->width*
1143 fourier_info->height);
1144 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1145 for (x=0L; x < (ssize_t) fourier_info->center; x++)
1146 {
1147#if defined(MAGICKCORE_HAVE_COMPLEX_H)
1148 fourier_pixels[i]*=gamma;
1149#else
1150 fourier_pixels[i][0]*=gamma;
1151 fourier_pixels[i][1]*=gamma;
1152#endif
1153 i++;
1154 }
1155 }
cristyb5d5f722009-11-04 03:03:49 +00001156#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001157 #pragma omp critical (MagickCore_InverseFourierTransform)
1158#endif
cristydf079ac2010-09-10 01:45:44 +00001159 {
1160 fftw_c2r_plan=fftw_plan_dft_c2r_2d(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +00001161 fourier_pixels,source_pixels,FFTW_ESTIMATE);
cristydf079ac2010-09-10 01:45:44 +00001162 fftw_execute(fftw_c2r_plan);
1163 fftw_destroy_plan(fftw_c2r_plan);
1164 }
cristy3ed852e2009-09-05 21:47:34 +00001165 i=0L;
cristy46ff2672012-12-14 15:32:26 +00001166 image_view=AcquireAuthenticCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +00001167 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001168 {
cristy85812052010-09-14 17:56:15 +00001169 if (y >= (ssize_t) image->rows)
1170 break;
1171 q=GetCacheViewAuthenticPixels(image_view,0L,y,fourier_info->width >
1172 image->columns ? image->columns : fourier_info->width,1UL,exception);
cristyacd2ed22011-08-30 01:44:23 +00001173 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001174 break;
cristybb503372010-05-27 20:51:26 +00001175 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001176 {
cristy233fe582012-07-07 14:00:18 +00001177 if (x < (ssize_t) image->columns)
1178 switch (fourier_info->channel)
cristy3ed852e2009-09-05 21:47:34 +00001179 {
cristy233fe582012-07-07 14:00:18 +00001180 case RedPixelChannel:
1181 default:
1182 {
cristy699ae5b2013-07-03 13:41:29 +00001183 SetPixelRed(image,ClampToQuantum(QuantumRange*source_pixels[i]),q);
cristy233fe582012-07-07 14:00:18 +00001184 break;
1185 }
1186 case GreenPixelChannel:
1187 {
cristy699ae5b2013-07-03 13:41:29 +00001188 SetPixelGreen(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1189 q);
cristy233fe582012-07-07 14:00:18 +00001190 break;
1191 }
1192 case BluePixelChannel:
1193 {
cristy699ae5b2013-07-03 13:41:29 +00001194 SetPixelBlue(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1195 q);
cristy233fe582012-07-07 14:00:18 +00001196 break;
1197 }
1198 case BlackPixelChannel:
1199 {
cristy699ae5b2013-07-03 13:41:29 +00001200 SetPixelBlack(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1201 q);
cristy233fe582012-07-07 14:00:18 +00001202 break;
1203 }
1204 case AlphaPixelChannel:
1205 {
cristy699ae5b2013-07-03 13:41:29 +00001206 SetPixelAlpha(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1207 q);
cristy233fe582012-07-07 14:00:18 +00001208 break;
1209 }
cristy3ed852e2009-09-05 21:47:34 +00001210 }
cristy3ed852e2009-09-05 21:47:34 +00001211 i++;
cristyed231572011-07-14 02:18:59 +00001212 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001213 }
1214 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1215 break;
1216 }
1217 image_view=DestroyCacheView(image_view);
cristy699ae5b2013-07-03 13:41:29 +00001218 source_info=RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +00001219 return(MagickTrue);
1220}
1221
cristyc9550792009-11-13 20:05:42 +00001222static MagickBooleanType InverseFourierTransformChannel(
1223 const Image *magnitude_image,const Image *phase_image,
cristyd3090f92011-10-18 00:05:15 +00001224 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +00001225 Image *fourier_image,ExceptionInfo *exception)
1226{
cristy3ed852e2009-09-05 21:47:34 +00001227 fftw_complex
cristy699ae5b2013-07-03 13:41:29 +00001228 *inverse_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001229
1230 FourierInfo
1231 fourier_info;
1232
1233 MagickBooleanType
1234 status;
1235
cristy699ae5b2013-07-03 13:41:29 +00001236 MemoryInfo
1237 *inverse_info;
1238
cristy3ed852e2009-09-05 21:47:34 +00001239 size_t
1240 extent;
1241
cristyc9550792009-11-13 20:05:42 +00001242 fourier_info.width=magnitude_image->columns;
1243 if ((magnitude_image->columns != magnitude_image->rows) ||
1244 ((magnitude_image->columns % 2) != 0) ||
1245 ((magnitude_image->rows % 2) != 0))
cristy3ed852e2009-09-05 21:47:34 +00001246 {
cristyc9550792009-11-13 20:05:42 +00001247 extent=magnitude_image->columns < magnitude_image->rows ?
1248 magnitude_image->rows : magnitude_image->columns;
cristy3ed852e2009-09-05 21:47:34 +00001249 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
1250 }
1251 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +00001252 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +00001253 fourier_info.channel=channel;
1254 fourier_info.modulus=modulus;
cristy699ae5b2013-07-03 13:41:29 +00001255 inverse_info=AcquireVirtualMemory((size_t) fourier_info.height,
1256 fourier_info.center*sizeof(*inverse_pixels));
1257 if (inverse_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001258 {
1259 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001260 ResourceLimitError,"MemoryAllocationFailed","`%s'",
cristyc9550792009-11-13 20:05:42 +00001261 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001262 return(MagickFalse);
1263 }
cristy699ae5b2013-07-03 13:41:29 +00001264 inverse_pixels=(fftw_complex *) GetVirtualMemoryBlob(inverse_info);
1265 status=InverseFourier(&fourier_info,magnitude_image,phase_image,
1266 inverse_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +00001267 if (status != MagickFalse)
cristy699ae5b2013-07-03 13:41:29 +00001268 status=InverseFourierTransform(&fourier_info,inverse_pixels,fourier_image,
cristy3ed852e2009-09-05 21:47:34 +00001269 exception);
cristy699ae5b2013-07-03 13:41:29 +00001270 inverse_info=RelinquishVirtualMemory(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +00001271 return(status);
1272}
1273#endif
1274
cristyc9550792009-11-13 20:05:42 +00001275MagickExport Image *InverseFourierTransformImage(const Image *magnitude_image,
1276 const Image *phase_image,const MagickBooleanType modulus,
1277 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001278{
1279 Image
1280 *fourier_image;
1281
cristyc9550792009-11-13 20:05:42 +00001282 assert(magnitude_image != (Image *) NULL);
1283 assert(magnitude_image->signature == MagickSignature);
1284 if (magnitude_image->debug != MagickFalse)
1285 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1286 magnitude_image->filename);
1287 if (phase_image == (Image *) NULL)
1288 {
1289 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
cristyefe601c2013-01-05 17:51:12 +00001290 "TwoOrMoreImagesRequired","`%s'",magnitude_image->filename);
cristy9372a152009-11-18 01:42:16 +00001291 return((Image *) NULL);
cristyc9550792009-11-13 20:05:42 +00001292 }
cristy3ed852e2009-09-05 21:47:34 +00001293#if !defined(MAGICKCORE_FFTW_DELEGATE)
1294 fourier_image=(Image *) NULL;
1295 (void) modulus;
1296 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00001297 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristyc9550792009-11-13 20:05:42 +00001298 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001299#else
1300 {
cristyc9550792009-11-13 20:05:42 +00001301 fourier_image=CloneImage(magnitude_image,magnitude_image->columns,
1302 magnitude_image->rows,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00001303 if (fourier_image != (Image *) NULL)
1304 {
1305 MagickBooleanType
1306 is_gray,
1307 status;
1308
cristy3ed852e2009-09-05 21:47:34 +00001309 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001310 is_gray=IsImageGray(magnitude_image,exception);
cristyc9550792009-11-13 20:05:42 +00001311 if (is_gray != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001312 is_gray=IsImageGray(phase_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001313#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb34ef052010-10-07 00:12:05 +00001314 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +00001315#endif
cristy3ed852e2009-09-05 21:47:34 +00001316 {
cristyb34ef052010-10-07 00:12:05 +00001317#if defined(MAGICKCORE_OPENMP_SUPPORT)
1318 #pragma omp section
1319#endif
cristy3ed852e2009-09-05 21:47:34 +00001320 {
cristyb34ef052010-10-07 00:12:05 +00001321 MagickBooleanType
1322 thread_status;
1323
1324 if (is_gray != MagickFalse)
1325 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001326 phase_image,GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001327 else
cristyc9550792009-11-13 20:05:42 +00001328 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001329 phase_image,RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001330 if (thread_status == MagickFalse)
1331 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +00001332 }
cristyb34ef052010-10-07 00:12:05 +00001333#if defined(MAGICKCORE_OPENMP_SUPPORT)
1334 #pragma omp section
1335#endif
1336 {
1337 MagickBooleanType
1338 thread_status;
1339
1340 thread_status=MagickTrue;
1341 if (is_gray == MagickFalse)
1342 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001343 phase_image,GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001344 if (thread_status == MagickFalse)
1345 status=thread_status;
1346 }
1347#if defined(MAGICKCORE_OPENMP_SUPPORT)
1348 #pragma omp section
1349#endif
1350 {
1351 MagickBooleanType
1352 thread_status;
1353
1354 thread_status=MagickTrue;
1355 if (is_gray == MagickFalse)
1356 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001357 phase_image,BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001358 if (thread_status == MagickFalse)
1359 status=thread_status;
1360 }
1361#if defined(MAGICKCORE_OPENMP_SUPPORT)
1362 #pragma omp section
1363#endif
1364 {
1365 MagickBooleanType
1366 thread_status;
1367
1368 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001369 if (magnitude_image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +00001370 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001371 phase_image,BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001372 if (thread_status == MagickFalse)
1373 status=thread_status;
1374 }
1375#if defined(MAGICKCORE_OPENMP_SUPPORT)
1376 #pragma omp section
1377#endif
1378 {
1379 MagickBooleanType
1380 thread_status;
1381
1382 thread_status=MagickTrue;
cristy8a46d822012-08-28 23:32:39 +00001383 if (magnitude_image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +00001384 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001385 phase_image,AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001386 if (thread_status == MagickFalse)
1387 status=thread_status;
1388 }
cristy3ed852e2009-09-05 21:47:34 +00001389 }
1390 if (status == MagickFalse)
1391 fourier_image=DestroyImage(fourier_image);
1392 }
cristyb34ef052010-10-07 00:12:05 +00001393 fftw_cleanup();
cristy3ed852e2009-09-05 21:47:34 +00001394 }
1395#endif
1396 return(fourier_image);
1397}