blob: 139ed4e4976271303512daefa0088198919e2f67 [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% %
cristy1454be72011-12-19 01:52:48 +000022% Copyright 1999-2012 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"
47#include "MagickCore/cache.h"
48#include "MagickCore/image.h"
49#include "MagickCore/image-private.h"
50#include "MagickCore/list.h"
51#include "MagickCore/fourier.h"
52#include "MagickCore/log.h"
53#include "MagickCore/memory_.h"
54#include "MagickCore/monitor.h"
55#include "MagickCore/pixel-accessor.h"
56#include "MagickCore/property.h"
57#include "MagickCore/quantum-private.h"
58#include "MagickCore/thread-private.h"
cristy3ed852e2009-09-05 21:47:34 +000059#if defined(MAGICKCORE_FFTW_DELEGATE)
cristy56ed31c2010-03-22 00:46:21 +000060#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy3ed852e2009-09-05 21:47:34 +000061#include <complex.h>
cristy56ed31c2010-03-22 00:46:21 +000062#endif
cristy3ed852e2009-09-05 21:47:34 +000063#include <fftw3.h>
cristy47b022b2011-01-18 22:29:21 +000064#if !defined(MAGICKCORE_HAVE_CABS)
65#define cabs(z) (sqrt(z[0]*z[0]+z[1]*z[1]))
66#endif
67#if !defined(MAGICKCORE_HAVE_CARG)
cristy4da3ba32011-02-07 16:58:11 +000068#define carg(z) (atan2(cimag(z),creal(z)))
cristy47b022b2011-01-18 22:29:21 +000069#endif
70#if !defined(MAGICKCORE_HAVE_CIMAG)
71#define cimag(z) (z[1])
72#endif
73#if !defined(MAGICKCORE_HAVE_CREAL)
74#define creal(z) (z[0])
75#endif
cristy3ed852e2009-09-05 21:47:34 +000076#endif
77
78/*
79 Typedef declarations.
80*/
81typedef struct _FourierInfo
82{
cristyd3090f92011-10-18 00:05:15 +000083 PixelChannel
cristy3ed852e2009-09-05 21:47:34 +000084 channel;
85
86 MagickBooleanType
87 modulus;
88
cristybb503372010-05-27 20:51:26 +000089 size_t
cristy3ed852e2009-09-05 21:47:34 +000090 width,
91 height;
92
cristybb503372010-05-27 20:51:26 +000093 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000094 center;
95} FourierInfo;
96
97/*
98%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
99% %
100% %
101% %
102% 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 %
103% %
104% %
105% %
106%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
107%
108% ForwardFourierTransformImage() implements the discrete Fourier transform
109% (DFT) of the image either as a magnitude / phase or real / imaginary image
110% pair.
111%
112% The format of the ForwadFourierTransformImage method is:
113%
114% Image *ForwardFourierTransformImage(const Image *image,
115% const MagickBooleanType modulus,ExceptionInfo *exception)
116%
117% A description of each parameter follows:
118%
119% o image: the image.
120%
121% o modulus: if true, return as transform as a magnitude / phase pair
122% otherwise a real / imaginary image pair.
123%
124% o exception: return any errors or warnings in this structure.
125%
126*/
127
128#if defined(MAGICKCORE_FFTW_DELEGATE)
129
cristyc4ea4a42011-01-24 01:43:30 +0000130static MagickBooleanType RollFourier(const size_t width,const size_t height,
131 const ssize_t x_offset,const ssize_t y_offset,double *fourier)
cristy3ed852e2009-09-05 21:47:34 +0000132{
133 double
134 *roll;
135
cristyc4ea4a42011-01-24 01:43:30 +0000136 register ssize_t
137 i,
138 x;
139
cristybb503372010-05-27 20:51:26 +0000140 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000141 u,
142 v,
143 y;
144
cristy3ed852e2009-09-05 21:47:34 +0000145 /*
cristy2da05352010-09-15 19:22:17 +0000146 Move zero frequency (DC, average color) from (0,0) to (width/2,height/2).
cristy3ed852e2009-09-05 21:47:34 +0000147 */
cristyc4ea4a42011-01-24 01:43:30 +0000148 roll=(double *) AcquireQuantumMemory((size_t) height,width*sizeof(*roll));
cristy3ed852e2009-09-05 21:47:34 +0000149 if (roll == (double *) NULL)
150 return(MagickFalse);
151 i=0L;
cristybb503372010-05-27 20:51:26 +0000152 for (y=0L; y < (ssize_t) height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000153 {
154 if (y_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000155 v=((y+y_offset) < 0L) ? y+y_offset+(ssize_t) height : y+y_offset;
cristy3ed852e2009-09-05 21:47:34 +0000156 else
cristybb503372010-05-27 20:51:26 +0000157 v=((y+y_offset) > ((ssize_t) height-1L)) ? y+y_offset-(ssize_t) height :
cristy3ed852e2009-09-05 21:47:34 +0000158 y+y_offset;
cristybb503372010-05-27 20:51:26 +0000159 for (x=0L; x < (ssize_t) width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000160 {
161 if (x_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000162 u=((x+x_offset) < 0L) ? x+x_offset+(ssize_t) width : x+x_offset;
cristy3ed852e2009-09-05 21:47:34 +0000163 else
cristybb503372010-05-27 20:51:26 +0000164 u=((x+x_offset) > ((ssize_t) width-1L)) ? x+x_offset-(ssize_t) width :
cristy3ed852e2009-09-05 21:47:34 +0000165 x+x_offset;
166 roll[v*width+u]=fourier[i++];
cristyc4ea4a42011-01-24 01:43:30 +0000167 }
cristy3ed852e2009-09-05 21:47:34 +0000168 }
cristyc4ea4a42011-01-24 01:43:30 +0000169 (void) CopyMagickMemory(fourier,roll,height*width*sizeof(*roll));
cristy3ed852e2009-09-05 21:47:34 +0000170 roll=(double *) RelinquishMagickMemory(roll);
171 return(MagickTrue);
172}
173
cristybb503372010-05-27 20:51:26 +0000174static MagickBooleanType ForwardQuadrantSwap(const size_t width,
175 const size_t height,double *source,double *destination)
cristy3ed852e2009-09-05 21:47:34 +0000176{
cristy3ed852e2009-09-05 21:47:34 +0000177 MagickBooleanType
178 status;
179
cristybb503372010-05-27 20:51:26 +0000180 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000181 x;
182
cristyc4ea4a42011-01-24 01:43:30 +0000183 ssize_t
184 center,
185 y;
186
cristy3ed852e2009-09-05 21:47:34 +0000187 /*
188 Swap quadrants.
189 */
cristybb503372010-05-27 20:51:26 +0000190 center=(ssize_t) floor((double) width/2L)+1L;
191 status=RollFourier((size_t) center,height,0L,(ssize_t) height/2L,source);
cristy3ed852e2009-09-05 21:47:34 +0000192 if (status == MagickFalse)
193 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000194 for (y=0L; y < (ssize_t) height; y++)
195 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000196 destination[width*y+x+width/2L]=source[center*y+x];
cristybb503372010-05-27 20:51:26 +0000197 for (y=1; y < (ssize_t) height; y++)
198 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000199 destination[width*(height-y)+width/2L-x-1L]=source[center*y+x+1L];
cristybb503372010-05-27 20:51:26 +0000200 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000201 destination[-x+width/2L-1L]=destination[x+width/2L+1L];
202 return(MagickTrue);
203}
204
cristyc4ea4a42011-01-24 01:43:30 +0000205static void CorrectPhaseLHS(const size_t width,const size_t height,
206 double *fourier)
cristy3ed852e2009-09-05 21:47:34 +0000207{
cristybb503372010-05-27 20:51:26 +0000208 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000209 x;
210
cristy9d314ff2011-03-09 01:30:28 +0000211 ssize_t
212 y;
213
cristybb503372010-05-27 20:51:26 +0000214 for (y=0L; y < (ssize_t) height; y++)
215 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000216 fourier[y*width+x]*=(-1.0);
217}
218
219static MagickBooleanType ForwardFourier(const FourierInfo *fourier_info,
220 Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
221{
222 CacheView
223 *magnitude_view,
224 *phase_view;
225
226 double
227 *magnitude_source,
228 *phase_source;
229
230 Image
231 *magnitude_image,
232 *phase_image;
233
cristy3ed852e2009-09-05 21:47:34 +0000234 MagickBooleanType
235 status;
236
cristybb503372010-05-27 20:51:26 +0000237 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000238 x;
239
cristy4c08aed2011-07-01 19:47:50 +0000240 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000241 *q;
242
cristyc4ea4a42011-01-24 01:43:30 +0000243 ssize_t
244 i,
245 y;
246
cristy3ed852e2009-09-05 21:47:34 +0000247 magnitude_image=GetFirstImageInList(image);
248 phase_image=GetNextImageInList(image);
249 if (phase_image == (Image *) NULL)
250 {
251 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
anthonye5b39652012-04-21 05:37:29 +0000252 "TwoOrMoreImagesRequired","'%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000253 return(MagickFalse);
254 }
255 /*
256 Create "Fourier Transform" image from constituent arrays.
257 */
258 magnitude_source=(double *) AcquireQuantumMemory((size_t)
259 fourier_info->height,fourier_info->width*sizeof(*magnitude_source));
260 if (magnitude_source == (double *) NULL)
261 return(MagickFalse);
cristyc4ea4a42011-01-24 01:43:30 +0000262 (void) ResetMagickMemory(magnitude_source,0,fourier_info->height*
263 fourier_info->width*sizeof(*magnitude_source));
cristy3ed852e2009-09-05 21:47:34 +0000264 phase_source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
265 fourier_info->width*sizeof(*phase_source));
cristyc4ea4a42011-01-24 01:43:30 +0000266 if (phase_source == (double *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000267 {
268 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000269 ResourceLimitError,"MemoryAllocationFailed","'%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000270 magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
271 return(MagickFalse);
272 }
273 status=ForwardQuadrantSwap(fourier_info->height,fourier_info->height,
274 magnitude,magnitude_source);
275 if (status != MagickFalse)
276 status=ForwardQuadrantSwap(fourier_info->height,fourier_info->height,phase,
277 phase_source);
278 CorrectPhaseLHS(fourier_info->height,fourier_info->height,phase_source);
279 if (fourier_info->modulus != MagickFalse)
280 {
281 i=0L;
cristybb503372010-05-27 20:51:26 +0000282 for (y=0L; y < (ssize_t) fourier_info->height; y++)
283 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000284 {
285 phase_source[i]/=(2.0*MagickPI);
286 phase_source[i]+=0.5;
287 i++;
288 }
289 }
cristydb070952012-04-20 14:33:00 +0000290 magnitude_view=AcquireAuthenticCacheView(magnitude_image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000291 i=0L;
cristybb503372010-05-27 20:51:26 +0000292 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000293 {
294 q=GetCacheViewAuthenticPixels(magnitude_view,0L,y,fourier_info->height,1UL,
295 exception);
cristyacd2ed22011-08-30 01:44:23 +0000296 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000297 break;
cristybb503372010-05-27 20:51:26 +0000298 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000299 {
300 switch (fourier_info->channel)
301 {
cristyd3090f92011-10-18 00:05:15 +0000302 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000303 default:
304 {
cristy4c08aed2011-07-01 19:47:50 +0000305 SetPixelRed(magnitude_image,ClampToQuantum(QuantumRange*
306 magnitude_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000307 break;
308 }
cristyd3090f92011-10-18 00:05:15 +0000309 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000310 {
cristy4c08aed2011-07-01 19:47:50 +0000311 SetPixelGreen(magnitude_image,ClampToQuantum(QuantumRange*
312 magnitude_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000313 break;
314 }
cristyd3090f92011-10-18 00:05:15 +0000315 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000316 {
cristy4c08aed2011-07-01 19:47:50 +0000317 SetPixelBlue(magnitude_image,ClampToQuantum(QuantumRange*
318 magnitude_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000319 break;
320 }
cristyd3090f92011-10-18 00:05:15 +0000321 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000322 {
cristy4c08aed2011-07-01 19:47:50 +0000323 SetPixelBlack(magnitude_image,ClampToQuantum(QuantumRange*
324 magnitude_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000325 break;
326 }
cristyd3090f92011-10-18 00:05:15 +0000327 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000328 {
cristy4c08aed2011-07-01 19:47:50 +0000329 SetPixelAlpha(magnitude_image,ClampToQuantum(QuantumRange*
330 magnitude_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000331 break;
332 }
cristy3ed852e2009-09-05 21:47:34 +0000333 }
334 i++;
cristyed231572011-07-14 02:18:59 +0000335 q+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +0000336 }
337 status=SyncCacheViewAuthenticPixels(magnitude_view,exception);
338 if (status == MagickFalse)
339 break;
340 }
cristydb070952012-04-20 14:33:00 +0000341 magnitude_view=DestroyCacheView(magnitude_view);
cristy3ed852e2009-09-05 21:47:34 +0000342 i=0L;
cristydb070952012-04-20 14:33:00 +0000343 phase_view=AcquireAuthenticCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +0000344 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000345 {
346 q=GetCacheViewAuthenticPixels(phase_view,0L,y,fourier_info->height,1UL,
347 exception);
cristyacd2ed22011-08-30 01:44:23 +0000348 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000349 break;
cristybb503372010-05-27 20:51:26 +0000350 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000351 {
352 switch (fourier_info->channel)
353 {
cristyd3090f92011-10-18 00:05:15 +0000354 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000355 default:
356 {
cristy4c08aed2011-07-01 19:47:50 +0000357 SetPixelRed(phase_image,ClampToQuantum(QuantumRange*
358 phase_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000359 break;
360 }
cristyd3090f92011-10-18 00:05:15 +0000361 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000362 {
cristy4c08aed2011-07-01 19:47:50 +0000363 SetPixelGreen(phase_image,ClampToQuantum(QuantumRange*
364 phase_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000365 break;
366 }
cristyd3090f92011-10-18 00:05:15 +0000367 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000368 {
cristy4c08aed2011-07-01 19:47:50 +0000369 SetPixelBlue(phase_image,ClampToQuantum(QuantumRange*
370 phase_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000371 break;
372 }
cristyd3090f92011-10-18 00:05:15 +0000373 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000374 {
cristy4c08aed2011-07-01 19:47:50 +0000375 SetPixelBlack(phase_image,ClampToQuantum(QuantumRange*
376 phase_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000377 break;
378 }
cristyd3090f92011-10-18 00:05:15 +0000379 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000380 {
cristy4c08aed2011-07-01 19:47:50 +0000381 SetPixelAlpha(phase_image,ClampToQuantum(QuantumRange*
382 phase_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000383 break;
384 }
cristy3ed852e2009-09-05 21:47:34 +0000385 }
386 i++;
cristyed231572011-07-14 02:18:59 +0000387 q+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +0000388 }
389 status=SyncCacheViewAuthenticPixels(phase_view,exception);
390 if (status == MagickFalse)
391 break;
392 }
393 phase_view=DestroyCacheView(phase_view);
cristy3ed852e2009-09-05 21:47:34 +0000394 phase_source=(double *) RelinquishMagickMemory(phase_source);
395 magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
396 return(status);
397}
398
399static MagickBooleanType ForwardFourierTransform(FourierInfo *fourier_info,
400 const Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
401{
402 CacheView
403 *image_view;
404
405 double
406 n,
407 *source;
408
409 fftw_complex
410 *fourier;
411
412 fftw_plan
413 fftw_r2c_plan;
414
cristy4c08aed2011-07-01 19:47:50 +0000415 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000416 *p;
417
cristybb503372010-05-27 20:51:26 +0000418 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000419 i,
420 x;
421
cristyc4ea4a42011-01-24 01:43:30 +0000422 ssize_t
423 y;
424
cristy3ed852e2009-09-05 21:47:34 +0000425 /*
426 Generate the forward Fourier transform.
427 */
428 source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
429 fourier_info->width*sizeof(*source));
430 if (source == (double *) NULL)
431 {
432 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000433 ResourceLimitError,"MemoryAllocationFailed","'%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000434 return(MagickFalse);
435 }
cristyc4ea4a42011-01-24 01:43:30 +0000436 ResetMagickMemory(source,0,fourier_info->height*fourier_info->width*
cristy3ed852e2009-09-05 21:47:34 +0000437 sizeof(*source));
438 i=0L;
cristydb070952012-04-20 14:33:00 +0000439 image_view=AcquireVirtualCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +0000440 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000441 {
442 p=GetCacheViewVirtualPixels(image_view,0L,y,fourier_info->width,1UL,
443 exception);
cristy4c08aed2011-07-01 19:47:50 +0000444 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000445 break;
cristybb503372010-05-27 20:51:26 +0000446 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000447 {
448 switch (fourier_info->channel)
449 {
cristyd3090f92011-10-18 00:05:15 +0000450 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000451 default:
452 {
cristy4c08aed2011-07-01 19:47:50 +0000453 source[i]=QuantumScale*GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000454 break;
455 }
cristyd3090f92011-10-18 00:05:15 +0000456 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000457 {
cristy4c08aed2011-07-01 19:47:50 +0000458 source[i]=QuantumScale*GetPixelGreen(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000459 break;
460 }
cristyd3090f92011-10-18 00:05:15 +0000461 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000462 {
cristy4c08aed2011-07-01 19:47:50 +0000463 source[i]=QuantumScale*GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000464 break;
465 }
cristyd3090f92011-10-18 00:05:15 +0000466 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000467 {
cristy4c08aed2011-07-01 19:47:50 +0000468 source[i]=QuantumScale*GetPixelBlack(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000469 break;
470 }
cristyd3090f92011-10-18 00:05:15 +0000471 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000472 {
cristy4c08aed2011-07-01 19:47:50 +0000473 source[i]=QuantumScale*GetPixelAlpha(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000474 break;
475 }
cristy3ed852e2009-09-05 21:47:34 +0000476 }
477 i++;
cristyed231572011-07-14 02:18:59 +0000478 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000479 }
480 }
481 image_view=DestroyCacheView(image_view);
cristyb41ee102010-10-04 16:46:15 +0000482 fourier=(fftw_complex *) AcquireQuantumMemory((size_t) fourier_info->height,
cristy3ed852e2009-09-05 21:47:34 +0000483 fourier_info->center*sizeof(*fourier));
484 if (fourier == (fftw_complex *) NULL)
485 {
486 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000487 ResourceLimitError,"MemoryAllocationFailed","'%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000488 source=(double *) RelinquishMagickMemory(source);
489 return(MagickFalse);
490 }
cristyb5d5f722009-11-04 03:03:49 +0000491#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000492 #pragma omp critical (MagickCore_ForwardFourierTransform)
493#endif
494 fftw_r2c_plan=fftw_plan_dft_r2c_2d(fourier_info->width,fourier_info->width,
495 source,fourier,FFTW_ESTIMATE);
496 fftw_execute(fftw_r2c_plan);
497 fftw_destroy_plan(fftw_r2c_plan);
498 source=(double *) RelinquishMagickMemory(source);
499 /*
500 Normalize Fourier transform.
501 */
502 n=(double) fourier_info->width*(double) fourier_info->width;
503 i=0L;
cristybb503372010-05-27 20:51:26 +0000504 for (y=0L; y < (ssize_t) fourier_info->height; y++)
505 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy56ed31c2010-03-22 00:46:21 +0000506 {
507#if defined(MAGICKCORE_HAVE_COMPLEX_H)
508 fourier[i]/=n;
509#else
510 fourier[i][0]/=n;
511 fourier[i][1]/=n;
512#endif
513 i++;
514 }
cristy3ed852e2009-09-05 21:47:34 +0000515 /*
516 Generate magnitude and phase (or real and imaginary).
517 */
518 i=0L;
519 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000520 for (y=0L; y < (ssize_t) fourier_info->height; y++)
521 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000522 {
523 magnitude[i]=cabs(fourier[i]);
524 phase[i]=carg(fourier[i]);
525 i++;
526 }
527 else
cristybb503372010-05-27 20:51:26 +0000528 for (y=0L; y < (ssize_t) fourier_info->height; y++)
529 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000530 {
531 magnitude[i]=creal(fourier[i]);
532 phase[i]=cimag(fourier[i]);
533 i++;
534 }
cristyb41ee102010-10-04 16:46:15 +0000535 fourier=(fftw_complex *) RelinquishMagickMemory(fourier);
cristy3ed852e2009-09-05 21:47:34 +0000536 return(MagickTrue);
537}
538
539static MagickBooleanType ForwardFourierTransformChannel(const Image *image,
cristyd3090f92011-10-18 00:05:15 +0000540 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +0000541 Image *fourier_image,ExceptionInfo *exception)
542{
543 double
544 *magnitude,
545 *phase;
546
547 fftw_complex
548 *fourier;
549
cristy56ed31c2010-03-22 00:46:21 +0000550 FourierInfo
551 fourier_info;
552
cristyc4ea4a42011-01-24 01:43:30 +0000553 MagickBooleanType
554 status;
555
cristy3ed852e2009-09-05 21:47:34 +0000556 size_t
557 extent;
558
559 fourier_info.width=image->columns;
560 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
561 ((image->rows % 2) != 0))
562 {
563 extent=image->columns < image->rows ? image->rows : image->columns;
564 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
565 }
566 fourier_info.height=fourier_info.width;
cristybb503372010-05-27 20:51:26 +0000567 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2.0)+1L;
cristy3ed852e2009-09-05 21:47:34 +0000568 fourier_info.channel=channel;
569 fourier_info.modulus=modulus;
570 magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
571 fourier_info.center*sizeof(*magnitude));
572 if (magnitude == (double *) NULL)
573 {
574 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000575 ResourceLimitError,"MemoryAllocationFailed","'%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000576 return(MagickFalse);
577 }
578 phase=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
579 fourier_info.center*sizeof(*phase));
580 if (phase == (double *) NULL)
581 {
582 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000583 ResourceLimitError,"MemoryAllocationFailed","'%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000584 magnitude=(double *) RelinquishMagickMemory(magnitude);
585 return(MagickFalse);
586 }
cristyb41ee102010-10-04 16:46:15 +0000587 fourier=(fftw_complex *) AcquireQuantumMemory((size_t) fourier_info.height,
cristy3ed852e2009-09-05 21:47:34 +0000588 fourier_info.center*sizeof(*fourier));
589 if (fourier == (fftw_complex *) NULL)
590 {
591 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000592 ResourceLimitError,"MemoryAllocationFailed","'%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000593 phase=(double *) RelinquishMagickMemory(phase);
594 magnitude=(double *) RelinquishMagickMemory(magnitude);
595 return(MagickFalse);
596 }
597 status=ForwardFourierTransform(&fourier_info,image,magnitude,phase,exception);
598 if (status != MagickFalse)
599 status=ForwardFourier(&fourier_info,fourier_image,magnitude,phase,
600 exception);
cristyb41ee102010-10-04 16:46:15 +0000601 fourier=(fftw_complex *) RelinquishMagickMemory(fourier);
cristy3ed852e2009-09-05 21:47:34 +0000602 phase=(double *) RelinquishMagickMemory(phase);
603 magnitude=(double *) RelinquishMagickMemory(magnitude);
604 return(status);
605}
606#endif
607
608MagickExport Image *ForwardFourierTransformImage(const Image *image,
609 const MagickBooleanType modulus,ExceptionInfo *exception)
610{
611 Image
612 *fourier_image;
613
614 fourier_image=NewImageList();
615#if !defined(MAGICKCORE_FFTW_DELEGATE)
616 (void) modulus;
617 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000618 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristy3ed852e2009-09-05 21:47:34 +0000619 image->filename);
620#else
621 {
622 Image
623 *magnitude_image;
624
cristybb503372010-05-27 20:51:26 +0000625 size_t
cristy3ed852e2009-09-05 21:47:34 +0000626 extent,
627 width;
628
629 width=image->columns;
630 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
631 ((image->rows % 2) != 0))
632 {
633 extent=image->columns < image->rows ? image->rows : image->columns;
634 width=(extent & 0x01) == 1 ? extent+1UL : extent;
635 }
636 magnitude_image=CloneImage(image,width,width,MagickFalse,exception);
637 if (magnitude_image != (Image *) NULL)
638 {
639 Image
640 *phase_image;
641
642 magnitude_image->storage_class=DirectClass;
643 magnitude_image->depth=32UL;
644 phase_image=CloneImage(image,width,width,MagickFalse,exception);
645 if (phase_image == (Image *) NULL)
646 magnitude_image=DestroyImage(magnitude_image);
647 else
648 {
649 MagickBooleanType
650 is_gray,
651 status;
652
cristy3ed852e2009-09-05 21:47:34 +0000653 phase_image->storage_class=DirectClass;
654 phase_image->depth=32UL;
655 AppendImageToList(&fourier_image,magnitude_image);
656 AppendImageToList(&fourier_image,phase_image);
657 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000658 is_gray=IsImageGray(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000659#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy564a5692012-01-20 23:56:26 +0000660 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +0000661#endif
cristy3ed852e2009-09-05 21:47:34 +0000662 {
cristyb34ef052010-10-07 00:12:05 +0000663#if defined(MAGICKCORE_OPENMP_SUPPORT)
664 #pragma omp section
665#endif
cristy3ed852e2009-09-05 21:47:34 +0000666 {
cristyb34ef052010-10-07 00:12:05 +0000667 MagickBooleanType
668 thread_status;
669
670 if (is_gray != MagickFalse)
671 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000672 GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000673 else
674 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000675 RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000676 if (thread_status == MagickFalse)
677 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +0000678 }
cristyb34ef052010-10-07 00:12:05 +0000679#if defined(MAGICKCORE_OPENMP_SUPPORT)
680 #pragma omp section
681#endif
682 {
683 MagickBooleanType
684 thread_status;
685
686 thread_status=MagickTrue;
687 if (is_gray == MagickFalse)
688 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000689 GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000690 if (thread_status == MagickFalse)
691 status=thread_status;
692 }
693#if defined(MAGICKCORE_OPENMP_SUPPORT)
694 #pragma omp section
695#endif
696 {
697 MagickBooleanType
698 thread_status;
699
700 thread_status=MagickTrue;
701 if (is_gray == MagickFalse)
702 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000703 BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000704 if (thread_status == MagickFalse)
705 status=thread_status;
706 }
707#if defined(MAGICKCORE_OPENMP_SUPPORT)
708 #pragma omp section
709#endif
710 {
711 MagickBooleanType
712 thread_status;
713
714 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000715 if (image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +0000716 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000717 BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000718 if (thread_status == MagickFalse)
719 status=thread_status;
720 }
721#if defined(MAGICKCORE_OPENMP_SUPPORT)
722 #pragma omp section
723#endif
724 {
725 MagickBooleanType
726 thread_status;
727
728 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000729 if (image->matte != MagickFalse)
cristyb34ef052010-10-07 00:12:05 +0000730 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000731 AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000732 if (thread_status == MagickFalse)
733 status=thread_status;
734 }
cristy3ed852e2009-09-05 21:47:34 +0000735 }
736 if (status == MagickFalse)
737 fourier_image=DestroyImageList(fourier_image);
738 fftw_cleanup();
739 }
740 }
741 }
742#endif
743 return(fourier_image);
744}
745
746/*
747%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
748% %
749% %
750% %
751% 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 %
752% %
753% %
754% %
755%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
756%
757% InverseFourierTransformImage() implements the inverse discrete Fourier
758% transform (DFT) of the image either as a magnitude / phase or real /
759% imaginary image pair.
760%
761% The format of the InverseFourierTransformImage method is:
762%
cristyc9550792009-11-13 20:05:42 +0000763% Image *InverseFourierTransformImage(const Image *magnitude_image,
764% const Image *phase_image,const MagickBooleanType modulus,
765% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000766%
767% A description of each parameter follows:
768%
cristyc9550792009-11-13 20:05:42 +0000769% o magnitude_image: the magnitude or real image.
770%
771% o phase_image: the phase or imaginary image.
cristy3ed852e2009-09-05 21:47:34 +0000772%
773% o modulus: if true, return transform as a magnitude / phase pair
774% otherwise a real / imaginary image pair.
775%
776% o exception: return any errors or warnings in this structure.
777%
778*/
779
780#if defined(MAGICKCORE_FFTW_DELEGATE)
cristybb503372010-05-27 20:51:26 +0000781static MagickBooleanType InverseQuadrantSwap(const size_t width,
782 const size_t height,const double *source,double *destination)
cristy3ed852e2009-09-05 21:47:34 +0000783{
cristyc4ea4a42011-01-24 01:43:30 +0000784 register ssize_t
785 x;
786
cristybb503372010-05-27 20:51:26 +0000787 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000788 center,
789 y;
790
cristy3ed852e2009-09-05 21:47:34 +0000791 /*
792 Swap quadrants.
793 */
cristybb503372010-05-27 20:51:26 +0000794 center=(ssize_t) floor((double) width/2.0)+1L;
795 for (y=1L; y < (ssize_t) height; y++)
796 for (x=0L; x < (ssize_t) (width/2L+1L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000797 destination[center*(height-y)-x+width/2L]=source[y*width+x];
cristybb503372010-05-27 20:51:26 +0000798 for (y=0L; y < (ssize_t) height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000799 destination[center*y]=source[y*width+width/2L];
800 for (x=0L; x < center; x++)
801 destination[x]=source[center-x-1L];
cristybb503372010-05-27 20:51:26 +0000802 return(RollFourier(center,height,0L,(ssize_t) height/-2L,destination));
cristy3ed852e2009-09-05 21:47:34 +0000803}
804
805static MagickBooleanType InverseFourier(FourierInfo *fourier_info,
cristyc9550792009-11-13 20:05:42 +0000806 const Image *magnitude_image,const Image *phase_image,fftw_complex *fourier,
807 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000808{
809 CacheView
810 *magnitude_view,
811 *phase_view;
812
813 double
814 *magnitude,
815 *phase,
816 *magnitude_source,
817 *phase_source;
818
cristy3ed852e2009-09-05 21:47:34 +0000819 MagickBooleanType
820 status;
821
cristy4c08aed2011-07-01 19:47:50 +0000822 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000823 *p;
824
cristybb503372010-05-27 20:51:26 +0000825 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000826 i,
827 x;
828
cristyc4ea4a42011-01-24 01:43:30 +0000829 ssize_t
830 y;
831
cristy3ed852e2009-09-05 21:47:34 +0000832 /*
833 Inverse fourier - read image and break down into a double array.
834 */
cristy3ed852e2009-09-05 21:47:34 +0000835 magnitude_source=(double *) AcquireQuantumMemory((size_t)
836 fourier_info->height,fourier_info->width*sizeof(*magnitude_source));
837 if (magnitude_source == (double *) NULL)
838 {
839 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000840 ResourceLimitError,"MemoryAllocationFailed","'%s'",
cristyc9550792009-11-13 20:05:42 +0000841 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000842 return(MagickFalse);
843 }
844 phase_source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
cristyc4ea4a42011-01-24 01:43:30 +0000845 fourier_info->width*sizeof(*phase_source));
cristy3ed852e2009-09-05 21:47:34 +0000846 if (phase_source == (double *) NULL)
847 {
848 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000849 ResourceLimitError,"MemoryAllocationFailed","'%s'",
cristyc9550792009-11-13 20:05:42 +0000850 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000851 magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
852 return(MagickFalse);
853 }
854 i=0L;
cristydb070952012-04-20 14:33:00 +0000855 magnitude_view=AcquireVirtualCacheView(magnitude_image,exception);
cristybb503372010-05-27 20:51:26 +0000856 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000857 {
858 p=GetCacheViewVirtualPixels(magnitude_view,0L,y,fourier_info->width,1UL,
859 exception);
cristy4c08aed2011-07-01 19:47:50 +0000860 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000861 break;
cristybb503372010-05-27 20:51:26 +0000862 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000863 {
864 switch (fourier_info->channel)
865 {
cristyd3090f92011-10-18 00:05:15 +0000866 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000867 default:
868 {
cristy4c08aed2011-07-01 19:47:50 +0000869 magnitude_source[i]=QuantumScale*GetPixelRed(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000870 break;
871 }
cristyd3090f92011-10-18 00:05:15 +0000872 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000873 {
cristy4c08aed2011-07-01 19:47:50 +0000874 magnitude_source[i]=QuantumScale*GetPixelGreen(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000875 break;
876 }
cristyd3090f92011-10-18 00:05:15 +0000877 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000878 {
cristy4c08aed2011-07-01 19:47:50 +0000879 magnitude_source[i]=QuantumScale*GetPixelBlue(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000880 break;
881 }
cristyd3090f92011-10-18 00:05:15 +0000882 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000883 {
cristy4c08aed2011-07-01 19:47:50 +0000884 magnitude_source[i]=QuantumScale*GetPixelBlack(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000885 break;
886 }
cristyd3090f92011-10-18 00:05:15 +0000887 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000888 {
cristy4c08aed2011-07-01 19:47:50 +0000889 magnitude_source[i]=QuantumScale*GetPixelAlpha(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000890 break;
891 }
cristy3ed852e2009-09-05 21:47:34 +0000892 }
893 i++;
cristyed231572011-07-14 02:18:59 +0000894 p+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +0000895 }
896 }
897 i=0L;
cristydb070952012-04-20 14:33:00 +0000898 phase_view=AcquireVirtualCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +0000899 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000900 {
901 p=GetCacheViewVirtualPixels(phase_view,0,y,fourier_info->width,1,
902 exception);
cristy4c08aed2011-07-01 19:47:50 +0000903 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000904 break;
cristybb503372010-05-27 20:51:26 +0000905 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000906 {
907 switch (fourier_info->channel)
908 {
cristyd3090f92011-10-18 00:05:15 +0000909 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000910 default:
911 {
cristy4c08aed2011-07-01 19:47:50 +0000912 phase_source[i]=QuantumScale*GetPixelRed(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000913 break;
914 }
cristyd3090f92011-10-18 00:05:15 +0000915 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000916 {
cristy4c08aed2011-07-01 19:47:50 +0000917 phase_source[i]=QuantumScale*GetPixelGreen(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000918 break;
919 }
cristyd3090f92011-10-18 00:05:15 +0000920 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000921 {
cristy4c08aed2011-07-01 19:47:50 +0000922 phase_source[i]=QuantumScale*GetPixelBlue(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000923 break;
924 }
cristyd3090f92011-10-18 00:05:15 +0000925 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000926 {
cristy4c08aed2011-07-01 19:47:50 +0000927 phase_source[i]=QuantumScale*GetPixelBlack(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000928 break;
929 }
cristyd3090f92011-10-18 00:05:15 +0000930 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000931 {
cristy4c08aed2011-07-01 19:47:50 +0000932 phase_source[i]=QuantumScale*GetPixelAlpha(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000933 break;
934 }
cristy3ed852e2009-09-05 21:47:34 +0000935 }
936 i++;
cristyed231572011-07-14 02:18:59 +0000937 p+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +0000938 }
939 }
940 if (fourier_info->modulus != MagickFalse)
941 {
942 i=0L;
cristybb503372010-05-27 20:51:26 +0000943 for (y=0L; y < (ssize_t) fourier_info->height; y++)
944 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000945 {
946 phase_source[i]-=0.5;
947 phase_source[i]*=(2.0*MagickPI);
948 i++;
949 }
950 }
951 magnitude_view=DestroyCacheView(magnitude_view);
952 phase_view=DestroyCacheView(phase_view);
953 magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
954 fourier_info->center*sizeof(*magnitude));
955 if (magnitude == (double *) NULL)
956 {
957 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000958 ResourceLimitError,"MemoryAllocationFailed","'%s'",
cristyc9550792009-11-13 20:05:42 +0000959 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000960 magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
961 phase_source=(double *) RelinquishMagickMemory(phase_source);
962 return(MagickFalse);
963 }
964 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
965 magnitude_source,magnitude);
966 magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
cristyc4ea4a42011-01-24 01:43:30 +0000967 phase=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
968 fourier_info->width*sizeof(*phase));
cristy3ed852e2009-09-05 21:47:34 +0000969 if (phase == (double *) NULL)
970 {
971 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000972 ResourceLimitError,"MemoryAllocationFailed","'%s'",
cristyc9550792009-11-13 20:05:42 +0000973 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000974 phase_source=(double *) RelinquishMagickMemory(phase_source);
975 return(MagickFalse);
976 }
977 CorrectPhaseLHS(fourier_info->width,fourier_info->width,phase_source);
978 if (status != MagickFalse)
979 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
980 phase_source,phase);
981 phase_source=(double *) RelinquishMagickMemory(phase_source);
982 /*
983 Merge two sets.
984 */
985 i=0L;
986 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000987 for (y=0L; y < (ssize_t) fourier_info->height; y++)
988 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000989 {
cristy56ed31c2010-03-22 00:46:21 +0000990#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy3ed852e2009-09-05 21:47:34 +0000991 fourier[i]=magnitude[i]*cos(phase[i])+I*magnitude[i]*sin(phase[i]);
cristy56ed31c2010-03-22 00:46:21 +0000992#else
993 fourier[i][0]=magnitude[i]*cos(phase[i]);
994 fourier[i][1]=magnitude[i]*sin(phase[i]);
995#endif
cristy3ed852e2009-09-05 21:47:34 +0000996 i++;
997 }
998 else
cristybb503372010-05-27 20:51:26 +0000999 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1000 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001001 {
cristy56ed31c2010-03-22 00:46:21 +00001002#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy3ed852e2009-09-05 21:47:34 +00001003 fourier[i]=magnitude[i]+I*phase[i];
cristy56ed31c2010-03-22 00:46:21 +00001004#else
1005 fourier[i][0]=magnitude[i];
1006 fourier[i][1]=phase[i];
1007#endif
cristy3ed852e2009-09-05 21:47:34 +00001008 i++;
1009 }
1010 phase=(double *) RelinquishMagickMemory(phase);
1011 magnitude=(double *) RelinquishMagickMemory(magnitude);
1012 return(status);
1013}
1014
1015static MagickBooleanType InverseFourierTransform(FourierInfo *fourier_info,
1016 fftw_complex *fourier,Image *image,ExceptionInfo *exception)
1017{
1018 CacheView
1019 *image_view;
1020
1021 double
1022 *source;
1023
1024 fftw_plan
1025 fftw_c2r_plan;
1026
cristy4c08aed2011-07-01 19:47:50 +00001027 register Quantum
cristyc4ea4a42011-01-24 01:43:30 +00001028 *q;
1029
cristybb503372010-05-27 20:51:26 +00001030 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001031 i,
1032 x;
1033
cristyc4ea4a42011-01-24 01:43:30 +00001034 ssize_t
1035 y;
cristy3ed852e2009-09-05 21:47:34 +00001036
cristyc4ea4a42011-01-24 01:43:30 +00001037 source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
1038 fourier_info->width*sizeof(*source));
cristy3ed852e2009-09-05 21:47:34 +00001039 if (source == (double *) NULL)
1040 {
1041 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00001042 ResourceLimitError,"MemoryAllocationFailed","'%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001043 return(MagickFalse);
1044 }
cristyb5d5f722009-11-04 03:03:49 +00001045#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001046 #pragma omp critical (MagickCore_InverseFourierTransform)
1047#endif
cristydf079ac2010-09-10 01:45:44 +00001048 {
1049 fftw_c2r_plan=fftw_plan_dft_c2r_2d(fourier_info->width,fourier_info->height,
1050 fourier,source,FFTW_ESTIMATE);
1051 fftw_execute(fftw_c2r_plan);
1052 fftw_destroy_plan(fftw_c2r_plan);
1053 }
cristy3ed852e2009-09-05 21:47:34 +00001054 i=0L;
cristydb070952012-04-20 14:33:00 +00001055 image_view=AcquireAuthenticCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +00001056 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001057 {
cristy85812052010-09-14 17:56:15 +00001058 if (y >= (ssize_t) image->rows)
1059 break;
1060 q=GetCacheViewAuthenticPixels(image_view,0L,y,fourier_info->width >
1061 image->columns ? image->columns : fourier_info->width,1UL,exception);
cristyacd2ed22011-08-30 01:44:23 +00001062 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001063 break;
cristybb503372010-05-27 20:51:26 +00001064 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001065 {
1066 switch (fourier_info->channel)
1067 {
cristyd3090f92011-10-18 00:05:15 +00001068 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001069 default:
1070 {
cristy4c08aed2011-07-01 19:47:50 +00001071 SetPixelRed(image,ClampToQuantum(QuantumRange*source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +00001072 break;
1073 }
cristyd3090f92011-10-18 00:05:15 +00001074 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001075 {
cristy4c08aed2011-07-01 19:47:50 +00001076 SetPixelGreen(image,ClampToQuantum(QuantumRange*source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +00001077 break;
1078 }
cristyd3090f92011-10-18 00:05:15 +00001079 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001080 {
cristy4c08aed2011-07-01 19:47:50 +00001081 SetPixelBlue(image,ClampToQuantum(QuantumRange*source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +00001082 break;
1083 }
cristyd3090f92011-10-18 00:05:15 +00001084 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001085 {
cristy4c08aed2011-07-01 19:47:50 +00001086 SetPixelBlack(image,ClampToQuantum(QuantumRange*source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +00001087 break;
1088 }
cristyd3090f92011-10-18 00:05:15 +00001089 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001090 {
cristy4c08aed2011-07-01 19:47:50 +00001091 SetPixelAlpha(image,ClampToQuantum(QuantumRange*source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +00001092 break;
1093 }
cristy3ed852e2009-09-05 21:47:34 +00001094 }
1095 i++;
cristyed231572011-07-14 02:18:59 +00001096 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001097 }
1098 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1099 break;
1100 }
1101 image_view=DestroyCacheView(image_view);
1102 source=(double *) RelinquishMagickMemory(source);
1103 return(MagickTrue);
1104}
1105
cristyc9550792009-11-13 20:05:42 +00001106static MagickBooleanType InverseFourierTransformChannel(
1107 const Image *magnitude_image,const Image *phase_image,
cristyd3090f92011-10-18 00:05:15 +00001108 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +00001109 Image *fourier_image,ExceptionInfo *exception)
1110{
1111 double
1112 *magnitude,
1113 *phase;
1114
1115 fftw_complex
1116 *fourier;
1117
1118 FourierInfo
1119 fourier_info;
1120
1121 MagickBooleanType
1122 status;
1123
1124 size_t
1125 extent;
1126
cristyc9550792009-11-13 20:05:42 +00001127 fourier_info.width=magnitude_image->columns;
1128 if ((magnitude_image->columns != magnitude_image->rows) ||
1129 ((magnitude_image->columns % 2) != 0) ||
1130 ((magnitude_image->rows % 2) != 0))
cristy3ed852e2009-09-05 21:47:34 +00001131 {
cristyc9550792009-11-13 20:05:42 +00001132 extent=magnitude_image->columns < magnitude_image->rows ?
1133 magnitude_image->rows : magnitude_image->columns;
cristy3ed852e2009-09-05 21:47:34 +00001134 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
1135 }
1136 fourier_info.height=fourier_info.width;
cristybb503372010-05-27 20:51:26 +00001137 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2.0)+1L;
cristy3ed852e2009-09-05 21:47:34 +00001138 fourier_info.channel=channel;
1139 fourier_info.modulus=modulus;
1140 magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
cristyc4ea4a42011-01-24 01:43:30 +00001141 fourier_info.center*sizeof(*magnitude));
cristy3ed852e2009-09-05 21:47:34 +00001142 if (magnitude == (double *) NULL)
1143 {
1144 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00001145 ResourceLimitError,"MemoryAllocationFailed","'%s'",
cristyc9550792009-11-13 20:05:42 +00001146 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001147 return(MagickFalse);
1148 }
1149 phase=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
cristyc4ea4a42011-01-24 01:43:30 +00001150 fourier_info.center*sizeof(*phase));
cristy3ed852e2009-09-05 21:47:34 +00001151 if (phase == (double *) NULL)
1152 {
1153 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00001154 ResourceLimitError,"MemoryAllocationFailed","'%s'",
cristyc9550792009-11-13 20:05:42 +00001155 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001156 magnitude=(double *) RelinquishMagickMemory(magnitude);
1157 return(MagickFalse);
1158 }
cristyb41ee102010-10-04 16:46:15 +00001159 fourier=(fftw_complex *) AcquireQuantumMemory((size_t) fourier_info.height,
cristy3ed852e2009-09-05 21:47:34 +00001160 fourier_info.center*sizeof(*fourier));
1161 if (fourier == (fftw_complex *) NULL)
1162 {
1163 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00001164 ResourceLimitError,"MemoryAllocationFailed","'%s'",
cristyc9550792009-11-13 20:05:42 +00001165 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001166 phase=(double *) RelinquishMagickMemory(phase);
1167 magnitude=(double *) RelinquishMagickMemory(magnitude);
1168 return(MagickFalse);
1169 }
cristyc9550792009-11-13 20:05:42 +00001170 status=InverseFourier(&fourier_info,magnitude_image,phase_image,fourier,
1171 exception);
cristy3ed852e2009-09-05 21:47:34 +00001172 if (status != MagickFalse)
1173 status=InverseFourierTransform(&fourier_info,fourier,fourier_image,
1174 exception);
cristyb41ee102010-10-04 16:46:15 +00001175 fourier=(fftw_complex *) RelinquishMagickMemory(fourier);
cristy3ed852e2009-09-05 21:47:34 +00001176 phase=(double *) RelinquishMagickMemory(phase);
1177 magnitude=(double *) RelinquishMagickMemory(magnitude);
1178 return(status);
1179}
1180#endif
1181
cristyc9550792009-11-13 20:05:42 +00001182MagickExport Image *InverseFourierTransformImage(const Image *magnitude_image,
1183 const Image *phase_image,const MagickBooleanType modulus,
1184 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001185{
1186 Image
1187 *fourier_image;
1188
cristyc9550792009-11-13 20:05:42 +00001189 assert(magnitude_image != (Image *) NULL);
1190 assert(magnitude_image->signature == MagickSignature);
1191 if (magnitude_image->debug != MagickFalse)
1192 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1193 magnitude_image->filename);
1194 if (phase_image == (Image *) NULL)
1195 {
1196 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
anthonye5b39652012-04-21 05:37:29 +00001197 "TwoOrMoreImagesRequired","'%s'",magnitude_image->filename);
cristy9372a152009-11-18 01:42:16 +00001198 return((Image *) NULL);
cristyc9550792009-11-13 20:05:42 +00001199 }
cristy3ed852e2009-09-05 21:47:34 +00001200#if !defined(MAGICKCORE_FFTW_DELEGATE)
1201 fourier_image=(Image *) NULL;
1202 (void) modulus;
1203 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00001204 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristyc9550792009-11-13 20:05:42 +00001205 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001206#else
1207 {
cristyc9550792009-11-13 20:05:42 +00001208 fourier_image=CloneImage(magnitude_image,magnitude_image->columns,
1209 magnitude_image->rows,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00001210 if (fourier_image != (Image *) NULL)
1211 {
1212 MagickBooleanType
1213 is_gray,
1214 status;
1215
cristy3ed852e2009-09-05 21:47:34 +00001216 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001217 is_gray=IsImageGray(magnitude_image,exception);
cristyc9550792009-11-13 20:05:42 +00001218 if (is_gray != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001219 is_gray=IsImageGray(phase_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001220#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb34ef052010-10-07 00:12:05 +00001221 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +00001222#endif
cristy3ed852e2009-09-05 21:47:34 +00001223 {
cristyb34ef052010-10-07 00:12:05 +00001224#if defined(MAGICKCORE_OPENMP_SUPPORT)
1225 #pragma omp section
1226#endif
cristy3ed852e2009-09-05 21:47:34 +00001227 {
cristyb34ef052010-10-07 00:12:05 +00001228 MagickBooleanType
1229 thread_status;
1230
1231 if (is_gray != MagickFalse)
1232 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001233 phase_image,GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001234 else
cristyc9550792009-11-13 20:05:42 +00001235 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001236 phase_image,RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001237 if (thread_status == MagickFalse)
1238 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +00001239 }
cristyb34ef052010-10-07 00:12:05 +00001240#if defined(MAGICKCORE_OPENMP_SUPPORT)
1241 #pragma omp section
1242#endif
1243 {
1244 MagickBooleanType
1245 thread_status;
1246
1247 thread_status=MagickTrue;
1248 if (is_gray == MagickFalse)
1249 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001250 phase_image,GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001251 if (thread_status == MagickFalse)
1252 status=thread_status;
1253 }
1254#if defined(MAGICKCORE_OPENMP_SUPPORT)
1255 #pragma omp section
1256#endif
1257 {
1258 MagickBooleanType
1259 thread_status;
1260
1261 thread_status=MagickTrue;
1262 if (is_gray == MagickFalse)
1263 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001264 phase_image,BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001265 if (thread_status == MagickFalse)
1266 status=thread_status;
1267 }
1268#if defined(MAGICKCORE_OPENMP_SUPPORT)
1269 #pragma omp section
1270#endif
1271 {
1272 MagickBooleanType
1273 thread_status;
1274
1275 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001276 if (magnitude_image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +00001277 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001278 phase_image,BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001279 if (thread_status == MagickFalse)
1280 status=thread_status;
1281 }
1282#if defined(MAGICKCORE_OPENMP_SUPPORT)
1283 #pragma omp section
1284#endif
1285 {
1286 MagickBooleanType
1287 thread_status;
1288
1289 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001290 if (magnitude_image->matte != MagickFalse)
cristyb34ef052010-10-07 00:12:05 +00001291 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001292 phase_image,AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001293 if (thread_status == MagickFalse)
1294 status=thread_status;
1295 }
cristy3ed852e2009-09-05 21:47:34 +00001296 }
1297 if (status == MagickFalse)
1298 fourier_image=DestroyImage(fourier_image);
1299 }
cristyb34ef052010-10-07 00:12:05 +00001300 fftw_cleanup();
cristy3ed852e2009-09-05 21:47:34 +00001301 }
1302#endif
1303 return(fourier_image);
1304}