blob: 91e2de98ecd7c13d595b7484737ceb9d70340076 [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"
46#include "MagickCore/attribute.h"
cristy7d4aa382013-06-30 01:59:39 +000047#include "MagickCore/blob.h"
cristy4c08aed2011-07-01 19:47:50 +000048#include "MagickCore/cache.h"
49#include "MagickCore/image.h"
50#include "MagickCore/image-private.h"
51#include "MagickCore/list.h"
52#include "MagickCore/fourier.h"
53#include "MagickCore/log.h"
54#include "MagickCore/memory_.h"
55#include "MagickCore/monitor.h"
56#include "MagickCore/pixel-accessor.h"
57#include "MagickCore/property.h"
58#include "MagickCore/quantum-private.h"
cristyac245f82012-05-05 17:13:57 +000059#include "MagickCore/resource_.h"
cristy4c08aed2011-07-01 19:47:50 +000060#include "MagickCore/thread-private.h"
cristy3ed852e2009-09-05 21:47:34 +000061#if defined(MAGICKCORE_FFTW_DELEGATE)
cristy56ed31c2010-03-22 00:46:21 +000062#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy3ed852e2009-09-05 21:47:34 +000063#include <complex.h>
cristy56ed31c2010-03-22 00:46:21 +000064#endif
cristy3ed852e2009-09-05 21:47:34 +000065#include <fftw3.h>
cristy47b022b2011-01-18 22:29:21 +000066#if !defined(MAGICKCORE_HAVE_CABS)
67#define cabs(z) (sqrt(z[0]*z[0]+z[1]*z[1]))
68#endif
69#if !defined(MAGICKCORE_HAVE_CARG)
cristy4da3ba32011-02-07 16:58:11 +000070#define carg(z) (atan2(cimag(z),creal(z)))
cristy47b022b2011-01-18 22:29:21 +000071#endif
72#if !defined(MAGICKCORE_HAVE_CIMAG)
73#define cimag(z) (z[1])
74#endif
75#if !defined(MAGICKCORE_HAVE_CREAL)
76#define creal(z) (z[0])
77#endif
cristy3ed852e2009-09-05 21:47:34 +000078#endif
79
80/*
81 Typedef declarations.
82*/
83typedef struct _FourierInfo
84{
cristyd3090f92011-10-18 00:05:15 +000085 PixelChannel
cristy3ed852e2009-09-05 21:47:34 +000086 channel;
87
88 MagickBooleanType
89 modulus;
90
cristybb503372010-05-27 20:51:26 +000091 size_t
cristy3ed852e2009-09-05 21:47:34 +000092 width,
93 height;
94
cristybb503372010-05-27 20:51:26 +000095 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000096 center;
97} FourierInfo;
98
99/*
100%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
101% %
102% %
103% %
104% 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 %
105% %
106% %
107% %
108%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
109%
110% ForwardFourierTransformImage() implements the discrete Fourier transform
111% (DFT) of the image either as a magnitude / phase or real / imaginary image
112% pair.
113%
114% The format of the ForwadFourierTransformImage method is:
115%
116% Image *ForwardFourierTransformImage(const Image *image,
117% const MagickBooleanType modulus,ExceptionInfo *exception)
118%
119% A description of each parameter follows:
120%
121% o image: the image.
122%
123% o modulus: if true, return as transform as a magnitude / phase pair
124% otherwise a real / imaginary image pair.
125%
126% o exception: return any errors or warnings in this structure.
127%
128*/
129
130#if defined(MAGICKCORE_FFTW_DELEGATE)
131
cristyc4ea4a42011-01-24 01:43:30 +0000132static MagickBooleanType RollFourier(const size_t width,const size_t height,
133 const ssize_t x_offset,const ssize_t y_offset,double *fourier)
cristy3ed852e2009-09-05 21:47:34 +0000134{
135 double
136 *roll;
137
cristy7d4aa382013-06-30 01:59:39 +0000138 MemoryInfo
139 *roll_info;
140
cristyc4ea4a42011-01-24 01:43:30 +0000141 register ssize_t
142 i,
143 x;
144
cristybb503372010-05-27 20:51:26 +0000145 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000146 u,
147 v,
148 y;
149
cristy3ed852e2009-09-05 21:47:34 +0000150 /*
cristy2da05352010-09-15 19:22:17 +0000151 Move zero frequency (DC, average color) from (0,0) to (width/2,height/2).
cristy3ed852e2009-09-05 21:47:34 +0000152 */
cristy7d4aa382013-06-30 01:59:39 +0000153 roll_info=AcquireVirtualMemory(height,width*sizeof(*roll));
154 if (roll_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000155 return(MagickFalse);
cristy7d4aa382013-06-30 01:59:39 +0000156 roll=(double *) GetVirtualMemoryBlob(roll_info);
cristy3ed852e2009-09-05 21:47:34 +0000157 i=0L;
cristybb503372010-05-27 20:51:26 +0000158 for (y=0L; y < (ssize_t) height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000159 {
160 if (y_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000161 v=((y+y_offset) < 0L) ? y+y_offset+(ssize_t) height : y+y_offset;
cristy3ed852e2009-09-05 21:47:34 +0000162 else
cristybb503372010-05-27 20:51:26 +0000163 v=((y+y_offset) > ((ssize_t) height-1L)) ? y+y_offset-(ssize_t) height :
cristy3ed852e2009-09-05 21:47:34 +0000164 y+y_offset;
cristybb503372010-05-27 20:51:26 +0000165 for (x=0L; x < (ssize_t) width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000166 {
167 if (x_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000168 u=((x+x_offset) < 0L) ? x+x_offset+(ssize_t) width : x+x_offset;
cristy3ed852e2009-09-05 21:47:34 +0000169 else
cristybb503372010-05-27 20:51:26 +0000170 u=((x+x_offset) > ((ssize_t) width-1L)) ? x+x_offset-(ssize_t) width :
cristy3ed852e2009-09-05 21:47:34 +0000171 x+x_offset;
172 roll[v*width+u]=fourier[i++];
cristyc4ea4a42011-01-24 01:43:30 +0000173 }
cristy3ed852e2009-09-05 21:47:34 +0000174 }
cristyc4ea4a42011-01-24 01:43:30 +0000175 (void) CopyMagickMemory(fourier,roll,height*width*sizeof(*roll));
cristy7d4aa382013-06-30 01:59:39 +0000176 roll_info=RelinquishVirtualMemory(roll_info);
cristy3ed852e2009-09-05 21:47:34 +0000177 return(MagickTrue);
178}
179
cristybb503372010-05-27 20:51:26 +0000180static MagickBooleanType ForwardQuadrantSwap(const size_t width,
181 const size_t height,double *source,double *destination)
cristy3ed852e2009-09-05 21:47:34 +0000182{
cristy3ed852e2009-09-05 21:47:34 +0000183 MagickBooleanType
184 status;
185
cristybb503372010-05-27 20:51:26 +0000186 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000187 x;
188
cristyc4ea4a42011-01-24 01:43:30 +0000189 ssize_t
190 center,
191 y;
192
cristy3ed852e2009-09-05 21:47:34 +0000193 /*
194 Swap quadrants.
195 */
cristybb503372010-05-27 20:51:26 +0000196 center=(ssize_t) floor((double) width/2L)+1L;
197 status=RollFourier((size_t) center,height,0L,(ssize_t) height/2L,source);
cristy3ed852e2009-09-05 21:47:34 +0000198 if (status == MagickFalse)
199 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000200 for (y=0L; y < (ssize_t) height; y++)
201 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000202 destination[width*y+x+width/2L]=source[center*y+x];
cristybb503372010-05-27 20:51:26 +0000203 for (y=1; y < (ssize_t) height; y++)
204 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000205 destination[width*(height-y)+width/2L-x-1L]=source[center*y+x+1L];
cristybb503372010-05-27 20:51:26 +0000206 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000207 destination[-x+width/2L-1L]=destination[x+width/2L+1L];
208 return(MagickTrue);
209}
210
cristyc4ea4a42011-01-24 01:43:30 +0000211static void CorrectPhaseLHS(const size_t width,const size_t height,
212 double *fourier)
cristy3ed852e2009-09-05 21:47:34 +0000213{
cristybb503372010-05-27 20:51:26 +0000214 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000215 x;
216
cristy9d314ff2011-03-09 01:30:28 +0000217 ssize_t
218 y;
219
cristybb503372010-05-27 20:51:26 +0000220 for (y=0L; y < (ssize_t) height; y++)
221 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000222 fourier[y*width+x]*=(-1.0);
223}
224
225static MagickBooleanType ForwardFourier(const FourierInfo *fourier_info,
226 Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
227{
228 CacheView
229 *magnitude_view,
230 *phase_view;
231
232 double
cristy7d4aa382013-06-30 01:59:39 +0000233 *magnitude_pixels,
234 *phase_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000235
236 Image
237 *magnitude_image,
238 *phase_image;
239
cristy3ed852e2009-09-05 21:47:34 +0000240 MagickBooleanType
241 status;
242
cristy7d4aa382013-06-30 01:59:39 +0000243 MemoryInfo
244 *magnitude_info,
245 *phase_info;
246
cristy4c08aed2011-07-01 19:47:50 +0000247 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000248 *q;
249
cristyf5742792012-11-27 18:31:26 +0000250 register ssize_t
251 x;
252
cristyc4ea4a42011-01-24 01:43:30 +0000253 ssize_t
254 i,
255 y;
256
cristy3ed852e2009-09-05 21:47:34 +0000257 magnitude_image=GetFirstImageInList(image);
258 phase_image=GetNextImageInList(image);
259 if (phase_image == (Image *) NULL)
260 {
261 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
cristyefe601c2013-01-05 17:51:12 +0000262 "TwoOrMoreImagesRequired","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000263 return(MagickFalse);
264 }
265 /*
266 Create "Fourier Transform" image from constituent arrays.
267 */
cristy7d4aa382013-06-30 01:59:39 +0000268 magnitude_info=AcquireVirtualMemory((size_t) fourier_info->height,
269 fourier_info->width*sizeof(*magnitude_pixels));
270 phase_info=AcquireVirtualMemory((size_t) fourier_info->height,
271 fourier_info->width*sizeof(*phase_pixels));
272 if ((phase_info == (MemoryInfo *) NULL) ||
273 (magnitude_info == (MemoryInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000274 {
cristy7d4aa382013-06-30 01:59:39 +0000275 if (phase_info != (MemoryInfo *) NULL)
276 phase_info=RelinquishVirtualMemory(phase_info);
277 if (magnitude_info != (MemoryInfo *) NULL)
278 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000279 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000280 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000281 return(MagickFalse);
282 }
cristy7d4aa382013-06-30 01:59:39 +0000283 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
284 (void) ResetMagickMemory(magnitude_pixels,0,fourier_info->height*
285 fourier_info->width*sizeof(*magnitude_pixels));
286 phase_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
287 (void) ResetMagickMemory(phase_pixels,0,fourier_info->height*
288 fourier_info->width*sizeof(*phase_pixels));
cristy3ed852e2009-09-05 21:47:34 +0000289 status=ForwardQuadrantSwap(fourier_info->height,fourier_info->height,
cristy7d4aa382013-06-30 01:59:39 +0000290 magnitude,magnitude_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000291 if (status != MagickFalse)
292 status=ForwardQuadrantSwap(fourier_info->height,fourier_info->height,phase,
cristy7d4aa382013-06-30 01:59:39 +0000293 phase_pixels);
294 CorrectPhaseLHS(fourier_info->height,fourier_info->height,phase_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000295 if (fourier_info->modulus != MagickFalse)
296 {
297 i=0L;
cristybb503372010-05-27 20:51:26 +0000298 for (y=0L; y < (ssize_t) fourier_info->height; y++)
299 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000300 {
cristy7d4aa382013-06-30 01:59:39 +0000301 phase_pixels[i]/=(2.0*MagickPI);
302 phase_pixels[i]+=0.5;
cristy3ed852e2009-09-05 21:47:34 +0000303 i++;
304 }
305 }
cristy46ff2672012-12-14 15:32:26 +0000306 magnitude_view=AcquireAuthenticCacheView(magnitude_image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000307 i=0L;
cristybb503372010-05-27 20:51:26 +0000308 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000309 {
310 q=GetCacheViewAuthenticPixels(magnitude_view,0L,y,fourier_info->height,1UL,
311 exception);
cristyacd2ed22011-08-30 01:44:23 +0000312 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000313 break;
cristybb503372010-05-27 20:51:26 +0000314 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000315 {
316 switch (fourier_info->channel)
317 {
cristyd3090f92011-10-18 00:05:15 +0000318 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000319 default:
320 {
cristy4c08aed2011-07-01 19:47:50 +0000321 SetPixelRed(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000322 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000323 break;
324 }
cristyd3090f92011-10-18 00:05:15 +0000325 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000326 {
cristy4c08aed2011-07-01 19:47:50 +0000327 SetPixelGreen(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000328 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000329 break;
330 }
cristyd3090f92011-10-18 00:05:15 +0000331 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000332 {
cristy4c08aed2011-07-01 19:47:50 +0000333 SetPixelBlue(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000334 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000335 break;
336 }
cristyd3090f92011-10-18 00:05:15 +0000337 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000338 {
cristy4c08aed2011-07-01 19:47:50 +0000339 SetPixelBlack(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000340 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000341 break;
342 }
cristyd3090f92011-10-18 00:05:15 +0000343 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000344 {
cristy4c08aed2011-07-01 19:47:50 +0000345 SetPixelAlpha(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000346 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000347 break;
348 }
cristy3ed852e2009-09-05 21:47:34 +0000349 }
350 i++;
cristyed231572011-07-14 02:18:59 +0000351 q+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +0000352 }
353 status=SyncCacheViewAuthenticPixels(magnitude_view,exception);
354 if (status == MagickFalse)
355 break;
356 }
cristydb070952012-04-20 14:33:00 +0000357 magnitude_view=DestroyCacheView(magnitude_view);
cristy3ed852e2009-09-05 21:47:34 +0000358 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000359 phase_view=AcquireAuthenticCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +0000360 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000361 {
362 q=GetCacheViewAuthenticPixels(phase_view,0L,y,fourier_info->height,1UL,
363 exception);
cristyacd2ed22011-08-30 01:44:23 +0000364 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000365 break;
cristybb503372010-05-27 20:51:26 +0000366 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000367 {
368 switch (fourier_info->channel)
369 {
cristyd3090f92011-10-18 00:05:15 +0000370 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000371 default:
372 {
cristy4c08aed2011-07-01 19:47:50 +0000373 SetPixelRed(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000374 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000375 break;
376 }
cristyd3090f92011-10-18 00:05:15 +0000377 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000378 {
cristy4c08aed2011-07-01 19:47:50 +0000379 SetPixelGreen(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000380 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000381 break;
382 }
cristyd3090f92011-10-18 00:05:15 +0000383 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000384 {
cristy4c08aed2011-07-01 19:47:50 +0000385 SetPixelBlue(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000386 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000387 break;
388 }
cristyd3090f92011-10-18 00:05:15 +0000389 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000390 {
cristy4c08aed2011-07-01 19:47:50 +0000391 SetPixelBlack(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000392 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000393 break;
394 }
cristyd3090f92011-10-18 00:05:15 +0000395 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000396 {
cristy4c08aed2011-07-01 19:47:50 +0000397 SetPixelAlpha(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000398 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000399 break;
400 }
cristy3ed852e2009-09-05 21:47:34 +0000401 }
402 i++;
cristyed231572011-07-14 02:18:59 +0000403 q+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +0000404 }
405 status=SyncCacheViewAuthenticPixels(phase_view,exception);
406 if (status == MagickFalse)
407 break;
408 }
409 phase_view=DestroyCacheView(phase_view);
cristy7d4aa382013-06-30 01:59:39 +0000410 phase_info=RelinquishVirtualMemory(phase_info);
411 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000412 return(status);
413}
414
415static MagickBooleanType ForwardFourierTransform(FourierInfo *fourier_info,
416 const Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
417{
418 CacheView
419 *image_view;
420
421 double
422 n,
423 *source;
424
425 fftw_complex
426 *fourier;
427
428 fftw_plan
429 fftw_r2c_plan;
430
cristy7d4aa382013-06-30 01:59:39 +0000431 MemoryInfo
432 *source_info;
433
cristy4c08aed2011-07-01 19:47:50 +0000434 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000435 *p;
436
cristybb503372010-05-27 20:51:26 +0000437 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000438 i,
439 x;
440
cristyc4ea4a42011-01-24 01:43:30 +0000441 ssize_t
442 y;
443
cristy3ed852e2009-09-05 21:47:34 +0000444 /*
445 Generate the forward Fourier transform.
446 */
cristy7d4aa382013-06-30 01:59:39 +0000447 source_info=AcquireVirtualMemory((size_t) fourier_info->height,
cristy3ed852e2009-09-05 21:47:34 +0000448 fourier_info->width*sizeof(*source));
cristy7d4aa382013-06-30 01:59:39 +0000449 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000450 {
451 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000452 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000453 return(MagickFalse);
454 }
cristy7d4aa382013-06-30 01:59:39 +0000455 source=(double *) GetVirtualMemoryBlob(source_info);
cristyc4ea4a42011-01-24 01:43:30 +0000456 ResetMagickMemory(source,0,fourier_info->height*fourier_info->width*
cristy3ed852e2009-09-05 21:47:34 +0000457 sizeof(*source));
458 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000459 image_view=AcquireVirtualCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +0000460 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000461 {
462 p=GetCacheViewVirtualPixels(image_view,0L,y,fourier_info->width,1UL,
463 exception);
cristy4c08aed2011-07-01 19:47:50 +0000464 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000465 break;
cristybb503372010-05-27 20:51:26 +0000466 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000467 {
468 switch (fourier_info->channel)
469 {
cristyd3090f92011-10-18 00:05:15 +0000470 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000471 default:
472 {
cristy4c08aed2011-07-01 19:47:50 +0000473 source[i]=QuantumScale*GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000474 break;
475 }
cristyd3090f92011-10-18 00:05:15 +0000476 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000477 {
cristy4c08aed2011-07-01 19:47:50 +0000478 source[i]=QuantumScale*GetPixelGreen(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000479 break;
480 }
cristyd3090f92011-10-18 00:05:15 +0000481 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000482 {
cristy4c08aed2011-07-01 19:47:50 +0000483 source[i]=QuantumScale*GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000484 break;
485 }
cristyd3090f92011-10-18 00:05:15 +0000486 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000487 {
cristy4c08aed2011-07-01 19:47:50 +0000488 source[i]=QuantumScale*GetPixelBlack(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000489 break;
490 }
cristyd3090f92011-10-18 00:05:15 +0000491 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000492 {
cristy4c08aed2011-07-01 19:47:50 +0000493 source[i]=QuantumScale*GetPixelAlpha(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000494 break;
495 }
cristy3ed852e2009-09-05 21:47:34 +0000496 }
497 i++;
cristyed231572011-07-14 02:18:59 +0000498 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000499 }
500 }
501 image_view=DestroyCacheView(image_view);
cristyb41ee102010-10-04 16:46:15 +0000502 fourier=(fftw_complex *) AcquireQuantumMemory((size_t) fourier_info->height,
cristy3ed852e2009-09-05 21:47:34 +0000503 fourier_info->center*sizeof(*fourier));
504 if (fourier == (fftw_complex *) NULL)
505 {
506 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000507 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000508 source=(double *) RelinquishMagickMemory(source);
509 return(MagickFalse);
510 }
cristyb5d5f722009-11-04 03:03:49 +0000511#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000512 #pragma omp critical (MagickCore_ForwardFourierTransform)
513#endif
cristy70841a12012-10-27 20:52:57 +0000514 fftw_r2c_plan=fftw_plan_dft_r2c_2d(fourier_info->width,fourier_info->height,
cristy3ed852e2009-09-05 21:47:34 +0000515 source,fourier,FFTW_ESTIMATE);
516 fftw_execute(fftw_r2c_plan);
517 fftw_destroy_plan(fftw_r2c_plan);
cristy7d4aa382013-06-30 01:59:39 +0000518 source_info=RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000519 /*
520 Normalize Fourier transform.
521 */
522 n=(double) fourier_info->width*(double) fourier_info->width;
523 i=0L;
cristybb503372010-05-27 20:51:26 +0000524 for (y=0L; y < (ssize_t) fourier_info->height; y++)
525 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy56ed31c2010-03-22 00:46:21 +0000526 {
527#if defined(MAGICKCORE_HAVE_COMPLEX_H)
528 fourier[i]/=n;
529#else
530 fourier[i][0]/=n;
531 fourier[i][1]/=n;
532#endif
533 i++;
534 }
cristy3ed852e2009-09-05 21:47:34 +0000535 /*
536 Generate magnitude and phase (or real and imaginary).
537 */
538 i=0L;
539 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000540 for (y=0L; y < (ssize_t) fourier_info->height; y++)
541 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000542 {
543 magnitude[i]=cabs(fourier[i]);
544 phase[i]=carg(fourier[i]);
545 i++;
546 }
547 else
cristybb503372010-05-27 20:51:26 +0000548 for (y=0L; y < (ssize_t) fourier_info->height; y++)
549 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000550 {
551 magnitude[i]=creal(fourier[i]);
552 phase[i]=cimag(fourier[i]);
553 i++;
554 }
cristyb41ee102010-10-04 16:46:15 +0000555 fourier=(fftw_complex *) RelinquishMagickMemory(fourier);
cristy3ed852e2009-09-05 21:47:34 +0000556 return(MagickTrue);
557}
558
559static MagickBooleanType ForwardFourierTransformChannel(const Image *image,
cristyd3090f92011-10-18 00:05:15 +0000560 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +0000561 Image *fourier_image,ExceptionInfo *exception)
562{
563 double
564 *magnitude,
565 *phase;
566
cristy56ed31c2010-03-22 00:46:21 +0000567 FourierInfo
568 fourier_info;
569
cristyc4ea4a42011-01-24 01:43:30 +0000570 MagickBooleanType
571 status;
572
cristy7d4aa382013-06-30 01:59:39 +0000573 MemoryInfo
574 *magnitude_info,
575 *phase_info;
576
cristy3ed852e2009-09-05 21:47:34 +0000577 size_t
578 extent;
579
580 fourier_info.width=image->columns;
581 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
582 ((image->rows % 2) != 0))
583 {
584 extent=image->columns < image->rows ? image->rows : image->columns;
585 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
586 }
587 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +0000588 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +0000589 fourier_info.channel=channel;
590 fourier_info.modulus=modulus;
cristy7d4aa382013-06-30 01:59:39 +0000591 magnitude_info=AcquireVirtualMemory((size_t) fourier_info.height,
cristy3ed852e2009-09-05 21:47:34 +0000592 fourier_info.center*sizeof(*magnitude));
cristy7d4aa382013-06-30 01:59:39 +0000593 phase_info=AcquireVirtualMemory((size_t) fourier_info.height,
cristy3ed852e2009-09-05 21:47:34 +0000594 fourier_info.center*sizeof(*phase));
cristy7d4aa382013-06-30 01:59:39 +0000595 if ((magnitude_info == (MemoryInfo *) NULL) ||
596 (phase_info == (MemoryInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000597 {
cristy7d4aa382013-06-30 01:59:39 +0000598 if (phase_info != (MemoryInfo *) NULL)
599 phase_info=RelinquishVirtualMemory(phase_info);
600 if (magnitude_info != (MemoryInfo *) NULL)
601 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000602 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000603 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000604 return(MagickFalse);
605 }
cristy7d4aa382013-06-30 01:59:39 +0000606 magnitude=(double *) GetVirtualMemoryBlob(magnitude_info);
607 phase=(double *) GetVirtualMemoryBlob(phase_info);
cristy3ed852e2009-09-05 21:47:34 +0000608 status=ForwardFourierTransform(&fourier_info,image,magnitude,phase,exception);
609 if (status != MagickFalse)
610 status=ForwardFourier(&fourier_info,fourier_image,magnitude,phase,
611 exception);
cristy7d4aa382013-06-30 01:59:39 +0000612 phase_info=RelinquishVirtualMemory(phase_info);
613 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000614 return(status);
615}
616#endif
617
618MagickExport Image *ForwardFourierTransformImage(const Image *image,
619 const MagickBooleanType modulus,ExceptionInfo *exception)
620{
621 Image
622 *fourier_image;
623
624 fourier_image=NewImageList();
625#if !defined(MAGICKCORE_FFTW_DELEGATE)
626 (void) modulus;
627 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000628 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristy3ed852e2009-09-05 21:47:34 +0000629 image->filename);
630#else
631 {
632 Image
633 *magnitude_image;
634
cristybb503372010-05-27 20:51:26 +0000635 size_t
cristy3ed852e2009-09-05 21:47:34 +0000636 extent,
637 width;
638
639 width=image->columns;
640 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
641 ((image->rows % 2) != 0))
642 {
643 extent=image->columns < image->rows ? image->rows : image->columns;
644 width=(extent & 0x01) == 1 ? extent+1UL : extent;
645 }
646 magnitude_image=CloneImage(image,width,width,MagickFalse,exception);
647 if (magnitude_image != (Image *) NULL)
648 {
649 Image
650 *phase_image;
651
652 magnitude_image->storage_class=DirectClass;
653 magnitude_image->depth=32UL;
654 phase_image=CloneImage(image,width,width,MagickFalse,exception);
655 if (phase_image == (Image *) NULL)
656 magnitude_image=DestroyImage(magnitude_image);
657 else
658 {
659 MagickBooleanType
660 is_gray,
661 status;
662
cristy3ed852e2009-09-05 21:47:34 +0000663 phase_image->storage_class=DirectClass;
664 phase_image->depth=32UL;
665 AppendImageToList(&fourier_image,magnitude_image);
666 AppendImageToList(&fourier_image,phase_image);
667 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000668 is_gray=IsImageGray(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000669#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy564a5692012-01-20 23:56:26 +0000670 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +0000671#endif
cristy3ed852e2009-09-05 21:47:34 +0000672 {
cristyb34ef052010-10-07 00:12:05 +0000673#if defined(MAGICKCORE_OPENMP_SUPPORT)
674 #pragma omp section
675#endif
cristy3ed852e2009-09-05 21:47:34 +0000676 {
cristyb34ef052010-10-07 00:12:05 +0000677 MagickBooleanType
678 thread_status;
679
680 if (is_gray != MagickFalse)
681 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000682 GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000683 else
684 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000685 RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000686 if (thread_status == MagickFalse)
687 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +0000688 }
cristyb34ef052010-10-07 00:12:05 +0000689#if defined(MAGICKCORE_OPENMP_SUPPORT)
690 #pragma omp section
691#endif
692 {
693 MagickBooleanType
694 thread_status;
695
696 thread_status=MagickTrue;
697 if (is_gray == MagickFalse)
698 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000699 GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000700 if (thread_status == MagickFalse)
701 status=thread_status;
702 }
703#if defined(MAGICKCORE_OPENMP_SUPPORT)
704 #pragma omp section
705#endif
706 {
707 MagickBooleanType
708 thread_status;
709
710 thread_status=MagickTrue;
711 if (is_gray == MagickFalse)
712 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000713 BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000714 if (thread_status == MagickFalse)
715 status=thread_status;
716 }
717#if defined(MAGICKCORE_OPENMP_SUPPORT)
718 #pragma omp section
719#endif
720 {
721 MagickBooleanType
722 thread_status;
723
724 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000725 if (image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +0000726 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000727 BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000728 if (thread_status == MagickFalse)
729 status=thread_status;
730 }
731#if defined(MAGICKCORE_OPENMP_SUPPORT)
732 #pragma omp section
733#endif
734 {
735 MagickBooleanType
736 thread_status;
737
738 thread_status=MagickTrue;
cristy8a46d822012-08-28 23:32:39 +0000739 if (image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +0000740 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000741 AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000742 if (thread_status == MagickFalse)
743 status=thread_status;
744 }
cristy3ed852e2009-09-05 21:47:34 +0000745 }
746 if (status == MagickFalse)
747 fourier_image=DestroyImageList(fourier_image);
748 fftw_cleanup();
749 }
750 }
751 }
752#endif
753 return(fourier_image);
754}
755
756/*
757%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
758% %
759% %
760% %
761% 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 %
762% %
763% %
764% %
765%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
766%
767% InverseFourierTransformImage() implements the inverse discrete Fourier
768% transform (DFT) of the image either as a magnitude / phase or real /
769% imaginary image pair.
770%
771% The format of the InverseFourierTransformImage method is:
772%
cristyc9550792009-11-13 20:05:42 +0000773% Image *InverseFourierTransformImage(const Image *magnitude_image,
774% const Image *phase_image,const MagickBooleanType modulus,
775% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000776%
777% A description of each parameter follows:
778%
cristyc9550792009-11-13 20:05:42 +0000779% o magnitude_image: the magnitude or real image.
780%
781% o phase_image: the phase or imaginary image.
cristy3ed852e2009-09-05 21:47:34 +0000782%
783% o modulus: if true, return transform as a magnitude / phase pair
784% otherwise a real / imaginary image pair.
785%
786% o exception: return any errors or warnings in this structure.
787%
788*/
789
790#if defined(MAGICKCORE_FFTW_DELEGATE)
cristybb503372010-05-27 20:51:26 +0000791static MagickBooleanType InverseQuadrantSwap(const size_t width,
792 const size_t height,const double *source,double *destination)
cristy3ed852e2009-09-05 21:47:34 +0000793{
cristyc4ea4a42011-01-24 01:43:30 +0000794 register ssize_t
795 x;
796
cristybb503372010-05-27 20:51:26 +0000797 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000798 center,
799 y;
800
cristy3ed852e2009-09-05 21:47:34 +0000801 /*
802 Swap quadrants.
803 */
cristy233fe582012-07-07 14:00:18 +0000804 center=(ssize_t) floor((double) width/2L)+1L;
cristybb503372010-05-27 20:51:26 +0000805 for (y=1L; y < (ssize_t) height; y++)
806 for (x=0L; x < (ssize_t) (width/2L+1L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000807 destination[center*(height-y)-x+width/2L]=source[y*width+x];
cristybb503372010-05-27 20:51:26 +0000808 for (y=0L; y < (ssize_t) height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000809 destination[center*y]=source[y*width+width/2L];
810 for (x=0L; x < center; x++)
811 destination[x]=source[center-x-1L];
cristybb503372010-05-27 20:51:26 +0000812 return(RollFourier(center,height,0L,(ssize_t) height/-2L,destination));
cristy3ed852e2009-09-05 21:47:34 +0000813}
814
815static MagickBooleanType InverseFourier(FourierInfo *fourier_info,
cristyc9550792009-11-13 20:05:42 +0000816 const Image *magnitude_image,const Image *phase_image,fftw_complex *fourier,
817 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000818{
819 CacheView
820 *magnitude_view,
821 *phase_view;
822
823 double
cristy7d4aa382013-06-30 01:59:39 +0000824 *buffer,
825 *magnitude_pixels,
826 *phase_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000827
cristy3ed852e2009-09-05 21:47:34 +0000828 MagickBooleanType
829 status;
830
cristy7d4aa382013-06-30 01:59:39 +0000831 MemoryInfo
832 *buffer_info,
833 *magnitude_info,
834 *phase_info;
835
cristy4c08aed2011-07-01 19:47:50 +0000836 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000837 *p;
838
cristybb503372010-05-27 20:51:26 +0000839 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000840 i,
841 x;
842
cristyc4ea4a42011-01-24 01:43:30 +0000843 ssize_t
844 y;
845
cristy3ed852e2009-09-05 21:47:34 +0000846 /*
847 Inverse fourier - read image and break down into a double array.
848 */
cristy7d4aa382013-06-30 01:59:39 +0000849 magnitude_info=AcquireVirtualMemory((size_t) fourier_info->height,
850 fourier_info->width*sizeof(*magnitude_pixels));
851 phase_info=AcquireVirtualMemory((size_t) fourier_info->height,
852 fourier_info->width*sizeof(*phase_pixels));
853 buffer_info=AcquireVirtualMemory((size_t) fourier_info->height,
854 fourier_info->width*sizeof(*buffer));
855 if ((phase_info == (MemoryInfo *) NULL) ||
856 (magnitude_info == (MemoryInfo *) NULL) ||
857 (buffer_info == (MemoryInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000858 {
cristy7d4aa382013-06-30 01:59:39 +0000859 if (buffer_info != (MemoryInfo *) NULL)
860 buffer_info=RelinquishVirtualMemory(buffer_info);
861 if (phase_info != (MemoryInfo *) NULL)
862 phase_info=RelinquishVirtualMemory(phase_info);
863 if (magnitude_info != (MemoryInfo *) NULL)
864 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000865 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000866 ResourceLimitError,"MemoryAllocationFailed","`%s'",
cristyc9550792009-11-13 20:05:42 +0000867 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000868 return(MagickFalse);
869 }
cristy7d4aa382013-06-30 01:59:39 +0000870 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
871 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
872 buffer=(double *) GetVirtualMemoryBlob(buffer_info);
cristy3ed852e2009-09-05 21:47:34 +0000873 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000874 magnitude_view=AcquireVirtualCacheView(magnitude_image,exception);
cristybb503372010-05-27 20:51:26 +0000875 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000876 {
877 p=GetCacheViewVirtualPixels(magnitude_view,0L,y,fourier_info->width,1UL,
878 exception);
cristy4c08aed2011-07-01 19:47:50 +0000879 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000880 break;
cristybb503372010-05-27 20:51:26 +0000881 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000882 {
883 switch (fourier_info->channel)
884 {
cristyd3090f92011-10-18 00:05:15 +0000885 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000886 default:
887 {
cristy7d4aa382013-06-30 01:59:39 +0000888 magnitude_pixels[i]=QuantumScale*GetPixelRed(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000889 break;
890 }
cristyd3090f92011-10-18 00:05:15 +0000891 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000892 {
cristy7d4aa382013-06-30 01:59:39 +0000893 magnitude_pixels[i]=QuantumScale*GetPixelGreen(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000894 break;
895 }
cristyd3090f92011-10-18 00:05:15 +0000896 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000897 {
cristy7d4aa382013-06-30 01:59:39 +0000898 magnitude_pixels[i]=QuantumScale*GetPixelBlue(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000899 break;
900 }
cristyd3090f92011-10-18 00:05:15 +0000901 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000902 {
cristy7d4aa382013-06-30 01:59:39 +0000903 magnitude_pixels[i]=QuantumScale*GetPixelBlack(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000904 break;
905 }
cristyd3090f92011-10-18 00:05:15 +0000906 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000907 {
cristy7d4aa382013-06-30 01:59:39 +0000908 magnitude_pixels[i]=QuantumScale*GetPixelAlpha(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000909 break;
910 }
cristy3ed852e2009-09-05 21:47:34 +0000911 }
912 i++;
cristyed231572011-07-14 02:18:59 +0000913 p+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +0000914 }
915 }
916 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000917 phase_view=AcquireVirtualCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +0000918 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000919 {
920 p=GetCacheViewVirtualPixels(phase_view,0,y,fourier_info->width,1,
921 exception);
cristy4c08aed2011-07-01 19:47:50 +0000922 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000923 break;
cristybb503372010-05-27 20:51:26 +0000924 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000925 {
926 switch (fourier_info->channel)
927 {
cristyd3090f92011-10-18 00:05:15 +0000928 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000929 default:
930 {
cristy7d4aa382013-06-30 01:59:39 +0000931 phase_pixels[i]=QuantumScale*GetPixelRed(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000932 break;
933 }
cristyd3090f92011-10-18 00:05:15 +0000934 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000935 {
cristy7d4aa382013-06-30 01:59:39 +0000936 phase_pixels[i]=QuantumScale*GetPixelGreen(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000937 break;
938 }
cristyd3090f92011-10-18 00:05:15 +0000939 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000940 {
cristy7d4aa382013-06-30 01:59:39 +0000941 phase_pixels[i]=QuantumScale*GetPixelBlue(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000942 break;
943 }
cristyd3090f92011-10-18 00:05:15 +0000944 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000945 {
cristy7d4aa382013-06-30 01:59:39 +0000946 phase_pixels[i]=QuantumScale*GetPixelBlack(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000947 break;
948 }
cristyd3090f92011-10-18 00:05:15 +0000949 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000950 {
cristy7d4aa382013-06-30 01:59:39 +0000951 phase_pixels[i]=QuantumScale*GetPixelAlpha(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000952 break;
953 }
cristy3ed852e2009-09-05 21:47:34 +0000954 }
955 i++;
cristyed231572011-07-14 02:18:59 +0000956 p+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +0000957 }
958 }
959 if (fourier_info->modulus != MagickFalse)
960 {
961 i=0L;
cristybb503372010-05-27 20:51:26 +0000962 for (y=0L; y < (ssize_t) fourier_info->height; y++)
963 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000964 {
cristy7d4aa382013-06-30 01:59:39 +0000965 phase_pixels[i]-=0.5;
966 phase_pixels[i]*=(2.0*MagickPI);
cristy3ed852e2009-09-05 21:47:34 +0000967 i++;
968 }
969 }
970 magnitude_view=DestroyCacheView(magnitude_view);
971 phase_view=DestroyCacheView(phase_view);
cristy3ed852e2009-09-05 21:47:34 +0000972 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
cristy7d4aa382013-06-30 01:59:39 +0000973 magnitude_pixels,buffer);
974 (void) CopyMagickMemory(magnitude_pixels,buffer,(size_t) fourier_info->height*
975 fourier_info->width*sizeof(*magnitude_pixels));
976 CorrectPhaseLHS(fourier_info->width,fourier_info->width,phase_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000977 if (status != MagickFalse)
978 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
cristy7d4aa382013-06-30 01:59:39 +0000979 phase_pixels,buffer);
980 (void) CopyMagickMemory(phase_pixels,buffer,(size_t) fourier_info->height*
981 fourier_info->width*sizeof(*phase_pixels));
982 buffer_info=RelinquishVirtualMemory(buffer_info);
cristy3ed852e2009-09-05 21:47:34 +0000983 /*
984 Merge two sets.
985 */
986 i=0L;
987 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000988 for (y=0L; y < (ssize_t) fourier_info->height; y++)
989 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000990 {
cristy56ed31c2010-03-22 00:46:21 +0000991#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy7d4aa382013-06-30 01:59:39 +0000992 fourier[i]=magnitude_pixels[i]*cos(phase_pixels[i])+I*
993 magnitude_pixels[i]*sin(phase_pixels[i]);
cristy56ed31c2010-03-22 00:46:21 +0000994#else
cristy7d4aa382013-06-30 01:59:39 +0000995 fourier[i][0]=magnitude_pixels[i]*cos(phase_pixels[i]);
996 fourier[i][1]=magnitude_pixels[i]*sin(phase_pixels[i]);
cristy56ed31c2010-03-22 00:46:21 +0000997#endif
cristy3ed852e2009-09-05 21:47:34 +0000998 i++;
999 }
1000 else
cristybb503372010-05-27 20:51:26 +00001001 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1002 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001003 {
cristy56ed31c2010-03-22 00:46:21 +00001004#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy7d4aa382013-06-30 01:59:39 +00001005 fourier[i]=magnitude_pixels[i]+I*phase_pixels[i];
cristy56ed31c2010-03-22 00:46:21 +00001006#else
cristy7d4aa382013-06-30 01:59:39 +00001007 fourier[i][0]=magnitude_pixels[i];
1008 fourier[i][1]=phase_pixels[i];
cristy56ed31c2010-03-22 00:46:21 +00001009#endif
cristy3ed852e2009-09-05 21:47:34 +00001010 i++;
1011 }
cristy7d4aa382013-06-30 01:59:39 +00001012 phase_info=RelinquishVirtualMemory(phase_info);
1013 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +00001014 return(status);
1015}
1016
1017static MagickBooleanType InverseFourierTransform(FourierInfo *fourier_info,
1018 fftw_complex *fourier,Image *image,ExceptionInfo *exception)
1019{
1020 CacheView
1021 *image_view;
1022
1023 double
1024 *source;
1025
1026 fftw_plan
1027 fftw_c2r_plan;
1028
cristy7d4aa382013-06-30 01:59:39 +00001029 MemoryInfo
1030 *source_info;
1031
cristy4c08aed2011-07-01 19:47:50 +00001032 register Quantum
cristyc4ea4a42011-01-24 01:43:30 +00001033 *q;
1034
cristybb503372010-05-27 20:51:26 +00001035 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001036 i,
1037 x;
1038
cristyc4ea4a42011-01-24 01:43:30 +00001039 ssize_t
1040 y;
cristy3ed852e2009-09-05 21:47:34 +00001041
cristy7d4aa382013-06-30 01:59:39 +00001042 source_info=AcquireVirtualMemory((size_t) fourier_info->height,
cristyc4ea4a42011-01-24 01:43:30 +00001043 fourier_info->width*sizeof(*source));
cristy7d4aa382013-06-30 01:59:39 +00001044 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001045 {
1046 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001047 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001048 return(MagickFalse);
1049 }
cristy7d4aa382013-06-30 01:59:39 +00001050 source=(double *) GetVirtualMemoryBlob(source_info);
cristyb5d5f722009-11-04 03:03:49 +00001051#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001052 #pragma omp critical (MagickCore_InverseFourierTransform)
1053#endif
cristydf079ac2010-09-10 01:45:44 +00001054 {
1055 fftw_c2r_plan=fftw_plan_dft_c2r_2d(fourier_info->width,fourier_info->height,
1056 fourier,source,FFTW_ESTIMATE);
1057 fftw_execute(fftw_c2r_plan);
1058 fftw_destroy_plan(fftw_c2r_plan);
1059 }
cristy3ed852e2009-09-05 21:47:34 +00001060 i=0L;
cristy46ff2672012-12-14 15:32:26 +00001061 image_view=AcquireAuthenticCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +00001062 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001063 {
cristy85812052010-09-14 17:56:15 +00001064 if (y >= (ssize_t) image->rows)
1065 break;
1066 q=GetCacheViewAuthenticPixels(image_view,0L,y,fourier_info->width >
1067 image->columns ? image->columns : fourier_info->width,1UL,exception);
cristyacd2ed22011-08-30 01:44:23 +00001068 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001069 break;
cristybb503372010-05-27 20:51:26 +00001070 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001071 {
cristy233fe582012-07-07 14:00:18 +00001072 if (x < (ssize_t) image->columns)
1073 switch (fourier_info->channel)
cristy3ed852e2009-09-05 21:47:34 +00001074 {
cristy233fe582012-07-07 14:00:18 +00001075 case RedPixelChannel:
1076 default:
1077 {
1078 SetPixelRed(image,ClampToQuantum(QuantumRange*source[i]),q);
1079 break;
1080 }
1081 case GreenPixelChannel:
1082 {
1083 SetPixelGreen(image,ClampToQuantum(QuantumRange*source[i]),q);
1084 break;
1085 }
1086 case BluePixelChannel:
1087 {
1088 SetPixelBlue(image,ClampToQuantum(QuantumRange*source[i]),q);
1089 break;
1090 }
1091 case BlackPixelChannel:
1092 {
1093 SetPixelBlack(image,ClampToQuantum(QuantumRange*source[i]),q);
1094 break;
1095 }
1096 case AlphaPixelChannel:
1097 {
1098 SetPixelAlpha(image,ClampToQuantum(QuantumRange*source[i]),q);
1099 break;
1100 }
cristy3ed852e2009-09-05 21:47:34 +00001101 }
cristy3ed852e2009-09-05 21:47:34 +00001102 i++;
cristyed231572011-07-14 02:18:59 +00001103 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001104 }
1105 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1106 break;
1107 }
1108 image_view=DestroyCacheView(image_view);
cristy7d4aa382013-06-30 01:59:39 +00001109 source_info=RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +00001110 return(MagickTrue);
1111}
1112
cristyc9550792009-11-13 20:05:42 +00001113static MagickBooleanType InverseFourierTransformChannel(
1114 const Image *magnitude_image,const Image *phase_image,
cristyd3090f92011-10-18 00:05:15 +00001115 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +00001116 Image *fourier_image,ExceptionInfo *exception)
1117{
1118 double
1119 *magnitude,
1120 *phase;
1121
1122 fftw_complex
cristy7d4aa382013-06-30 01:59:39 +00001123 *buffer;
cristy3ed852e2009-09-05 21:47:34 +00001124
1125 FourierInfo
1126 fourier_info;
1127
1128 MagickBooleanType
1129 status;
1130
cristy7d4aa382013-06-30 01:59:39 +00001131 MemoryInfo
1132 *buffer_info,
1133 *magnitude_info,
1134 *phase_info;
1135
cristy3ed852e2009-09-05 21:47:34 +00001136 size_t
1137 extent;
1138
cristyc9550792009-11-13 20:05:42 +00001139 fourier_info.width=magnitude_image->columns;
1140 if ((magnitude_image->columns != magnitude_image->rows) ||
1141 ((magnitude_image->columns % 2) != 0) ||
1142 ((magnitude_image->rows % 2) != 0))
cristy3ed852e2009-09-05 21:47:34 +00001143 {
cristyc9550792009-11-13 20:05:42 +00001144 extent=magnitude_image->columns < magnitude_image->rows ?
1145 magnitude_image->rows : magnitude_image->columns;
cristy3ed852e2009-09-05 21:47:34 +00001146 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
1147 }
1148 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +00001149 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +00001150 fourier_info.channel=channel;
1151 fourier_info.modulus=modulus;
cristy7d4aa382013-06-30 01:59:39 +00001152 magnitude_info=AcquireVirtualMemory((size_t) fourier_info.height,
cristyc4ea4a42011-01-24 01:43:30 +00001153 fourier_info.center*sizeof(*magnitude));
cristy7d4aa382013-06-30 01:59:39 +00001154 phase_info=AcquireVirtualMemory((size_t) fourier_info.height,
cristyc4ea4a42011-01-24 01:43:30 +00001155 fourier_info.center*sizeof(*phase));
cristy7d4aa382013-06-30 01:59:39 +00001156 buffer_info=AcquireVirtualMemory((size_t) fourier_info.height,
1157 fourier_info.center*sizeof(*buffer));
1158 if ((magnitude_info == (MemoryInfo *) NULL) ||
1159 (phase_info == (MemoryInfo *) NULL) ||
1160 (buffer_info == (MemoryInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001161 {
cristy7d4aa382013-06-30 01:59:39 +00001162 if (buffer_info != (MemoryInfo *) NULL)
1163 buffer_info=RelinquishVirtualMemory(buffer_info);
1164 if (phase_info != (MemoryInfo *) NULL)
1165 phase_info=RelinquishVirtualMemory(phase_info);
1166 if (magnitude_info != (MemoryInfo *) NULL)
1167 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +00001168 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001169 ResourceLimitError,"MemoryAllocationFailed","`%s'",
cristyc9550792009-11-13 20:05:42 +00001170 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001171 return(MagickFalse);
1172 }
cristy7d4aa382013-06-30 01:59:39 +00001173 magnitude=(double *) GetVirtualMemoryBlob(magnitude_info);
1174 phase=(double *) GetVirtualMemoryBlob(phase_info);
1175 buffer=(fftw_complex *) GetVirtualMemoryBlob(buffer_info);
1176 status=InverseFourier(&fourier_info,magnitude_image,phase_image,buffer,
cristyc9550792009-11-13 20:05:42 +00001177 exception);
cristy3ed852e2009-09-05 21:47:34 +00001178 if (status != MagickFalse)
cristy7d4aa382013-06-30 01:59:39 +00001179 status=InverseFourierTransform(&fourier_info,buffer,fourier_image,
cristy3ed852e2009-09-05 21:47:34 +00001180 exception);
cristy7d4aa382013-06-30 01:59:39 +00001181 buffer_info=RelinquishMagickMemory(buffer_info);
1182 phase_info=RelinquishMagickMemory(phase_info);
1183 magnitude_info=RelinquishMagickMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +00001184 return(status);
1185}
1186#endif
1187
cristyc9550792009-11-13 20:05:42 +00001188MagickExport Image *InverseFourierTransformImage(const Image *magnitude_image,
1189 const Image *phase_image,const MagickBooleanType modulus,
1190 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001191{
1192 Image
1193 *fourier_image;
1194
cristyc9550792009-11-13 20:05:42 +00001195 assert(magnitude_image != (Image *) NULL);
1196 assert(magnitude_image->signature == MagickSignature);
1197 if (magnitude_image->debug != MagickFalse)
1198 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1199 magnitude_image->filename);
1200 if (phase_image == (Image *) NULL)
1201 {
1202 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
cristyefe601c2013-01-05 17:51:12 +00001203 "TwoOrMoreImagesRequired","`%s'",magnitude_image->filename);
cristy9372a152009-11-18 01:42:16 +00001204 return((Image *) NULL);
cristyc9550792009-11-13 20:05:42 +00001205 }
cristy3ed852e2009-09-05 21:47:34 +00001206#if !defined(MAGICKCORE_FFTW_DELEGATE)
1207 fourier_image=(Image *) NULL;
1208 (void) modulus;
1209 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00001210 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristyc9550792009-11-13 20:05:42 +00001211 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001212#else
1213 {
cristyc9550792009-11-13 20:05:42 +00001214 fourier_image=CloneImage(magnitude_image,magnitude_image->columns,
1215 magnitude_image->rows,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00001216 if (fourier_image != (Image *) NULL)
1217 {
1218 MagickBooleanType
1219 is_gray,
1220 status;
1221
cristy3ed852e2009-09-05 21:47:34 +00001222 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001223 is_gray=IsImageGray(magnitude_image,exception);
cristyc9550792009-11-13 20:05:42 +00001224 if (is_gray != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001225 is_gray=IsImageGray(phase_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001226#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb34ef052010-10-07 00:12:05 +00001227 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +00001228#endif
cristy3ed852e2009-09-05 21:47:34 +00001229 {
cristyb34ef052010-10-07 00:12:05 +00001230#if defined(MAGICKCORE_OPENMP_SUPPORT)
1231 #pragma omp section
1232#endif
cristy3ed852e2009-09-05 21:47:34 +00001233 {
cristyb34ef052010-10-07 00:12:05 +00001234 MagickBooleanType
1235 thread_status;
1236
1237 if (is_gray != MagickFalse)
1238 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001239 phase_image,GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001240 else
cristyc9550792009-11-13 20:05:42 +00001241 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001242 phase_image,RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001243 if (thread_status == MagickFalse)
1244 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +00001245 }
cristyb34ef052010-10-07 00:12:05 +00001246#if defined(MAGICKCORE_OPENMP_SUPPORT)
1247 #pragma omp section
1248#endif
1249 {
1250 MagickBooleanType
1251 thread_status;
1252
1253 thread_status=MagickTrue;
1254 if (is_gray == MagickFalse)
1255 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001256 phase_image,GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001257 if (thread_status == MagickFalse)
1258 status=thread_status;
1259 }
1260#if defined(MAGICKCORE_OPENMP_SUPPORT)
1261 #pragma omp section
1262#endif
1263 {
1264 MagickBooleanType
1265 thread_status;
1266
1267 thread_status=MagickTrue;
1268 if (is_gray == MagickFalse)
1269 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001270 phase_image,BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001271 if (thread_status == MagickFalse)
1272 status=thread_status;
1273 }
1274#if defined(MAGICKCORE_OPENMP_SUPPORT)
1275 #pragma omp section
1276#endif
1277 {
1278 MagickBooleanType
1279 thread_status;
1280
1281 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001282 if (magnitude_image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +00001283 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001284 phase_image,BlackPixelChannel,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;
cristy8a46d822012-08-28 23:32:39 +00001296 if (magnitude_image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +00001297 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001298 phase_image,AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001299 if (thread_status == MagickFalse)
1300 status=thread_status;
1301 }
cristy3ed852e2009-09-05 21:47:34 +00001302 }
1303 if (status == MagickFalse)
1304 fourier_image=DestroyImage(fourier_image);
1305 }
cristyb34ef052010-10-07 00:12:05 +00001306 fftw_cleanup();
cristy3ed852e2009-09-05 21:47:34 +00001307 }
1308#endif
1309 return(fourier_image);
1310}