blob: 625622ac6d77ea56a1551b0b94d301d8d3ce5e4c [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,
cristy699ae5b2013-07-03 13:41:29 +0000133 const ssize_t x_offset,const ssize_t y_offset,double *roll_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000134{
135 double
cristy699ae5b2013-07-03 13:41:29 +0000136 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000137
cristy7d4aa382013-06-30 01:59:39 +0000138 MemoryInfo
cristy699ae5b2013-07-03 13:41:29 +0000139 *source_info;
cristy7d4aa382013-06-30 01:59:39 +0000140
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 */
cristy699ae5b2013-07-03 13:41:29 +0000153 source_info=AcquireVirtualMemory(height,width*sizeof(*source_pixels));
154 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000155 return(MagickFalse);
cristy699ae5b2013-07-03 13:41:29 +0000156 source_pixels=(double *) GetVirtualMemoryBlob(source_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;
cristy699ae5b2013-07-03 13:41:29 +0000172 source_pixels[v*width+u]=roll_pixels[i++];
cristyc4ea4a42011-01-24 01:43:30 +0000173 }
cristy3ed852e2009-09-05 21:47:34 +0000174 }
cristy699ae5b2013-07-03 13:41:29 +0000175 (void) CopyMagickMemory(roll_pixels,source_pixels,height*width*
176 sizeof(*source_pixels));
177 source_info=RelinquishVirtualMemory(source_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,
cristy699ae5b2013-07-03 13:41:29 +0000182 const size_t height,double *source_pixels,double *forward_pixels)
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;
cristy699ae5b2013-07-03 13:41:29 +0000198 status=RollFourier((size_t) center,height,0L,(ssize_t) height/2L,
199 source_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000200 if (status == MagickFalse)
201 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000202 for (y=0L; y < (ssize_t) height; y++)
203 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy3d04ed72013-08-18 17:51:01 +0000204 forward_pixels[y*width+x+width/2L]=source_pixels[y*center+x];
cristybb503372010-05-27 20:51:26 +0000205 for (y=1; y < (ssize_t) height; y++)
206 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy9b1d6c72013-08-18 17:53:56 +0000207 forward_pixels[(height-y)*width+width/2L-x-1L]=
cristy3d04ed72013-08-18 17:51:01 +0000208 source_pixels[y*center+x+1L];
cristybb503372010-05-27 20:51:26 +0000209 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy699ae5b2013-07-03 13:41:29 +0000210 forward_pixels[-x+width/2L-1L]=forward_pixels[x+width/2L+1L];
cristy3ed852e2009-09-05 21:47:34 +0000211 return(MagickTrue);
212}
213
cristyc4ea4a42011-01-24 01:43:30 +0000214static void CorrectPhaseLHS(const size_t width,const size_t height,
cristy699ae5b2013-07-03 13:41:29 +0000215 double *fourier_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000216{
cristybb503372010-05-27 20:51:26 +0000217 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000218 x;
219
cristy9d314ff2011-03-09 01:30:28 +0000220 ssize_t
221 y;
222
cristybb503372010-05-27 20:51:26 +0000223 for (y=0L; y < (ssize_t) height; y++)
224 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy699ae5b2013-07-03 13:41:29 +0000225 fourier_pixels[y*width+x]*=(-1.0);
cristy3ed852e2009-09-05 21:47:34 +0000226}
227
228static MagickBooleanType ForwardFourier(const FourierInfo *fourier_info,
229 Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
230{
231 CacheView
232 *magnitude_view,
233 *phase_view;
234
235 double
cristy7d4aa382013-06-30 01:59:39 +0000236 *magnitude_pixels,
237 *phase_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000238
239 Image
240 *magnitude_image,
241 *phase_image;
242
cristy3ed852e2009-09-05 21:47:34 +0000243 MagickBooleanType
244 status;
245
cristy7d4aa382013-06-30 01:59:39 +0000246 MemoryInfo
247 *magnitude_info,
248 *phase_info;
249
cristy4c08aed2011-07-01 19:47:50 +0000250 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000251 *q;
252
cristyf5742792012-11-27 18:31:26 +0000253 register ssize_t
254 x;
255
cristyc4ea4a42011-01-24 01:43:30 +0000256 ssize_t
257 i,
258 y;
259
cristy3ed852e2009-09-05 21:47:34 +0000260 magnitude_image=GetFirstImageInList(image);
261 phase_image=GetNextImageInList(image);
262 if (phase_image == (Image *) NULL)
263 {
264 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
cristyefe601c2013-01-05 17:51:12 +0000265 "TwoOrMoreImagesRequired","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000266 return(MagickFalse);
267 }
268 /*
269 Create "Fourier Transform" image from constituent arrays.
270 */
cristy7d4aa382013-06-30 01:59:39 +0000271 magnitude_info=AcquireVirtualMemory((size_t) fourier_info->height,
272 fourier_info->width*sizeof(*magnitude_pixels));
273 phase_info=AcquireVirtualMemory((size_t) fourier_info->height,
274 fourier_info->width*sizeof(*phase_pixels));
cristybb3c02e2013-07-02 00:43:10 +0000275 if ((magnitude_info == (MemoryInfo *) NULL) ||
276 (phase_info == (MemoryInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000277 {
cristy7d4aa382013-06-30 01:59:39 +0000278 if (phase_info != (MemoryInfo *) NULL)
279 phase_info=RelinquishVirtualMemory(phase_info);
280 if (magnitude_info != (MemoryInfo *) NULL)
281 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000282 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000283 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000284 return(MagickFalse);
285 }
cristy7d4aa382013-06-30 01:59:39 +0000286 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
287 (void) ResetMagickMemory(magnitude_pixels,0,fourier_info->height*
288 fourier_info->width*sizeof(*magnitude_pixels));
cristybb3c02e2013-07-02 00:43:10 +0000289 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
cristy7d4aa382013-06-30 01:59:39 +0000290 (void) ResetMagickMemory(phase_pixels,0,fourier_info->height*
291 fourier_info->width*sizeof(*phase_pixels));
cristy13c99c42013-08-18 13:53:30 +0000292 status=ForwardQuadrantSwap(fourier_info->width,fourier_info->height,
cristy7d4aa382013-06-30 01:59:39 +0000293 magnitude,magnitude_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000294 if (status != MagickFalse)
cristy13c99c42013-08-18 13:53:30 +0000295 status=ForwardQuadrantSwap(fourier_info->width,fourier_info->height,phase,
cristy7d4aa382013-06-30 01:59:39 +0000296 phase_pixels);
cristy13c99c42013-08-18 13:53:30 +0000297 CorrectPhaseLHS(fourier_info->width,fourier_info->height,phase_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000298 if (fourier_info->modulus != MagickFalse)
299 {
300 i=0L;
cristybb503372010-05-27 20:51:26 +0000301 for (y=0L; y < (ssize_t) fourier_info->height; y++)
302 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000303 {
cristy7d4aa382013-06-30 01:59:39 +0000304 phase_pixels[i]/=(2.0*MagickPI);
305 phase_pixels[i]+=0.5;
cristy3ed852e2009-09-05 21:47:34 +0000306 i++;
307 }
308 }
cristy46ff2672012-12-14 15:32:26 +0000309 magnitude_view=AcquireAuthenticCacheView(magnitude_image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000310 i=0L;
cristybb503372010-05-27 20:51:26 +0000311 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000312 {
313 q=GetCacheViewAuthenticPixels(magnitude_view,0L,y,fourier_info->height,1UL,
314 exception);
cristyacd2ed22011-08-30 01:44:23 +0000315 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000316 break;
cristybb503372010-05-27 20:51:26 +0000317 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000318 {
319 switch (fourier_info->channel)
320 {
cristyd3090f92011-10-18 00:05:15 +0000321 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000322 default:
323 {
cristy4c08aed2011-07-01 19:47:50 +0000324 SetPixelRed(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000325 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000326 break;
327 }
cristyd3090f92011-10-18 00:05:15 +0000328 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000329 {
cristy4c08aed2011-07-01 19:47:50 +0000330 SetPixelGreen(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000331 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000332 break;
333 }
cristyd3090f92011-10-18 00:05:15 +0000334 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000335 {
cristy4c08aed2011-07-01 19:47:50 +0000336 SetPixelBlue(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000337 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000338 break;
339 }
cristyd3090f92011-10-18 00:05:15 +0000340 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000341 {
cristy4c08aed2011-07-01 19:47:50 +0000342 SetPixelBlack(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000343 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000344 break;
345 }
cristyd3090f92011-10-18 00:05:15 +0000346 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000347 {
cristy4c08aed2011-07-01 19:47:50 +0000348 SetPixelAlpha(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000349 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000350 break;
351 }
cristy3ed852e2009-09-05 21:47:34 +0000352 }
353 i++;
cristyed231572011-07-14 02:18:59 +0000354 q+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +0000355 }
356 status=SyncCacheViewAuthenticPixels(magnitude_view,exception);
357 if (status == MagickFalse)
358 break;
359 }
cristydb070952012-04-20 14:33:00 +0000360 magnitude_view=DestroyCacheView(magnitude_view);
cristy3ed852e2009-09-05 21:47:34 +0000361 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000362 phase_view=AcquireAuthenticCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +0000363 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000364 {
365 q=GetCacheViewAuthenticPixels(phase_view,0L,y,fourier_info->height,1UL,
366 exception);
cristyacd2ed22011-08-30 01:44:23 +0000367 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000368 break;
cristybb503372010-05-27 20:51:26 +0000369 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000370 {
371 switch (fourier_info->channel)
372 {
cristyd3090f92011-10-18 00:05:15 +0000373 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000374 default:
375 {
cristy4c08aed2011-07-01 19:47:50 +0000376 SetPixelRed(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000377 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000378 break;
379 }
cristyd3090f92011-10-18 00:05:15 +0000380 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000381 {
cristy4c08aed2011-07-01 19:47:50 +0000382 SetPixelGreen(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000383 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000384 break;
385 }
cristyd3090f92011-10-18 00:05:15 +0000386 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000387 {
cristy4c08aed2011-07-01 19:47:50 +0000388 SetPixelBlue(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000389 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000390 break;
391 }
cristyd3090f92011-10-18 00:05:15 +0000392 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000393 {
cristy4c08aed2011-07-01 19:47:50 +0000394 SetPixelBlack(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000395 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000396 break;
397 }
cristyd3090f92011-10-18 00:05:15 +0000398 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000399 {
cristy4c08aed2011-07-01 19:47:50 +0000400 SetPixelAlpha(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000401 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000402 break;
403 }
cristy3ed852e2009-09-05 21:47:34 +0000404 }
405 i++;
cristyed231572011-07-14 02:18:59 +0000406 q+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +0000407 }
408 status=SyncCacheViewAuthenticPixels(phase_view,exception);
409 if (status == MagickFalse)
410 break;
411 }
412 phase_view=DestroyCacheView(phase_view);
cristy7d4aa382013-06-30 01:59:39 +0000413 phase_info=RelinquishVirtualMemory(phase_info);
414 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000415 return(status);
416}
417
418static MagickBooleanType ForwardFourierTransform(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +0000419 const Image *image,double *magnitude_pixels,double *phase_pixels,
420 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000421{
422 CacheView
423 *image_view;
424
425 double
cristy20c2ba52013-09-15 16:05:37 +0000426 gamma,
cristybb3c02e2013-07-02 00:43:10 +0000427 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000428
429 fftw_complex
cristy699ae5b2013-07-03 13:41:29 +0000430 *forward_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000431
432 fftw_plan
433 fftw_r2c_plan;
434
cristy7d4aa382013-06-30 01:59:39 +0000435 MemoryInfo
cristy699ae5b2013-07-03 13:41:29 +0000436 *forward_info,
cristy7d4aa382013-06-30 01:59:39 +0000437 *source_info;
438
cristy4c08aed2011-07-01 19:47:50 +0000439 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000440 *p;
441
cristybb503372010-05-27 20:51:26 +0000442 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000443 i,
444 x;
445
cristyc4ea4a42011-01-24 01:43:30 +0000446 ssize_t
447 y;
448
cristy3ed852e2009-09-05 21:47:34 +0000449 /*
450 Generate the forward Fourier transform.
451 */
cristy7d4aa382013-06-30 01:59:39 +0000452 source_info=AcquireVirtualMemory((size_t) fourier_info->height,
cristybb3c02e2013-07-02 00:43:10 +0000453 fourier_info->width*sizeof(*source_pixels));
cristy7d4aa382013-06-30 01:59:39 +0000454 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000455 {
456 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000457 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000458 return(MagickFalse);
459 }
cristybb3c02e2013-07-02 00:43:10 +0000460 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
461 ResetMagickMemory(source_pixels,0,fourier_info->height*fourier_info->width*
462 sizeof(*source_pixels));
cristy3ed852e2009-09-05 21:47:34 +0000463 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000464 image_view=AcquireVirtualCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +0000465 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000466 {
467 p=GetCacheViewVirtualPixels(image_view,0L,y,fourier_info->width,1UL,
468 exception);
cristy4c08aed2011-07-01 19:47:50 +0000469 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000470 break;
cristybb503372010-05-27 20:51:26 +0000471 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000472 {
473 switch (fourier_info->channel)
474 {
cristyd3090f92011-10-18 00:05:15 +0000475 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000476 default:
477 {
cristybb3c02e2013-07-02 00:43:10 +0000478 source_pixels[i]=QuantumScale*GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000479 break;
480 }
cristyd3090f92011-10-18 00:05:15 +0000481 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000482 {
cristybb3c02e2013-07-02 00:43:10 +0000483 source_pixels[i]=QuantumScale*GetPixelGreen(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000484 break;
485 }
cristyd3090f92011-10-18 00:05:15 +0000486 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000487 {
cristybb3c02e2013-07-02 00:43:10 +0000488 source_pixels[i]=QuantumScale*GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000489 break;
490 }
cristyd3090f92011-10-18 00:05:15 +0000491 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000492 {
cristybb3c02e2013-07-02 00:43:10 +0000493 source_pixels[i]=QuantumScale*GetPixelBlack(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000494 break;
495 }
cristyd3090f92011-10-18 00:05:15 +0000496 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000497 {
cristybb3c02e2013-07-02 00:43:10 +0000498 source_pixels[i]=QuantumScale*GetPixelAlpha(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000499 break;
500 }
cristy3ed852e2009-09-05 21:47:34 +0000501 }
502 i++;
cristyed231572011-07-14 02:18:59 +0000503 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000504 }
505 }
506 image_view=DestroyCacheView(image_view);
cristy699ae5b2013-07-03 13:41:29 +0000507 forward_info=AcquireVirtualMemory((size_t) fourier_info->height,
508 fourier_info->center*sizeof(*forward_pixels));
509 if (forward_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000510 {
511 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000512 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristybb3c02e2013-07-02 00:43:10 +0000513 source_info=(MemoryInfo *) RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000514 return(MagickFalse);
515 }
cristy699ae5b2013-07-03 13:41:29 +0000516 forward_pixels=(fftw_complex *) GetVirtualMemoryBlob(forward_info);
cristyb5d5f722009-11-04 03:03:49 +0000517#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000518 #pragma omp critical (MagickCore_ForwardFourierTransform)
519#endif
cristy70841a12012-10-27 20:52:57 +0000520 fftw_r2c_plan=fftw_plan_dft_r2c_2d(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +0000521 source_pixels,forward_pixels,FFTW_ESTIMATE);
cristy3ed852e2009-09-05 21:47:34 +0000522 fftw_execute(fftw_r2c_plan);
523 fftw_destroy_plan(fftw_r2c_plan);
cristybb3c02e2013-07-02 00:43:10 +0000524 source_info=(MemoryInfo *) RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000525 /*
526 Normalize Fourier transform.
527 */
cristy3ed852e2009-09-05 21:47:34 +0000528 i=0L;
cristy20c2ba52013-09-15 16:05:37 +0000529 gamma=PerceptibleReciprocal((double) fourier_info->width*
530 fourier_info->height);
cristybb503372010-05-27 20:51:26 +0000531 for (y=0L; y < (ssize_t) fourier_info->height; y++)
532 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy56ed31c2010-03-22 00:46:21 +0000533 {
534#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy20c2ba52013-09-15 16:05:37 +0000535 forward_pixels[i]*=gamma;
cristy56ed31c2010-03-22 00:46:21 +0000536#else
cristy20c2ba52013-09-15 16:05:37 +0000537 forward_pixels[i][0]*=gamma;
538 forward_pixels[i][1]*=gamma;
cristy56ed31c2010-03-22 00:46:21 +0000539#endif
540 i++;
541 }
cristy3ed852e2009-09-05 21:47:34 +0000542 /*
543 Generate magnitude and phase (or real and imaginary).
544 */
545 i=0L;
546 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000547 for (y=0L; y < (ssize_t) fourier_info->height; y++)
548 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000549 {
cristy699ae5b2013-07-03 13:41:29 +0000550 magnitude_pixels[i]=cabs(forward_pixels[i]);
551 phase_pixels[i]=carg(forward_pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000552 i++;
553 }
554 else
cristybb503372010-05-27 20:51:26 +0000555 for (y=0L; y < (ssize_t) fourier_info->height; y++)
556 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000557 {
cristy699ae5b2013-07-03 13:41:29 +0000558 magnitude_pixels[i]=creal(forward_pixels[i]);
559 phase_pixels[i]=cimag(forward_pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000560 i++;
561 }
cristy699ae5b2013-07-03 13:41:29 +0000562 forward_info=(MemoryInfo *) RelinquishVirtualMemory(forward_info);
cristy3ed852e2009-09-05 21:47:34 +0000563 return(MagickTrue);
564}
565
566static MagickBooleanType ForwardFourierTransformChannel(const Image *image,
cristyd3090f92011-10-18 00:05:15 +0000567 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +0000568 Image *fourier_image,ExceptionInfo *exception)
569{
570 double
cristyce9fe782013-07-03 00:59:41 +0000571 *magnitude_pixels,
572 *phase_pixels;
cristybb3c02e2013-07-02 00:43:10 +0000573
cristy56ed31c2010-03-22 00:46:21 +0000574 FourierInfo
575 fourier_info;
576
cristyc4ea4a42011-01-24 01:43:30 +0000577 MagickBooleanType
578 status;
579
cristyce9fe782013-07-03 00:59:41 +0000580 MemoryInfo
581 *magnitude_info,
582 *phase_info;
583
cristy3ed852e2009-09-05 21:47:34 +0000584 size_t
585 extent;
586
587 fourier_info.width=image->columns;
588 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
589 ((image->rows % 2) != 0))
590 {
591 extent=image->columns < image->rows ? image->rows : image->columns;
592 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
593 }
594 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +0000595 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +0000596 fourier_info.channel=channel;
597 fourier_info.modulus=modulus;
cristyce9fe782013-07-03 00:59:41 +0000598 magnitude_info=AcquireVirtualMemory((size_t) fourier_info.height,
599 fourier_info.center*sizeof(*magnitude_pixels));
600 phase_info=AcquireVirtualMemory((size_t) fourier_info.height,
601 fourier_info.center*sizeof(*phase_pixels));
602 if ((magnitude_info == (MemoryInfo *) NULL) ||
603 (phase_info == (MemoryInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000604 {
cristyce9fe782013-07-03 00:59:41 +0000605 if (phase_info != (MemoryInfo *) NULL)
606 phase_info=RelinquishVirtualMemory(phase_info);
607 if (magnitude_info == (MemoryInfo *) NULL)
608 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000609 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000610 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000611 return(MagickFalse);
612 }
cristyce9fe782013-07-03 00:59:41 +0000613 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
614 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
615 status=ForwardFourierTransform(&fourier_info,image,magnitude_pixels,
616 phase_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +0000617 if (status != MagickFalse)
cristyce9fe782013-07-03 00:59:41 +0000618 status=ForwardFourier(&fourier_info,fourier_image,magnitude_pixels,
619 phase_pixels,exception);
620 phase_info=RelinquishVirtualMemory(phase_info);
621 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000622 return(status);
623}
624#endif
625
626MagickExport Image *ForwardFourierTransformImage(const Image *image,
627 const MagickBooleanType modulus,ExceptionInfo *exception)
628{
629 Image
630 *fourier_image;
631
632 fourier_image=NewImageList();
633#if !defined(MAGICKCORE_FFTW_DELEGATE)
634 (void) modulus;
635 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000636 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristy3ed852e2009-09-05 21:47:34 +0000637 image->filename);
638#else
639 {
640 Image
641 *magnitude_image;
642
cristybb503372010-05-27 20:51:26 +0000643 size_t
cristy3ed852e2009-09-05 21:47:34 +0000644 extent,
645 width;
646
647 width=image->columns;
648 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
649 ((image->rows % 2) != 0))
650 {
651 extent=image->columns < image->rows ? image->rows : image->columns;
652 width=(extent & 0x01) == 1 ? extent+1UL : extent;
653 }
654 magnitude_image=CloneImage(image,width,width,MagickFalse,exception);
655 if (magnitude_image != (Image *) NULL)
656 {
657 Image
658 *phase_image;
659
660 magnitude_image->storage_class=DirectClass;
661 magnitude_image->depth=32UL;
662 phase_image=CloneImage(image,width,width,MagickFalse,exception);
663 if (phase_image == (Image *) NULL)
664 magnitude_image=DestroyImage(magnitude_image);
665 else
666 {
667 MagickBooleanType
668 is_gray,
669 status;
670
cristy3ed852e2009-09-05 21:47:34 +0000671 phase_image->storage_class=DirectClass;
672 phase_image->depth=32UL;
673 AppendImageToList(&fourier_image,magnitude_image);
674 AppendImageToList(&fourier_image,phase_image);
675 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000676 is_gray=IsImageGray(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000677#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy564a5692012-01-20 23:56:26 +0000678 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +0000679#endif
cristy3ed852e2009-09-05 21:47:34 +0000680 {
cristyb34ef052010-10-07 00:12:05 +0000681#if defined(MAGICKCORE_OPENMP_SUPPORT)
682 #pragma omp section
683#endif
cristy3ed852e2009-09-05 21:47:34 +0000684 {
cristyb34ef052010-10-07 00:12:05 +0000685 MagickBooleanType
686 thread_status;
687
688 if (is_gray != MagickFalse)
689 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000690 GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000691 else
692 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000693 RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000694 if (thread_status == MagickFalse)
695 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +0000696 }
cristyb34ef052010-10-07 00:12:05 +0000697#if defined(MAGICKCORE_OPENMP_SUPPORT)
698 #pragma omp section
699#endif
700 {
701 MagickBooleanType
702 thread_status;
703
704 thread_status=MagickTrue;
705 if (is_gray == MagickFalse)
706 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000707 GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000708 if (thread_status == MagickFalse)
709 status=thread_status;
710 }
711#if defined(MAGICKCORE_OPENMP_SUPPORT)
712 #pragma omp section
713#endif
714 {
715 MagickBooleanType
716 thread_status;
717
718 thread_status=MagickTrue;
719 if (is_gray == MagickFalse)
720 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000721 BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000722 if (thread_status == MagickFalse)
723 status=thread_status;
724 }
725#if defined(MAGICKCORE_OPENMP_SUPPORT)
726 #pragma omp section
727#endif
728 {
729 MagickBooleanType
730 thread_status;
731
732 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000733 if (image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +0000734 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000735 BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000736 if (thread_status == MagickFalse)
737 status=thread_status;
738 }
739#if defined(MAGICKCORE_OPENMP_SUPPORT)
740 #pragma omp section
741#endif
742 {
743 MagickBooleanType
744 thread_status;
745
746 thread_status=MagickTrue;
cristy8a46d822012-08-28 23:32:39 +0000747 if (image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +0000748 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000749 AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000750 if (thread_status == MagickFalse)
751 status=thread_status;
752 }
cristy3ed852e2009-09-05 21:47:34 +0000753 }
754 if (status == MagickFalse)
755 fourier_image=DestroyImageList(fourier_image);
756 fftw_cleanup();
757 }
758 }
759 }
760#endif
761 return(fourier_image);
762}
763
764/*
765%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
766% %
767% %
768% %
769% 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 %
770% %
771% %
772% %
773%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
774%
775% InverseFourierTransformImage() implements the inverse discrete Fourier
776% transform (DFT) of the image either as a magnitude / phase or real /
777% imaginary image pair.
778%
779% The format of the InverseFourierTransformImage method is:
780%
cristyc9550792009-11-13 20:05:42 +0000781% Image *InverseFourierTransformImage(const Image *magnitude_image,
782% const Image *phase_image,const MagickBooleanType modulus,
783% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000784%
785% A description of each parameter follows:
786%
cristyc9550792009-11-13 20:05:42 +0000787% o magnitude_image: the magnitude or real image.
788%
789% o phase_image: the phase or imaginary image.
cristy3ed852e2009-09-05 21:47:34 +0000790%
791% o modulus: if true, return transform as a magnitude / phase pair
792% otherwise a real / imaginary image pair.
793%
794% o exception: return any errors or warnings in this structure.
795%
796*/
797
798#if defined(MAGICKCORE_FFTW_DELEGATE)
cristybb503372010-05-27 20:51:26 +0000799static MagickBooleanType InverseQuadrantSwap(const size_t width,
800 const size_t height,const double *source,double *destination)
cristy3ed852e2009-09-05 21:47:34 +0000801{
cristyc4ea4a42011-01-24 01:43:30 +0000802 register ssize_t
803 x;
804
cristybb503372010-05-27 20:51:26 +0000805 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000806 center,
807 y;
808
cristy3ed852e2009-09-05 21:47:34 +0000809 /*
810 Swap quadrants.
811 */
cristy233fe582012-07-07 14:00:18 +0000812 center=(ssize_t) floor((double) width/2L)+1L;
cristybb503372010-05-27 20:51:26 +0000813 for (y=1L; y < (ssize_t) height; y++)
814 for (x=0L; x < (ssize_t) (width/2L+1L); x++)
cristy9b1d6c72013-08-18 17:53:56 +0000815 destination[(height-y)*center-x+width/2L]=source[y*width+x];
cristybb503372010-05-27 20:51:26 +0000816 for (y=0L; y < (ssize_t) height; y++)
cristy3d04ed72013-08-18 17:51:01 +0000817 destination[y*center]=source[y*width+width/2L];
cristy3ed852e2009-09-05 21:47:34 +0000818 for (x=0L; x < center; x++)
819 destination[x]=source[center-x-1L];
cristybb503372010-05-27 20:51:26 +0000820 return(RollFourier(center,height,0L,(ssize_t) height/-2L,destination));
cristy3ed852e2009-09-05 21:47:34 +0000821}
822
823static MagickBooleanType InverseFourier(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +0000824 const Image *magnitude_image,const Image *phase_image,
825 fftw_complex *fourier_pixels,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000826{
827 CacheView
828 *magnitude_view,
829 *phase_view;
830
831 double
cristy699ae5b2013-07-03 13:41:29 +0000832 *inverse_pixels,
cristy7d4aa382013-06-30 01:59:39 +0000833 *magnitude_pixels,
834 *phase_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000835
cristy3ed852e2009-09-05 21:47:34 +0000836 MagickBooleanType
837 status;
838
cristy699ae5b2013-07-03 13:41:29 +0000839 MemoryInfo
840 *inverse_info,
841 *magnitude_info,
842 *phase_info;
843
cristy4c08aed2011-07-01 19:47:50 +0000844 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000845 *p;
846
cristybb503372010-05-27 20:51:26 +0000847 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000848 i,
849 x;
850
cristyc4ea4a42011-01-24 01:43:30 +0000851 ssize_t
852 y;
853
cristy3ed852e2009-09-05 21:47:34 +0000854 /*
855 Inverse fourier - read image and break down into a double array.
856 */
cristy699ae5b2013-07-03 13:41:29 +0000857 magnitude_info=AcquireVirtualMemory((size_t)fourier_info->height,
858 fourier_info->width*sizeof(*magnitude_pixels));
859 phase_info=AcquireVirtualMemory((size_t) fourier_info->height,
cristybb3c02e2013-07-02 00:43:10 +0000860 fourier_info->width*sizeof(*phase_pixels));
cristy699ae5b2013-07-03 13:41:29 +0000861 inverse_info=AcquireVirtualMemory((size_t) fourier_info->height,
862 fourier_info->center*sizeof(*inverse_pixels));
863 if ((magnitude_info == (MemoryInfo *) NULL) ||
864 (phase_info == (MemoryInfo *) NULL) ||
865 (inverse_info == (MemoryInfo *) NULL))
cristybb3c02e2013-07-02 00:43:10 +0000866 {
cristy699ae5b2013-07-03 13:41:29 +0000867 if (magnitude_info != (MemoryInfo *) NULL)
868 magnitude_info=RelinquishVirtualMemory(magnitude_info);
869 if (phase_info != (MemoryInfo *) NULL)
870 phase_info=RelinquishVirtualMemory(phase_info);
871 if (inverse_info != (MemoryInfo *) NULL)
872 inverse_info=RelinquishVirtualMemory(inverse_info);
cristybb3c02e2013-07-02 00:43:10 +0000873 (void) ThrowMagickException(exception,GetMagickModule(),
874 ResourceLimitError,"MemoryAllocationFailed","`%s'",
875 magnitude_image->filename);
cristybb3c02e2013-07-02 00:43:10 +0000876 return(MagickFalse);
877 }
cristy699ae5b2013-07-03 13:41:29 +0000878 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
879 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
880 inverse_pixels=(double *) GetVirtualMemoryBlob(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +0000881 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000882 magnitude_view=AcquireVirtualCacheView(magnitude_image,exception);
cristybb503372010-05-27 20:51:26 +0000883 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000884 {
885 p=GetCacheViewVirtualPixels(magnitude_view,0L,y,fourier_info->width,1UL,
886 exception);
cristy4c08aed2011-07-01 19:47:50 +0000887 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000888 break;
cristybb503372010-05-27 20:51:26 +0000889 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000890 {
891 switch (fourier_info->channel)
892 {
cristyd3090f92011-10-18 00:05:15 +0000893 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000894 default:
895 {
cristy7d4aa382013-06-30 01:59:39 +0000896 magnitude_pixels[i]=QuantumScale*GetPixelRed(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000897 break;
898 }
cristyd3090f92011-10-18 00:05:15 +0000899 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000900 {
cristy7d4aa382013-06-30 01:59:39 +0000901 magnitude_pixels[i]=QuantumScale*GetPixelGreen(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000902 break;
903 }
cristyd3090f92011-10-18 00:05:15 +0000904 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000905 {
cristy7d4aa382013-06-30 01:59:39 +0000906 magnitude_pixels[i]=QuantumScale*GetPixelBlue(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000907 break;
908 }
cristyd3090f92011-10-18 00:05:15 +0000909 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000910 {
cristy7d4aa382013-06-30 01:59:39 +0000911 magnitude_pixels[i]=QuantumScale*GetPixelBlack(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000912 break;
913 }
cristyd3090f92011-10-18 00:05:15 +0000914 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000915 {
cristy7d4aa382013-06-30 01:59:39 +0000916 magnitude_pixels[i]=QuantumScale*GetPixelAlpha(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000917 break;
918 }
cristy3ed852e2009-09-05 21:47:34 +0000919 }
920 i++;
cristyed231572011-07-14 02:18:59 +0000921 p+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +0000922 }
923 }
cristy699ae5b2013-07-03 13:41:29 +0000924 magnitude_view=DestroyCacheView(magnitude_view);
925 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
926 magnitude_pixels,inverse_pixels);
927 (void) CopyMagickMemory(magnitude_pixels,inverse_pixels,fourier_info->height*
928 fourier_info->center*sizeof(*magnitude_pixels));
cristy3ed852e2009-09-05 21:47:34 +0000929 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000930 phase_view=AcquireVirtualCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +0000931 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000932 {
933 p=GetCacheViewVirtualPixels(phase_view,0,y,fourier_info->width,1,
934 exception);
cristy4c08aed2011-07-01 19:47:50 +0000935 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000936 break;
cristybb503372010-05-27 20:51:26 +0000937 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000938 {
939 switch (fourier_info->channel)
940 {
cristyd3090f92011-10-18 00:05:15 +0000941 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000942 default:
943 {
cristy7d4aa382013-06-30 01:59:39 +0000944 phase_pixels[i]=QuantumScale*GetPixelRed(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000945 break;
946 }
cristyd3090f92011-10-18 00:05:15 +0000947 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000948 {
cristy7d4aa382013-06-30 01:59:39 +0000949 phase_pixels[i]=QuantumScale*GetPixelGreen(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000950 break;
951 }
cristyd3090f92011-10-18 00:05:15 +0000952 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000953 {
cristy7d4aa382013-06-30 01:59:39 +0000954 phase_pixels[i]=QuantumScale*GetPixelBlue(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000955 break;
956 }
cristyd3090f92011-10-18 00:05:15 +0000957 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000958 {
cristy7d4aa382013-06-30 01:59:39 +0000959 phase_pixels[i]=QuantumScale*GetPixelBlack(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000960 break;
961 }
cristyd3090f92011-10-18 00:05:15 +0000962 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000963 {
cristy7d4aa382013-06-30 01:59:39 +0000964 phase_pixels[i]=QuantumScale*GetPixelAlpha(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000965 break;
966 }
cristy3ed852e2009-09-05 21:47:34 +0000967 }
968 i++;
cristyed231572011-07-14 02:18:59 +0000969 p+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +0000970 }
971 }
972 if (fourier_info->modulus != MagickFalse)
973 {
974 i=0L;
cristybb503372010-05-27 20:51:26 +0000975 for (y=0L; y < (ssize_t) fourier_info->height; y++)
976 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000977 {
cristy7d4aa382013-06-30 01:59:39 +0000978 phase_pixels[i]-=0.5;
979 phase_pixels[i]*=(2.0*MagickPI);
cristy3ed852e2009-09-05 21:47:34 +0000980 i++;
981 }
982 }
cristy3ed852e2009-09-05 21:47:34 +0000983 phase_view=DestroyCacheView(phase_view);
cristy13c99c42013-08-18 13:53:30 +0000984 CorrectPhaseLHS(fourier_info->width,fourier_info->height,phase_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000985 if (status != MagickFalse)
986 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +0000987 phase_pixels,inverse_pixels);
988 (void) CopyMagickMemory(phase_pixels,inverse_pixels,fourier_info->height*
989 fourier_info->center*sizeof(*phase_pixels));
990 inverse_info=RelinquishVirtualMemory(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +0000991 /*
992 Merge two sets.
993 */
994 i=0L;
995 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000996 for (y=0L; y < (ssize_t) fourier_info->height; y++)
997 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000998 {
cristy56ed31c2010-03-22 00:46:21 +0000999#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy699ae5b2013-07-03 13:41:29 +00001000 fourier_pixels[i]=magnitude_pixels[i]*cos(phase_pixels[i])+I*
1001 magnitude_pixels[i]*sin(phase_pixels[i]);
cristy56ed31c2010-03-22 00:46:21 +00001002#else
cristy699ae5b2013-07-03 13:41:29 +00001003 fourier_pixels[i][0]=magnitude_pixels[i]*cos(phase_pixels[i]);
1004 fourier_pixels[i][1]=magnitude_pixels[i]*sin(phase_pixels[i]);
cristy56ed31c2010-03-22 00:46:21 +00001005#endif
cristy3ed852e2009-09-05 21:47:34 +00001006 i++;
1007 }
1008 else
cristybb503372010-05-27 20:51:26 +00001009 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1010 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001011 {
cristy56ed31c2010-03-22 00:46:21 +00001012#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy699ae5b2013-07-03 13:41:29 +00001013 fourier_pixels[i]=magnitude_pixels[i]+I*phase_pixels[i];
cristy56ed31c2010-03-22 00:46:21 +00001014#else
cristy699ae5b2013-07-03 13:41:29 +00001015 fourier_pixels[i][0]=magnitude_pixels[i];
1016 fourier_pixels[i][1]=phase_pixels[i];
cristy56ed31c2010-03-22 00:46:21 +00001017#endif
cristy3ed852e2009-09-05 21:47:34 +00001018 i++;
1019 }
cristy699ae5b2013-07-03 13:41:29 +00001020 magnitude_info=RelinquishVirtualMemory(magnitude_info);
1021 phase_info=RelinquishVirtualMemory(phase_info);
cristy3ed852e2009-09-05 21:47:34 +00001022 return(status);
1023}
1024
1025static MagickBooleanType InverseFourierTransform(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +00001026 fftw_complex *fourier_pixels,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001027{
1028 CacheView
1029 *image_view;
1030
1031 double
cristy699ae5b2013-07-03 13:41:29 +00001032 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001033
1034 fftw_plan
1035 fftw_c2r_plan;
1036
cristy699ae5b2013-07-03 13:41:29 +00001037 MemoryInfo
1038 *source_info;
1039
cristy4c08aed2011-07-01 19:47:50 +00001040 register Quantum
cristyc4ea4a42011-01-24 01:43:30 +00001041 *q;
1042
cristybb503372010-05-27 20:51:26 +00001043 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001044 i,
1045 x;
1046
cristyc4ea4a42011-01-24 01:43:30 +00001047 ssize_t
1048 y;
cristy3ed852e2009-09-05 21:47:34 +00001049
cristy699ae5b2013-07-03 13:41:29 +00001050 source_info=AcquireVirtualMemory((size_t) fourier_info->height,
1051 fourier_info->width*sizeof(*source_pixels));
1052 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001053 {
1054 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001055 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001056 return(MagickFalse);
1057 }
cristy699ae5b2013-07-03 13:41:29 +00001058 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
cristyb5d5f722009-11-04 03:03:49 +00001059#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001060 #pragma omp critical (MagickCore_InverseFourierTransform)
1061#endif
cristydf079ac2010-09-10 01:45:44 +00001062 {
1063 fftw_c2r_plan=fftw_plan_dft_c2r_2d(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +00001064 fourier_pixels,source_pixels,FFTW_ESTIMATE);
cristydf079ac2010-09-10 01:45:44 +00001065 fftw_execute(fftw_c2r_plan);
1066 fftw_destroy_plan(fftw_c2r_plan);
1067 }
cristy3ed852e2009-09-05 21:47:34 +00001068 i=0L;
cristy46ff2672012-12-14 15:32:26 +00001069 image_view=AcquireAuthenticCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +00001070 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001071 {
cristy85812052010-09-14 17:56:15 +00001072 if (y >= (ssize_t) image->rows)
1073 break;
1074 q=GetCacheViewAuthenticPixels(image_view,0L,y,fourier_info->width >
1075 image->columns ? image->columns : fourier_info->width,1UL,exception);
cristyacd2ed22011-08-30 01:44:23 +00001076 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001077 break;
cristybb503372010-05-27 20:51:26 +00001078 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001079 {
cristy233fe582012-07-07 14:00:18 +00001080 if (x < (ssize_t) image->columns)
1081 switch (fourier_info->channel)
cristy3ed852e2009-09-05 21:47:34 +00001082 {
cristy233fe582012-07-07 14:00:18 +00001083 case RedPixelChannel:
1084 default:
1085 {
cristy699ae5b2013-07-03 13:41:29 +00001086 SetPixelRed(image,ClampToQuantum(QuantumRange*source_pixels[i]),q);
cristy233fe582012-07-07 14:00:18 +00001087 break;
1088 }
1089 case GreenPixelChannel:
1090 {
cristy699ae5b2013-07-03 13:41:29 +00001091 SetPixelGreen(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1092 q);
cristy233fe582012-07-07 14:00:18 +00001093 break;
1094 }
1095 case BluePixelChannel:
1096 {
cristy699ae5b2013-07-03 13:41:29 +00001097 SetPixelBlue(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1098 q);
cristy233fe582012-07-07 14:00:18 +00001099 break;
1100 }
1101 case BlackPixelChannel:
1102 {
cristy699ae5b2013-07-03 13:41:29 +00001103 SetPixelBlack(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1104 q);
cristy233fe582012-07-07 14:00:18 +00001105 break;
1106 }
1107 case AlphaPixelChannel:
1108 {
cristy699ae5b2013-07-03 13:41:29 +00001109 SetPixelAlpha(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1110 q);
cristy233fe582012-07-07 14:00:18 +00001111 break;
1112 }
cristy3ed852e2009-09-05 21:47:34 +00001113 }
cristy3ed852e2009-09-05 21:47:34 +00001114 i++;
cristyed231572011-07-14 02:18:59 +00001115 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001116 }
1117 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1118 break;
1119 }
1120 image_view=DestroyCacheView(image_view);
cristy699ae5b2013-07-03 13:41:29 +00001121 source_info=RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +00001122 return(MagickTrue);
1123}
1124
cristyc9550792009-11-13 20:05:42 +00001125static MagickBooleanType InverseFourierTransformChannel(
1126 const Image *magnitude_image,const Image *phase_image,
cristyd3090f92011-10-18 00:05:15 +00001127 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +00001128 Image *fourier_image,ExceptionInfo *exception)
1129{
cristy3ed852e2009-09-05 21:47:34 +00001130 fftw_complex
cristy699ae5b2013-07-03 13:41:29 +00001131 *inverse_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001132
1133 FourierInfo
1134 fourier_info;
1135
1136 MagickBooleanType
1137 status;
1138
cristy699ae5b2013-07-03 13:41:29 +00001139 MemoryInfo
1140 *inverse_info;
1141
cristy3ed852e2009-09-05 21:47:34 +00001142 size_t
1143 extent;
1144
cristyc9550792009-11-13 20:05:42 +00001145 fourier_info.width=magnitude_image->columns;
1146 if ((magnitude_image->columns != magnitude_image->rows) ||
1147 ((magnitude_image->columns % 2) != 0) ||
1148 ((magnitude_image->rows % 2) != 0))
cristy3ed852e2009-09-05 21:47:34 +00001149 {
cristyc9550792009-11-13 20:05:42 +00001150 extent=magnitude_image->columns < magnitude_image->rows ?
1151 magnitude_image->rows : magnitude_image->columns;
cristy3ed852e2009-09-05 21:47:34 +00001152 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
1153 }
1154 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +00001155 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +00001156 fourier_info.channel=channel;
1157 fourier_info.modulus=modulus;
cristy699ae5b2013-07-03 13:41:29 +00001158 inverse_info=AcquireVirtualMemory((size_t) fourier_info.height,
1159 fourier_info.center*sizeof(*inverse_pixels));
1160 if (inverse_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001161 {
1162 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001163 ResourceLimitError,"MemoryAllocationFailed","`%s'",
cristyc9550792009-11-13 20:05:42 +00001164 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001165 return(MagickFalse);
1166 }
cristy699ae5b2013-07-03 13:41:29 +00001167 inverse_pixels=(fftw_complex *) GetVirtualMemoryBlob(inverse_info);
1168 status=InverseFourier(&fourier_info,magnitude_image,phase_image,
1169 inverse_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +00001170 if (status != MagickFalse)
cristy699ae5b2013-07-03 13:41:29 +00001171 status=InverseFourierTransform(&fourier_info,inverse_pixels,fourier_image,
cristy3ed852e2009-09-05 21:47:34 +00001172 exception);
cristy699ae5b2013-07-03 13:41:29 +00001173 inverse_info=RelinquishVirtualMemory(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +00001174 return(status);
1175}
1176#endif
1177
cristyc9550792009-11-13 20:05:42 +00001178MagickExport Image *InverseFourierTransformImage(const Image *magnitude_image,
1179 const Image *phase_image,const MagickBooleanType modulus,
1180 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001181{
1182 Image
1183 *fourier_image;
1184
cristyc9550792009-11-13 20:05:42 +00001185 assert(magnitude_image != (Image *) NULL);
1186 assert(magnitude_image->signature == MagickSignature);
1187 if (magnitude_image->debug != MagickFalse)
1188 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1189 magnitude_image->filename);
1190 if (phase_image == (Image *) NULL)
1191 {
1192 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
cristyefe601c2013-01-05 17:51:12 +00001193 "TwoOrMoreImagesRequired","`%s'",magnitude_image->filename);
cristy9372a152009-11-18 01:42:16 +00001194 return((Image *) NULL);
cristyc9550792009-11-13 20:05:42 +00001195 }
cristy3ed852e2009-09-05 21:47:34 +00001196#if !defined(MAGICKCORE_FFTW_DELEGATE)
1197 fourier_image=(Image *) NULL;
1198 (void) modulus;
1199 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00001200 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristyc9550792009-11-13 20:05:42 +00001201 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001202#else
1203 {
cristyc9550792009-11-13 20:05:42 +00001204 fourier_image=CloneImage(magnitude_image,magnitude_image->columns,
1205 magnitude_image->rows,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00001206 if (fourier_image != (Image *) NULL)
1207 {
1208 MagickBooleanType
1209 is_gray,
1210 status;
1211
cristy3ed852e2009-09-05 21:47:34 +00001212 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001213 is_gray=IsImageGray(magnitude_image,exception);
cristyc9550792009-11-13 20:05:42 +00001214 if (is_gray != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001215 is_gray=IsImageGray(phase_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001216#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb34ef052010-10-07 00:12:05 +00001217 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +00001218#endif
cristy3ed852e2009-09-05 21:47:34 +00001219 {
cristyb34ef052010-10-07 00:12:05 +00001220#if defined(MAGICKCORE_OPENMP_SUPPORT)
1221 #pragma omp section
1222#endif
cristy3ed852e2009-09-05 21:47:34 +00001223 {
cristyb34ef052010-10-07 00:12:05 +00001224 MagickBooleanType
1225 thread_status;
1226
1227 if (is_gray != MagickFalse)
1228 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001229 phase_image,GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001230 else
cristyc9550792009-11-13 20:05:42 +00001231 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001232 phase_image,RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001233 if (thread_status == MagickFalse)
1234 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +00001235 }
cristyb34ef052010-10-07 00:12:05 +00001236#if defined(MAGICKCORE_OPENMP_SUPPORT)
1237 #pragma omp section
1238#endif
1239 {
1240 MagickBooleanType
1241 thread_status;
1242
1243 thread_status=MagickTrue;
1244 if (is_gray == MagickFalse)
1245 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001246 phase_image,GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001247 if (thread_status == MagickFalse)
1248 status=thread_status;
1249 }
1250#if defined(MAGICKCORE_OPENMP_SUPPORT)
1251 #pragma omp section
1252#endif
1253 {
1254 MagickBooleanType
1255 thread_status;
1256
1257 thread_status=MagickTrue;
1258 if (is_gray == MagickFalse)
1259 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001260 phase_image,BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001261 if (thread_status == MagickFalse)
1262 status=thread_status;
1263 }
1264#if defined(MAGICKCORE_OPENMP_SUPPORT)
1265 #pragma omp section
1266#endif
1267 {
1268 MagickBooleanType
1269 thread_status;
1270
1271 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001272 if (magnitude_image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +00001273 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001274 phase_image,BlackPixelChannel,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;
cristy8a46d822012-08-28 23:32:39 +00001286 if (magnitude_image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +00001287 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001288 phase_image,AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001289 if (thread_status == MagickFalse)
1290 status=thread_status;
1291 }
cristy3ed852e2009-09-05 21:47:34 +00001292 }
1293 if (status == MagickFalse)
1294 fourier_image=DestroyImage(fourier_image);
1295 }
cristyb34ef052010-10-07 00:12:05 +00001296 fftw_cleanup();
cristy3ed852e2009-09-05 21:47:34 +00001297 }
1298#endif
1299 return(fourier_image);
1300}