blob: 41442ce718ee3afe83d382556fe717a8bc671c1b [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"
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"
cristyac245f82012-05-05 17:13:57 +000058#include "MagickCore/resource_.h"
cristy4c08aed2011-07-01 19:47:50 +000059#include "MagickCore/thread-private.h"
cristy3ed852e2009-09-05 21:47:34 +000060#if defined(MAGICKCORE_FFTW_DELEGATE)
cristy56ed31c2010-03-22 00:46:21 +000061#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy3ed852e2009-09-05 21:47:34 +000062#include <complex.h>
cristy56ed31c2010-03-22 00:46:21 +000063#endif
cristy3ed852e2009-09-05 21:47:34 +000064#include <fftw3.h>
cristy47b022b2011-01-18 22:29:21 +000065#if !defined(MAGICKCORE_HAVE_CABS)
66#define cabs(z) (sqrt(z[0]*z[0]+z[1]*z[1]))
67#endif
68#if !defined(MAGICKCORE_HAVE_CARG)
cristy4da3ba32011-02-07 16:58:11 +000069#define carg(z) (atan2(cimag(z),creal(z)))
cristy47b022b2011-01-18 22:29:21 +000070#endif
71#if !defined(MAGICKCORE_HAVE_CIMAG)
72#define cimag(z) (z[1])
73#endif
74#if !defined(MAGICKCORE_HAVE_CREAL)
75#define creal(z) (z[0])
76#endif
cristy3ed852e2009-09-05 21:47:34 +000077#endif
78
79/*
80 Typedef declarations.
81*/
82typedef struct _FourierInfo
83{
cristyd3090f92011-10-18 00:05:15 +000084 PixelChannel
cristy3ed852e2009-09-05 21:47:34 +000085 channel;
86
87 MagickBooleanType
88 modulus;
89
cristybb503372010-05-27 20:51:26 +000090 size_t
cristy3ed852e2009-09-05 21:47:34 +000091 width,
92 height;
93
cristybb503372010-05-27 20:51:26 +000094 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000095 center;
96} FourierInfo;
97
98/*
99%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
100% %
101% %
102% %
103% 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 %
104% %
105% %
106% %
107%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
108%
109% ForwardFourierTransformImage() implements the discrete Fourier transform
110% (DFT) of the image either as a magnitude / phase or real / imaginary image
111% pair.
112%
113% The format of the ForwadFourierTransformImage method is:
114%
115% Image *ForwardFourierTransformImage(const Image *image,
116% const MagickBooleanType modulus,ExceptionInfo *exception)
117%
118% A description of each parameter follows:
119%
120% o image: the image.
121%
122% o modulus: if true, return as transform as a magnitude / phase pair
123% otherwise a real / imaginary image pair.
124%
125% o exception: return any errors or warnings in this structure.
126%
127*/
128
129#if defined(MAGICKCORE_FFTW_DELEGATE)
130
cristyc4ea4a42011-01-24 01:43:30 +0000131static MagickBooleanType RollFourier(const size_t width,const size_t height,
132 const ssize_t x_offset,const ssize_t y_offset,double *fourier)
cristy3ed852e2009-09-05 21:47:34 +0000133{
134 double
135 *roll;
136
cristyc4ea4a42011-01-24 01:43:30 +0000137 register ssize_t
138 i,
139 x;
140
cristybb503372010-05-27 20:51:26 +0000141 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000142 u,
143 v,
144 y;
145
cristy3ed852e2009-09-05 21:47:34 +0000146 /*
cristy2da05352010-09-15 19:22:17 +0000147 Move zero frequency (DC, average color) from (0,0) to (width/2,height/2).
cristy3ed852e2009-09-05 21:47:34 +0000148 */
cristyc4ea4a42011-01-24 01:43:30 +0000149 roll=(double *) AcquireQuantumMemory((size_t) height,width*sizeof(*roll));
cristy3ed852e2009-09-05 21:47:34 +0000150 if (roll == (double *) NULL)
151 return(MagickFalse);
152 i=0L;
cristybb503372010-05-27 20:51:26 +0000153 for (y=0L; y < (ssize_t) height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000154 {
155 if (y_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000156 v=((y+y_offset) < 0L) ? y+y_offset+(ssize_t) height : y+y_offset;
cristy3ed852e2009-09-05 21:47:34 +0000157 else
cristybb503372010-05-27 20:51:26 +0000158 v=((y+y_offset) > ((ssize_t) height-1L)) ? y+y_offset-(ssize_t) height :
cristy3ed852e2009-09-05 21:47:34 +0000159 y+y_offset;
cristybb503372010-05-27 20:51:26 +0000160 for (x=0L; x < (ssize_t) width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000161 {
162 if (x_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000163 u=((x+x_offset) < 0L) ? x+x_offset+(ssize_t) width : x+x_offset;
cristy3ed852e2009-09-05 21:47:34 +0000164 else
cristybb503372010-05-27 20:51:26 +0000165 u=((x+x_offset) > ((ssize_t) width-1L)) ? x+x_offset-(ssize_t) width :
cristy3ed852e2009-09-05 21:47:34 +0000166 x+x_offset;
167 roll[v*width+u]=fourier[i++];
cristyc4ea4a42011-01-24 01:43:30 +0000168 }
cristy3ed852e2009-09-05 21:47:34 +0000169 }
cristyc4ea4a42011-01-24 01:43:30 +0000170 (void) CopyMagickMemory(fourier,roll,height*width*sizeof(*roll));
cristy3ed852e2009-09-05 21:47:34 +0000171 roll=(double *) RelinquishMagickMemory(roll);
172 return(MagickTrue);
173}
174
cristybb503372010-05-27 20:51:26 +0000175static MagickBooleanType ForwardQuadrantSwap(const size_t width,
176 const size_t height,double *source,double *destination)
cristy3ed852e2009-09-05 21:47:34 +0000177{
cristy3ed852e2009-09-05 21:47:34 +0000178 MagickBooleanType
179 status;
180
cristybb503372010-05-27 20:51:26 +0000181 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000182 x;
183
cristyc4ea4a42011-01-24 01:43:30 +0000184 ssize_t
185 center,
186 y;
187
cristy3ed852e2009-09-05 21:47:34 +0000188 /*
189 Swap quadrants.
190 */
cristybb503372010-05-27 20:51:26 +0000191 center=(ssize_t) floor((double) width/2L)+1L;
192 status=RollFourier((size_t) center,height,0L,(ssize_t) height/2L,source);
cristy3ed852e2009-09-05 21:47:34 +0000193 if (status == MagickFalse)
194 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000195 for (y=0L; y < (ssize_t) height; y++)
196 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000197 destination[width*y+x+width/2L]=source[center*y+x];
cristybb503372010-05-27 20:51:26 +0000198 for (y=1; y < (ssize_t) height; y++)
199 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000200 destination[width*(height-y)+width/2L-x-1L]=source[center*y+x+1L];
cristybb503372010-05-27 20:51:26 +0000201 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000202 destination[-x+width/2L-1L]=destination[x+width/2L+1L];
203 return(MagickTrue);
204}
205
cristyc4ea4a42011-01-24 01:43:30 +0000206static void CorrectPhaseLHS(const size_t width,const size_t height,
207 double *fourier)
cristy3ed852e2009-09-05 21:47:34 +0000208{
cristybb503372010-05-27 20:51:26 +0000209 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000210 x;
211
cristy9d314ff2011-03-09 01:30:28 +0000212 ssize_t
213 y;
214
cristybb503372010-05-27 20:51:26 +0000215 for (y=0L; y < (ssize_t) height; y++)
216 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000217 fourier[y*width+x]*=(-1.0);
218}
219
220static MagickBooleanType ForwardFourier(const FourierInfo *fourier_info,
221 Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
222{
223 CacheView
224 *magnitude_view,
225 *phase_view;
226
227 double
228 *magnitude_source,
229 *phase_source;
230
231 Image
232 *magnitude_image,
233 *phase_image;
234
cristy3ed852e2009-09-05 21:47:34 +0000235 MagickBooleanType
236 status;
237
cristy4c08aed2011-07-01 19:47:50 +0000238 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000239 *q;
240
cristyf5742792012-11-27 18:31:26 +0000241 register ssize_t
242 x;
243
cristyc4ea4a42011-01-24 01:43:30 +0000244 ssize_t
245 i,
246 y;
247
cristy3ed852e2009-09-05 21:47:34 +0000248 magnitude_image=GetFirstImageInList(image);
249 phase_image=GetNextImageInList(image);
250 if (phase_image == (Image *) NULL)
251 {
252 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
cristyefe601c2013-01-05 17:51:12 +0000253 "TwoOrMoreImagesRequired","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000254 return(MagickFalse);
255 }
256 /*
257 Create "Fourier Transform" image from constituent arrays.
258 */
259 magnitude_source=(double *) AcquireQuantumMemory((size_t)
260 fourier_info->height,fourier_info->width*sizeof(*magnitude_source));
261 if (magnitude_source == (double *) NULL)
262 return(MagickFalse);
cristyc4ea4a42011-01-24 01:43:30 +0000263 (void) ResetMagickMemory(magnitude_source,0,fourier_info->height*
264 fourier_info->width*sizeof(*magnitude_source));
cristy3ed852e2009-09-05 21:47:34 +0000265 phase_source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
266 fourier_info->width*sizeof(*phase_source));
cristyc4ea4a42011-01-24 01:43:30 +0000267 if (phase_source == (double *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000268 {
269 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000270 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000271 magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
272 return(MagickFalse);
273 }
cristy6424d322012-11-27 18:41:45 +0000274 (void) ResetMagickMemory(phase_source,0,fourier_info->height*
275 fourier_info->width*sizeof(*phase_source));
cristy3ed852e2009-09-05 21:47:34 +0000276 status=ForwardQuadrantSwap(fourier_info->height,fourier_info->height,
277 magnitude,magnitude_source);
278 if (status != MagickFalse)
279 status=ForwardQuadrantSwap(fourier_info->height,fourier_info->height,phase,
280 phase_source);
281 CorrectPhaseLHS(fourier_info->height,fourier_info->height,phase_source);
282 if (fourier_info->modulus != MagickFalse)
283 {
284 i=0L;
cristybb503372010-05-27 20:51:26 +0000285 for (y=0L; y < (ssize_t) fourier_info->height; y++)
286 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000287 {
288 phase_source[i]/=(2.0*MagickPI);
289 phase_source[i]+=0.5;
290 i++;
291 }
292 }
cristy46ff2672012-12-14 15:32:26 +0000293 magnitude_view=AcquireAuthenticCacheView(magnitude_image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000294 i=0L;
cristybb503372010-05-27 20:51:26 +0000295 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000296 {
297 q=GetCacheViewAuthenticPixels(magnitude_view,0L,y,fourier_info->height,1UL,
298 exception);
cristyacd2ed22011-08-30 01:44:23 +0000299 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000300 break;
cristybb503372010-05-27 20:51:26 +0000301 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000302 {
303 switch (fourier_info->channel)
304 {
cristyd3090f92011-10-18 00:05:15 +0000305 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000306 default:
307 {
cristy4c08aed2011-07-01 19:47:50 +0000308 SetPixelRed(magnitude_image,ClampToQuantum(QuantumRange*
309 magnitude_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000310 break;
311 }
cristyd3090f92011-10-18 00:05:15 +0000312 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000313 {
cristy4c08aed2011-07-01 19:47:50 +0000314 SetPixelGreen(magnitude_image,ClampToQuantum(QuantumRange*
315 magnitude_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000316 break;
317 }
cristyd3090f92011-10-18 00:05:15 +0000318 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000319 {
cristy4c08aed2011-07-01 19:47:50 +0000320 SetPixelBlue(magnitude_image,ClampToQuantum(QuantumRange*
321 magnitude_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000322 break;
323 }
cristyd3090f92011-10-18 00:05:15 +0000324 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000325 {
cristy4c08aed2011-07-01 19:47:50 +0000326 SetPixelBlack(magnitude_image,ClampToQuantum(QuantumRange*
327 magnitude_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000328 break;
329 }
cristyd3090f92011-10-18 00:05:15 +0000330 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000331 {
cristy4c08aed2011-07-01 19:47:50 +0000332 SetPixelAlpha(magnitude_image,ClampToQuantum(QuantumRange*
333 magnitude_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000334 break;
335 }
cristy3ed852e2009-09-05 21:47:34 +0000336 }
337 i++;
cristyed231572011-07-14 02:18:59 +0000338 q+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +0000339 }
340 status=SyncCacheViewAuthenticPixels(magnitude_view,exception);
341 if (status == MagickFalse)
342 break;
343 }
cristydb070952012-04-20 14:33:00 +0000344 magnitude_view=DestroyCacheView(magnitude_view);
cristy3ed852e2009-09-05 21:47:34 +0000345 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000346 phase_view=AcquireAuthenticCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +0000347 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000348 {
349 q=GetCacheViewAuthenticPixels(phase_view,0L,y,fourier_info->height,1UL,
350 exception);
cristyacd2ed22011-08-30 01:44:23 +0000351 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000352 break;
cristybb503372010-05-27 20:51:26 +0000353 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000354 {
355 switch (fourier_info->channel)
356 {
cristyd3090f92011-10-18 00:05:15 +0000357 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000358 default:
359 {
cristy4c08aed2011-07-01 19:47:50 +0000360 SetPixelRed(phase_image,ClampToQuantum(QuantumRange*
361 phase_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000362 break;
363 }
cristyd3090f92011-10-18 00:05:15 +0000364 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000365 {
cristy4c08aed2011-07-01 19:47:50 +0000366 SetPixelGreen(phase_image,ClampToQuantum(QuantumRange*
367 phase_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000368 break;
369 }
cristyd3090f92011-10-18 00:05:15 +0000370 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000371 {
cristy4c08aed2011-07-01 19:47:50 +0000372 SetPixelBlue(phase_image,ClampToQuantum(QuantumRange*
373 phase_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000374 break;
375 }
cristyd3090f92011-10-18 00:05:15 +0000376 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000377 {
cristy4c08aed2011-07-01 19:47:50 +0000378 SetPixelBlack(phase_image,ClampToQuantum(QuantumRange*
379 phase_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000380 break;
381 }
cristyd3090f92011-10-18 00:05:15 +0000382 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000383 {
cristy4c08aed2011-07-01 19:47:50 +0000384 SetPixelAlpha(phase_image,ClampToQuantum(QuantumRange*
385 phase_source[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000386 break;
387 }
cristy3ed852e2009-09-05 21:47:34 +0000388 }
389 i++;
cristyed231572011-07-14 02:18:59 +0000390 q+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +0000391 }
392 status=SyncCacheViewAuthenticPixels(phase_view,exception);
393 if (status == MagickFalse)
394 break;
395 }
396 phase_view=DestroyCacheView(phase_view);
cristy3ed852e2009-09-05 21:47:34 +0000397 phase_source=(double *) RelinquishMagickMemory(phase_source);
398 magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
399 return(status);
400}
401
402static MagickBooleanType ForwardFourierTransform(FourierInfo *fourier_info,
403 const Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
404{
405 CacheView
406 *image_view;
407
408 double
409 n,
410 *source;
411
412 fftw_complex
413 *fourier;
414
415 fftw_plan
416 fftw_r2c_plan;
417
cristy4c08aed2011-07-01 19:47:50 +0000418 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000419 *p;
420
cristybb503372010-05-27 20:51:26 +0000421 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000422 i,
423 x;
424
cristyc4ea4a42011-01-24 01:43:30 +0000425 ssize_t
426 y;
427
cristy3ed852e2009-09-05 21:47:34 +0000428 /*
429 Generate the forward Fourier transform.
430 */
431 source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
432 fourier_info->width*sizeof(*source));
433 if (source == (double *) NULL)
434 {
435 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000436 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000437 return(MagickFalse);
438 }
cristyc4ea4a42011-01-24 01:43:30 +0000439 ResetMagickMemory(source,0,fourier_info->height*fourier_info->width*
cristy3ed852e2009-09-05 21:47:34 +0000440 sizeof(*source));
441 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000442 image_view=AcquireVirtualCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +0000443 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000444 {
445 p=GetCacheViewVirtualPixels(image_view,0L,y,fourier_info->width,1UL,
446 exception);
cristy4c08aed2011-07-01 19:47:50 +0000447 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000448 break;
cristybb503372010-05-27 20:51:26 +0000449 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000450 {
451 switch (fourier_info->channel)
452 {
cristyd3090f92011-10-18 00:05:15 +0000453 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000454 default:
455 {
cristy4c08aed2011-07-01 19:47:50 +0000456 source[i]=QuantumScale*GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000457 break;
458 }
cristyd3090f92011-10-18 00:05:15 +0000459 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000460 {
cristy4c08aed2011-07-01 19:47:50 +0000461 source[i]=QuantumScale*GetPixelGreen(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000462 break;
463 }
cristyd3090f92011-10-18 00:05:15 +0000464 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000465 {
cristy4c08aed2011-07-01 19:47:50 +0000466 source[i]=QuantumScale*GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000467 break;
468 }
cristyd3090f92011-10-18 00:05:15 +0000469 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000470 {
cristy4c08aed2011-07-01 19:47:50 +0000471 source[i]=QuantumScale*GetPixelBlack(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000472 break;
473 }
cristyd3090f92011-10-18 00:05:15 +0000474 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000475 {
cristy4c08aed2011-07-01 19:47:50 +0000476 source[i]=QuantumScale*GetPixelAlpha(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000477 break;
478 }
cristy3ed852e2009-09-05 21:47:34 +0000479 }
480 i++;
cristyed231572011-07-14 02:18:59 +0000481 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000482 }
483 }
484 image_view=DestroyCacheView(image_view);
cristyb41ee102010-10-04 16:46:15 +0000485 fourier=(fftw_complex *) AcquireQuantumMemory((size_t) fourier_info->height,
cristy3ed852e2009-09-05 21:47:34 +0000486 fourier_info->center*sizeof(*fourier));
487 if (fourier == (fftw_complex *) NULL)
488 {
489 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000490 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000491 source=(double *) RelinquishMagickMemory(source);
492 return(MagickFalse);
493 }
cristyb5d5f722009-11-04 03:03:49 +0000494#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000495 #pragma omp critical (MagickCore_ForwardFourierTransform)
496#endif
cristy70841a12012-10-27 20:52:57 +0000497 fftw_r2c_plan=fftw_plan_dft_r2c_2d(fourier_info->width,fourier_info->height,
cristy3ed852e2009-09-05 21:47:34 +0000498 source,fourier,FFTW_ESTIMATE);
499 fftw_execute(fftw_r2c_plan);
500 fftw_destroy_plan(fftw_r2c_plan);
501 source=(double *) RelinquishMagickMemory(source);
502 /*
503 Normalize Fourier transform.
504 */
505 n=(double) fourier_info->width*(double) fourier_info->width;
506 i=0L;
cristybb503372010-05-27 20:51:26 +0000507 for (y=0L; y < (ssize_t) fourier_info->height; y++)
508 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy56ed31c2010-03-22 00:46:21 +0000509 {
510#if defined(MAGICKCORE_HAVE_COMPLEX_H)
511 fourier[i]/=n;
512#else
513 fourier[i][0]/=n;
514 fourier[i][1]/=n;
515#endif
516 i++;
517 }
cristy3ed852e2009-09-05 21:47:34 +0000518 /*
519 Generate magnitude and phase (or real and imaginary).
520 */
521 i=0L;
522 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000523 for (y=0L; y < (ssize_t) fourier_info->height; y++)
524 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000525 {
526 magnitude[i]=cabs(fourier[i]);
527 phase[i]=carg(fourier[i]);
528 i++;
529 }
530 else
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++)
cristy3ed852e2009-09-05 21:47:34 +0000533 {
534 magnitude[i]=creal(fourier[i]);
535 phase[i]=cimag(fourier[i]);
536 i++;
537 }
cristyb41ee102010-10-04 16:46:15 +0000538 fourier=(fftw_complex *) RelinquishMagickMemory(fourier);
cristy3ed852e2009-09-05 21:47:34 +0000539 return(MagickTrue);
540}
541
542static MagickBooleanType ForwardFourierTransformChannel(const Image *image,
cristyd3090f92011-10-18 00:05:15 +0000543 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +0000544 Image *fourier_image,ExceptionInfo *exception)
545{
546 double
547 *magnitude,
548 *phase;
549
550 fftw_complex
551 *fourier;
552
cristy56ed31c2010-03-22 00:46:21 +0000553 FourierInfo
554 fourier_info;
555
cristyc4ea4a42011-01-24 01:43:30 +0000556 MagickBooleanType
557 status;
558
cristy3ed852e2009-09-05 21:47:34 +0000559 size_t
560 extent;
561
562 fourier_info.width=image->columns;
563 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
564 ((image->rows % 2) != 0))
565 {
566 extent=image->columns < image->rows ? image->rows : image->columns;
567 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
568 }
569 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +0000570 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +0000571 fourier_info.channel=channel;
572 fourier_info.modulus=modulus;
573 magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
574 fourier_info.center*sizeof(*magnitude));
575 if (magnitude == (double *) NULL)
576 {
577 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000578 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000579 return(MagickFalse);
580 }
581 phase=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
582 fourier_info.center*sizeof(*phase));
583 if (phase == (double *) NULL)
584 {
585 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000586 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000587 magnitude=(double *) RelinquishMagickMemory(magnitude);
588 return(MagickFalse);
589 }
cristyb41ee102010-10-04 16:46:15 +0000590 fourier=(fftw_complex *) AcquireQuantumMemory((size_t) fourier_info.height,
cristy3ed852e2009-09-05 21:47:34 +0000591 fourier_info.center*sizeof(*fourier));
592 if (fourier == (fftw_complex *) NULL)
593 {
594 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000595 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000596 phase=(double *) RelinquishMagickMemory(phase);
597 magnitude=(double *) RelinquishMagickMemory(magnitude);
598 return(MagickFalse);
599 }
600 status=ForwardFourierTransform(&fourier_info,image,magnitude,phase,exception);
601 if (status != MagickFalse)
602 status=ForwardFourier(&fourier_info,fourier_image,magnitude,phase,
603 exception);
cristyb41ee102010-10-04 16:46:15 +0000604 fourier=(fftw_complex *) RelinquishMagickMemory(fourier);
cristy3ed852e2009-09-05 21:47:34 +0000605 phase=(double *) RelinquishMagickMemory(phase);
606 magnitude=(double *) RelinquishMagickMemory(magnitude);
607 return(status);
608}
609#endif
610
611MagickExport Image *ForwardFourierTransformImage(const Image *image,
612 const MagickBooleanType modulus,ExceptionInfo *exception)
613{
614 Image
615 *fourier_image;
616
617 fourier_image=NewImageList();
618#if !defined(MAGICKCORE_FFTW_DELEGATE)
619 (void) modulus;
620 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000621 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristy3ed852e2009-09-05 21:47:34 +0000622 image->filename);
623#else
624 {
625 Image
626 *magnitude_image;
627
cristybb503372010-05-27 20:51:26 +0000628 size_t
cristy3ed852e2009-09-05 21:47:34 +0000629 extent,
630 width;
631
632 width=image->columns;
633 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
634 ((image->rows % 2) != 0))
635 {
636 extent=image->columns < image->rows ? image->rows : image->columns;
637 width=(extent & 0x01) == 1 ? extent+1UL : extent;
638 }
639 magnitude_image=CloneImage(image,width,width,MagickFalse,exception);
640 if (magnitude_image != (Image *) NULL)
641 {
642 Image
643 *phase_image;
644
645 magnitude_image->storage_class=DirectClass;
646 magnitude_image->depth=32UL;
647 phase_image=CloneImage(image,width,width,MagickFalse,exception);
648 if (phase_image == (Image *) NULL)
649 magnitude_image=DestroyImage(magnitude_image);
650 else
651 {
652 MagickBooleanType
653 is_gray,
654 status;
655
cristy3ed852e2009-09-05 21:47:34 +0000656 phase_image->storage_class=DirectClass;
657 phase_image->depth=32UL;
658 AppendImageToList(&fourier_image,magnitude_image);
659 AppendImageToList(&fourier_image,phase_image);
660 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000661 is_gray=IsImageGray(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000662#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy564a5692012-01-20 23:56:26 +0000663 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +0000664#endif
cristy3ed852e2009-09-05 21:47:34 +0000665 {
cristyb34ef052010-10-07 00:12:05 +0000666#if defined(MAGICKCORE_OPENMP_SUPPORT)
667 #pragma omp section
668#endif
cristy3ed852e2009-09-05 21:47:34 +0000669 {
cristyb34ef052010-10-07 00:12:05 +0000670 MagickBooleanType
671 thread_status;
672
673 if (is_gray != MagickFalse)
674 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000675 GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000676 else
677 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000678 RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000679 if (thread_status == MagickFalse)
680 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +0000681 }
cristyb34ef052010-10-07 00:12:05 +0000682#if defined(MAGICKCORE_OPENMP_SUPPORT)
683 #pragma omp section
684#endif
685 {
686 MagickBooleanType
687 thread_status;
688
689 thread_status=MagickTrue;
690 if (is_gray == MagickFalse)
691 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000692 GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000693 if (thread_status == MagickFalse)
694 status=thread_status;
695 }
696#if defined(MAGICKCORE_OPENMP_SUPPORT)
697 #pragma omp section
698#endif
699 {
700 MagickBooleanType
701 thread_status;
702
703 thread_status=MagickTrue;
704 if (is_gray == MagickFalse)
705 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000706 BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000707 if (thread_status == MagickFalse)
708 status=thread_status;
709 }
710#if defined(MAGICKCORE_OPENMP_SUPPORT)
711 #pragma omp section
712#endif
713 {
714 MagickBooleanType
715 thread_status;
716
717 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000718 if (image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +0000719 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000720 BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000721 if (thread_status == MagickFalse)
722 status=thread_status;
723 }
724#if defined(MAGICKCORE_OPENMP_SUPPORT)
725 #pragma omp section
726#endif
727 {
728 MagickBooleanType
729 thread_status;
730
731 thread_status=MagickTrue;
cristy8a46d822012-08-28 23:32:39 +0000732 if (image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +0000733 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000734 AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000735 if (thread_status == MagickFalse)
736 status=thread_status;
737 }
cristy3ed852e2009-09-05 21:47:34 +0000738 }
739 if (status == MagickFalse)
740 fourier_image=DestroyImageList(fourier_image);
741 fftw_cleanup();
742 }
743 }
744 }
745#endif
746 return(fourier_image);
747}
748
749/*
750%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
751% %
752% %
753% %
754% 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 %
755% %
756% %
757% %
758%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
759%
760% InverseFourierTransformImage() implements the inverse discrete Fourier
761% transform (DFT) of the image either as a magnitude / phase or real /
762% imaginary image pair.
763%
764% The format of the InverseFourierTransformImage method is:
765%
cristyc9550792009-11-13 20:05:42 +0000766% Image *InverseFourierTransformImage(const Image *magnitude_image,
767% const Image *phase_image,const MagickBooleanType modulus,
768% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000769%
770% A description of each parameter follows:
771%
cristyc9550792009-11-13 20:05:42 +0000772% o magnitude_image: the magnitude or real image.
773%
774% o phase_image: the phase or imaginary image.
cristy3ed852e2009-09-05 21:47:34 +0000775%
776% o modulus: if true, return transform as a magnitude / phase pair
777% otherwise a real / imaginary image pair.
778%
779% o exception: return any errors or warnings in this structure.
780%
781*/
782
783#if defined(MAGICKCORE_FFTW_DELEGATE)
cristybb503372010-05-27 20:51:26 +0000784static MagickBooleanType InverseQuadrantSwap(const size_t width,
785 const size_t height,const double *source,double *destination)
cristy3ed852e2009-09-05 21:47:34 +0000786{
cristyc4ea4a42011-01-24 01:43:30 +0000787 register ssize_t
788 x;
789
cristybb503372010-05-27 20:51:26 +0000790 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000791 center,
792 y;
793
cristy3ed852e2009-09-05 21:47:34 +0000794 /*
795 Swap quadrants.
796 */
cristy233fe582012-07-07 14:00:18 +0000797 center=(ssize_t) floor((double) width/2L)+1L;
cristybb503372010-05-27 20:51:26 +0000798 for (y=1L; y < (ssize_t) height; y++)
799 for (x=0L; x < (ssize_t) (width/2L+1L); x++)
cristy3ed852e2009-09-05 21:47:34 +0000800 destination[center*(height-y)-x+width/2L]=source[y*width+x];
cristybb503372010-05-27 20:51:26 +0000801 for (y=0L; y < (ssize_t) height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000802 destination[center*y]=source[y*width+width/2L];
803 for (x=0L; x < center; x++)
804 destination[x]=source[center-x-1L];
cristybb503372010-05-27 20:51:26 +0000805 return(RollFourier(center,height,0L,(ssize_t) height/-2L,destination));
cristy3ed852e2009-09-05 21:47:34 +0000806}
807
808static MagickBooleanType InverseFourier(FourierInfo *fourier_info,
cristyc9550792009-11-13 20:05:42 +0000809 const Image *magnitude_image,const Image *phase_image,fftw_complex *fourier,
810 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000811{
812 CacheView
813 *magnitude_view,
814 *phase_view;
815
816 double
817 *magnitude,
818 *phase,
819 *magnitude_source,
820 *phase_source;
821
cristy3ed852e2009-09-05 21:47:34 +0000822 MagickBooleanType
823 status;
824
cristy4c08aed2011-07-01 19:47:50 +0000825 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000826 *p;
827
cristybb503372010-05-27 20:51:26 +0000828 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000829 i,
830 x;
831
cristyc4ea4a42011-01-24 01:43:30 +0000832 ssize_t
833 y;
834
cristy3ed852e2009-09-05 21:47:34 +0000835 /*
836 Inverse fourier - read image and break down into a double array.
837 */
cristy3ed852e2009-09-05 21:47:34 +0000838 magnitude_source=(double *) AcquireQuantumMemory((size_t)
839 fourier_info->height,fourier_info->width*sizeof(*magnitude_source));
840 if (magnitude_source == (double *) NULL)
841 {
842 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000843 ResourceLimitError,"MemoryAllocationFailed","`%s'",
cristyc9550792009-11-13 20:05:42 +0000844 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000845 return(MagickFalse);
846 }
847 phase_source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
cristyc4ea4a42011-01-24 01:43:30 +0000848 fourier_info->width*sizeof(*phase_source));
cristy3ed852e2009-09-05 21:47:34 +0000849 if (phase_source == (double *) NULL)
850 {
851 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000852 ResourceLimitError,"MemoryAllocationFailed","`%s'",
cristyc9550792009-11-13 20:05:42 +0000853 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000854 magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
855 return(MagickFalse);
856 }
857 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000858 magnitude_view=AcquireVirtualCacheView(magnitude_image,exception);
cristybb503372010-05-27 20:51:26 +0000859 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000860 {
861 p=GetCacheViewVirtualPixels(magnitude_view,0L,y,fourier_info->width,1UL,
862 exception);
cristy4c08aed2011-07-01 19:47:50 +0000863 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000864 break;
cristybb503372010-05-27 20:51:26 +0000865 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000866 {
867 switch (fourier_info->channel)
868 {
cristyd3090f92011-10-18 00:05:15 +0000869 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000870 default:
871 {
cristy4c08aed2011-07-01 19:47:50 +0000872 magnitude_source[i]=QuantumScale*GetPixelRed(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000873 break;
874 }
cristyd3090f92011-10-18 00:05:15 +0000875 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000876 {
cristy4c08aed2011-07-01 19:47:50 +0000877 magnitude_source[i]=QuantumScale*GetPixelGreen(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000878 break;
879 }
cristyd3090f92011-10-18 00:05:15 +0000880 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000881 {
cristy4c08aed2011-07-01 19:47:50 +0000882 magnitude_source[i]=QuantumScale*GetPixelBlue(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000883 break;
884 }
cristyd3090f92011-10-18 00:05:15 +0000885 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000886 {
cristy4c08aed2011-07-01 19:47:50 +0000887 magnitude_source[i]=QuantumScale*GetPixelBlack(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000888 break;
889 }
cristyd3090f92011-10-18 00:05:15 +0000890 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000891 {
cristy4c08aed2011-07-01 19:47:50 +0000892 magnitude_source[i]=QuantumScale*GetPixelAlpha(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000893 break;
894 }
cristy3ed852e2009-09-05 21:47:34 +0000895 }
896 i++;
cristyed231572011-07-14 02:18:59 +0000897 p+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +0000898 }
899 }
900 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000901 phase_view=AcquireVirtualCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +0000902 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000903 {
904 p=GetCacheViewVirtualPixels(phase_view,0,y,fourier_info->width,1,
905 exception);
cristy4c08aed2011-07-01 19:47:50 +0000906 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000907 break;
cristybb503372010-05-27 20:51:26 +0000908 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000909 {
910 switch (fourier_info->channel)
911 {
cristyd3090f92011-10-18 00:05:15 +0000912 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000913 default:
914 {
cristy4c08aed2011-07-01 19:47:50 +0000915 phase_source[i]=QuantumScale*GetPixelRed(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000916 break;
917 }
cristyd3090f92011-10-18 00:05:15 +0000918 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000919 {
cristy4c08aed2011-07-01 19:47:50 +0000920 phase_source[i]=QuantumScale*GetPixelGreen(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000921 break;
922 }
cristyd3090f92011-10-18 00:05:15 +0000923 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000924 {
cristy4c08aed2011-07-01 19:47:50 +0000925 phase_source[i]=QuantumScale*GetPixelBlue(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000926 break;
927 }
cristyd3090f92011-10-18 00:05:15 +0000928 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000929 {
cristy4c08aed2011-07-01 19:47:50 +0000930 phase_source[i]=QuantumScale*GetPixelBlack(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000931 break;
932 }
cristyd3090f92011-10-18 00:05:15 +0000933 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000934 {
cristy4c08aed2011-07-01 19:47:50 +0000935 phase_source[i]=QuantumScale*GetPixelAlpha(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +0000936 break;
937 }
cristy3ed852e2009-09-05 21:47:34 +0000938 }
939 i++;
cristyed231572011-07-14 02:18:59 +0000940 p+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +0000941 }
942 }
943 if (fourier_info->modulus != MagickFalse)
944 {
945 i=0L;
cristybb503372010-05-27 20:51:26 +0000946 for (y=0L; y < (ssize_t) fourier_info->height; y++)
947 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000948 {
949 phase_source[i]-=0.5;
950 phase_source[i]*=(2.0*MagickPI);
951 i++;
952 }
953 }
954 magnitude_view=DestroyCacheView(magnitude_view);
955 phase_view=DestroyCacheView(phase_view);
956 magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
957 fourier_info->center*sizeof(*magnitude));
958 if (magnitude == (double *) NULL)
959 {
960 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000961 ResourceLimitError,"MemoryAllocationFailed","`%s'",
cristyc9550792009-11-13 20:05:42 +0000962 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000963 magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
964 phase_source=(double *) RelinquishMagickMemory(phase_source);
965 return(MagickFalse);
966 }
967 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
968 magnitude_source,magnitude);
969 magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
cristyc4ea4a42011-01-24 01:43:30 +0000970 phase=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
971 fourier_info->width*sizeof(*phase));
cristy3ed852e2009-09-05 21:47:34 +0000972 if (phase == (double *) NULL)
973 {
974 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000975 ResourceLimitError,"MemoryAllocationFailed","`%s'",
cristyc9550792009-11-13 20:05:42 +0000976 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000977 phase_source=(double *) RelinquishMagickMemory(phase_source);
978 return(MagickFalse);
979 }
980 CorrectPhaseLHS(fourier_info->width,fourier_info->width,phase_source);
981 if (status != MagickFalse)
982 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
983 phase_source,phase);
984 phase_source=(double *) RelinquishMagickMemory(phase_source);
985 /*
986 Merge two sets.
987 */
988 i=0L;
989 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000990 for (y=0L; y < (ssize_t) fourier_info->height; y++)
991 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000992 {
cristy56ed31c2010-03-22 00:46:21 +0000993#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy3ed852e2009-09-05 21:47:34 +0000994 fourier[i]=magnitude[i]*cos(phase[i])+I*magnitude[i]*sin(phase[i]);
cristy56ed31c2010-03-22 00:46:21 +0000995#else
996 fourier[i][0]=magnitude[i]*cos(phase[i]);
997 fourier[i][1]=magnitude[i]*sin(phase[i]);
998#endif
cristy3ed852e2009-09-05 21:47:34 +0000999 i++;
1000 }
1001 else
cristybb503372010-05-27 20:51:26 +00001002 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1003 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001004 {
cristy56ed31c2010-03-22 00:46:21 +00001005#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy3ed852e2009-09-05 21:47:34 +00001006 fourier[i]=magnitude[i]+I*phase[i];
cristy56ed31c2010-03-22 00:46:21 +00001007#else
1008 fourier[i][0]=magnitude[i];
1009 fourier[i][1]=phase[i];
1010#endif
cristy3ed852e2009-09-05 21:47:34 +00001011 i++;
1012 }
1013 phase=(double *) RelinquishMagickMemory(phase);
1014 magnitude=(double *) RelinquishMagickMemory(magnitude);
1015 return(status);
1016}
1017
1018static MagickBooleanType InverseFourierTransform(FourierInfo *fourier_info,
1019 fftw_complex *fourier,Image *image,ExceptionInfo *exception)
1020{
1021 CacheView
1022 *image_view;
1023
1024 double
1025 *source;
1026
1027 fftw_plan
1028 fftw_c2r_plan;
1029
cristy4c08aed2011-07-01 19:47:50 +00001030 register Quantum
cristyc4ea4a42011-01-24 01:43:30 +00001031 *q;
1032
cristybb503372010-05-27 20:51:26 +00001033 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001034 i,
1035 x;
1036
cristyc4ea4a42011-01-24 01:43:30 +00001037 ssize_t
1038 y;
cristy3ed852e2009-09-05 21:47:34 +00001039
cristyc4ea4a42011-01-24 01:43:30 +00001040 source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
1041 fourier_info->width*sizeof(*source));
cristy3ed852e2009-09-05 21:47:34 +00001042 if (source == (double *) NULL)
1043 {
1044 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001045 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001046 return(MagickFalse);
1047 }
cristyb5d5f722009-11-04 03:03:49 +00001048#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001049 #pragma omp critical (MagickCore_InverseFourierTransform)
1050#endif
cristydf079ac2010-09-10 01:45:44 +00001051 {
1052 fftw_c2r_plan=fftw_plan_dft_c2r_2d(fourier_info->width,fourier_info->height,
1053 fourier,source,FFTW_ESTIMATE);
1054 fftw_execute(fftw_c2r_plan);
1055 fftw_destroy_plan(fftw_c2r_plan);
1056 }
cristy3ed852e2009-09-05 21:47:34 +00001057 i=0L;
cristy46ff2672012-12-14 15:32:26 +00001058 image_view=AcquireAuthenticCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +00001059 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001060 {
cristy85812052010-09-14 17:56:15 +00001061 if (y >= (ssize_t) image->rows)
1062 break;
1063 q=GetCacheViewAuthenticPixels(image_view,0L,y,fourier_info->width >
1064 image->columns ? image->columns : fourier_info->width,1UL,exception);
cristyacd2ed22011-08-30 01:44:23 +00001065 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001066 break;
cristybb503372010-05-27 20:51:26 +00001067 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001068 {
cristy233fe582012-07-07 14:00:18 +00001069 if (x < (ssize_t) image->columns)
1070 switch (fourier_info->channel)
cristy3ed852e2009-09-05 21:47:34 +00001071 {
cristy233fe582012-07-07 14:00:18 +00001072 case RedPixelChannel:
1073 default:
1074 {
1075 SetPixelRed(image,ClampToQuantum(QuantumRange*source[i]),q);
1076 break;
1077 }
1078 case GreenPixelChannel:
1079 {
1080 SetPixelGreen(image,ClampToQuantum(QuantumRange*source[i]),q);
1081 break;
1082 }
1083 case BluePixelChannel:
1084 {
1085 SetPixelBlue(image,ClampToQuantum(QuantumRange*source[i]),q);
1086 break;
1087 }
1088 case BlackPixelChannel:
1089 {
1090 SetPixelBlack(image,ClampToQuantum(QuantumRange*source[i]),q);
1091 break;
1092 }
1093 case AlphaPixelChannel:
1094 {
1095 SetPixelAlpha(image,ClampToQuantum(QuantumRange*source[i]),q);
1096 break;
1097 }
cristy3ed852e2009-09-05 21:47:34 +00001098 }
cristy3ed852e2009-09-05 21:47:34 +00001099 i++;
cristyed231572011-07-14 02:18:59 +00001100 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001101 }
1102 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1103 break;
1104 }
1105 image_view=DestroyCacheView(image_view);
1106 source=(double *) RelinquishMagickMemory(source);
1107 return(MagickTrue);
1108}
1109
cristyc9550792009-11-13 20:05:42 +00001110static MagickBooleanType InverseFourierTransformChannel(
1111 const Image *magnitude_image,const Image *phase_image,
cristyd3090f92011-10-18 00:05:15 +00001112 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +00001113 Image *fourier_image,ExceptionInfo *exception)
1114{
1115 double
1116 *magnitude,
1117 *phase;
1118
1119 fftw_complex
1120 *fourier;
1121
1122 FourierInfo
1123 fourier_info;
1124
1125 MagickBooleanType
1126 status;
1127
1128 size_t
1129 extent;
1130
cristyc9550792009-11-13 20:05:42 +00001131 fourier_info.width=magnitude_image->columns;
1132 if ((magnitude_image->columns != magnitude_image->rows) ||
1133 ((magnitude_image->columns % 2) != 0) ||
1134 ((magnitude_image->rows % 2) != 0))
cristy3ed852e2009-09-05 21:47:34 +00001135 {
cristyc9550792009-11-13 20:05:42 +00001136 extent=magnitude_image->columns < magnitude_image->rows ?
1137 magnitude_image->rows : magnitude_image->columns;
cristy3ed852e2009-09-05 21:47:34 +00001138 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
1139 }
1140 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +00001141 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +00001142 fourier_info.channel=channel;
1143 fourier_info.modulus=modulus;
1144 magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
cristyc4ea4a42011-01-24 01:43:30 +00001145 fourier_info.center*sizeof(*magnitude));
cristy3ed852e2009-09-05 21:47:34 +00001146 if (magnitude == (double *) NULL)
1147 {
1148 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001149 ResourceLimitError,"MemoryAllocationFailed","`%s'",
cristyc9550792009-11-13 20:05:42 +00001150 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001151 return(MagickFalse);
1152 }
1153 phase=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
cristyc4ea4a42011-01-24 01:43:30 +00001154 fourier_info.center*sizeof(*phase));
cristy3ed852e2009-09-05 21:47:34 +00001155 if (phase == (double *) NULL)
1156 {
1157 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001158 ResourceLimitError,"MemoryAllocationFailed","`%s'",
cristyc9550792009-11-13 20:05:42 +00001159 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001160 magnitude=(double *) RelinquishMagickMemory(magnitude);
1161 return(MagickFalse);
1162 }
cristyb41ee102010-10-04 16:46:15 +00001163 fourier=(fftw_complex *) AcquireQuantumMemory((size_t) fourier_info.height,
cristy3ed852e2009-09-05 21:47:34 +00001164 fourier_info.center*sizeof(*fourier));
1165 if (fourier == (fftw_complex *) NULL)
1166 {
1167 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001168 ResourceLimitError,"MemoryAllocationFailed","`%s'",
cristyc9550792009-11-13 20:05:42 +00001169 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001170 phase=(double *) RelinquishMagickMemory(phase);
1171 magnitude=(double *) RelinquishMagickMemory(magnitude);
1172 return(MagickFalse);
1173 }
cristyc9550792009-11-13 20:05:42 +00001174 status=InverseFourier(&fourier_info,magnitude_image,phase_image,fourier,
1175 exception);
cristy3ed852e2009-09-05 21:47:34 +00001176 if (status != MagickFalse)
1177 status=InverseFourierTransform(&fourier_info,fourier,fourier_image,
1178 exception);
cristyb41ee102010-10-04 16:46:15 +00001179 fourier=(fftw_complex *) RelinquishMagickMemory(fourier);
cristy3ed852e2009-09-05 21:47:34 +00001180 phase=(double *) RelinquishMagickMemory(phase);
1181 magnitude=(double *) RelinquishMagickMemory(magnitude);
1182 return(status);
1183}
1184#endif
1185
cristyc9550792009-11-13 20:05:42 +00001186MagickExport Image *InverseFourierTransformImage(const Image *magnitude_image,
1187 const Image *phase_image,const MagickBooleanType modulus,
1188 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001189{
1190 Image
1191 *fourier_image;
1192
cristyc9550792009-11-13 20:05:42 +00001193 assert(magnitude_image != (Image *) NULL);
1194 assert(magnitude_image->signature == MagickSignature);
1195 if (magnitude_image->debug != MagickFalse)
1196 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1197 magnitude_image->filename);
1198 if (phase_image == (Image *) NULL)
1199 {
1200 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
cristyefe601c2013-01-05 17:51:12 +00001201 "TwoOrMoreImagesRequired","`%s'",magnitude_image->filename);
cristy9372a152009-11-18 01:42:16 +00001202 return((Image *) NULL);
cristyc9550792009-11-13 20:05:42 +00001203 }
cristy3ed852e2009-09-05 21:47:34 +00001204#if !defined(MAGICKCORE_FFTW_DELEGATE)
1205 fourier_image=(Image *) NULL;
1206 (void) modulus;
1207 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00001208 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristyc9550792009-11-13 20:05:42 +00001209 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001210#else
1211 {
cristyc9550792009-11-13 20:05:42 +00001212 fourier_image=CloneImage(magnitude_image,magnitude_image->columns,
1213 magnitude_image->rows,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00001214 if (fourier_image != (Image *) NULL)
1215 {
1216 MagickBooleanType
1217 is_gray,
1218 status;
1219
cristy3ed852e2009-09-05 21:47:34 +00001220 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001221 is_gray=IsImageGray(magnitude_image,exception);
cristyc9550792009-11-13 20:05:42 +00001222 if (is_gray != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001223 is_gray=IsImageGray(phase_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001224#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb34ef052010-10-07 00:12:05 +00001225 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +00001226#endif
cristy3ed852e2009-09-05 21:47:34 +00001227 {
cristyb34ef052010-10-07 00:12:05 +00001228#if defined(MAGICKCORE_OPENMP_SUPPORT)
1229 #pragma omp section
1230#endif
cristy3ed852e2009-09-05 21:47:34 +00001231 {
cristyb34ef052010-10-07 00:12:05 +00001232 MagickBooleanType
1233 thread_status;
1234
1235 if (is_gray != MagickFalse)
1236 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001237 phase_image,GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001238 else
cristyc9550792009-11-13 20:05:42 +00001239 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001240 phase_image,RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001241 if (thread_status == MagickFalse)
1242 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +00001243 }
cristyb34ef052010-10-07 00:12:05 +00001244#if defined(MAGICKCORE_OPENMP_SUPPORT)
1245 #pragma omp section
1246#endif
1247 {
1248 MagickBooleanType
1249 thread_status;
1250
1251 thread_status=MagickTrue;
1252 if (is_gray == MagickFalse)
1253 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001254 phase_image,GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001255 if (thread_status == MagickFalse)
1256 status=thread_status;
1257 }
1258#if defined(MAGICKCORE_OPENMP_SUPPORT)
1259 #pragma omp section
1260#endif
1261 {
1262 MagickBooleanType
1263 thread_status;
1264
1265 thread_status=MagickTrue;
1266 if (is_gray == MagickFalse)
1267 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001268 phase_image,BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001269 if (thread_status == MagickFalse)
1270 status=thread_status;
1271 }
1272#if defined(MAGICKCORE_OPENMP_SUPPORT)
1273 #pragma omp section
1274#endif
1275 {
1276 MagickBooleanType
1277 thread_status;
1278
1279 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001280 if (magnitude_image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +00001281 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001282 phase_image,BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001283 if (thread_status == MagickFalse)
1284 status=thread_status;
1285 }
1286#if defined(MAGICKCORE_OPENMP_SUPPORT)
1287 #pragma omp section
1288#endif
1289 {
1290 MagickBooleanType
1291 thread_status;
1292
1293 thread_status=MagickTrue;
cristy8a46d822012-08-28 23:32:39 +00001294 if (magnitude_image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +00001295 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001296 phase_image,AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001297 if (thread_status == MagickFalse)
1298 status=thread_status;
1299 }
cristy3ed852e2009-09-05 21:47:34 +00001300 }
1301 if (status == MagickFalse)
1302 fourier_image=DestroyImage(fourier_image);
1303 }
cristyb34ef052010-10-07 00:12:05 +00001304 fftw_cleanup();
cristy3ed852e2009-09-05 21:47:34 +00001305 }
1306#endif
1307 return(fourier_image);
1308}