blob: fc38fd7d1fe2ce51a2dc52e3ddeaf1f30cbe36be [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% %
106% 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 %
107% %
108% %
109% %
110%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
111%
112% ForwardFourierTransformImage() implements the discrete Fourier transform
113% (DFT) of the image either as a magnitude / phase or real / imaginary image
114% pair.
115%
116% The format of the ForwadFourierTransformImage method is:
117%
118% Image *ForwardFourierTransformImage(const Image *image,
119% const MagickBooleanType modulus,ExceptionInfo *exception)
120%
121% A description of each parameter follows:
122%
123% o image: the image.
124%
125% o modulus: if true, return as transform as a magnitude / phase pair
126% otherwise a real / imaginary image pair.
127%
128% o exception: return any errors or warnings in this structure.
129%
130*/
131
132#if defined(MAGICKCORE_FFTW_DELEGATE)
133
cristyc4ea4a42011-01-24 01:43:30 +0000134static MagickBooleanType RollFourier(const size_t width,const size_t height,
cristy699ae5b2013-07-03 13:41:29 +0000135 const ssize_t x_offset,const ssize_t y_offset,double *roll_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000136{
137 double
cristy699ae5b2013-07-03 13:41:29 +0000138 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000139
cristy7d4aa382013-06-30 01:59:39 +0000140 MemoryInfo
cristy699ae5b2013-07-03 13:41:29 +0000141 *source_info;
cristy7d4aa382013-06-30 01:59:39 +0000142
cristyc4ea4a42011-01-24 01:43:30 +0000143 register ssize_t
144 i,
145 x;
146
cristybb503372010-05-27 20:51:26 +0000147 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000148 u,
149 v,
150 y;
151
cristy3ed852e2009-09-05 21:47:34 +0000152 /*
cristy2da05352010-09-15 19:22:17 +0000153 Move zero frequency (DC, average color) from (0,0) to (width/2,height/2).
cristy3ed852e2009-09-05 21:47:34 +0000154 */
cristy699ae5b2013-07-03 13:41:29 +0000155 source_info=AcquireVirtualMemory(height,width*sizeof(*source_pixels));
156 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000157 return(MagickFalse);
cristy699ae5b2013-07-03 13:41:29 +0000158 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000159 i=0L;
cristybb503372010-05-27 20:51:26 +0000160 for (y=0L; y < (ssize_t) height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000161 {
162 if (y_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000163 v=((y+y_offset) < 0L) ? y+y_offset+(ssize_t) height : y+y_offset;
cristy3ed852e2009-09-05 21:47:34 +0000164 else
cristybb503372010-05-27 20:51:26 +0000165 v=((y+y_offset) > ((ssize_t) height-1L)) ? y+y_offset-(ssize_t) height :
cristy3ed852e2009-09-05 21:47:34 +0000166 y+y_offset;
cristybb503372010-05-27 20:51:26 +0000167 for (x=0L; x < (ssize_t) width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000168 {
169 if (x_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000170 u=((x+x_offset) < 0L) ? x+x_offset+(ssize_t) width : x+x_offset;
cristy3ed852e2009-09-05 21:47:34 +0000171 else
cristybb503372010-05-27 20:51:26 +0000172 u=((x+x_offset) > ((ssize_t) width-1L)) ? x+x_offset-(ssize_t) width :
cristy3ed852e2009-09-05 21:47:34 +0000173 x+x_offset;
cristy699ae5b2013-07-03 13:41:29 +0000174 source_pixels[v*width+u]=roll_pixels[i++];
cristyc4ea4a42011-01-24 01:43:30 +0000175 }
cristy3ed852e2009-09-05 21:47:34 +0000176 }
cristy699ae5b2013-07-03 13:41:29 +0000177 (void) CopyMagickMemory(roll_pixels,source_pixels,height*width*
178 sizeof(*source_pixels));
179 source_info=RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000180 return(MagickTrue);
181}
182
cristybb503372010-05-27 20:51:26 +0000183static MagickBooleanType ForwardQuadrantSwap(const size_t width,
cristy699ae5b2013-07-03 13:41:29 +0000184 const size_t height,double *source_pixels,double *forward_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000185{
cristy3ed852e2009-09-05 21:47:34 +0000186 MagickBooleanType
187 status;
188
cristybb503372010-05-27 20:51:26 +0000189 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000190 x;
191
cristyc4ea4a42011-01-24 01:43:30 +0000192 ssize_t
193 center,
194 y;
195
cristy3ed852e2009-09-05 21:47:34 +0000196 /*
197 Swap quadrants.
198 */
cristybb503372010-05-27 20:51:26 +0000199 center=(ssize_t) floor((double) width/2L)+1L;
cristy699ae5b2013-07-03 13:41:29 +0000200 status=RollFourier((size_t) center,height,0L,(ssize_t) height/2L,
201 source_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000202 if (status == MagickFalse)
203 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000204 for (y=0L; y < (ssize_t) height; y++)
205 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy3d04ed72013-08-18 17:51:01 +0000206 forward_pixels[y*width+x+width/2L]=source_pixels[y*center+x];
cristybb503372010-05-27 20:51:26 +0000207 for (y=1; y < (ssize_t) height; y++)
208 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy9b1d6c72013-08-18 17:53:56 +0000209 forward_pixels[(height-y)*width+width/2L-x-1L]=
cristy3d04ed72013-08-18 17:51:01 +0000210 source_pixels[y*center+x+1L];
cristybb503372010-05-27 20:51:26 +0000211 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy699ae5b2013-07-03 13:41:29 +0000212 forward_pixels[-x+width/2L-1L]=forward_pixels[x+width/2L+1L];
cristy3ed852e2009-09-05 21:47:34 +0000213 return(MagickTrue);
214}
215
cristyc4ea4a42011-01-24 01:43:30 +0000216static void CorrectPhaseLHS(const size_t width,const size_t height,
cristy699ae5b2013-07-03 13:41:29 +0000217 double *fourier_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000218{
cristybb503372010-05-27 20:51:26 +0000219 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000220 x;
221
cristy9d314ff2011-03-09 01:30:28 +0000222 ssize_t
223 y;
224
cristybb503372010-05-27 20:51:26 +0000225 for (y=0L; y < (ssize_t) height; y++)
226 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy699ae5b2013-07-03 13:41:29 +0000227 fourier_pixels[y*width+x]*=(-1.0);
cristy3ed852e2009-09-05 21:47:34 +0000228}
229
230static MagickBooleanType ForwardFourier(const FourierInfo *fourier_info,
231 Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
232{
233 CacheView
234 *magnitude_view,
235 *phase_view;
236
237 double
cristy7d4aa382013-06-30 01:59:39 +0000238 *magnitude_pixels,
239 *phase_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000240
241 Image
242 *magnitude_image,
243 *phase_image;
244
cristy3ed852e2009-09-05 21:47:34 +0000245 MagickBooleanType
246 status;
247
cristy7d4aa382013-06-30 01:59:39 +0000248 MemoryInfo
249 *magnitude_info,
250 *phase_info;
251
cristy4c08aed2011-07-01 19:47:50 +0000252 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000253 *q;
254
cristyf5742792012-11-27 18:31:26 +0000255 register ssize_t
256 x;
257
cristyc4ea4a42011-01-24 01:43:30 +0000258 ssize_t
259 i,
260 y;
261
cristy3ed852e2009-09-05 21:47:34 +0000262 magnitude_image=GetFirstImageInList(image);
263 phase_image=GetNextImageInList(image);
264 if (phase_image == (Image *) NULL)
265 {
266 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
cristyefe601c2013-01-05 17:51:12 +0000267 "TwoOrMoreImagesRequired","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000268 return(MagickFalse);
269 }
270 /*
271 Create "Fourier Transform" image from constituent arrays.
272 */
cristy7d4aa382013-06-30 01:59:39 +0000273 magnitude_info=AcquireVirtualMemory((size_t) fourier_info->height,
274 fourier_info->width*sizeof(*magnitude_pixels));
275 phase_info=AcquireVirtualMemory((size_t) fourier_info->height,
276 fourier_info->width*sizeof(*phase_pixels));
cristybb3c02e2013-07-02 00:43:10 +0000277 if ((magnitude_info == (MemoryInfo *) NULL) ||
278 (phase_info == (MemoryInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000279 {
cristy7d4aa382013-06-30 01:59:39 +0000280 if (phase_info != (MemoryInfo *) NULL)
281 phase_info=RelinquishVirtualMemory(phase_info);
282 if (magnitude_info != (MemoryInfo *) NULL)
283 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000284 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000285 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000286 return(MagickFalse);
287 }
cristy7d4aa382013-06-30 01:59:39 +0000288 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
289 (void) ResetMagickMemory(magnitude_pixels,0,fourier_info->height*
290 fourier_info->width*sizeof(*magnitude_pixels));
cristybb3c02e2013-07-02 00:43:10 +0000291 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
cristy7d4aa382013-06-30 01:59:39 +0000292 (void) ResetMagickMemory(phase_pixels,0,fourier_info->height*
293 fourier_info->width*sizeof(*phase_pixels));
cristy13c99c42013-08-18 13:53:30 +0000294 status=ForwardQuadrantSwap(fourier_info->width,fourier_info->height,
cristy7d4aa382013-06-30 01:59:39 +0000295 magnitude,magnitude_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000296 if (status != MagickFalse)
cristy13c99c42013-08-18 13:53:30 +0000297 status=ForwardQuadrantSwap(fourier_info->width,fourier_info->height,phase,
cristy7d4aa382013-06-30 01:59:39 +0000298 phase_pixels);
cristy13c99c42013-08-18 13:53:30 +0000299 CorrectPhaseLHS(fourier_info->width,fourier_info->height,phase_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000300 if (fourier_info->modulus != MagickFalse)
301 {
302 i=0L;
cristybb503372010-05-27 20:51:26 +0000303 for (y=0L; y < (ssize_t) fourier_info->height; y++)
304 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000305 {
cristy7d4aa382013-06-30 01:59:39 +0000306 phase_pixels[i]/=(2.0*MagickPI);
307 phase_pixels[i]+=0.5;
cristy3ed852e2009-09-05 21:47:34 +0000308 i++;
309 }
310 }
cristy46ff2672012-12-14 15:32:26 +0000311 magnitude_view=AcquireAuthenticCacheView(magnitude_image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000312 i=0L;
cristybb503372010-05-27 20:51:26 +0000313 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000314 {
315 q=GetCacheViewAuthenticPixels(magnitude_view,0L,y,fourier_info->height,1UL,
316 exception);
cristyacd2ed22011-08-30 01:44:23 +0000317 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000318 break;
cristybb503372010-05-27 20:51:26 +0000319 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000320 {
321 switch (fourier_info->channel)
322 {
cristyd3090f92011-10-18 00:05:15 +0000323 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000324 default:
325 {
cristy4c08aed2011-07-01 19:47:50 +0000326 SetPixelRed(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000327 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000328 break;
329 }
cristyd3090f92011-10-18 00:05:15 +0000330 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000331 {
cristy4c08aed2011-07-01 19:47:50 +0000332 SetPixelGreen(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000333 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000334 break;
335 }
cristyd3090f92011-10-18 00:05:15 +0000336 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000337 {
cristy4c08aed2011-07-01 19:47:50 +0000338 SetPixelBlue(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000339 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000340 break;
341 }
cristyd3090f92011-10-18 00:05:15 +0000342 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000343 {
cristy4c08aed2011-07-01 19:47:50 +0000344 SetPixelBlack(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000345 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000346 break;
347 }
cristyd3090f92011-10-18 00:05:15 +0000348 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000349 {
cristy4c08aed2011-07-01 19:47:50 +0000350 SetPixelAlpha(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000351 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000352 break;
353 }
cristy3ed852e2009-09-05 21:47:34 +0000354 }
355 i++;
cristyed231572011-07-14 02:18:59 +0000356 q+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +0000357 }
358 status=SyncCacheViewAuthenticPixels(magnitude_view,exception);
359 if (status == MagickFalse)
360 break;
361 }
cristydb070952012-04-20 14:33:00 +0000362 magnitude_view=DestroyCacheView(magnitude_view);
cristy3ed852e2009-09-05 21:47:34 +0000363 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000364 phase_view=AcquireAuthenticCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +0000365 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000366 {
367 q=GetCacheViewAuthenticPixels(phase_view,0L,y,fourier_info->height,1UL,
368 exception);
cristyacd2ed22011-08-30 01:44:23 +0000369 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000370 break;
cristybb503372010-05-27 20:51:26 +0000371 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000372 {
373 switch (fourier_info->channel)
374 {
cristyd3090f92011-10-18 00:05:15 +0000375 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000376 default:
377 {
cristy4c08aed2011-07-01 19:47:50 +0000378 SetPixelRed(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000379 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000380 break;
381 }
cristyd3090f92011-10-18 00:05:15 +0000382 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000383 {
cristy4c08aed2011-07-01 19:47:50 +0000384 SetPixelGreen(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000385 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000386 break;
387 }
cristyd3090f92011-10-18 00:05:15 +0000388 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000389 {
cristy4c08aed2011-07-01 19:47:50 +0000390 SetPixelBlue(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000391 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000392 break;
393 }
cristyd3090f92011-10-18 00:05:15 +0000394 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000395 {
cristy4c08aed2011-07-01 19:47:50 +0000396 SetPixelBlack(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000397 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000398 break;
399 }
cristyd3090f92011-10-18 00:05:15 +0000400 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000401 {
cristy4c08aed2011-07-01 19:47:50 +0000402 SetPixelAlpha(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000403 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000404 break;
405 }
cristy3ed852e2009-09-05 21:47:34 +0000406 }
407 i++;
cristyed231572011-07-14 02:18:59 +0000408 q+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +0000409 }
410 status=SyncCacheViewAuthenticPixels(phase_view,exception);
411 if (status == MagickFalse)
412 break;
413 }
414 phase_view=DestroyCacheView(phase_view);
cristy7d4aa382013-06-30 01:59:39 +0000415 phase_info=RelinquishVirtualMemory(phase_info);
416 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000417 return(status);
418}
419
420static MagickBooleanType ForwardFourierTransform(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +0000421 const Image *image,double *magnitude_pixels,double *phase_pixels,
422 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000423{
424 CacheView
425 *image_view;
426
cristy99dc0362013-09-15 18:32:54 +0000427 const char
428 *value;
429
cristy3ed852e2009-09-05 21:47:34 +0000430 double
cristybb3c02e2013-07-02 00:43:10 +0000431 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000432
433 fftw_complex
cristy699ae5b2013-07-03 13:41:29 +0000434 *forward_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000435
436 fftw_plan
437 fftw_r2c_plan;
438
cristy7d4aa382013-06-30 01:59:39 +0000439 MemoryInfo
cristy699ae5b2013-07-03 13:41:29 +0000440 *forward_info,
cristy7d4aa382013-06-30 01:59:39 +0000441 *source_info;
442
cristy4c08aed2011-07-01 19:47:50 +0000443 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000444 *p;
445
cristybb503372010-05-27 20:51:26 +0000446 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000447 i,
448 x;
449
cristyc4ea4a42011-01-24 01:43:30 +0000450 ssize_t
451 y;
452
cristy3ed852e2009-09-05 21:47:34 +0000453 /*
454 Generate the forward Fourier transform.
455 */
cristy7d4aa382013-06-30 01:59:39 +0000456 source_info=AcquireVirtualMemory((size_t) fourier_info->height,
cristybb3c02e2013-07-02 00:43:10 +0000457 fourier_info->width*sizeof(*source_pixels));
cristy7d4aa382013-06-30 01:59:39 +0000458 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000459 {
460 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000461 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000462 return(MagickFalse);
463 }
cristybb3c02e2013-07-02 00:43:10 +0000464 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
465 ResetMagickMemory(source_pixels,0,fourier_info->height*fourier_info->width*
466 sizeof(*source_pixels));
cristy3ed852e2009-09-05 21:47:34 +0000467 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000468 image_view=AcquireVirtualCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +0000469 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000470 {
471 p=GetCacheViewVirtualPixels(image_view,0L,y,fourier_info->width,1UL,
472 exception);
cristy4c08aed2011-07-01 19:47:50 +0000473 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000474 break;
cristybb503372010-05-27 20:51:26 +0000475 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000476 {
477 switch (fourier_info->channel)
478 {
cristyd3090f92011-10-18 00:05:15 +0000479 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000480 default:
481 {
cristybb3c02e2013-07-02 00:43:10 +0000482 source_pixels[i]=QuantumScale*GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000483 break;
484 }
cristyd3090f92011-10-18 00:05:15 +0000485 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000486 {
cristybb3c02e2013-07-02 00:43:10 +0000487 source_pixels[i]=QuantumScale*GetPixelGreen(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000488 break;
489 }
cristyd3090f92011-10-18 00:05:15 +0000490 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000491 {
cristybb3c02e2013-07-02 00:43:10 +0000492 source_pixels[i]=QuantumScale*GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000493 break;
494 }
cristyd3090f92011-10-18 00:05:15 +0000495 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000496 {
cristybb3c02e2013-07-02 00:43:10 +0000497 source_pixels[i]=QuantumScale*GetPixelBlack(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000498 break;
499 }
cristyd3090f92011-10-18 00:05:15 +0000500 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000501 {
cristybb3c02e2013-07-02 00:43:10 +0000502 source_pixels[i]=QuantumScale*GetPixelAlpha(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000503 break;
504 }
cristy3ed852e2009-09-05 21:47:34 +0000505 }
506 i++;
cristyed231572011-07-14 02:18:59 +0000507 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000508 }
509 }
510 image_view=DestroyCacheView(image_view);
cristy699ae5b2013-07-03 13:41:29 +0000511 forward_info=AcquireVirtualMemory((size_t) fourier_info->height,
512 fourier_info->center*sizeof(*forward_pixels));
513 if (forward_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000514 {
515 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000516 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristybb3c02e2013-07-02 00:43:10 +0000517 source_info=(MemoryInfo *) RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000518 return(MagickFalse);
519 }
cristy699ae5b2013-07-03 13:41:29 +0000520 forward_pixels=(fftw_complex *) GetVirtualMemoryBlob(forward_info);
cristyb5d5f722009-11-04 03:03:49 +0000521#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000522 #pragma omp critical (MagickCore_ForwardFourierTransform)
523#endif
cristy70841a12012-10-27 20:52:57 +0000524 fftw_r2c_plan=fftw_plan_dft_r2c_2d(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +0000525 source_pixels,forward_pixels,FFTW_ESTIMATE);
cristy3ed852e2009-09-05 21:47:34 +0000526 fftw_execute(fftw_r2c_plan);
527 fftw_destroy_plan(fftw_r2c_plan);
cristybb3c02e2013-07-02 00:43:10 +0000528 source_info=(MemoryInfo *) RelinquishVirtualMemory(source_info);
cristy99dc0362013-09-15 18:32:54 +0000529 value=GetImageArtifact(image,"fourier:normalize");
cristyf8ecbfd2013-09-15 19:11:14 +0000530 if ((value == (const char *) NULL) || (LocaleCompare(value,"forward") == 0))
cristy56ed31c2010-03-22 00:46:21 +0000531 {
cristy99dc0362013-09-15 18:32:54 +0000532 double
533 gamma;
534
535 /*
536 Normalize Fourier transform.
537 */
538 i=0L;
539 gamma=PerceptibleReciprocal((double) fourier_info->width*
540 fourier_info->height);
541 for (y=0L; y < (ssize_t) fourier_info->height; y++)
542 for (x=0L; x < (ssize_t) fourier_info->center; x++)
543 {
cristy56ed31c2010-03-22 00:46:21 +0000544#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy99dc0362013-09-15 18:32:54 +0000545 forward_pixels[i]*=gamma;
cristy56ed31c2010-03-22 00:46:21 +0000546#else
cristy99dc0362013-09-15 18:32:54 +0000547 forward_pixels[i][0]*=gamma;
548 forward_pixels[i][1]*=gamma;
cristy56ed31c2010-03-22 00:46:21 +0000549#endif
cristy99dc0362013-09-15 18:32:54 +0000550 i++;
551 }
cristy56ed31c2010-03-22 00:46:21 +0000552 }
cristy3ed852e2009-09-05 21:47:34 +0000553 /*
554 Generate magnitude and phase (or real and imaginary).
555 */
556 i=0L;
557 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000558 for (y=0L; y < (ssize_t) fourier_info->height; y++)
559 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000560 {
cristy699ae5b2013-07-03 13:41:29 +0000561 magnitude_pixels[i]=cabs(forward_pixels[i]);
562 phase_pixels[i]=carg(forward_pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000563 i++;
564 }
565 else
cristybb503372010-05-27 20:51:26 +0000566 for (y=0L; y < (ssize_t) fourier_info->height; y++)
567 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000568 {
cristy699ae5b2013-07-03 13:41:29 +0000569 magnitude_pixels[i]=creal(forward_pixels[i]);
570 phase_pixels[i]=cimag(forward_pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000571 i++;
572 }
cristy699ae5b2013-07-03 13:41:29 +0000573 forward_info=(MemoryInfo *) RelinquishVirtualMemory(forward_info);
cristy3ed852e2009-09-05 21:47:34 +0000574 return(MagickTrue);
575}
576
577static MagickBooleanType ForwardFourierTransformChannel(const Image *image,
cristyd3090f92011-10-18 00:05:15 +0000578 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +0000579 Image *fourier_image,ExceptionInfo *exception)
580{
581 double
cristyce9fe782013-07-03 00:59:41 +0000582 *magnitude_pixels,
583 *phase_pixels;
cristybb3c02e2013-07-02 00:43:10 +0000584
cristy56ed31c2010-03-22 00:46:21 +0000585 FourierInfo
586 fourier_info;
587
cristyc4ea4a42011-01-24 01:43:30 +0000588 MagickBooleanType
589 status;
590
cristyce9fe782013-07-03 00:59:41 +0000591 MemoryInfo
592 *magnitude_info,
593 *phase_info;
594
cristy3ed852e2009-09-05 21:47:34 +0000595 size_t
596 extent;
597
598 fourier_info.width=image->columns;
599 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
600 ((image->rows % 2) != 0))
601 {
602 extent=image->columns < image->rows ? image->rows : image->columns;
603 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
604 }
605 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +0000606 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +0000607 fourier_info.channel=channel;
608 fourier_info.modulus=modulus;
cristyce9fe782013-07-03 00:59:41 +0000609 magnitude_info=AcquireVirtualMemory((size_t) fourier_info.height,
610 fourier_info.center*sizeof(*magnitude_pixels));
611 phase_info=AcquireVirtualMemory((size_t) fourier_info.height,
612 fourier_info.center*sizeof(*phase_pixels));
613 if ((magnitude_info == (MemoryInfo *) NULL) ||
614 (phase_info == (MemoryInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000615 {
cristyce9fe782013-07-03 00:59:41 +0000616 if (phase_info != (MemoryInfo *) NULL)
617 phase_info=RelinquishVirtualMemory(phase_info);
618 if (magnitude_info == (MemoryInfo *) NULL)
619 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000620 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000621 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000622 return(MagickFalse);
623 }
cristyce9fe782013-07-03 00:59:41 +0000624 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
625 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
626 status=ForwardFourierTransform(&fourier_info,image,magnitude_pixels,
627 phase_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +0000628 if (status != MagickFalse)
cristyce9fe782013-07-03 00:59:41 +0000629 status=ForwardFourier(&fourier_info,fourier_image,magnitude_pixels,
630 phase_pixels,exception);
631 phase_info=RelinquishVirtualMemory(phase_info);
632 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000633 return(status);
634}
635#endif
636
637MagickExport Image *ForwardFourierTransformImage(const Image *image,
638 const MagickBooleanType modulus,ExceptionInfo *exception)
639{
640 Image
641 *fourier_image;
642
643 fourier_image=NewImageList();
644#if !defined(MAGICKCORE_FFTW_DELEGATE)
645 (void) modulus;
646 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000647 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristy3ed852e2009-09-05 21:47:34 +0000648 image->filename);
649#else
650 {
651 Image
652 *magnitude_image;
653
cristybb503372010-05-27 20:51:26 +0000654 size_t
cristy3ed852e2009-09-05 21:47:34 +0000655 extent,
656 width;
657
658 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 width=(extent & 0x01) == 1 ? extent+1UL : extent;
664 }
665 magnitude_image=CloneImage(image,width,width,MagickFalse,exception);
666 if (magnitude_image != (Image *) NULL)
667 {
668 Image
669 *phase_image;
670
671 magnitude_image->storage_class=DirectClass;
672 magnitude_image->depth=32UL;
673 phase_image=CloneImage(image,width,width,MagickFalse,exception);
674 if (phase_image == (Image *) NULL)
675 magnitude_image=DestroyImage(magnitude_image);
676 else
677 {
678 MagickBooleanType
679 is_gray,
680 status;
681
cristy3ed852e2009-09-05 21:47:34 +0000682 phase_image->storage_class=DirectClass;
683 phase_image->depth=32UL;
684 AppendImageToList(&fourier_image,magnitude_image);
685 AppendImageToList(&fourier_image,phase_image);
686 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000687 is_gray=IsImageGray(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000688#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy564a5692012-01-20 23:56:26 +0000689 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +0000690#endif
cristy3ed852e2009-09-05 21:47:34 +0000691 {
cristyb34ef052010-10-07 00:12:05 +0000692#if defined(MAGICKCORE_OPENMP_SUPPORT)
693 #pragma omp section
694#endif
cristy3ed852e2009-09-05 21:47:34 +0000695 {
cristyb34ef052010-10-07 00:12:05 +0000696 MagickBooleanType
697 thread_status;
698
699 if (is_gray != MagickFalse)
700 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000701 GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000702 else
703 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000704 RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000705 if (thread_status == MagickFalse)
706 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +0000707 }
cristyb34ef052010-10-07 00:12:05 +0000708#if defined(MAGICKCORE_OPENMP_SUPPORT)
709 #pragma omp section
710#endif
711 {
712 MagickBooleanType
713 thread_status;
714
715 thread_status=MagickTrue;
716 if (is_gray == MagickFalse)
717 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000718 GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000719 if (thread_status == MagickFalse)
720 status=thread_status;
721 }
722#if defined(MAGICKCORE_OPENMP_SUPPORT)
723 #pragma omp section
724#endif
725 {
726 MagickBooleanType
727 thread_status;
728
729 thread_status=MagickTrue;
730 if (is_gray == MagickFalse)
731 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000732 BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000733 if (thread_status == MagickFalse)
734 status=thread_status;
735 }
736#if defined(MAGICKCORE_OPENMP_SUPPORT)
737 #pragma omp section
738#endif
739 {
740 MagickBooleanType
741 thread_status;
742
743 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000744 if (image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +0000745 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000746 BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000747 if (thread_status == MagickFalse)
748 status=thread_status;
749 }
750#if defined(MAGICKCORE_OPENMP_SUPPORT)
751 #pragma omp section
752#endif
753 {
754 MagickBooleanType
755 thread_status;
756
757 thread_status=MagickTrue;
cristy8a46d822012-08-28 23:32:39 +0000758 if (image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +0000759 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000760 AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000761 if (thread_status == MagickFalse)
762 status=thread_status;
763 }
cristy3ed852e2009-09-05 21:47:34 +0000764 }
765 if (status == MagickFalse)
766 fourier_image=DestroyImageList(fourier_image);
767 fftw_cleanup();
768 }
769 }
770 }
771#endif
772 return(fourier_image);
773}
774
775/*
776%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
777% %
778% %
779% %
780% 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 %
781% %
782% %
783% %
784%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
785%
786% InverseFourierTransformImage() implements the inverse discrete Fourier
787% transform (DFT) of the image either as a magnitude / phase or real /
788% imaginary image pair.
789%
790% The format of the InverseFourierTransformImage method is:
791%
cristyc9550792009-11-13 20:05:42 +0000792% Image *InverseFourierTransformImage(const Image *magnitude_image,
793% const Image *phase_image,const MagickBooleanType modulus,
794% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000795%
796% A description of each parameter follows:
797%
cristyc9550792009-11-13 20:05:42 +0000798% o magnitude_image: the magnitude or real image.
799%
800% o phase_image: the phase or imaginary image.
cristy3ed852e2009-09-05 21:47:34 +0000801%
802% o modulus: if true, return transform as a magnitude / phase pair
803% otherwise a real / imaginary image pair.
804%
805% o exception: return any errors or warnings in this structure.
806%
807*/
808
809#if defined(MAGICKCORE_FFTW_DELEGATE)
cristybb503372010-05-27 20:51:26 +0000810static MagickBooleanType InverseQuadrantSwap(const size_t width,
811 const size_t height,const double *source,double *destination)
cristy3ed852e2009-09-05 21:47:34 +0000812{
cristyc4ea4a42011-01-24 01:43:30 +0000813 register ssize_t
814 x;
815
cristybb503372010-05-27 20:51:26 +0000816 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000817 center,
818 y;
819
cristy3ed852e2009-09-05 21:47:34 +0000820 /*
821 Swap quadrants.
822 */
cristy233fe582012-07-07 14:00:18 +0000823 center=(ssize_t) floor((double) width/2L)+1L;
cristybb503372010-05-27 20:51:26 +0000824 for (y=1L; y < (ssize_t) height; y++)
825 for (x=0L; x < (ssize_t) (width/2L+1L); x++)
cristy9b1d6c72013-08-18 17:53:56 +0000826 destination[(height-y)*center-x+width/2L]=source[y*width+x];
cristybb503372010-05-27 20:51:26 +0000827 for (y=0L; y < (ssize_t) height; y++)
cristy3d04ed72013-08-18 17:51:01 +0000828 destination[y*center]=source[y*width+width/2L];
cristy3ed852e2009-09-05 21:47:34 +0000829 for (x=0L; x < center; x++)
830 destination[x]=source[center-x-1L];
cristybb503372010-05-27 20:51:26 +0000831 return(RollFourier(center,height,0L,(ssize_t) height/-2L,destination));
cristy3ed852e2009-09-05 21:47:34 +0000832}
833
834static MagickBooleanType InverseFourier(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +0000835 const Image *magnitude_image,const Image *phase_image,
836 fftw_complex *fourier_pixels,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000837{
838 CacheView
839 *magnitude_view,
840 *phase_view;
841
842 double
cristy699ae5b2013-07-03 13:41:29 +0000843 *inverse_pixels,
cristy7d4aa382013-06-30 01:59:39 +0000844 *magnitude_pixels,
845 *phase_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000846
cristy3ed852e2009-09-05 21:47:34 +0000847 MagickBooleanType
848 status;
849
cristy699ae5b2013-07-03 13:41:29 +0000850 MemoryInfo
851 *inverse_info,
852 *magnitude_info,
853 *phase_info;
854
cristy4c08aed2011-07-01 19:47:50 +0000855 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000856 *p;
857
cristybb503372010-05-27 20:51:26 +0000858 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000859 i,
860 x;
861
cristyc4ea4a42011-01-24 01:43:30 +0000862 ssize_t
863 y;
864
cristy3ed852e2009-09-05 21:47:34 +0000865 /*
866 Inverse fourier - read image and break down into a double array.
867 */
cristy699ae5b2013-07-03 13:41:29 +0000868 magnitude_info=AcquireVirtualMemory((size_t)fourier_info->height,
869 fourier_info->width*sizeof(*magnitude_pixels));
870 phase_info=AcquireVirtualMemory((size_t) fourier_info->height,
cristybb3c02e2013-07-02 00:43:10 +0000871 fourier_info->width*sizeof(*phase_pixels));
cristy699ae5b2013-07-03 13:41:29 +0000872 inverse_info=AcquireVirtualMemory((size_t) fourier_info->height,
873 fourier_info->center*sizeof(*inverse_pixels));
874 if ((magnitude_info == (MemoryInfo *) NULL) ||
875 (phase_info == (MemoryInfo *) NULL) ||
876 (inverse_info == (MemoryInfo *) NULL))
cristybb3c02e2013-07-02 00:43:10 +0000877 {
cristy699ae5b2013-07-03 13:41:29 +0000878 if (magnitude_info != (MemoryInfo *) NULL)
879 magnitude_info=RelinquishVirtualMemory(magnitude_info);
880 if (phase_info != (MemoryInfo *) NULL)
881 phase_info=RelinquishVirtualMemory(phase_info);
882 if (inverse_info != (MemoryInfo *) NULL)
883 inverse_info=RelinquishVirtualMemory(inverse_info);
cristybb3c02e2013-07-02 00:43:10 +0000884 (void) ThrowMagickException(exception,GetMagickModule(),
885 ResourceLimitError,"MemoryAllocationFailed","`%s'",
886 magnitude_image->filename);
cristybb3c02e2013-07-02 00:43:10 +0000887 return(MagickFalse);
888 }
cristy699ae5b2013-07-03 13:41:29 +0000889 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
890 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
891 inverse_pixels=(double *) GetVirtualMemoryBlob(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +0000892 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000893 magnitude_view=AcquireVirtualCacheView(magnitude_image,exception);
cristybb503372010-05-27 20:51:26 +0000894 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000895 {
896 p=GetCacheViewVirtualPixels(magnitude_view,0L,y,fourier_info->width,1UL,
897 exception);
cristy4c08aed2011-07-01 19:47:50 +0000898 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000899 break;
cristybb503372010-05-27 20:51:26 +0000900 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000901 {
902 switch (fourier_info->channel)
903 {
cristyd3090f92011-10-18 00:05:15 +0000904 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000905 default:
906 {
cristy7d4aa382013-06-30 01:59:39 +0000907 magnitude_pixels[i]=QuantumScale*GetPixelRed(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000908 break;
909 }
cristyd3090f92011-10-18 00:05:15 +0000910 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000911 {
cristy7d4aa382013-06-30 01:59:39 +0000912 magnitude_pixels[i]=QuantumScale*GetPixelGreen(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000913 break;
914 }
cristyd3090f92011-10-18 00:05:15 +0000915 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000916 {
cristy7d4aa382013-06-30 01:59:39 +0000917 magnitude_pixels[i]=QuantumScale*GetPixelBlue(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000918 break;
919 }
cristyd3090f92011-10-18 00:05:15 +0000920 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000921 {
cristy7d4aa382013-06-30 01:59:39 +0000922 magnitude_pixels[i]=QuantumScale*GetPixelBlack(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000923 break;
924 }
cristyd3090f92011-10-18 00:05:15 +0000925 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000926 {
cristy7d4aa382013-06-30 01:59:39 +0000927 magnitude_pixels[i]=QuantumScale*GetPixelAlpha(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000928 break;
929 }
cristy3ed852e2009-09-05 21:47:34 +0000930 }
931 i++;
cristyed231572011-07-14 02:18:59 +0000932 p+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +0000933 }
934 }
cristy699ae5b2013-07-03 13:41:29 +0000935 magnitude_view=DestroyCacheView(magnitude_view);
936 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
937 magnitude_pixels,inverse_pixels);
938 (void) CopyMagickMemory(magnitude_pixels,inverse_pixels,fourier_info->height*
939 fourier_info->center*sizeof(*magnitude_pixels));
cristy3ed852e2009-09-05 21:47:34 +0000940 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000941 phase_view=AcquireVirtualCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +0000942 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000943 {
944 p=GetCacheViewVirtualPixels(phase_view,0,y,fourier_info->width,1,
945 exception);
cristy4c08aed2011-07-01 19:47:50 +0000946 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000947 break;
cristybb503372010-05-27 20:51:26 +0000948 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000949 {
950 switch (fourier_info->channel)
951 {
cristyd3090f92011-10-18 00:05:15 +0000952 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000953 default:
954 {
cristy7d4aa382013-06-30 01:59:39 +0000955 phase_pixels[i]=QuantumScale*GetPixelRed(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000956 break;
957 }
cristyd3090f92011-10-18 00:05:15 +0000958 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000959 {
cristy7d4aa382013-06-30 01:59:39 +0000960 phase_pixels[i]=QuantumScale*GetPixelGreen(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000961 break;
962 }
cristyd3090f92011-10-18 00:05:15 +0000963 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000964 {
cristy7d4aa382013-06-30 01:59:39 +0000965 phase_pixels[i]=QuantumScale*GetPixelBlue(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000966 break;
967 }
cristyd3090f92011-10-18 00:05:15 +0000968 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000969 {
cristy7d4aa382013-06-30 01:59:39 +0000970 phase_pixels[i]=QuantumScale*GetPixelBlack(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000971 break;
972 }
cristyd3090f92011-10-18 00:05:15 +0000973 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000974 {
cristy7d4aa382013-06-30 01:59:39 +0000975 phase_pixels[i]=QuantumScale*GetPixelAlpha(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000976 break;
977 }
cristy3ed852e2009-09-05 21:47:34 +0000978 }
979 i++;
cristyed231572011-07-14 02:18:59 +0000980 p+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +0000981 }
982 }
983 if (fourier_info->modulus != MagickFalse)
984 {
985 i=0L;
cristybb503372010-05-27 20:51:26 +0000986 for (y=0L; y < (ssize_t) fourier_info->height; y++)
987 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000988 {
cristy7d4aa382013-06-30 01:59:39 +0000989 phase_pixels[i]-=0.5;
990 phase_pixels[i]*=(2.0*MagickPI);
cristy3ed852e2009-09-05 21:47:34 +0000991 i++;
992 }
993 }
cristy3ed852e2009-09-05 21:47:34 +0000994 phase_view=DestroyCacheView(phase_view);
cristy13c99c42013-08-18 13:53:30 +0000995 CorrectPhaseLHS(fourier_info->width,fourier_info->height,phase_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000996 if (status != MagickFalse)
997 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +0000998 phase_pixels,inverse_pixels);
999 (void) CopyMagickMemory(phase_pixels,inverse_pixels,fourier_info->height*
1000 fourier_info->center*sizeof(*phase_pixels));
1001 inverse_info=RelinquishVirtualMemory(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +00001002 /*
1003 Merge two sets.
1004 */
1005 i=0L;
1006 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +00001007 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1008 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001009 {
cristy56ed31c2010-03-22 00:46:21 +00001010#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy699ae5b2013-07-03 13:41:29 +00001011 fourier_pixels[i]=magnitude_pixels[i]*cos(phase_pixels[i])+I*
1012 magnitude_pixels[i]*sin(phase_pixels[i]);
cristy56ed31c2010-03-22 00:46:21 +00001013#else
cristy699ae5b2013-07-03 13:41:29 +00001014 fourier_pixels[i][0]=magnitude_pixels[i]*cos(phase_pixels[i]);
1015 fourier_pixels[i][1]=magnitude_pixels[i]*sin(phase_pixels[i]);
cristy56ed31c2010-03-22 00:46:21 +00001016#endif
cristy3ed852e2009-09-05 21:47:34 +00001017 i++;
1018 }
1019 else
cristybb503372010-05-27 20:51:26 +00001020 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1021 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001022 {
cristy56ed31c2010-03-22 00:46:21 +00001023#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy699ae5b2013-07-03 13:41:29 +00001024 fourier_pixels[i]=magnitude_pixels[i]+I*phase_pixels[i];
cristy56ed31c2010-03-22 00:46:21 +00001025#else
cristy699ae5b2013-07-03 13:41:29 +00001026 fourier_pixels[i][0]=magnitude_pixels[i];
1027 fourier_pixels[i][1]=phase_pixels[i];
cristy56ed31c2010-03-22 00:46:21 +00001028#endif
cristy3ed852e2009-09-05 21:47:34 +00001029 i++;
1030 }
cristy699ae5b2013-07-03 13:41:29 +00001031 magnitude_info=RelinquishVirtualMemory(magnitude_info);
1032 phase_info=RelinquishVirtualMemory(phase_info);
cristy3ed852e2009-09-05 21:47:34 +00001033 return(status);
1034}
1035
1036static MagickBooleanType InverseFourierTransform(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +00001037 fftw_complex *fourier_pixels,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001038{
1039 CacheView
1040 *image_view;
1041
cristy99dc0362013-09-15 18:32:54 +00001042 const char
1043 *value;
1044
cristy3ed852e2009-09-05 21:47:34 +00001045 double
cristy699ae5b2013-07-03 13:41:29 +00001046 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001047
1048 fftw_plan
1049 fftw_c2r_plan;
1050
cristy699ae5b2013-07-03 13:41:29 +00001051 MemoryInfo
1052 *source_info;
1053
cristy4c08aed2011-07-01 19:47:50 +00001054 register Quantum
cristyc4ea4a42011-01-24 01:43:30 +00001055 *q;
1056
cristybb503372010-05-27 20:51:26 +00001057 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001058 i,
1059 x;
1060
cristyc4ea4a42011-01-24 01:43:30 +00001061 ssize_t
1062 y;
cristy3ed852e2009-09-05 21:47:34 +00001063
cristy699ae5b2013-07-03 13:41:29 +00001064 source_info=AcquireVirtualMemory((size_t) fourier_info->height,
1065 fourier_info->width*sizeof(*source_pixels));
1066 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001067 {
1068 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001069 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001070 return(MagickFalse);
1071 }
cristy699ae5b2013-07-03 13:41:29 +00001072 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
cristy99dc0362013-09-15 18:32:54 +00001073 value=GetImageArtifact(image,"fourier:normalize");
cristyf8ecbfd2013-09-15 19:11:14 +00001074 if (LocaleCompare(value,"inverse") == 0)
cristy99dc0362013-09-15 18:32:54 +00001075 {
1076 double
1077 gamma;
1078
1079 /*
1080 Normalize inverse transform.
1081 */
1082 i=0L;
1083 gamma=PerceptibleReciprocal((double) fourier_info->width*
1084 fourier_info->height);
1085 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1086 for (x=0L; x < (ssize_t) fourier_info->center; x++)
1087 {
1088#if defined(MAGICKCORE_HAVE_COMPLEX_H)
1089 fourier_pixels[i]*=gamma;
1090#else
1091 fourier_pixels[i][0]*=gamma;
1092 fourier_pixels[i][1]*=gamma;
1093#endif
1094 i++;
1095 }
1096 }
cristyb5d5f722009-11-04 03:03:49 +00001097#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001098 #pragma omp critical (MagickCore_InverseFourierTransform)
1099#endif
cristydf079ac2010-09-10 01:45:44 +00001100 {
1101 fftw_c2r_plan=fftw_plan_dft_c2r_2d(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +00001102 fourier_pixels,source_pixels,FFTW_ESTIMATE);
cristydf079ac2010-09-10 01:45:44 +00001103 fftw_execute(fftw_c2r_plan);
1104 fftw_destroy_plan(fftw_c2r_plan);
1105 }
cristy3ed852e2009-09-05 21:47:34 +00001106 i=0L;
cristy46ff2672012-12-14 15:32:26 +00001107 image_view=AcquireAuthenticCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +00001108 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001109 {
cristy85812052010-09-14 17:56:15 +00001110 if (y >= (ssize_t) image->rows)
1111 break;
1112 q=GetCacheViewAuthenticPixels(image_view,0L,y,fourier_info->width >
1113 image->columns ? image->columns : fourier_info->width,1UL,exception);
cristyacd2ed22011-08-30 01:44:23 +00001114 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001115 break;
cristybb503372010-05-27 20:51:26 +00001116 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001117 {
cristy233fe582012-07-07 14:00:18 +00001118 if (x < (ssize_t) image->columns)
1119 switch (fourier_info->channel)
cristy3ed852e2009-09-05 21:47:34 +00001120 {
cristy233fe582012-07-07 14:00:18 +00001121 case RedPixelChannel:
1122 default:
1123 {
cristy699ae5b2013-07-03 13:41:29 +00001124 SetPixelRed(image,ClampToQuantum(QuantumRange*source_pixels[i]),q);
cristy233fe582012-07-07 14:00:18 +00001125 break;
1126 }
1127 case GreenPixelChannel:
1128 {
cristy699ae5b2013-07-03 13:41:29 +00001129 SetPixelGreen(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1130 q);
cristy233fe582012-07-07 14:00:18 +00001131 break;
1132 }
1133 case BluePixelChannel:
1134 {
cristy699ae5b2013-07-03 13:41:29 +00001135 SetPixelBlue(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1136 q);
cristy233fe582012-07-07 14:00:18 +00001137 break;
1138 }
1139 case BlackPixelChannel:
1140 {
cristy699ae5b2013-07-03 13:41:29 +00001141 SetPixelBlack(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1142 q);
cristy233fe582012-07-07 14:00:18 +00001143 break;
1144 }
1145 case AlphaPixelChannel:
1146 {
cristy699ae5b2013-07-03 13:41:29 +00001147 SetPixelAlpha(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1148 q);
cristy233fe582012-07-07 14:00:18 +00001149 break;
1150 }
cristy3ed852e2009-09-05 21:47:34 +00001151 }
cristy3ed852e2009-09-05 21:47:34 +00001152 i++;
cristyed231572011-07-14 02:18:59 +00001153 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001154 }
1155 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1156 break;
1157 }
1158 image_view=DestroyCacheView(image_view);
cristy699ae5b2013-07-03 13:41:29 +00001159 source_info=RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +00001160 return(MagickTrue);
1161}
1162
cristyc9550792009-11-13 20:05:42 +00001163static MagickBooleanType InverseFourierTransformChannel(
1164 const Image *magnitude_image,const Image *phase_image,
cristyd3090f92011-10-18 00:05:15 +00001165 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +00001166 Image *fourier_image,ExceptionInfo *exception)
1167{
cristy3ed852e2009-09-05 21:47:34 +00001168 fftw_complex
cristy699ae5b2013-07-03 13:41:29 +00001169 *inverse_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001170
1171 FourierInfo
1172 fourier_info;
1173
1174 MagickBooleanType
1175 status;
1176
cristy699ae5b2013-07-03 13:41:29 +00001177 MemoryInfo
1178 *inverse_info;
1179
cristy3ed852e2009-09-05 21:47:34 +00001180 size_t
1181 extent;
1182
cristyc9550792009-11-13 20:05:42 +00001183 fourier_info.width=magnitude_image->columns;
1184 if ((magnitude_image->columns != magnitude_image->rows) ||
1185 ((magnitude_image->columns % 2) != 0) ||
1186 ((magnitude_image->rows % 2) != 0))
cristy3ed852e2009-09-05 21:47:34 +00001187 {
cristyc9550792009-11-13 20:05:42 +00001188 extent=magnitude_image->columns < magnitude_image->rows ?
1189 magnitude_image->rows : magnitude_image->columns;
cristy3ed852e2009-09-05 21:47:34 +00001190 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
1191 }
1192 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +00001193 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +00001194 fourier_info.channel=channel;
1195 fourier_info.modulus=modulus;
cristy699ae5b2013-07-03 13:41:29 +00001196 inverse_info=AcquireVirtualMemory((size_t) fourier_info.height,
1197 fourier_info.center*sizeof(*inverse_pixels));
1198 if (inverse_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001199 {
1200 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001201 ResourceLimitError,"MemoryAllocationFailed","`%s'",
cristyc9550792009-11-13 20:05:42 +00001202 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001203 return(MagickFalse);
1204 }
cristy699ae5b2013-07-03 13:41:29 +00001205 inverse_pixels=(fftw_complex *) GetVirtualMemoryBlob(inverse_info);
1206 status=InverseFourier(&fourier_info,magnitude_image,phase_image,
1207 inverse_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +00001208 if (status != MagickFalse)
cristy699ae5b2013-07-03 13:41:29 +00001209 status=InverseFourierTransform(&fourier_info,inverse_pixels,fourier_image,
cristy3ed852e2009-09-05 21:47:34 +00001210 exception);
cristy699ae5b2013-07-03 13:41:29 +00001211 inverse_info=RelinquishVirtualMemory(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +00001212 return(status);
1213}
1214#endif
1215
cristyc9550792009-11-13 20:05:42 +00001216MagickExport Image *InverseFourierTransformImage(const Image *magnitude_image,
1217 const Image *phase_image,const MagickBooleanType modulus,
1218 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001219{
1220 Image
1221 *fourier_image;
1222
cristyc9550792009-11-13 20:05:42 +00001223 assert(magnitude_image != (Image *) NULL);
1224 assert(magnitude_image->signature == MagickSignature);
1225 if (magnitude_image->debug != MagickFalse)
1226 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1227 magnitude_image->filename);
1228 if (phase_image == (Image *) NULL)
1229 {
1230 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
cristyefe601c2013-01-05 17:51:12 +00001231 "TwoOrMoreImagesRequired","`%s'",magnitude_image->filename);
cristy9372a152009-11-18 01:42:16 +00001232 return((Image *) NULL);
cristyc9550792009-11-13 20:05:42 +00001233 }
cristy3ed852e2009-09-05 21:47:34 +00001234#if !defined(MAGICKCORE_FFTW_DELEGATE)
1235 fourier_image=(Image *) NULL;
1236 (void) modulus;
1237 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00001238 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristyc9550792009-11-13 20:05:42 +00001239 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001240#else
1241 {
cristyc9550792009-11-13 20:05:42 +00001242 fourier_image=CloneImage(magnitude_image,magnitude_image->columns,
1243 magnitude_image->rows,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00001244 if (fourier_image != (Image *) NULL)
1245 {
1246 MagickBooleanType
1247 is_gray,
1248 status;
1249
cristy3ed852e2009-09-05 21:47:34 +00001250 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001251 is_gray=IsImageGray(magnitude_image,exception);
cristyc9550792009-11-13 20:05:42 +00001252 if (is_gray != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001253 is_gray=IsImageGray(phase_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001254#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb34ef052010-10-07 00:12:05 +00001255 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +00001256#endif
cristy3ed852e2009-09-05 21:47:34 +00001257 {
cristyb34ef052010-10-07 00:12:05 +00001258#if defined(MAGICKCORE_OPENMP_SUPPORT)
1259 #pragma omp section
1260#endif
cristy3ed852e2009-09-05 21:47:34 +00001261 {
cristyb34ef052010-10-07 00:12:05 +00001262 MagickBooleanType
1263 thread_status;
1264
1265 if (is_gray != MagickFalse)
1266 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001267 phase_image,GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001268 else
cristyc9550792009-11-13 20:05:42 +00001269 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001270 phase_image,RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001271 if (thread_status == MagickFalse)
1272 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +00001273 }
cristyb34ef052010-10-07 00:12:05 +00001274#if defined(MAGICKCORE_OPENMP_SUPPORT)
1275 #pragma omp section
1276#endif
1277 {
1278 MagickBooleanType
1279 thread_status;
1280
1281 thread_status=MagickTrue;
1282 if (is_gray == MagickFalse)
1283 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001284 phase_image,GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001285 if (thread_status == MagickFalse)
1286 status=thread_status;
1287 }
1288#if defined(MAGICKCORE_OPENMP_SUPPORT)
1289 #pragma omp section
1290#endif
1291 {
1292 MagickBooleanType
1293 thread_status;
1294
1295 thread_status=MagickTrue;
1296 if (is_gray == MagickFalse)
1297 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001298 phase_image,BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001299 if (thread_status == MagickFalse)
1300 status=thread_status;
1301 }
1302#if defined(MAGICKCORE_OPENMP_SUPPORT)
1303 #pragma omp section
1304#endif
1305 {
1306 MagickBooleanType
1307 thread_status;
1308
1309 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001310 if (magnitude_image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +00001311 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001312 phase_image,BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001313 if (thread_status == MagickFalse)
1314 status=thread_status;
1315 }
1316#if defined(MAGICKCORE_OPENMP_SUPPORT)
1317 #pragma omp section
1318#endif
1319 {
1320 MagickBooleanType
1321 thread_status;
1322
1323 thread_status=MagickTrue;
cristy8a46d822012-08-28 23:32:39 +00001324 if (magnitude_image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +00001325 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001326 phase_image,AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001327 if (thread_status == MagickFalse)
1328 status=thread_status;
1329 }
cristy3ed852e2009-09-05 21:47:34 +00001330 }
1331 if (status == MagickFalse)
1332 fourier_image=DestroyImage(fourier_image);
1333 }
cristyb34ef052010-10-07 00:12:05 +00001334 fftw_cleanup();
cristy3ed852e2009-09-05 21:47:34 +00001335 }
1336#endif
1337 return(fourier_image);
1338}