blob: 5e87a91a6862f12034172f5c73503f19ab730757 [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,
cristybb3c02e2013-07-02 00:43:10 +0000133 const ssize_t x_offset,const ssize_t y_offset,double *fourier_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000134{
135 double
cristybb3c02e2013-07-02 00:43:10 +0000136 *roll_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000137
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 */
cristybb3c02e2013-07-02 00:43:10 +0000153 roll_info=AcquireVirtualMemory(height,width*sizeof(*roll_pixels));
cristy7d4aa382013-06-30 01:59:39 +0000154 if (roll_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000155 return(MagickFalse);
cristybb3c02e2013-07-02 00:43:10 +0000156 roll_pixels=(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;
cristybb3c02e2013-07-02 00:43:10 +0000172 roll_pixels[v*width+u]=fourier_pixels[i++];
cristyc4ea4a42011-01-24 01:43:30 +0000173 }
cristy3ed852e2009-09-05 21:47:34 +0000174 }
cristybb3c02e2013-07-02 00:43:10 +0000175 (void) CopyMagickMemory(fourier_pixels,roll_pixels,height*width*
176 sizeof(*roll_pixels));
cristy7d4aa382013-06-30 01:59:39 +0000177 roll_info=RelinquishVirtualMemory(roll_info);
cristy3ed852e2009-09-05 21:47:34 +0000178 return(MagickTrue);
179}
180
cristybb503372010-05-27 20:51:26 +0000181static MagickBooleanType ForwardQuadrantSwap(const size_t width,
182 const size_t height,double *source,double *destination)
cristy3ed852e2009-09-05 21:47:34 +0000183{
cristy3ed852e2009-09-05 21:47:34 +0000184 MagickBooleanType
185 status;
186
cristybb503372010-05-27 20:51:26 +0000187 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000188 x;
189
cristyc4ea4a42011-01-24 01:43:30 +0000190 ssize_t
191 center,
192 y;
193
cristy3ed852e2009-09-05 21:47:34 +0000194 /*
195 Swap quadrants.
196 */
cristybb503372010-05-27 20:51:26 +0000197 center=(ssize_t) floor((double) width/2L)+1L;
198 status=RollFourier((size_t) center,height,0L,(ssize_t) height/2L,source);
cristy3ed852e2009-09-05 21:47:34 +0000199 if (status == MagickFalse)
200 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000201 for (y=0L; y < (ssize_t) height; y++)
202 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000203 destination[width*y+x+width/2L]=source[center*y+x];
cristybb503372010-05-27 20:51:26 +0000204 for (y=1; y < (ssize_t) height; y++)
205 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000206 destination[width*(height-y)+width/2L-x-1L]=source[center*y+x+1L];
cristybb503372010-05-27 20:51:26 +0000207 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000208 destination[-x+width/2L-1L]=destination[x+width/2L+1L];
209 return(MagickTrue);
210}
211
cristyc4ea4a42011-01-24 01:43:30 +0000212static void CorrectPhaseLHS(const size_t width,const size_t height,
213 double *fourier)
cristy3ed852e2009-09-05 21:47:34 +0000214{
cristybb503372010-05-27 20:51:26 +0000215 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000216 x;
217
cristy9d314ff2011-03-09 01:30:28 +0000218 ssize_t
219 y;
220
cristybb503372010-05-27 20:51:26 +0000221 for (y=0L; y < (ssize_t) height; y++)
222 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000223 fourier[y*width+x]*=(-1.0);
224}
225
226static MagickBooleanType ForwardFourier(const FourierInfo *fourier_info,
227 Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
228{
229 CacheView
230 *magnitude_view,
231 *phase_view;
232
233 double
cristy7d4aa382013-06-30 01:59:39 +0000234 *magnitude_pixels,
235 *phase_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000236
237 Image
238 *magnitude_image,
239 *phase_image;
240
cristy3ed852e2009-09-05 21:47:34 +0000241 MagickBooleanType
242 status;
243
cristy7d4aa382013-06-30 01:59:39 +0000244 MemoryInfo
245 *magnitude_info,
246 *phase_info;
247
cristy4c08aed2011-07-01 19:47:50 +0000248 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000249 *q;
250
cristyf5742792012-11-27 18:31:26 +0000251 register ssize_t
252 x;
253
cristyc4ea4a42011-01-24 01:43:30 +0000254 ssize_t
255 i,
256 y;
257
cristy3ed852e2009-09-05 21:47:34 +0000258 magnitude_image=GetFirstImageInList(image);
259 phase_image=GetNextImageInList(image);
260 if (phase_image == (Image *) NULL)
261 {
262 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
cristyefe601c2013-01-05 17:51:12 +0000263 "TwoOrMoreImagesRequired","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000264 return(MagickFalse);
265 }
266 /*
267 Create "Fourier Transform" image from constituent arrays.
268 */
cristy7d4aa382013-06-30 01:59:39 +0000269 magnitude_info=AcquireVirtualMemory((size_t) fourier_info->height,
270 fourier_info->width*sizeof(*magnitude_pixels));
271 phase_info=AcquireVirtualMemory((size_t) fourier_info->height,
272 fourier_info->width*sizeof(*phase_pixels));
cristybb3c02e2013-07-02 00:43:10 +0000273 if ((magnitude_info == (MemoryInfo *) NULL) ||
274 (phase_info == (MemoryInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000275 {
cristy7d4aa382013-06-30 01:59:39 +0000276 if (phase_info != (MemoryInfo *) NULL)
277 phase_info=RelinquishVirtualMemory(phase_info);
278 if (magnitude_info != (MemoryInfo *) NULL)
279 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000280 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000281 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000282 return(MagickFalse);
283 }
cristy7d4aa382013-06-30 01:59:39 +0000284 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
285 (void) ResetMagickMemory(magnitude_pixels,0,fourier_info->height*
286 fourier_info->width*sizeof(*magnitude_pixels));
cristybb3c02e2013-07-02 00:43:10 +0000287 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
cristy7d4aa382013-06-30 01:59:39 +0000288 (void) ResetMagickMemory(phase_pixels,0,fourier_info->height*
289 fourier_info->width*sizeof(*phase_pixels));
cristy3ed852e2009-09-05 21:47:34 +0000290 status=ForwardQuadrantSwap(fourier_info->height,fourier_info->height,
cristy7d4aa382013-06-30 01:59:39 +0000291 magnitude,magnitude_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000292 if (status != MagickFalse)
293 status=ForwardQuadrantSwap(fourier_info->height,fourier_info->height,phase,
cristy7d4aa382013-06-30 01:59:39 +0000294 phase_pixels);
295 CorrectPhaseLHS(fourier_info->height,fourier_info->height,phase_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000296 if (fourier_info->modulus != MagickFalse)
297 {
298 i=0L;
cristybb503372010-05-27 20:51:26 +0000299 for (y=0L; y < (ssize_t) fourier_info->height; y++)
300 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000301 {
cristy7d4aa382013-06-30 01:59:39 +0000302 phase_pixels[i]/=(2.0*MagickPI);
303 phase_pixels[i]+=0.5;
cristy3ed852e2009-09-05 21:47:34 +0000304 i++;
305 }
306 }
cristy46ff2672012-12-14 15:32:26 +0000307 magnitude_view=AcquireAuthenticCacheView(magnitude_image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000308 i=0L;
cristybb503372010-05-27 20:51:26 +0000309 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000310 {
311 q=GetCacheViewAuthenticPixels(magnitude_view,0L,y,fourier_info->height,1UL,
312 exception);
cristyacd2ed22011-08-30 01:44:23 +0000313 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000314 break;
cristybb503372010-05-27 20:51:26 +0000315 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000316 {
317 switch (fourier_info->channel)
318 {
cristyd3090f92011-10-18 00:05:15 +0000319 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000320 default:
321 {
cristy4c08aed2011-07-01 19:47:50 +0000322 SetPixelRed(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000323 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000324 break;
325 }
cristyd3090f92011-10-18 00:05:15 +0000326 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000327 {
cristy4c08aed2011-07-01 19:47:50 +0000328 SetPixelGreen(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000329 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000330 break;
331 }
cristyd3090f92011-10-18 00:05:15 +0000332 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000333 {
cristy4c08aed2011-07-01 19:47:50 +0000334 SetPixelBlue(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000335 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000336 break;
337 }
cristyd3090f92011-10-18 00:05:15 +0000338 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000339 {
cristy4c08aed2011-07-01 19:47:50 +0000340 SetPixelBlack(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000341 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000342 break;
343 }
cristyd3090f92011-10-18 00:05:15 +0000344 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000345 {
cristy4c08aed2011-07-01 19:47:50 +0000346 SetPixelAlpha(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000347 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000348 break;
349 }
cristy3ed852e2009-09-05 21:47:34 +0000350 }
351 i++;
cristyed231572011-07-14 02:18:59 +0000352 q+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +0000353 }
354 status=SyncCacheViewAuthenticPixels(magnitude_view,exception);
355 if (status == MagickFalse)
356 break;
357 }
cristydb070952012-04-20 14:33:00 +0000358 magnitude_view=DestroyCacheView(magnitude_view);
cristy3ed852e2009-09-05 21:47:34 +0000359 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000360 phase_view=AcquireAuthenticCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +0000361 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000362 {
363 q=GetCacheViewAuthenticPixels(phase_view,0L,y,fourier_info->height,1UL,
364 exception);
cristyacd2ed22011-08-30 01:44:23 +0000365 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000366 break;
cristybb503372010-05-27 20:51:26 +0000367 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000368 {
369 switch (fourier_info->channel)
370 {
cristyd3090f92011-10-18 00:05:15 +0000371 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000372 default:
373 {
cristy4c08aed2011-07-01 19:47:50 +0000374 SetPixelRed(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000375 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000376 break;
377 }
cristyd3090f92011-10-18 00:05:15 +0000378 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000379 {
cristy4c08aed2011-07-01 19:47:50 +0000380 SetPixelGreen(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000381 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000382 break;
383 }
cristyd3090f92011-10-18 00:05:15 +0000384 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000385 {
cristy4c08aed2011-07-01 19:47:50 +0000386 SetPixelBlue(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000387 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000388 break;
389 }
cristyd3090f92011-10-18 00:05:15 +0000390 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000391 {
cristy4c08aed2011-07-01 19:47:50 +0000392 SetPixelBlack(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000393 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000394 break;
395 }
cristyd3090f92011-10-18 00:05:15 +0000396 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000397 {
cristy4c08aed2011-07-01 19:47:50 +0000398 SetPixelAlpha(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000399 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000400 break;
401 }
cristy3ed852e2009-09-05 21:47:34 +0000402 }
403 i++;
cristyed231572011-07-14 02:18:59 +0000404 q+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +0000405 }
406 status=SyncCacheViewAuthenticPixels(phase_view,exception);
407 if (status == MagickFalse)
408 break;
409 }
410 phase_view=DestroyCacheView(phase_view);
cristy7d4aa382013-06-30 01:59:39 +0000411 phase_info=RelinquishVirtualMemory(phase_info);
412 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000413 return(status);
414}
415
416static MagickBooleanType ForwardFourierTransform(FourierInfo *fourier_info,
417 const Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
418{
419 CacheView
420 *image_view;
421
422 double
423 n,
cristybb3c02e2013-07-02 00:43:10 +0000424 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000425
426 fftw_complex
cristy5f13d952013-07-02 14:36:09 +0000427 *destination_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000428
429 fftw_plan
430 fftw_r2c_plan;
431
cristy7d4aa382013-06-30 01:59:39 +0000432 MemoryInfo
cristy5f13d952013-07-02 14:36:09 +0000433 *destination_info,
cristy7d4aa382013-06-30 01:59:39 +0000434 *source_info;
435
cristy4c08aed2011-07-01 19:47:50 +0000436 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000437 *p;
438
cristybb503372010-05-27 20:51:26 +0000439 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000440 i,
441 x;
442
cristyc4ea4a42011-01-24 01:43:30 +0000443 ssize_t
444 y;
445
cristy3ed852e2009-09-05 21:47:34 +0000446 /*
447 Generate the forward Fourier transform.
448 */
cristy7d4aa382013-06-30 01:59:39 +0000449 source_info=AcquireVirtualMemory((size_t) fourier_info->height,
cristybb3c02e2013-07-02 00:43:10 +0000450 fourier_info->width*sizeof(*source_pixels));
cristy7d4aa382013-06-30 01:59:39 +0000451 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000452 {
453 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000454 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000455 return(MagickFalse);
456 }
cristybb3c02e2013-07-02 00:43:10 +0000457 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
458 ResetMagickMemory(source_pixels,0,fourier_info->height*fourier_info->width*
459 sizeof(*source_pixels));
cristy3ed852e2009-09-05 21:47:34 +0000460 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000461 image_view=AcquireVirtualCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +0000462 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000463 {
464 p=GetCacheViewVirtualPixels(image_view,0L,y,fourier_info->width,1UL,
465 exception);
cristy4c08aed2011-07-01 19:47:50 +0000466 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000467 break;
cristybb503372010-05-27 20:51:26 +0000468 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000469 {
470 switch (fourier_info->channel)
471 {
cristyd3090f92011-10-18 00:05:15 +0000472 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000473 default:
474 {
cristybb3c02e2013-07-02 00:43:10 +0000475 source_pixels[i]=QuantumScale*GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000476 break;
477 }
cristyd3090f92011-10-18 00:05:15 +0000478 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000479 {
cristybb3c02e2013-07-02 00:43:10 +0000480 source_pixels[i]=QuantumScale*GetPixelGreen(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000481 break;
482 }
cristyd3090f92011-10-18 00:05:15 +0000483 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000484 {
cristybb3c02e2013-07-02 00:43:10 +0000485 source_pixels[i]=QuantumScale*GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000486 break;
487 }
cristyd3090f92011-10-18 00:05:15 +0000488 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000489 {
cristybb3c02e2013-07-02 00:43:10 +0000490 source_pixels[i]=QuantumScale*GetPixelBlack(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000491 break;
492 }
cristyd3090f92011-10-18 00:05:15 +0000493 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000494 {
cristybb3c02e2013-07-02 00:43:10 +0000495 source_pixels[i]=QuantumScale*GetPixelAlpha(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000496 break;
497 }
cristy3ed852e2009-09-05 21:47:34 +0000498 }
499 i++;
cristyed231572011-07-14 02:18:59 +0000500 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000501 }
502 }
503 image_view=DestroyCacheView(image_view);
cristy5f13d952013-07-02 14:36:09 +0000504 destination_info=AcquireVirtualMemory((size_t) fourier_info->height,
505 fourier_info->center*sizeof(*destination_pixels));
506 if (destination_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000507 {
508 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000509 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristybb3c02e2013-07-02 00:43:10 +0000510 source_info=(MemoryInfo *) RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000511 return(MagickFalse);
512 }
cristy5f13d952013-07-02 14:36:09 +0000513 destination_pixels=(fftw_complex *) GetVirtualMemoryBlob(destination_info);
cristyb5d5f722009-11-04 03:03:49 +0000514#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000515 #pragma omp critical (MagickCore_ForwardFourierTransform)
516#endif
cristy70841a12012-10-27 20:52:57 +0000517 fftw_r2c_plan=fftw_plan_dft_r2c_2d(fourier_info->width,fourier_info->height,
cristy5f13d952013-07-02 14:36:09 +0000518 source_pixels,destination_pixels,FFTW_ESTIMATE);
cristy3ed852e2009-09-05 21:47:34 +0000519 fftw_execute(fftw_r2c_plan);
520 fftw_destroy_plan(fftw_r2c_plan);
cristybb3c02e2013-07-02 00:43:10 +0000521 source_info=(MemoryInfo *) RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000522 /*
523 Normalize Fourier transform.
524 */
525 n=(double) fourier_info->width*(double) fourier_info->width;
526 i=0L;
cristybb503372010-05-27 20:51:26 +0000527 for (y=0L; y < (ssize_t) fourier_info->height; y++)
528 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy56ed31c2010-03-22 00:46:21 +0000529 {
530#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy5f13d952013-07-02 14:36:09 +0000531 destination_pixels[i]/=n;
cristy56ed31c2010-03-22 00:46:21 +0000532#else
cristy5f13d952013-07-02 14:36:09 +0000533 destination_pixels[i][0]/=n;
534 destination_pixels[i][1]/=n;
cristy56ed31c2010-03-22 00:46:21 +0000535#endif
536 i++;
537 }
cristy3ed852e2009-09-05 21:47:34 +0000538 /*
539 Generate magnitude and phase (or real and imaginary).
540 */
541 i=0L;
542 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000543 for (y=0L; y < (ssize_t) fourier_info->height; y++)
544 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000545 {
cristy5f13d952013-07-02 14:36:09 +0000546 magnitude[i]=cabs(destination_pixels[i]);
547 phase[i]=carg(destination_pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000548 i++;
549 }
550 else
cristybb503372010-05-27 20:51:26 +0000551 for (y=0L; y < (ssize_t) fourier_info->height; y++)
552 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000553 {
cristy5f13d952013-07-02 14:36:09 +0000554 magnitude[i]=creal(destination_pixels[i]);
555 phase[i]=cimag(destination_pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000556 i++;
557 }
cristy5f13d952013-07-02 14:36:09 +0000558 destination_info=(MemoryInfo *) RelinquishVirtualMemory(destination_info);
cristy3ed852e2009-09-05 21:47:34 +0000559 return(MagickTrue);
560}
561
562static MagickBooleanType ForwardFourierTransformChannel(const Image *image,
cristyd3090f92011-10-18 00:05:15 +0000563 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +0000564 Image *fourier_image,ExceptionInfo *exception)
565{
566 double
567 *magnitude,
568 *phase;
569
cristybb3c02e2013-07-02 00:43:10 +0000570 fftw_complex
571 *fourier;
572
cristy56ed31c2010-03-22 00:46:21 +0000573 FourierInfo
574 fourier_info;
575
cristyc4ea4a42011-01-24 01:43:30 +0000576 MagickBooleanType
577 status;
578
cristy3ed852e2009-09-05 21:47:34 +0000579 size_t
580 extent;
581
582 fourier_info.width=image->columns;
583 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
584 ((image->rows % 2) != 0))
585 {
586 extent=image->columns < image->rows ? image->rows : image->columns;
587 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
588 }
589 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +0000590 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +0000591 fourier_info.channel=channel;
592 fourier_info.modulus=modulus;
cristybb3c02e2013-07-02 00:43:10 +0000593 magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
cristy3ed852e2009-09-05 21:47:34 +0000594 fourier_info.center*sizeof(*magnitude));
cristybb3c02e2013-07-02 00:43:10 +0000595 if (magnitude == (double *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000596 {
597 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000598 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000599 return(MagickFalse);
600 }
cristybb3c02e2013-07-02 00:43:10 +0000601 phase=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
602 fourier_info.center*sizeof(*phase));
603 if (phase == (double *) NULL)
604 {
605 (void) ThrowMagickException(exception,GetMagickModule(),
606 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
607 magnitude=(double *) RelinquishMagickMemory(magnitude);
608 return(MagickFalse);
609 }
610 fourier=(fftw_complex *) AcquireQuantumMemory((size_t) fourier_info.height,
611 fourier_info.center*sizeof(*fourier));
612 if (fourier == (fftw_complex *) NULL)
613 {
614 (void) ThrowMagickException(exception,GetMagickModule(),
615 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
616 phase=(double *) RelinquishMagickMemory(phase);
617 magnitude=(double *) RelinquishMagickMemory(magnitude);
618 return(MagickFalse);
619 }
cristy3ed852e2009-09-05 21:47:34 +0000620 status=ForwardFourierTransform(&fourier_info,image,magnitude,phase,exception);
621 if (status != MagickFalse)
622 status=ForwardFourier(&fourier_info,fourier_image,magnitude,phase,
623 exception);
cristybb3c02e2013-07-02 00:43:10 +0000624 fourier=(fftw_complex *) RelinquishMagickMemory(fourier);
625 phase=(double *) RelinquishMagickMemory(phase);
626 magnitude=(double *) RelinquishMagickMemory(magnitude);
cristy3ed852e2009-09-05 21:47:34 +0000627 return(status);
628}
629#endif
630
631MagickExport Image *ForwardFourierTransformImage(const Image *image,
632 const MagickBooleanType modulus,ExceptionInfo *exception)
633{
634 Image
635 *fourier_image;
636
637 fourier_image=NewImageList();
638#if !defined(MAGICKCORE_FFTW_DELEGATE)
639 (void) modulus;
640 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000641 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristy3ed852e2009-09-05 21:47:34 +0000642 image->filename);
643#else
644 {
645 Image
646 *magnitude_image;
647
cristybb503372010-05-27 20:51:26 +0000648 size_t
cristy3ed852e2009-09-05 21:47:34 +0000649 extent,
650 width;
651
652 width=image->columns;
653 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
654 ((image->rows % 2) != 0))
655 {
656 extent=image->columns < image->rows ? image->rows : image->columns;
657 width=(extent & 0x01) == 1 ? extent+1UL : extent;
658 }
659 magnitude_image=CloneImage(image,width,width,MagickFalse,exception);
660 if (magnitude_image != (Image *) NULL)
661 {
662 Image
663 *phase_image;
664
665 magnitude_image->storage_class=DirectClass;
666 magnitude_image->depth=32UL;
667 phase_image=CloneImage(image,width,width,MagickFalse,exception);
668 if (phase_image == (Image *) NULL)
669 magnitude_image=DestroyImage(magnitude_image);
670 else
671 {
672 MagickBooleanType
673 is_gray,
674 status;
675
cristy3ed852e2009-09-05 21:47:34 +0000676 phase_image->storage_class=DirectClass;
677 phase_image->depth=32UL;
678 AppendImageToList(&fourier_image,magnitude_image);
679 AppendImageToList(&fourier_image,phase_image);
680 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000681 is_gray=IsImageGray(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000682#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy564a5692012-01-20 23:56:26 +0000683 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +0000684#endif
cristy3ed852e2009-09-05 21:47:34 +0000685 {
cristyb34ef052010-10-07 00:12:05 +0000686#if defined(MAGICKCORE_OPENMP_SUPPORT)
687 #pragma omp section
688#endif
cristy3ed852e2009-09-05 21:47:34 +0000689 {
cristyb34ef052010-10-07 00:12:05 +0000690 MagickBooleanType
691 thread_status;
692
693 if (is_gray != MagickFalse)
694 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000695 GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000696 else
697 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000698 RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000699 if (thread_status == MagickFalse)
700 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +0000701 }
cristyb34ef052010-10-07 00:12:05 +0000702#if defined(MAGICKCORE_OPENMP_SUPPORT)
703 #pragma omp section
704#endif
705 {
706 MagickBooleanType
707 thread_status;
708
709 thread_status=MagickTrue;
710 if (is_gray == MagickFalse)
711 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000712 GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000713 if (thread_status == MagickFalse)
714 status=thread_status;
715 }
716#if defined(MAGICKCORE_OPENMP_SUPPORT)
717 #pragma omp section
718#endif
719 {
720 MagickBooleanType
721 thread_status;
722
723 thread_status=MagickTrue;
724 if (is_gray == MagickFalse)
725 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000726 BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000727 if (thread_status == MagickFalse)
728 status=thread_status;
729 }
730#if defined(MAGICKCORE_OPENMP_SUPPORT)
731 #pragma omp section
732#endif
733 {
734 MagickBooleanType
735 thread_status;
736
737 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000738 if (image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +0000739 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000740 BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000741 if (thread_status == MagickFalse)
742 status=thread_status;
743 }
744#if defined(MAGICKCORE_OPENMP_SUPPORT)
745 #pragma omp section
746#endif
747 {
748 MagickBooleanType
749 thread_status;
750
751 thread_status=MagickTrue;
cristy8a46d822012-08-28 23:32:39 +0000752 if (image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +0000753 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000754 AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000755 if (thread_status == MagickFalse)
756 status=thread_status;
757 }
cristy3ed852e2009-09-05 21:47:34 +0000758 }
759 if (status == MagickFalse)
760 fourier_image=DestroyImageList(fourier_image);
761 fftw_cleanup();
762 }
763 }
764 }
765#endif
766 return(fourier_image);
767}
768
769/*
770%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
771% %
772% %
773% %
774% 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 %
775% %
776% %
777% %
778%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
779%
780% InverseFourierTransformImage() implements the inverse discrete Fourier
781% transform (DFT) of the image either as a magnitude / phase or real /
782% imaginary image pair.
783%
784% The format of the InverseFourierTransformImage method is:
785%
cristyc9550792009-11-13 20:05:42 +0000786% Image *InverseFourierTransformImage(const Image *magnitude_image,
787% const Image *phase_image,const MagickBooleanType modulus,
788% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000789%
790% A description of each parameter follows:
791%
cristyc9550792009-11-13 20:05:42 +0000792% o magnitude_image: the magnitude or real image.
793%
794% o phase_image: the phase or imaginary image.
cristy3ed852e2009-09-05 21:47:34 +0000795%
796% o modulus: if true, return transform as a magnitude / phase pair
797% otherwise a real / imaginary image pair.
798%
799% o exception: return any errors or warnings in this structure.
800%
801*/
802
803#if defined(MAGICKCORE_FFTW_DELEGATE)
cristybb503372010-05-27 20:51:26 +0000804static MagickBooleanType InverseQuadrantSwap(const size_t width,
805 const size_t height,const double *source,double *destination)
cristy3ed852e2009-09-05 21:47:34 +0000806{
cristyc4ea4a42011-01-24 01:43:30 +0000807 register ssize_t
808 x;
809
cristybb503372010-05-27 20:51:26 +0000810 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000811 center,
812 y;
813
cristy3ed852e2009-09-05 21:47:34 +0000814 /*
815 Swap quadrants.
816 */
cristy233fe582012-07-07 14:00:18 +0000817 center=(ssize_t) floor((double) width/2L)+1L;
cristybb503372010-05-27 20:51:26 +0000818 for (y=1L; y < (ssize_t) height; y++)
819 for (x=0L; x < (ssize_t) (width/2L+1L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000820 destination[center*(height-y)-x+width/2L]=source[y*width+x];
cristybb503372010-05-27 20:51:26 +0000821 for (y=0L; y < (ssize_t) height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000822 destination[center*y]=source[y*width+width/2L];
823 for (x=0L; x < center; x++)
824 destination[x]=source[center-x-1L];
cristybb503372010-05-27 20:51:26 +0000825 return(RollFourier(center,height,0L,(ssize_t) height/-2L,destination));
cristy3ed852e2009-09-05 21:47:34 +0000826}
827
828static MagickBooleanType InverseFourier(FourierInfo *fourier_info,
cristyc9550792009-11-13 20:05:42 +0000829 const Image *magnitude_image,const Image *phase_image,fftw_complex *fourier,
830 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000831{
832 CacheView
833 *magnitude_view,
834 *phase_view;
835
836 double
cristybb3c02e2013-07-02 00:43:10 +0000837 *magnitude,
838 *phase,
cristy7d4aa382013-06-30 01:59:39 +0000839 *magnitude_pixels,
840 *phase_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000841
cristy3ed852e2009-09-05 21:47:34 +0000842 MagickBooleanType
843 status;
844
cristy4c08aed2011-07-01 19:47:50 +0000845 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000846 *p;
847
cristybb503372010-05-27 20:51:26 +0000848 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000849 i,
850 x;
851
cristyc4ea4a42011-01-24 01:43:30 +0000852 ssize_t
853 y;
854
cristy3ed852e2009-09-05 21:47:34 +0000855 /*
856 Inverse fourier - read image and break down into a double array.
857 */
cristybb3c02e2013-07-02 00:43:10 +0000858 magnitude_pixels=(double *) AcquireQuantumMemory((size_t)
859 fourier_info->height,fourier_info->width*sizeof(*magnitude_pixels));
860 if (magnitude_pixels == (double *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000861 {
862 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000863 ResourceLimitError,"MemoryAllocationFailed","`%s'",
cristyc9550792009-11-13 20:05:42 +0000864 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000865 return(MagickFalse);
866 }
cristybb3c02e2013-07-02 00:43:10 +0000867 phase_pixels=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
868 fourier_info->width*sizeof(*phase_pixels));
869 if (phase_pixels == (double *) NULL)
870 {
871 (void) ThrowMagickException(exception,GetMagickModule(),
872 ResourceLimitError,"MemoryAllocationFailed","`%s'",
873 magnitude_image->filename);
874 magnitude_pixels=(double *) RelinquishMagickMemory(magnitude_pixels);
875 return(MagickFalse);
876 }
cristy3ed852e2009-09-05 21:47:34 +0000877 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000878 magnitude_view=AcquireVirtualCacheView(magnitude_image,exception);
cristybb503372010-05-27 20:51:26 +0000879 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000880 {
881 p=GetCacheViewVirtualPixels(magnitude_view,0L,y,fourier_info->width,1UL,
882 exception);
cristy4c08aed2011-07-01 19:47:50 +0000883 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000884 break;
cristybb503372010-05-27 20:51:26 +0000885 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000886 {
887 switch (fourier_info->channel)
888 {
cristyd3090f92011-10-18 00:05:15 +0000889 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000890 default:
891 {
cristy7d4aa382013-06-30 01:59:39 +0000892 magnitude_pixels[i]=QuantumScale*GetPixelRed(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000893 break;
894 }
cristyd3090f92011-10-18 00:05:15 +0000895 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000896 {
cristy7d4aa382013-06-30 01:59:39 +0000897 magnitude_pixels[i]=QuantumScale*GetPixelGreen(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000898 break;
899 }
cristyd3090f92011-10-18 00:05:15 +0000900 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000901 {
cristy7d4aa382013-06-30 01:59:39 +0000902 magnitude_pixels[i]=QuantumScale*GetPixelBlue(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000903 break;
904 }
cristyd3090f92011-10-18 00:05:15 +0000905 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000906 {
cristy7d4aa382013-06-30 01:59:39 +0000907 magnitude_pixels[i]=QuantumScale*GetPixelBlack(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000908 break;
909 }
cristyd3090f92011-10-18 00:05:15 +0000910 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000911 {
cristy7d4aa382013-06-30 01:59:39 +0000912 magnitude_pixels[i]=QuantumScale*GetPixelAlpha(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000913 break;
914 }
cristy3ed852e2009-09-05 21:47:34 +0000915 }
916 i++;
cristyed231572011-07-14 02:18:59 +0000917 p+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +0000918 }
919 }
920 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000921 phase_view=AcquireVirtualCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +0000922 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000923 {
924 p=GetCacheViewVirtualPixels(phase_view,0,y,fourier_info->width,1,
925 exception);
cristy4c08aed2011-07-01 19:47:50 +0000926 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000927 break;
cristybb503372010-05-27 20:51:26 +0000928 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000929 {
930 switch (fourier_info->channel)
931 {
cristyd3090f92011-10-18 00:05:15 +0000932 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000933 default:
934 {
cristy7d4aa382013-06-30 01:59:39 +0000935 phase_pixels[i]=QuantumScale*GetPixelRed(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000936 break;
937 }
cristyd3090f92011-10-18 00:05:15 +0000938 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000939 {
cristy7d4aa382013-06-30 01:59:39 +0000940 phase_pixels[i]=QuantumScale*GetPixelGreen(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000941 break;
942 }
cristyd3090f92011-10-18 00:05:15 +0000943 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000944 {
cristy7d4aa382013-06-30 01:59:39 +0000945 phase_pixels[i]=QuantumScale*GetPixelBlue(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000946 break;
947 }
cristyd3090f92011-10-18 00:05:15 +0000948 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000949 {
cristy7d4aa382013-06-30 01:59:39 +0000950 phase_pixels[i]=QuantumScale*GetPixelBlack(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000951 break;
952 }
cristyd3090f92011-10-18 00:05:15 +0000953 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000954 {
cristy7d4aa382013-06-30 01:59:39 +0000955 phase_pixels[i]=QuantumScale*GetPixelAlpha(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000956 break;
957 }
cristy3ed852e2009-09-05 21:47:34 +0000958 }
959 i++;
cristyed231572011-07-14 02:18:59 +0000960 p+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +0000961 }
962 }
963 if (fourier_info->modulus != MagickFalse)
964 {
965 i=0L;
cristybb503372010-05-27 20:51:26 +0000966 for (y=0L; y < (ssize_t) fourier_info->height; y++)
967 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000968 {
cristy7d4aa382013-06-30 01:59:39 +0000969 phase_pixels[i]-=0.5;
970 phase_pixels[i]*=(2.0*MagickPI);
cristy3ed852e2009-09-05 21:47:34 +0000971 i++;
972 }
973 }
974 magnitude_view=DestroyCacheView(magnitude_view);
975 phase_view=DestroyCacheView(phase_view);
cristybb3c02e2013-07-02 00:43:10 +0000976 magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
977 fourier_info->center*sizeof(*magnitude));
978 if (magnitude == (double *) NULL)
979 {
980 (void) ThrowMagickException(exception,GetMagickModule(),
981 ResourceLimitError,"MemoryAllocationFailed","`%s'",
982 magnitude_image->filename);
983 magnitude_pixels=(double *) RelinquishMagickMemory(magnitude_pixels);
984 phase_pixels=(double *) RelinquishMagickMemory(phase_pixels);
985 return(MagickFalse);
986 }
cristy3ed852e2009-09-05 21:47:34 +0000987 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
cristybb3c02e2013-07-02 00:43:10 +0000988 magnitude_pixels,magnitude);
989 magnitude_pixels=(double *) RelinquishMagickMemory(magnitude_pixels);
990 phase=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
991 fourier_info->width*sizeof(*phase));
992 if (phase == (double *) NULL)
993 {
994 (void) ThrowMagickException(exception,GetMagickModule(),
995 ResourceLimitError,"MemoryAllocationFailed","`%s'",
996 magnitude_image->filename);
997 phase_pixels=(double *) RelinquishMagickMemory(phase_pixels);
998 return(MagickFalse);
999 }
cristy7d4aa382013-06-30 01:59:39 +00001000 CorrectPhaseLHS(fourier_info->width,fourier_info->width,phase_pixels);
cristy3ed852e2009-09-05 21:47:34 +00001001 if (status != MagickFalse)
1002 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
cristybb3c02e2013-07-02 00:43:10 +00001003 phase_pixels,phase);
1004 phase_pixels=(double *) RelinquishMagickMemory(phase_pixels);
cristy3ed852e2009-09-05 21:47:34 +00001005 /*
1006 Merge two sets.
1007 */
1008 i=0L;
1009 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +00001010 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1011 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001012 {
cristy56ed31c2010-03-22 00:46:21 +00001013#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristybb3c02e2013-07-02 00:43:10 +00001014 fourier[i]=magnitude[i]*cos(phase[i])+I*magnitude[i]*sin(phase[i]);
cristy56ed31c2010-03-22 00:46:21 +00001015#else
cristybb3c02e2013-07-02 00:43:10 +00001016 fourier[i][0]=magnitude[i]*cos(phase[i]);
1017 fourier[i][1]=magnitude[i]*sin(phase[i]);
cristy56ed31c2010-03-22 00:46:21 +00001018#endif
cristy3ed852e2009-09-05 21:47:34 +00001019 i++;
1020 }
1021 else
cristybb503372010-05-27 20:51:26 +00001022 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1023 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001024 {
cristy56ed31c2010-03-22 00:46:21 +00001025#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristybb3c02e2013-07-02 00:43:10 +00001026 fourier[i]=magnitude[i]+I*phase[i];
cristy56ed31c2010-03-22 00:46:21 +00001027#else
cristybb3c02e2013-07-02 00:43:10 +00001028 fourier[i][0]=magnitude[i];
1029 fourier[i][1]=phase[i];
cristy56ed31c2010-03-22 00:46:21 +00001030#endif
cristy3ed852e2009-09-05 21:47:34 +00001031 i++;
1032 }
cristybb3c02e2013-07-02 00:43:10 +00001033 phase=(double *) RelinquishMagickMemory(phase);
1034 magnitude=(double *) RelinquishMagickMemory(magnitude);
cristy3ed852e2009-09-05 21:47:34 +00001035 return(status);
1036}
1037
1038static MagickBooleanType InverseFourierTransform(FourierInfo *fourier_info,
1039 fftw_complex *fourier,Image *image,ExceptionInfo *exception)
1040{
1041 CacheView
1042 *image_view;
1043
1044 double
1045 *source;
1046
1047 fftw_plan
1048 fftw_c2r_plan;
1049
cristy4c08aed2011-07-01 19:47:50 +00001050 register Quantum
cristyc4ea4a42011-01-24 01:43:30 +00001051 *q;
1052
cristybb503372010-05-27 20:51:26 +00001053 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001054 i,
1055 x;
1056
cristyc4ea4a42011-01-24 01:43:30 +00001057 ssize_t
1058 y;
cristy3ed852e2009-09-05 21:47:34 +00001059
cristybb3c02e2013-07-02 00:43:10 +00001060 source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
cristyc4ea4a42011-01-24 01:43:30 +00001061 fourier_info->width*sizeof(*source));
cristybb3c02e2013-07-02 00:43:10 +00001062 if (source == (double *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001063 {
1064 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001065 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001066 return(MagickFalse);
1067 }
cristyb5d5f722009-11-04 03:03:49 +00001068#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001069 #pragma omp critical (MagickCore_InverseFourierTransform)
1070#endif
cristydf079ac2010-09-10 01:45:44 +00001071 {
1072 fftw_c2r_plan=fftw_plan_dft_c2r_2d(fourier_info->width,fourier_info->height,
1073 fourier,source,FFTW_ESTIMATE);
1074 fftw_execute(fftw_c2r_plan);
1075 fftw_destroy_plan(fftw_c2r_plan);
1076 }
cristy3ed852e2009-09-05 21:47:34 +00001077 i=0L;
cristy46ff2672012-12-14 15:32:26 +00001078 image_view=AcquireAuthenticCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +00001079 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001080 {
cristy85812052010-09-14 17:56:15 +00001081 if (y >= (ssize_t) image->rows)
1082 break;
1083 q=GetCacheViewAuthenticPixels(image_view,0L,y,fourier_info->width >
1084 image->columns ? image->columns : fourier_info->width,1UL,exception);
cristyacd2ed22011-08-30 01:44:23 +00001085 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001086 break;
cristybb503372010-05-27 20:51:26 +00001087 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001088 {
cristy233fe582012-07-07 14:00:18 +00001089 if (x < (ssize_t) image->columns)
1090 switch (fourier_info->channel)
cristy3ed852e2009-09-05 21:47:34 +00001091 {
cristy233fe582012-07-07 14:00:18 +00001092 case RedPixelChannel:
1093 default:
1094 {
1095 SetPixelRed(image,ClampToQuantum(QuantumRange*source[i]),q);
1096 break;
1097 }
1098 case GreenPixelChannel:
1099 {
1100 SetPixelGreen(image,ClampToQuantum(QuantumRange*source[i]),q);
1101 break;
1102 }
1103 case BluePixelChannel:
1104 {
1105 SetPixelBlue(image,ClampToQuantum(QuantumRange*source[i]),q);
1106 break;
1107 }
1108 case BlackPixelChannel:
1109 {
1110 SetPixelBlack(image,ClampToQuantum(QuantumRange*source[i]),q);
1111 break;
1112 }
1113 case AlphaPixelChannel:
1114 {
1115 SetPixelAlpha(image,ClampToQuantum(QuantumRange*source[i]),q);
1116 break;
1117 }
cristy3ed852e2009-09-05 21:47:34 +00001118 }
cristy3ed852e2009-09-05 21:47:34 +00001119 i++;
cristyed231572011-07-14 02:18:59 +00001120 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001121 }
1122 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1123 break;
1124 }
1125 image_view=DestroyCacheView(image_view);
cristybb3c02e2013-07-02 00:43:10 +00001126 source=(double *) RelinquishMagickMemory(source);
cristy3ed852e2009-09-05 21:47:34 +00001127 return(MagickTrue);
1128}
1129
cristyc9550792009-11-13 20:05:42 +00001130static MagickBooleanType InverseFourierTransformChannel(
1131 const Image *magnitude_image,const Image *phase_image,
cristyd3090f92011-10-18 00:05:15 +00001132 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +00001133 Image *fourier_image,ExceptionInfo *exception)
1134{
1135 double
1136 *magnitude,
1137 *phase;
1138
1139 fftw_complex
cristybb3c02e2013-07-02 00:43:10 +00001140 *fourier;
cristy3ed852e2009-09-05 21:47:34 +00001141
1142 FourierInfo
1143 fourier_info;
1144
1145 MagickBooleanType
1146 status;
1147
1148 size_t
1149 extent;
1150
cristyc9550792009-11-13 20:05:42 +00001151 fourier_info.width=magnitude_image->columns;
1152 if ((magnitude_image->columns != magnitude_image->rows) ||
1153 ((magnitude_image->columns % 2) != 0) ||
1154 ((magnitude_image->rows % 2) != 0))
cristy3ed852e2009-09-05 21:47:34 +00001155 {
cristyc9550792009-11-13 20:05:42 +00001156 extent=magnitude_image->columns < magnitude_image->rows ?
1157 magnitude_image->rows : magnitude_image->columns;
cristy3ed852e2009-09-05 21:47:34 +00001158 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
1159 }
1160 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +00001161 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +00001162 fourier_info.channel=channel;
1163 fourier_info.modulus=modulus;
cristybb3c02e2013-07-02 00:43:10 +00001164 magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
cristyc4ea4a42011-01-24 01:43:30 +00001165 fourier_info.center*sizeof(*magnitude));
cristybb3c02e2013-07-02 00:43:10 +00001166 if (magnitude == (double *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001167 {
1168 (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 }
cristybb3c02e2013-07-02 00:43:10 +00001173 phase=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
1174 fourier_info.center*sizeof(*phase));
1175 if (phase == (double *) NULL)
1176 {
1177 (void) ThrowMagickException(exception,GetMagickModule(),
1178 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1179 magnitude_image->filename);
1180 magnitude=(double *) RelinquishMagickMemory(magnitude);
1181 return(MagickFalse);
1182 }
1183 fourier=(fftw_complex *) AcquireQuantumMemory((size_t) fourier_info.height,
1184 fourier_info.center*sizeof(*fourier));
1185 if (fourier == (fftw_complex *) NULL)
1186 {
1187 (void) ThrowMagickException(exception,GetMagickModule(),
1188 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1189 magnitude_image->filename);
1190 phase=(double *) RelinquishMagickMemory(phase);
1191 magnitude=(double *) RelinquishMagickMemory(magnitude);
1192 return(MagickFalse);
1193 }
1194 status=InverseFourier(&fourier_info,magnitude_image,phase_image,fourier,
cristyc9550792009-11-13 20:05:42 +00001195 exception);
cristy3ed852e2009-09-05 21:47:34 +00001196 if (status != MagickFalse)
cristybb3c02e2013-07-02 00:43:10 +00001197 status=InverseFourierTransform(&fourier_info,fourier,fourier_image,
cristy3ed852e2009-09-05 21:47:34 +00001198 exception);
cristybb3c02e2013-07-02 00:43:10 +00001199 fourier=(fftw_complex *) RelinquishMagickMemory(fourier);
1200 phase=(double *) RelinquishMagickMemory(phase);
1201 magnitude=(double *) RelinquishMagickMemory(magnitude);
cristy3ed852e2009-09-05 21:47:34 +00001202 return(status);
1203}
1204#endif
1205
cristyc9550792009-11-13 20:05:42 +00001206MagickExport Image *InverseFourierTransformImage(const Image *magnitude_image,
1207 const Image *phase_image,const MagickBooleanType modulus,
1208 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001209{
1210 Image
1211 *fourier_image;
1212
cristyc9550792009-11-13 20:05:42 +00001213 assert(magnitude_image != (Image *) NULL);
1214 assert(magnitude_image->signature == MagickSignature);
1215 if (magnitude_image->debug != MagickFalse)
1216 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1217 magnitude_image->filename);
1218 if (phase_image == (Image *) NULL)
1219 {
1220 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
cristyefe601c2013-01-05 17:51:12 +00001221 "TwoOrMoreImagesRequired","`%s'",magnitude_image->filename);
cristy9372a152009-11-18 01:42:16 +00001222 return((Image *) NULL);
cristyc9550792009-11-13 20:05:42 +00001223 }
cristy3ed852e2009-09-05 21:47:34 +00001224#if !defined(MAGICKCORE_FFTW_DELEGATE)
1225 fourier_image=(Image *) NULL;
1226 (void) modulus;
1227 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00001228 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristyc9550792009-11-13 20:05:42 +00001229 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001230#else
1231 {
cristyc9550792009-11-13 20:05:42 +00001232 fourier_image=CloneImage(magnitude_image,magnitude_image->columns,
1233 magnitude_image->rows,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00001234 if (fourier_image != (Image *) NULL)
1235 {
1236 MagickBooleanType
1237 is_gray,
1238 status;
1239
cristy3ed852e2009-09-05 21:47:34 +00001240 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001241 is_gray=IsImageGray(magnitude_image,exception);
cristyc9550792009-11-13 20:05:42 +00001242 if (is_gray != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001243 is_gray=IsImageGray(phase_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001244#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb34ef052010-10-07 00:12:05 +00001245 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +00001246#endif
cristy3ed852e2009-09-05 21:47:34 +00001247 {
cristyb34ef052010-10-07 00:12:05 +00001248#if defined(MAGICKCORE_OPENMP_SUPPORT)
1249 #pragma omp section
1250#endif
cristy3ed852e2009-09-05 21:47:34 +00001251 {
cristyb34ef052010-10-07 00:12:05 +00001252 MagickBooleanType
1253 thread_status;
1254
1255 if (is_gray != MagickFalse)
1256 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001257 phase_image,GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001258 else
cristyc9550792009-11-13 20:05:42 +00001259 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001260 phase_image,RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001261 if (thread_status == MagickFalse)
1262 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +00001263 }
cristyb34ef052010-10-07 00:12:05 +00001264#if defined(MAGICKCORE_OPENMP_SUPPORT)
1265 #pragma omp section
1266#endif
1267 {
1268 MagickBooleanType
1269 thread_status;
1270
1271 thread_status=MagickTrue;
1272 if (is_gray == MagickFalse)
1273 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001274 phase_image,GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001275 if (thread_status == MagickFalse)
1276 status=thread_status;
1277 }
1278#if defined(MAGICKCORE_OPENMP_SUPPORT)
1279 #pragma omp section
1280#endif
1281 {
1282 MagickBooleanType
1283 thread_status;
1284
1285 thread_status=MagickTrue;
1286 if (is_gray == MagickFalse)
1287 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001288 phase_image,BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001289 if (thread_status == MagickFalse)
1290 status=thread_status;
1291 }
1292#if defined(MAGICKCORE_OPENMP_SUPPORT)
1293 #pragma omp section
1294#endif
1295 {
1296 MagickBooleanType
1297 thread_status;
1298
1299 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001300 if (magnitude_image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +00001301 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001302 phase_image,BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001303 if (thread_status == MagickFalse)
1304 status=thread_status;
1305 }
1306#if defined(MAGICKCORE_OPENMP_SUPPORT)
1307 #pragma omp section
1308#endif
1309 {
1310 MagickBooleanType
1311 thread_status;
1312
1313 thread_status=MagickTrue;
cristy8a46d822012-08-28 23:32:39 +00001314 if (magnitude_image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +00001315 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001316 phase_image,AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001317 if (thread_status == MagickFalse)
1318 status=thread_status;
1319 }
cristy3ed852e2009-09-05 21:47:34 +00001320 }
1321 if (status == MagickFalse)
1322 fourier_image=DestroyImage(fourier_image);
1323 }
cristyb34ef052010-10-07 00:12:05 +00001324 fftw_cleanup();
cristy3ed852e2009-09-05 21:47:34 +00001325 }
1326#endif
1327 return(fourier_image);
1328}