blob: a649eca2f53c54d77987d1ba80e19eca51fcdeea [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 %
cristyde984cd2013-12-01 14:49:27 +000018% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000019% July 2009 %
20% %
21% %
cristyfe676ee2013-11-18 13:03:38 +000022% Copyright 1999-2014 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"
cristy99dc0362013-09-15 18:32:54 +000046#include "MagickCore/artifact.h"
cristy4c08aed2011-07-01 19:47:50 +000047#include "MagickCore/attribute.h"
cristy7d4aa382013-06-30 01:59:39 +000048#include "MagickCore/blob.h"
cristy4c08aed2011-07-01 19:47:50 +000049#include "MagickCore/cache.h"
50#include "MagickCore/image.h"
51#include "MagickCore/image-private.h"
52#include "MagickCore/list.h"
53#include "MagickCore/fourier.h"
54#include "MagickCore/log.h"
55#include "MagickCore/memory_.h"
56#include "MagickCore/monitor.h"
cristy1042ed22013-10-05 17:38:54 +000057#include "MagickCore/monitor-private.h"
cristy4c08aed2011-07-01 19:47:50 +000058#include "MagickCore/pixel-accessor.h"
cristy99dc0362013-09-15 18:32:54 +000059#include "MagickCore/pixel-private.h"
cristy4c08aed2011-07-01 19:47:50 +000060#include "MagickCore/property.h"
61#include "MagickCore/quantum-private.h"
cristyac245f82012-05-05 17:13:57 +000062#include "MagickCore/resource_.h"
cristy34b78ea2013-10-09 00:31:52 +000063#include "MagickCore/string-private.h"
cristy4c08aed2011-07-01 19:47:50 +000064#include "MagickCore/thread-private.h"
cristy3ed852e2009-09-05 21:47:34 +000065#if defined(MAGICKCORE_FFTW_DELEGATE)
cristy56ed31c2010-03-22 00:46:21 +000066#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy3ed852e2009-09-05 21:47:34 +000067#include <complex.h>
cristy56ed31c2010-03-22 00:46:21 +000068#endif
cristy3ed852e2009-09-05 21:47:34 +000069#include <fftw3.h>
cristy47b022b2011-01-18 22:29:21 +000070#if !defined(MAGICKCORE_HAVE_CABS)
71#define cabs(z) (sqrt(z[0]*z[0]+z[1]*z[1]))
72#endif
73#if !defined(MAGICKCORE_HAVE_CARG)
cristy4da3ba32011-02-07 16:58:11 +000074#define carg(z) (atan2(cimag(z),creal(z)))
cristy47b022b2011-01-18 22:29:21 +000075#endif
76#if !defined(MAGICKCORE_HAVE_CIMAG)
77#define cimag(z) (z[1])
78#endif
79#if !defined(MAGICKCORE_HAVE_CREAL)
80#define creal(z) (z[0])
81#endif
cristy3ed852e2009-09-05 21:47:34 +000082#endif
83
84/*
85 Typedef declarations.
86*/
87typedef struct _FourierInfo
88{
cristyd3090f92011-10-18 00:05:15 +000089 PixelChannel
cristy3ed852e2009-09-05 21:47:34 +000090 channel;
91
92 MagickBooleanType
93 modulus;
94
cristybb503372010-05-27 20:51:26 +000095 size_t
cristy3ed852e2009-09-05 21:47:34 +000096 width,
97 height;
98
cristybb503372010-05-27 20:51:26 +000099 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000100 center;
101} FourierInfo;
102
103/*
104%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
105% %
106% %
107% %
cristy790190d2013-10-04 00:51:51 +0000108% C o m p l e x I m a g e s %
109% %
110% %
111% %
112%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
113%
114% ComplexImages() performs complex mathematics on an image sequence.
115%
116% The format of the ComplexImages method is:
117%
118% MagickBooleanType ComplexImages(Image *images,
cristy220c4d52013-11-27 19:31:32 +0000119% const ComplexOperator op,ExceptionInfo *exception)
cristy790190d2013-10-04 00:51:51 +0000120%
121% A description of each parameter follows:
122%
123% o image: the image.
124%
cristy220c4d52013-11-27 19:31:32 +0000125% o op: A complex op.
cristy790190d2013-10-04 00:51:51 +0000126%
127% o exception: return any errors or warnings in this structure.
128%
129*/
cristy220c4d52013-11-27 19:31:32 +0000130MagickExport Image *ComplexImages(const Image *images,const ComplexOperator op,
131 ExceptionInfo *exception)
cristy790190d2013-10-04 00:51:51 +0000132{
133#define ComplexImageTag "Complex/Image"
134
135 CacheView
cristy1042ed22013-10-05 17:38:54 +0000136 *Ai_view,
137 *Ar_view,
138 *Bi_view,
139 *Br_view,
140 *Ci_view,
141 *Cr_view;
142
cristy34b78ea2013-10-09 00:31:52 +0000143 const char
144 *artifact;
145
cristy1042ed22013-10-05 17:38:54 +0000146 const Image
147 *Ai_image,
148 *Ar_image,
149 *Bi_image,
150 *Br_image;
cristy790190d2013-10-04 00:51:51 +0000151
cristy34b78ea2013-10-09 00:31:52 +0000152 double
153 snr;
154
cristy790190d2013-10-04 00:51:51 +0000155 Image
cristy1042ed22013-10-05 17:38:54 +0000156 *Ci_image,
157 *complex_images,
cristy34919ed2013-10-06 15:42:40 +0000158 *Cr_image,
159 *image;
cristy790190d2013-10-04 00:51:51 +0000160
161 MagickBooleanType
162 status;
163
164 MagickOffsetType
165 progress;
166
cristy790190d2013-10-04 00:51:51 +0000167 ssize_t
168 y;
169
170 assert(images != (Image *) NULL);
171 assert(images->signature == MagickSignature);
172 if (images->debug != MagickFalse)
173 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
174 assert(exception != (ExceptionInfo *) NULL);
175 assert(exception->signature == MagickSignature);
cristy1042ed22013-10-05 17:38:54 +0000176 if (images->next == (Image *) NULL)
177 {
178 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
179 "ImageSequenceRequired","`%s'",images->filename);
180 return((Image *) NULL);
181 }
cristy34919ed2013-10-06 15:42:40 +0000182 image=CloneImage(images,images->columns,images->rows,MagickTrue,exception);
183 if (image == (Image *) NULL)
cristy1042ed22013-10-05 17:38:54 +0000184 return((Image *) NULL);
cristy34919ed2013-10-06 15:42:40 +0000185 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
186 {
187 image=DestroyImageList(image);
188 return(image);
189 }
190 complex_images=NewImageList();
191 AppendImageToList(&complex_images,image);
192 image=CloneImage(images,images->columns,images->rows,MagickTrue,exception);
193 if (image == (Image *) NULL)
cristy1042ed22013-10-05 17:38:54 +0000194 {
195 complex_images=DestroyImageList(complex_images);
196 return(complex_images);
197 }
cristy34919ed2013-10-06 15:42:40 +0000198 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
199 {
200 complex_images=DestroyImageList(complex_images);
201 return(complex_images);
202 }
203 AppendImageToList(&complex_images,image);
cristy1042ed22013-10-05 17:38:54 +0000204 /*
205 Apply complex mathematics to image pixels.
206 */
cristy34b78ea2013-10-09 00:31:52 +0000207 artifact=GetImageArtifact(image,"complex:snr");
208 snr=0.0;
209 if (artifact != (const char *) NULL)
210 snr=StringToDouble(artifact,(char **) NULL);
cristy1042ed22013-10-05 17:38:54 +0000211 Ar_image=images;
212 Ai_image=images->next;
cristy8dc6a072013-10-08 12:00:48 +0000213 Br_image=images;
214 Bi_image=images->next;
215 if ((images->next->next != (Image *) NULL) &&
216 (images->next->next->next != (Image *) NULL))
cristy1042ed22013-10-05 17:38:54 +0000217 {
218 Br_image=images->next->next;
219 Bi_image=images->next->next->next;
220 }
221 Cr_image=complex_images;
222 Ci_image=complex_images->next;
223 Ar_view=AcquireVirtualCacheView(Ar_image,exception);
224 Ai_view=AcquireVirtualCacheView(Ai_image,exception);
225 Br_view=AcquireVirtualCacheView(Br_image,exception);
226 Bi_view=AcquireVirtualCacheView(Bi_image,exception);
227 Cr_view=AcquireAuthenticCacheView(Cr_image,exception);
228 Ci_view=AcquireAuthenticCacheView(Ci_image,exception);
229 status=MagickTrue;
230 progress=0;
231#if defined(MAGICKCORE_OPENMP_SUPPORT)
232 #pragma omp parallel for schedule(static,4) shared(progress,status) \
233 magick_threads(images,complex_images,images->rows,1L)
234#endif
235 for (y=0; y < (ssize_t) images->rows; y++)
236 {
237 register const Quantum
238 *restrict Ai,
239 *restrict Ar,
240 *restrict Bi,
241 *restrict Br;
242
243 register Quantum
244 *restrict Ci,
245 *restrict Cr;
246
247 register ssize_t
248 x;
249
cristy34919ed2013-10-06 15:42:40 +0000250 if (status == MagickFalse)
251 continue;
cristy1042ed22013-10-05 17:38:54 +0000252 Ar=GetCacheViewVirtualPixels(Ar_view,0,y,images->columns,1,exception);
253 Ai=GetCacheViewVirtualPixels(Ai_view,0,y,images->columns,1,exception);
254 Br=GetCacheViewVirtualPixels(Br_view,0,y,images->columns,1,exception);
255 Bi=GetCacheViewVirtualPixels(Bi_view,0,y,images->columns,1,exception);
256 Cr=QueueCacheViewAuthenticPixels(Cr_view,0,y,images->columns,1,exception);
257 Ci=QueueCacheViewAuthenticPixels(Ci_view,0,y,images->columns,1,exception);
258 if ((Ar == (const Quantum *) NULL) || (Ai == (const Quantum *) NULL) ||
259 (Br == (const Quantum *) NULL) || (Bi == (const Quantum *) NULL) ||
260 (Cr == (Quantum *) NULL) || (Ci == (Quantum *) NULL))
261 {
262 status=MagickFalse;
263 continue;
264 }
265 for (x=0; x < (ssize_t) images->columns; x++)
266 {
cristy9f654472013-10-05 19:44:06 +0000267 register ssize_t
268 i;
269
270 for (i=0; i < (ssize_t) GetPixelChannels(images); i++)
271 {
cristy220c4d52013-11-27 19:31:32 +0000272 switch (op)
cristy9f654472013-10-05 19:44:06 +0000273 {
cristy19f78862013-10-05 22:21:46 +0000274 case AddComplexOperator:
275 {
276 Cr[i]=Ar[i]+Br[i];
277 Ci[i]=Ai[i]+Bi[i];
278 break;
279 }
cristy9f654472013-10-05 19:44:06 +0000280 case ConjugateComplexOperator:
281 default:
282 {
283 Cr[i]=Ar[i];
284 Ci[i]=(-Bi[i]);
285 break;
286 }
287 case DivideComplexOperator:
288 {
289 double
290 gamma;
291
cristy34b78ea2013-10-09 00:31:52 +0000292 gamma=PerceptibleReciprocal(Br[i]*Br[i]+Bi[i]*Bi[i]+snr);
cristy9f654472013-10-05 19:44:06 +0000293 Cr[i]=gamma*(Ar[i]*Br[i]+Ai[i]*Bi[i]);
cristy857c8a12013-10-08 23:19:33 +0000294 Ci[i]=gamma*(Ai[i]*Br[i]-Ar[i]*Bi[i]);
cristy9f654472013-10-05 19:44:06 +0000295 break;
296 }
cristyf46941c2013-10-06 22:25:41 +0000297 case MagnitudePhaseComplexOperator:
298 {
cristy8526a972013-10-08 00:52:54 +0000299 Cr[i]=sqrt(Ar[i]*Ar[i]+Ai[i]*Ai[i]);
cristy4ab06942013-10-10 23:47:23 +0000300 Ci[i]=atan2(Ai[i],Ar[i])/(2.0*MagickPI)+0.5;
cristyf46941c2013-10-06 22:25:41 +0000301 break;
302 }
cristy9f654472013-10-05 19:44:06 +0000303 case MultiplyComplexOperator:
304 {
cristy15b07b32013-10-08 12:10:31 +0000305 Cr[i]=QuantumScale*(Ar[i]*Br[i]-Ai[i]*Bi[i]);
306 Ci[i]=QuantumScale*(Ai[i]*Br[i]+Ar[i]*Bi[i]);
cristyf46941c2013-10-06 22:25:41 +0000307 break;
308 }
309 case RealImaginaryComplexOperator:
310 {
cristy4ab06942013-10-10 23:47:23 +0000311 Cr[i]=Ar[i]*cos(2.0*MagickPI*(Ai[i]-0.5));
312 Ci[i]=Ar[i]*sin(2.0*MagickPI*(Ai[i]-0.5));
cristy9f654472013-10-05 19:44:06 +0000313 break;
314 }
cristy19f78862013-10-05 22:21:46 +0000315 case SubtractComplexOperator:
316 {
317 Cr[i]=Ar[i]-Br[i];
318 Ci[i]=Ai[i]-Bi[i];
319 break;
320 }
cristy9f654472013-10-05 19:44:06 +0000321 }
cristy19f78862013-10-05 22:21:46 +0000322 Ar+=GetPixelChannels(Ar_image);
323 Ai+=GetPixelChannels(Ai_image);
324 Br+=GetPixelChannels(Br_image);
325 Bi+=GetPixelChannels(Bi_image);
326 Cr+=GetPixelChannels(Cr_image);
327 Ci+=GetPixelChannels(Ci_image);
cristy9f654472013-10-05 19:44:06 +0000328 }
cristy1042ed22013-10-05 17:38:54 +0000329 }
330 if (SyncCacheViewAuthenticPixels(Ci_view,exception) == MagickFalse)
331 status=MagickFalse;
332 if (SyncCacheViewAuthenticPixels(Cr_view,exception) == MagickFalse)
333 status=MagickFalse;
334 if (images->progress_monitor != (MagickProgressMonitor) NULL)
335 {
336 MagickBooleanType
337 proceed;
338
339#if defined(MAGICKCORE_OPENMP_SUPPORT)
340 #pragma omp critical (MagickCore_ComplexImages)
341#endif
342 proceed=SetImageProgress(images,ComplexImageTag,progress++,
343 images->rows);
344 if (proceed == MagickFalse)
345 status=MagickFalse;
346 }
347 }
348 Cr_view=DestroyCacheView(Cr_view);
349 Ci_view=DestroyCacheView(Ci_view);
350 Br_view=DestroyCacheView(Br_view);
351 Bi_view=DestroyCacheView(Bi_view);
352 Ar_view=DestroyCacheView(Ar_view);
353 Ai_view=DestroyCacheView(Ai_view);
354 if (status == MagickFalse)
355 complex_images=DestroyImageList(complex_images);
356 return(complex_images);
cristy790190d2013-10-04 00:51:51 +0000357}
358
359/*
360%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
361% %
362% %
363% %
cristy3ed852e2009-09-05 21:47:34 +0000364% 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 %
365% %
366% %
367% %
368%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
369%
370% ForwardFourierTransformImage() implements the discrete Fourier transform
371% (DFT) of the image either as a magnitude / phase or real / imaginary image
372% pair.
373%
374% The format of the ForwadFourierTransformImage method is:
375%
376% Image *ForwardFourierTransformImage(const Image *image,
377% const MagickBooleanType modulus,ExceptionInfo *exception)
378%
379% A description of each parameter follows:
380%
381% o image: the image.
382%
383% o modulus: if true, return as transform as a magnitude / phase pair
384% otherwise a real / imaginary image pair.
385%
386% o exception: return any errors or warnings in this structure.
387%
388*/
389
390#if defined(MAGICKCORE_FFTW_DELEGATE)
391
cristyc4ea4a42011-01-24 01:43:30 +0000392static MagickBooleanType RollFourier(const size_t width,const size_t height,
cristy699ae5b2013-07-03 13:41:29 +0000393 const ssize_t x_offset,const ssize_t y_offset,double *roll_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000394{
395 double
cristy699ae5b2013-07-03 13:41:29 +0000396 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000397
cristy7d4aa382013-06-30 01:59:39 +0000398 MemoryInfo
cristy699ae5b2013-07-03 13:41:29 +0000399 *source_info;
cristy7d4aa382013-06-30 01:59:39 +0000400
cristyc4ea4a42011-01-24 01:43:30 +0000401 register ssize_t
402 i,
403 x;
404
cristybb503372010-05-27 20:51:26 +0000405 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000406 u,
407 v,
408 y;
409
cristy3ed852e2009-09-05 21:47:34 +0000410 /*
cristy2da05352010-09-15 19:22:17 +0000411 Move zero frequency (DC, average color) from (0,0) to (width/2,height/2).
cristy3ed852e2009-09-05 21:47:34 +0000412 */
cristy699ae5b2013-07-03 13:41:29 +0000413 source_info=AcquireVirtualMemory(height,width*sizeof(*source_pixels));
414 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000415 return(MagickFalse);
cristy699ae5b2013-07-03 13:41:29 +0000416 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000417 i=0L;
cristybb503372010-05-27 20:51:26 +0000418 for (y=0L; y < (ssize_t) height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000419 {
420 if (y_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000421 v=((y+y_offset) < 0L) ? y+y_offset+(ssize_t) height : y+y_offset;
cristy3ed852e2009-09-05 21:47:34 +0000422 else
cristybb503372010-05-27 20:51:26 +0000423 v=((y+y_offset) > ((ssize_t) height-1L)) ? y+y_offset-(ssize_t) height :
cristy3ed852e2009-09-05 21:47:34 +0000424 y+y_offset;
cristybb503372010-05-27 20:51:26 +0000425 for (x=0L; x < (ssize_t) width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000426 {
427 if (x_offset < 0L)
cristybb503372010-05-27 20:51:26 +0000428 u=((x+x_offset) < 0L) ? x+x_offset+(ssize_t) width : x+x_offset;
cristy3ed852e2009-09-05 21:47:34 +0000429 else
cristybb503372010-05-27 20:51:26 +0000430 u=((x+x_offset) > ((ssize_t) width-1L)) ? x+x_offset-(ssize_t) width :
cristy3ed852e2009-09-05 21:47:34 +0000431 x+x_offset;
cristy699ae5b2013-07-03 13:41:29 +0000432 source_pixels[v*width+u]=roll_pixels[i++];
cristyc4ea4a42011-01-24 01:43:30 +0000433 }
cristy3ed852e2009-09-05 21:47:34 +0000434 }
cristy699ae5b2013-07-03 13:41:29 +0000435 (void) CopyMagickMemory(roll_pixels,source_pixels,height*width*
436 sizeof(*source_pixels));
437 source_info=RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000438 return(MagickTrue);
439}
440
cristybb503372010-05-27 20:51:26 +0000441static MagickBooleanType ForwardQuadrantSwap(const size_t width,
cristy699ae5b2013-07-03 13:41:29 +0000442 const size_t height,double *source_pixels,double *forward_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000443{
cristy3ed852e2009-09-05 21:47:34 +0000444 MagickBooleanType
445 status;
446
cristybb503372010-05-27 20:51:26 +0000447 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000448 x;
449
cristyc4ea4a42011-01-24 01:43:30 +0000450 ssize_t
451 center,
452 y;
453
cristy3ed852e2009-09-05 21:47:34 +0000454 /*
455 Swap quadrants.
456 */
cristybb503372010-05-27 20:51:26 +0000457 center=(ssize_t) floor((double) width/2L)+1L;
cristy699ae5b2013-07-03 13:41:29 +0000458 status=RollFourier((size_t) center,height,0L,(ssize_t) height/2L,
459 source_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000460 if (status == MagickFalse)
461 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000462 for (y=0L; y < (ssize_t) height; y++)
463 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy3d04ed72013-08-18 17:51:01 +0000464 forward_pixels[y*width+x+width/2L]=source_pixels[y*center+x];
cristybb503372010-05-27 20:51:26 +0000465 for (y=1; y < (ssize_t) height; y++)
466 for (x=0L; x < (ssize_t) (width/2L-1L); x++)
cristy9b1d6c72013-08-18 17:53:56 +0000467 forward_pixels[(height-y)*width+width/2L-x-1L]=
cristy3d04ed72013-08-18 17:51:01 +0000468 source_pixels[y*center+x+1L];
cristybb503372010-05-27 20:51:26 +0000469 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy699ae5b2013-07-03 13:41:29 +0000470 forward_pixels[-x+width/2L-1L]=forward_pixels[x+width/2L+1L];
cristy3ed852e2009-09-05 21:47:34 +0000471 return(MagickTrue);
472}
473
cristyc4ea4a42011-01-24 01:43:30 +0000474static void CorrectPhaseLHS(const size_t width,const size_t height,
cristy699ae5b2013-07-03 13:41:29 +0000475 double *fourier_pixels)
cristy3ed852e2009-09-05 21:47:34 +0000476{
cristybb503372010-05-27 20:51:26 +0000477 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000478 x;
479
cristy9d314ff2011-03-09 01:30:28 +0000480 ssize_t
481 y;
482
cristybb503372010-05-27 20:51:26 +0000483 for (y=0L; y < (ssize_t) height; y++)
484 for (x=0L; x < (ssize_t) (width/2L); x++)
cristy699ae5b2013-07-03 13:41:29 +0000485 fourier_pixels[y*width+x]*=(-1.0);
cristy3ed852e2009-09-05 21:47:34 +0000486}
487
488static MagickBooleanType ForwardFourier(const FourierInfo *fourier_info,
489 Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
490{
491 CacheView
492 *magnitude_view,
493 *phase_view;
494
495 double
cristy7d4aa382013-06-30 01:59:39 +0000496 *magnitude_pixels,
497 *phase_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000498
499 Image
500 *magnitude_image,
501 *phase_image;
502
cristy3ed852e2009-09-05 21:47:34 +0000503 MagickBooleanType
504 status;
505
cristy7d4aa382013-06-30 01:59:39 +0000506 MemoryInfo
507 *magnitude_info,
508 *phase_info;
509
cristy4c08aed2011-07-01 19:47:50 +0000510 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000511 *q;
512
cristyf5742792012-11-27 18:31:26 +0000513 register ssize_t
514 x;
515
cristyc4ea4a42011-01-24 01:43:30 +0000516 ssize_t
517 i,
518 y;
519
cristy3ed852e2009-09-05 21:47:34 +0000520 magnitude_image=GetFirstImageInList(image);
521 phase_image=GetNextImageInList(image);
522 if (phase_image == (Image *) NULL)
523 {
524 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
cristyefe601c2013-01-05 17:51:12 +0000525 "TwoOrMoreImagesRequired","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000526 return(MagickFalse);
527 }
528 /*
529 Create "Fourier Transform" image from constituent arrays.
530 */
cristy7d4aa382013-06-30 01:59:39 +0000531 magnitude_info=AcquireVirtualMemory((size_t) fourier_info->height,
532 fourier_info->width*sizeof(*magnitude_pixels));
533 phase_info=AcquireVirtualMemory((size_t) fourier_info->height,
534 fourier_info->width*sizeof(*phase_pixels));
cristybb3c02e2013-07-02 00:43:10 +0000535 if ((magnitude_info == (MemoryInfo *) NULL) ||
536 (phase_info == (MemoryInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000537 {
cristy7d4aa382013-06-30 01:59:39 +0000538 if (phase_info != (MemoryInfo *) NULL)
539 phase_info=RelinquishVirtualMemory(phase_info);
540 if (magnitude_info != (MemoryInfo *) NULL)
541 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000542 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000543 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000544 return(MagickFalse);
545 }
cristy7d4aa382013-06-30 01:59:39 +0000546 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
547 (void) ResetMagickMemory(magnitude_pixels,0,fourier_info->height*
548 fourier_info->width*sizeof(*magnitude_pixels));
cristybb3c02e2013-07-02 00:43:10 +0000549 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
cristy7d4aa382013-06-30 01:59:39 +0000550 (void) ResetMagickMemory(phase_pixels,0,fourier_info->height*
551 fourier_info->width*sizeof(*phase_pixels));
cristy13c99c42013-08-18 13:53:30 +0000552 status=ForwardQuadrantSwap(fourier_info->width,fourier_info->height,
cristy7d4aa382013-06-30 01:59:39 +0000553 magnitude,magnitude_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000554 if (status != MagickFalse)
cristy13c99c42013-08-18 13:53:30 +0000555 status=ForwardQuadrantSwap(fourier_info->width,fourier_info->height,phase,
cristy7d4aa382013-06-30 01:59:39 +0000556 phase_pixels);
cristy13c99c42013-08-18 13:53:30 +0000557 CorrectPhaseLHS(fourier_info->width,fourier_info->height,phase_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000558 if (fourier_info->modulus != MagickFalse)
559 {
560 i=0L;
cristybb503372010-05-27 20:51:26 +0000561 for (y=0L; y < (ssize_t) fourier_info->height; y++)
562 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000563 {
cristy7d4aa382013-06-30 01:59:39 +0000564 phase_pixels[i]/=(2.0*MagickPI);
565 phase_pixels[i]+=0.5;
cristy3ed852e2009-09-05 21:47:34 +0000566 i++;
567 }
568 }
cristy46ff2672012-12-14 15:32:26 +0000569 magnitude_view=AcquireAuthenticCacheView(magnitude_image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000570 i=0L;
cristybb503372010-05-27 20:51:26 +0000571 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000572 {
573 q=GetCacheViewAuthenticPixels(magnitude_view,0L,y,fourier_info->height,1UL,
574 exception);
cristyacd2ed22011-08-30 01:44:23 +0000575 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000576 break;
cristybb503372010-05-27 20:51:26 +0000577 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000578 {
579 switch (fourier_info->channel)
580 {
cristyd3090f92011-10-18 00:05:15 +0000581 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000582 default:
583 {
cristy4c08aed2011-07-01 19:47:50 +0000584 SetPixelRed(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000585 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000586 break;
587 }
cristyd3090f92011-10-18 00:05:15 +0000588 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000589 {
cristy4c08aed2011-07-01 19:47:50 +0000590 SetPixelGreen(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000591 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000592 break;
593 }
cristyd3090f92011-10-18 00:05:15 +0000594 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000595 {
cristy4c08aed2011-07-01 19:47:50 +0000596 SetPixelBlue(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000597 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000598 break;
599 }
cristyd3090f92011-10-18 00:05:15 +0000600 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000601 {
cristy4c08aed2011-07-01 19:47:50 +0000602 SetPixelBlack(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000603 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000604 break;
605 }
cristyd3090f92011-10-18 00:05:15 +0000606 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000607 {
cristy4c08aed2011-07-01 19:47:50 +0000608 SetPixelAlpha(magnitude_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000609 magnitude_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000610 break;
611 }
cristy3ed852e2009-09-05 21:47:34 +0000612 }
613 i++;
cristyed231572011-07-14 02:18:59 +0000614 q+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +0000615 }
616 status=SyncCacheViewAuthenticPixels(magnitude_view,exception);
617 if (status == MagickFalse)
618 break;
619 }
cristydb070952012-04-20 14:33:00 +0000620 magnitude_view=DestroyCacheView(magnitude_view);
cristy3ed852e2009-09-05 21:47:34 +0000621 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000622 phase_view=AcquireAuthenticCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +0000623 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000624 {
625 q=GetCacheViewAuthenticPixels(phase_view,0L,y,fourier_info->height,1UL,
626 exception);
cristyacd2ed22011-08-30 01:44:23 +0000627 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000628 break;
cristybb503372010-05-27 20:51:26 +0000629 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000630 {
631 switch (fourier_info->channel)
632 {
cristyd3090f92011-10-18 00:05:15 +0000633 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000634 default:
635 {
cristy4c08aed2011-07-01 19:47:50 +0000636 SetPixelRed(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000637 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000638 break;
639 }
cristyd3090f92011-10-18 00:05:15 +0000640 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000641 {
cristy4c08aed2011-07-01 19:47:50 +0000642 SetPixelGreen(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000643 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000644 break;
645 }
cristyd3090f92011-10-18 00:05:15 +0000646 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000647 {
cristy4c08aed2011-07-01 19:47:50 +0000648 SetPixelBlue(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000649 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000650 break;
651 }
cristyd3090f92011-10-18 00:05:15 +0000652 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000653 {
cristy4c08aed2011-07-01 19:47:50 +0000654 SetPixelBlack(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000655 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000656 break;
657 }
cristyd3090f92011-10-18 00:05:15 +0000658 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000659 {
cristy4c08aed2011-07-01 19:47:50 +0000660 SetPixelAlpha(phase_image,ClampToQuantum(QuantumRange*
cristy7d4aa382013-06-30 01:59:39 +0000661 phase_pixels[i]),q);
cristy3ed852e2009-09-05 21:47:34 +0000662 break;
663 }
cristy3ed852e2009-09-05 21:47:34 +0000664 }
665 i++;
cristyed231572011-07-14 02:18:59 +0000666 q+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +0000667 }
668 status=SyncCacheViewAuthenticPixels(phase_view,exception);
669 if (status == MagickFalse)
670 break;
671 }
672 phase_view=DestroyCacheView(phase_view);
cristy7d4aa382013-06-30 01:59:39 +0000673 phase_info=RelinquishVirtualMemory(phase_info);
674 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000675 return(status);
676}
677
678static MagickBooleanType ForwardFourierTransform(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +0000679 const Image *image,double *magnitude_pixels,double *phase_pixels,
680 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000681{
682 CacheView
683 *image_view;
684
cristy99dc0362013-09-15 18:32:54 +0000685 const char
686 *value;
687
cristy3ed852e2009-09-05 21:47:34 +0000688 double
cristybb3c02e2013-07-02 00:43:10 +0000689 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000690
691 fftw_complex
cristy699ae5b2013-07-03 13:41:29 +0000692 *forward_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000693
694 fftw_plan
695 fftw_r2c_plan;
696
cristy7d4aa382013-06-30 01:59:39 +0000697 MemoryInfo
cristy699ae5b2013-07-03 13:41:29 +0000698 *forward_info,
cristy7d4aa382013-06-30 01:59:39 +0000699 *source_info;
700
cristy4c08aed2011-07-01 19:47:50 +0000701 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000702 *p;
703
cristybb503372010-05-27 20:51:26 +0000704 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000705 i,
706 x;
707
cristyc4ea4a42011-01-24 01:43:30 +0000708 ssize_t
709 y;
710
cristy3ed852e2009-09-05 21:47:34 +0000711 /*
712 Generate the forward Fourier transform.
713 */
cristy7d4aa382013-06-30 01:59:39 +0000714 source_info=AcquireVirtualMemory((size_t) fourier_info->height,
cristybb3c02e2013-07-02 00:43:10 +0000715 fourier_info->width*sizeof(*source_pixels));
cristy7d4aa382013-06-30 01:59:39 +0000716 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000717 {
718 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000719 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000720 return(MagickFalse);
721 }
cristybb3c02e2013-07-02 00:43:10 +0000722 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
723 ResetMagickMemory(source_pixels,0,fourier_info->height*fourier_info->width*
724 sizeof(*source_pixels));
cristy3ed852e2009-09-05 21:47:34 +0000725 i=0L;
cristy46ff2672012-12-14 15:32:26 +0000726 image_view=AcquireVirtualCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +0000727 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000728 {
729 p=GetCacheViewVirtualPixels(image_view,0L,y,fourier_info->width,1UL,
730 exception);
cristy4c08aed2011-07-01 19:47:50 +0000731 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000732 break;
cristybb503372010-05-27 20:51:26 +0000733 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000734 {
735 switch (fourier_info->channel)
736 {
cristyd3090f92011-10-18 00:05:15 +0000737 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000738 default:
739 {
cristybb3c02e2013-07-02 00:43:10 +0000740 source_pixels[i]=QuantumScale*GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000741 break;
742 }
cristyd3090f92011-10-18 00:05:15 +0000743 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000744 {
cristybb3c02e2013-07-02 00:43:10 +0000745 source_pixels[i]=QuantumScale*GetPixelGreen(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000746 break;
747 }
cristyd3090f92011-10-18 00:05:15 +0000748 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000749 {
cristybb3c02e2013-07-02 00:43:10 +0000750 source_pixels[i]=QuantumScale*GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000751 break;
752 }
cristyd3090f92011-10-18 00:05:15 +0000753 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000754 {
cristybb3c02e2013-07-02 00:43:10 +0000755 source_pixels[i]=QuantumScale*GetPixelBlack(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000756 break;
757 }
cristyd3090f92011-10-18 00:05:15 +0000758 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +0000759 {
cristybb3c02e2013-07-02 00:43:10 +0000760 source_pixels[i]=QuantumScale*GetPixelAlpha(image,p);
cristy3ed852e2009-09-05 21:47:34 +0000761 break;
762 }
cristy3ed852e2009-09-05 21:47:34 +0000763 }
764 i++;
cristyed231572011-07-14 02:18:59 +0000765 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000766 }
767 }
768 image_view=DestroyCacheView(image_view);
cristy699ae5b2013-07-03 13:41:29 +0000769 forward_info=AcquireVirtualMemory((size_t) fourier_info->height,
770 fourier_info->center*sizeof(*forward_pixels));
771 if (forward_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000772 {
773 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000774 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristybb3c02e2013-07-02 00:43:10 +0000775 source_info=(MemoryInfo *) RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +0000776 return(MagickFalse);
777 }
cristy699ae5b2013-07-03 13:41:29 +0000778 forward_pixels=(fftw_complex *) GetVirtualMemoryBlob(forward_info);
cristyb5d5f722009-11-04 03:03:49 +0000779#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000780 #pragma omp critical (MagickCore_ForwardFourierTransform)
781#endif
cristy70841a12012-10-27 20:52:57 +0000782 fftw_r2c_plan=fftw_plan_dft_r2c_2d(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +0000783 source_pixels,forward_pixels,FFTW_ESTIMATE);
cristy3ed852e2009-09-05 21:47:34 +0000784 fftw_execute(fftw_r2c_plan);
785 fftw_destroy_plan(fftw_r2c_plan);
cristybb3c02e2013-07-02 00:43:10 +0000786 source_info=(MemoryInfo *) RelinquishVirtualMemory(source_info);
cristy99dc0362013-09-15 18:32:54 +0000787 value=GetImageArtifact(image,"fourier:normalize");
cristyf8ecbfd2013-09-15 19:11:14 +0000788 if ((value == (const char *) NULL) || (LocaleCompare(value,"forward") == 0))
cristy56ed31c2010-03-22 00:46:21 +0000789 {
cristy99dc0362013-09-15 18:32:54 +0000790 double
791 gamma;
792
793 /*
794 Normalize Fourier transform.
795 */
796 i=0L;
797 gamma=PerceptibleReciprocal((double) fourier_info->width*
798 fourier_info->height);
799 for (y=0L; y < (ssize_t) fourier_info->height; y++)
800 for (x=0L; x < (ssize_t) fourier_info->center; x++)
801 {
cristy56ed31c2010-03-22 00:46:21 +0000802#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy99dc0362013-09-15 18:32:54 +0000803 forward_pixels[i]*=gamma;
cristy56ed31c2010-03-22 00:46:21 +0000804#else
cristy99dc0362013-09-15 18:32:54 +0000805 forward_pixels[i][0]*=gamma;
806 forward_pixels[i][1]*=gamma;
cristy56ed31c2010-03-22 00:46:21 +0000807#endif
cristy99dc0362013-09-15 18:32:54 +0000808 i++;
809 }
cristy56ed31c2010-03-22 00:46:21 +0000810 }
cristy3ed852e2009-09-05 21:47:34 +0000811 /*
812 Generate magnitude and phase (or real and imaginary).
813 */
814 i=0L;
815 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000816 for (y=0L; y < (ssize_t) fourier_info->height; y++)
817 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000818 {
cristy699ae5b2013-07-03 13:41:29 +0000819 magnitude_pixels[i]=cabs(forward_pixels[i]);
820 phase_pixels[i]=carg(forward_pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000821 i++;
822 }
823 else
cristybb503372010-05-27 20:51:26 +0000824 for (y=0L; y < (ssize_t) fourier_info->height; y++)
825 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +0000826 {
cristy699ae5b2013-07-03 13:41:29 +0000827 magnitude_pixels[i]=creal(forward_pixels[i]);
828 phase_pixels[i]=cimag(forward_pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000829 i++;
830 }
cristy699ae5b2013-07-03 13:41:29 +0000831 forward_info=(MemoryInfo *) RelinquishVirtualMemory(forward_info);
cristy3ed852e2009-09-05 21:47:34 +0000832 return(MagickTrue);
833}
834
835static MagickBooleanType ForwardFourierTransformChannel(const Image *image,
cristyd3090f92011-10-18 00:05:15 +0000836 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +0000837 Image *fourier_image,ExceptionInfo *exception)
838{
839 double
cristyce9fe782013-07-03 00:59:41 +0000840 *magnitude_pixels,
841 *phase_pixels;
cristybb3c02e2013-07-02 00:43:10 +0000842
cristy56ed31c2010-03-22 00:46:21 +0000843 FourierInfo
844 fourier_info;
845
cristyc4ea4a42011-01-24 01:43:30 +0000846 MagickBooleanType
847 status;
848
cristyce9fe782013-07-03 00:59:41 +0000849 MemoryInfo
850 *magnitude_info,
851 *phase_info;
852
cristy3ed852e2009-09-05 21:47:34 +0000853 size_t
854 extent;
855
856 fourier_info.width=image->columns;
cristy740b4f12013-10-08 19:51:07 +0000857 fourier_info.height=image->rows;
cristy3ed852e2009-09-05 21:47:34 +0000858 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
859 ((image->rows % 2) != 0))
860 {
861 extent=image->columns < image->rows ? image->rows : image->columns;
862 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
863 }
864 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +0000865 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +0000866 fourier_info.channel=channel;
867 fourier_info.modulus=modulus;
cristyce9fe782013-07-03 00:59:41 +0000868 magnitude_info=AcquireVirtualMemory((size_t) fourier_info.height,
869 fourier_info.center*sizeof(*magnitude_pixels));
870 phase_info=AcquireVirtualMemory((size_t) fourier_info.height,
871 fourier_info.center*sizeof(*phase_pixels));
872 if ((magnitude_info == (MemoryInfo *) NULL) ||
873 (phase_info == (MemoryInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000874 {
cristyce9fe782013-07-03 00:59:41 +0000875 if (phase_info != (MemoryInfo *) NULL)
876 phase_info=RelinquishVirtualMemory(phase_info);
877 if (magnitude_info == (MemoryInfo *) NULL)
878 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000879 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +0000880 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000881 return(MagickFalse);
882 }
cristyce9fe782013-07-03 00:59:41 +0000883 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
884 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
885 status=ForwardFourierTransform(&fourier_info,image,magnitude_pixels,
886 phase_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +0000887 if (status != MagickFalse)
cristyce9fe782013-07-03 00:59:41 +0000888 status=ForwardFourier(&fourier_info,fourier_image,magnitude_pixels,
889 phase_pixels,exception);
890 phase_info=RelinquishVirtualMemory(phase_info);
891 magnitude_info=RelinquishVirtualMemory(magnitude_info);
cristy3ed852e2009-09-05 21:47:34 +0000892 return(status);
893}
894#endif
895
896MagickExport Image *ForwardFourierTransformImage(const Image *image,
897 const MagickBooleanType modulus,ExceptionInfo *exception)
898{
899 Image
900 *fourier_image;
901
902 fourier_image=NewImageList();
903#if !defined(MAGICKCORE_FFTW_DELEGATE)
904 (void) modulus;
905 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000906 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristy3ed852e2009-09-05 21:47:34 +0000907 image->filename);
908#else
909 {
910 Image
911 *magnitude_image;
912
cristybb503372010-05-27 20:51:26 +0000913 size_t
cristy3ed852e2009-09-05 21:47:34 +0000914 extent,
cristyc9721ff2013-10-08 19:40:01 +0000915 height,
cristy3ed852e2009-09-05 21:47:34 +0000916 width;
917
918 width=image->columns;
cristyc9721ff2013-10-08 19:40:01 +0000919 height=image->rows;
cristy3ed852e2009-09-05 21:47:34 +0000920 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
921 ((image->rows % 2) != 0))
922 {
923 extent=image->columns < image->rows ? image->rows : image->columns;
924 width=(extent & 0x01) == 1 ? extent+1UL : extent;
925 }
cristyc9721ff2013-10-08 19:40:01 +0000926 height=width;
927 magnitude_image=CloneImage(image,width,height,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +0000928 if (magnitude_image != (Image *) NULL)
929 {
930 Image
931 *phase_image;
932
933 magnitude_image->storage_class=DirectClass;
934 magnitude_image->depth=32UL;
cristyc9721ff2013-10-08 19:40:01 +0000935 phase_image=CloneImage(image,width,height,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +0000936 if (phase_image == (Image *) NULL)
937 magnitude_image=DestroyImage(magnitude_image);
938 else
939 {
940 MagickBooleanType
941 is_gray,
942 status;
943
cristy3ed852e2009-09-05 21:47:34 +0000944 phase_image->storage_class=DirectClass;
945 phase_image->depth=32UL;
946 AppendImageToList(&fourier_image,magnitude_image);
947 AppendImageToList(&fourier_image,phase_image);
948 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000949 is_gray=IsImageGray(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000950#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy564a5692012-01-20 23:56:26 +0000951 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +0000952#endif
cristy3ed852e2009-09-05 21:47:34 +0000953 {
cristyb34ef052010-10-07 00:12:05 +0000954#if defined(MAGICKCORE_OPENMP_SUPPORT)
955 #pragma omp section
956#endif
cristy3ed852e2009-09-05 21:47:34 +0000957 {
cristyb34ef052010-10-07 00:12:05 +0000958 MagickBooleanType
959 thread_status;
960
961 if (is_gray != MagickFalse)
962 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000963 GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000964 else
965 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000966 RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000967 if (thread_status == MagickFalse)
968 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +0000969 }
cristyb34ef052010-10-07 00:12:05 +0000970#if defined(MAGICKCORE_OPENMP_SUPPORT)
971 #pragma omp section
972#endif
973 {
974 MagickBooleanType
975 thread_status;
976
977 thread_status=MagickTrue;
978 if (is_gray == MagickFalse)
979 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000980 GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000981 if (thread_status == MagickFalse)
982 status=thread_status;
983 }
984#if defined(MAGICKCORE_OPENMP_SUPPORT)
985 #pragma omp section
986#endif
987 {
988 MagickBooleanType
989 thread_status;
990
991 thread_status=MagickTrue;
992 if (is_gray == MagickFalse)
993 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +0000994 BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +0000995 if (thread_status == MagickFalse)
996 status=thread_status;
997 }
998#if defined(MAGICKCORE_OPENMP_SUPPORT)
999 #pragma omp section
1000#endif
1001 {
1002 MagickBooleanType
1003 thread_status;
1004
1005 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001006 if (image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +00001007 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +00001008 BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001009 if (thread_status == MagickFalse)
1010 status=thread_status;
1011 }
1012#if defined(MAGICKCORE_OPENMP_SUPPORT)
1013 #pragma omp section
1014#endif
1015 {
1016 MagickBooleanType
1017 thread_status;
1018
1019 thread_status=MagickTrue;
cristy8a46d822012-08-28 23:32:39 +00001020 if (image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +00001021 thread_status=ForwardFourierTransformChannel(image,
cristyd3090f92011-10-18 00:05:15 +00001022 AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001023 if (thread_status == MagickFalse)
1024 status=thread_status;
1025 }
cristy3ed852e2009-09-05 21:47:34 +00001026 }
1027 if (status == MagickFalse)
1028 fourier_image=DestroyImageList(fourier_image);
1029 fftw_cleanup();
1030 }
1031 }
1032 }
1033#endif
1034 return(fourier_image);
1035}
1036
1037/*
1038%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1039% %
1040% %
1041% %
1042% 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 %
1043% %
1044% %
1045% %
1046%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1047%
1048% InverseFourierTransformImage() implements the inverse discrete Fourier
1049% transform (DFT) of the image either as a magnitude / phase or real /
1050% imaginary image pair.
1051%
1052% The format of the InverseFourierTransformImage method is:
1053%
cristyc9550792009-11-13 20:05:42 +00001054% Image *InverseFourierTransformImage(const Image *magnitude_image,
1055% const Image *phase_image,const MagickBooleanType modulus,
1056% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001057%
1058% A description of each parameter follows:
1059%
cristyc9550792009-11-13 20:05:42 +00001060% o magnitude_image: the magnitude or real image.
1061%
1062% o phase_image: the phase or imaginary image.
cristy3ed852e2009-09-05 21:47:34 +00001063%
1064% o modulus: if true, return transform as a magnitude / phase pair
1065% otherwise a real / imaginary image pair.
1066%
1067% o exception: return any errors or warnings in this structure.
1068%
1069*/
1070
1071#if defined(MAGICKCORE_FFTW_DELEGATE)
cristybb503372010-05-27 20:51:26 +00001072static MagickBooleanType InverseQuadrantSwap(const size_t width,
1073 const size_t height,const double *source,double *destination)
cristy3ed852e2009-09-05 21:47:34 +00001074{
cristyc4ea4a42011-01-24 01:43:30 +00001075 register ssize_t
1076 x;
1077
cristybb503372010-05-27 20:51:26 +00001078 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001079 center,
1080 y;
1081
cristy3ed852e2009-09-05 21:47:34 +00001082 /*
1083 Swap quadrants.
1084 */
cristy233fe582012-07-07 14:00:18 +00001085 center=(ssize_t) floor((double) width/2L)+1L;
cristybb503372010-05-27 20:51:26 +00001086 for (y=1L; y < (ssize_t) height; y++)
1087 for (x=0L; x < (ssize_t) (width/2L+1L); x++)
cristy9b1d6c72013-08-18 17:53:56 +00001088 destination[(height-y)*center-x+width/2L]=source[y*width+x];
cristybb503372010-05-27 20:51:26 +00001089 for (y=0L; y < (ssize_t) height; y++)
cristy3d04ed72013-08-18 17:51:01 +00001090 destination[y*center]=source[y*width+width/2L];
cristy3ed852e2009-09-05 21:47:34 +00001091 for (x=0L; x < center; x++)
1092 destination[x]=source[center-x-1L];
cristybb503372010-05-27 20:51:26 +00001093 return(RollFourier(center,height,0L,(ssize_t) height/-2L,destination));
cristy3ed852e2009-09-05 21:47:34 +00001094}
1095
1096static MagickBooleanType InverseFourier(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +00001097 const Image *magnitude_image,const Image *phase_image,
1098 fftw_complex *fourier_pixels,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001099{
1100 CacheView
1101 *magnitude_view,
1102 *phase_view;
1103
1104 double
cristy699ae5b2013-07-03 13:41:29 +00001105 *inverse_pixels,
cristy7d4aa382013-06-30 01:59:39 +00001106 *magnitude_pixels,
1107 *phase_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001108
cristy3ed852e2009-09-05 21:47:34 +00001109 MagickBooleanType
1110 status;
1111
cristy699ae5b2013-07-03 13:41:29 +00001112 MemoryInfo
1113 *inverse_info,
1114 *magnitude_info,
1115 *phase_info;
1116
cristy4c08aed2011-07-01 19:47:50 +00001117 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001118 *p;
1119
cristybb503372010-05-27 20:51:26 +00001120 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001121 i,
1122 x;
1123
cristyc4ea4a42011-01-24 01:43:30 +00001124 ssize_t
1125 y;
1126
cristy3ed852e2009-09-05 21:47:34 +00001127 /*
1128 Inverse fourier - read image and break down into a double array.
1129 */
cristy699ae5b2013-07-03 13:41:29 +00001130 magnitude_info=AcquireVirtualMemory((size_t)fourier_info->height,
1131 fourier_info->width*sizeof(*magnitude_pixels));
1132 phase_info=AcquireVirtualMemory((size_t) fourier_info->height,
cristybb3c02e2013-07-02 00:43:10 +00001133 fourier_info->width*sizeof(*phase_pixels));
cristy699ae5b2013-07-03 13:41:29 +00001134 inverse_info=AcquireVirtualMemory((size_t) fourier_info->height,
1135 fourier_info->center*sizeof(*inverse_pixels));
1136 if ((magnitude_info == (MemoryInfo *) NULL) ||
1137 (phase_info == (MemoryInfo *) NULL) ||
1138 (inverse_info == (MemoryInfo *) NULL))
cristybb3c02e2013-07-02 00:43:10 +00001139 {
cristy699ae5b2013-07-03 13:41:29 +00001140 if (magnitude_info != (MemoryInfo *) NULL)
1141 magnitude_info=RelinquishVirtualMemory(magnitude_info);
1142 if (phase_info != (MemoryInfo *) NULL)
1143 phase_info=RelinquishVirtualMemory(phase_info);
1144 if (inverse_info != (MemoryInfo *) NULL)
1145 inverse_info=RelinquishVirtualMemory(inverse_info);
cristybb3c02e2013-07-02 00:43:10 +00001146 (void) ThrowMagickException(exception,GetMagickModule(),
1147 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1148 magnitude_image->filename);
cristybb3c02e2013-07-02 00:43:10 +00001149 return(MagickFalse);
1150 }
cristy699ae5b2013-07-03 13:41:29 +00001151 magnitude_pixels=(double *) GetVirtualMemoryBlob(magnitude_info);
1152 phase_pixels=(double *) GetVirtualMemoryBlob(phase_info);
1153 inverse_pixels=(double *) GetVirtualMemoryBlob(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +00001154 i=0L;
cristy46ff2672012-12-14 15:32:26 +00001155 magnitude_view=AcquireVirtualCacheView(magnitude_image,exception);
cristybb503372010-05-27 20:51:26 +00001156 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001157 {
1158 p=GetCacheViewVirtualPixels(magnitude_view,0L,y,fourier_info->width,1UL,
1159 exception);
cristy4c08aed2011-07-01 19:47:50 +00001160 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001161 break;
cristybb503372010-05-27 20:51:26 +00001162 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001163 {
1164 switch (fourier_info->channel)
1165 {
cristyd3090f92011-10-18 00:05:15 +00001166 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001167 default:
1168 {
cristy7d4aa382013-06-30 01:59:39 +00001169 magnitude_pixels[i]=QuantumScale*GetPixelRed(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001170 break;
1171 }
cristyd3090f92011-10-18 00:05:15 +00001172 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001173 {
cristy7d4aa382013-06-30 01:59:39 +00001174 magnitude_pixels[i]=QuantumScale*GetPixelGreen(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001175 break;
1176 }
cristyd3090f92011-10-18 00:05:15 +00001177 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001178 {
cristy7d4aa382013-06-30 01:59:39 +00001179 magnitude_pixels[i]=QuantumScale*GetPixelBlue(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001180 break;
1181 }
cristyd3090f92011-10-18 00:05:15 +00001182 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001183 {
cristy7d4aa382013-06-30 01:59:39 +00001184 magnitude_pixels[i]=QuantumScale*GetPixelBlack(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001185 break;
1186 }
cristyd3090f92011-10-18 00:05:15 +00001187 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001188 {
cristy7d4aa382013-06-30 01:59:39 +00001189 magnitude_pixels[i]=QuantumScale*GetPixelAlpha(magnitude_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001190 break;
1191 }
cristy3ed852e2009-09-05 21:47:34 +00001192 }
1193 i++;
cristyed231572011-07-14 02:18:59 +00001194 p+=GetPixelChannels(magnitude_image);
cristy3ed852e2009-09-05 21:47:34 +00001195 }
1196 }
cristy699ae5b2013-07-03 13:41:29 +00001197 magnitude_view=DestroyCacheView(magnitude_view);
1198 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
1199 magnitude_pixels,inverse_pixels);
1200 (void) CopyMagickMemory(magnitude_pixels,inverse_pixels,fourier_info->height*
1201 fourier_info->center*sizeof(*magnitude_pixels));
cristy3ed852e2009-09-05 21:47:34 +00001202 i=0L;
cristy46ff2672012-12-14 15:32:26 +00001203 phase_view=AcquireVirtualCacheView(phase_image,exception);
cristybb503372010-05-27 20:51:26 +00001204 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001205 {
1206 p=GetCacheViewVirtualPixels(phase_view,0,y,fourier_info->width,1,
1207 exception);
cristy4c08aed2011-07-01 19:47:50 +00001208 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001209 break;
cristybb503372010-05-27 20:51:26 +00001210 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001211 {
1212 switch (fourier_info->channel)
1213 {
cristyd3090f92011-10-18 00:05:15 +00001214 case RedPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001215 default:
1216 {
cristy7d4aa382013-06-30 01:59:39 +00001217 phase_pixels[i]=QuantumScale*GetPixelRed(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001218 break;
1219 }
cristyd3090f92011-10-18 00:05:15 +00001220 case GreenPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001221 {
cristy7d4aa382013-06-30 01:59:39 +00001222 phase_pixels[i]=QuantumScale*GetPixelGreen(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001223 break;
1224 }
cristyd3090f92011-10-18 00:05:15 +00001225 case BluePixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001226 {
cristy7d4aa382013-06-30 01:59:39 +00001227 phase_pixels[i]=QuantumScale*GetPixelBlue(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001228 break;
1229 }
cristyd3090f92011-10-18 00:05:15 +00001230 case BlackPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001231 {
cristy7d4aa382013-06-30 01:59:39 +00001232 phase_pixels[i]=QuantumScale*GetPixelBlack(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001233 break;
1234 }
cristyd3090f92011-10-18 00:05:15 +00001235 case AlphaPixelChannel:
cristy3ed852e2009-09-05 21:47:34 +00001236 {
cristy7d4aa382013-06-30 01:59:39 +00001237 phase_pixels[i]=QuantumScale*GetPixelAlpha(phase_image,p);
cristy3ed852e2009-09-05 21:47:34 +00001238 break;
1239 }
cristy3ed852e2009-09-05 21:47:34 +00001240 }
1241 i++;
cristyed231572011-07-14 02:18:59 +00001242 p+=GetPixelChannels(phase_image);
cristy3ed852e2009-09-05 21:47:34 +00001243 }
1244 }
1245 if (fourier_info->modulus != MagickFalse)
1246 {
1247 i=0L;
cristybb503372010-05-27 20:51:26 +00001248 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1249 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001250 {
cristy7d4aa382013-06-30 01:59:39 +00001251 phase_pixels[i]-=0.5;
1252 phase_pixels[i]*=(2.0*MagickPI);
cristy3ed852e2009-09-05 21:47:34 +00001253 i++;
1254 }
1255 }
cristy3ed852e2009-09-05 21:47:34 +00001256 phase_view=DestroyCacheView(phase_view);
cristy13c99c42013-08-18 13:53:30 +00001257 CorrectPhaseLHS(fourier_info->width,fourier_info->height,phase_pixels);
cristy3ed852e2009-09-05 21:47:34 +00001258 if (status != MagickFalse)
1259 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +00001260 phase_pixels,inverse_pixels);
1261 (void) CopyMagickMemory(phase_pixels,inverse_pixels,fourier_info->height*
1262 fourier_info->center*sizeof(*phase_pixels));
1263 inverse_info=RelinquishVirtualMemory(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +00001264 /*
1265 Merge two sets.
1266 */
1267 i=0L;
1268 if (fourier_info->modulus != MagickFalse)
cristybb503372010-05-27 20:51:26 +00001269 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1270 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001271 {
cristy56ed31c2010-03-22 00:46:21 +00001272#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy699ae5b2013-07-03 13:41:29 +00001273 fourier_pixels[i]=magnitude_pixels[i]*cos(phase_pixels[i])+I*
1274 magnitude_pixels[i]*sin(phase_pixels[i]);
cristy56ed31c2010-03-22 00:46:21 +00001275#else
cristy699ae5b2013-07-03 13:41:29 +00001276 fourier_pixels[i][0]=magnitude_pixels[i]*cos(phase_pixels[i]);
1277 fourier_pixels[i][1]=magnitude_pixels[i]*sin(phase_pixels[i]);
cristy56ed31c2010-03-22 00:46:21 +00001278#endif
cristy3ed852e2009-09-05 21:47:34 +00001279 i++;
1280 }
1281 else
cristybb503372010-05-27 20:51:26 +00001282 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1283 for (x=0L; x < (ssize_t) fourier_info->center; x++)
cristy3ed852e2009-09-05 21:47:34 +00001284 {
cristy56ed31c2010-03-22 00:46:21 +00001285#if defined(MAGICKCORE_HAVE_COMPLEX_H)
cristy699ae5b2013-07-03 13:41:29 +00001286 fourier_pixels[i]=magnitude_pixels[i]+I*phase_pixels[i];
cristy56ed31c2010-03-22 00:46:21 +00001287#else
cristy699ae5b2013-07-03 13:41:29 +00001288 fourier_pixels[i][0]=magnitude_pixels[i];
1289 fourier_pixels[i][1]=phase_pixels[i];
cristy56ed31c2010-03-22 00:46:21 +00001290#endif
cristy3ed852e2009-09-05 21:47:34 +00001291 i++;
1292 }
cristy699ae5b2013-07-03 13:41:29 +00001293 magnitude_info=RelinquishVirtualMemory(magnitude_info);
1294 phase_info=RelinquishVirtualMemory(phase_info);
cristy3ed852e2009-09-05 21:47:34 +00001295 return(status);
1296}
1297
1298static MagickBooleanType InverseFourierTransform(FourierInfo *fourier_info,
cristy699ae5b2013-07-03 13:41:29 +00001299 fftw_complex *fourier_pixels,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001300{
1301 CacheView
1302 *image_view;
1303
cristy99dc0362013-09-15 18:32:54 +00001304 const char
1305 *value;
1306
cristy3ed852e2009-09-05 21:47:34 +00001307 double
cristy699ae5b2013-07-03 13:41:29 +00001308 *source_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001309
1310 fftw_plan
1311 fftw_c2r_plan;
1312
cristy699ae5b2013-07-03 13:41:29 +00001313 MemoryInfo
1314 *source_info;
1315
cristy4c08aed2011-07-01 19:47:50 +00001316 register Quantum
cristyc4ea4a42011-01-24 01:43:30 +00001317 *q;
1318
cristybb503372010-05-27 20:51:26 +00001319 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001320 i,
1321 x;
1322
cristyc4ea4a42011-01-24 01:43:30 +00001323 ssize_t
1324 y;
cristy3ed852e2009-09-05 21:47:34 +00001325
cristy699ae5b2013-07-03 13:41:29 +00001326 source_info=AcquireVirtualMemory((size_t) fourier_info->height,
1327 fourier_info->width*sizeof(*source_pixels));
1328 if (source_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001329 {
1330 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001331 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001332 return(MagickFalse);
1333 }
cristy699ae5b2013-07-03 13:41:29 +00001334 source_pixels=(double *) GetVirtualMemoryBlob(source_info);
cristy99dc0362013-09-15 18:32:54 +00001335 value=GetImageArtifact(image,"fourier:normalize");
cristyf8ecbfd2013-09-15 19:11:14 +00001336 if (LocaleCompare(value,"inverse") == 0)
cristy99dc0362013-09-15 18:32:54 +00001337 {
1338 double
1339 gamma;
1340
1341 /*
1342 Normalize inverse transform.
1343 */
1344 i=0L;
1345 gamma=PerceptibleReciprocal((double) fourier_info->width*
1346 fourier_info->height);
1347 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1348 for (x=0L; x < (ssize_t) fourier_info->center; x++)
1349 {
1350#if defined(MAGICKCORE_HAVE_COMPLEX_H)
1351 fourier_pixels[i]*=gamma;
1352#else
1353 fourier_pixels[i][0]*=gamma;
1354 fourier_pixels[i][1]*=gamma;
1355#endif
1356 i++;
1357 }
1358 }
cristyb5d5f722009-11-04 03:03:49 +00001359#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001360 #pragma omp critical (MagickCore_InverseFourierTransform)
1361#endif
cristydf079ac2010-09-10 01:45:44 +00001362 {
1363 fftw_c2r_plan=fftw_plan_dft_c2r_2d(fourier_info->width,fourier_info->height,
cristy699ae5b2013-07-03 13:41:29 +00001364 fourier_pixels,source_pixels,FFTW_ESTIMATE);
cristydf079ac2010-09-10 01:45:44 +00001365 fftw_execute(fftw_c2r_plan);
1366 fftw_destroy_plan(fftw_c2r_plan);
1367 }
cristy3ed852e2009-09-05 21:47:34 +00001368 i=0L;
cristy46ff2672012-12-14 15:32:26 +00001369 image_view=AcquireAuthenticCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +00001370 for (y=0L; y < (ssize_t) fourier_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +00001371 {
cristy85812052010-09-14 17:56:15 +00001372 if (y >= (ssize_t) image->rows)
1373 break;
1374 q=GetCacheViewAuthenticPixels(image_view,0L,y,fourier_info->width >
1375 image->columns ? image->columns : fourier_info->width,1UL,exception);
cristyacd2ed22011-08-30 01:44:23 +00001376 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001377 break;
cristybb503372010-05-27 20:51:26 +00001378 for (x=0L; x < (ssize_t) fourier_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +00001379 {
cristy233fe582012-07-07 14:00:18 +00001380 if (x < (ssize_t) image->columns)
1381 switch (fourier_info->channel)
cristy3ed852e2009-09-05 21:47:34 +00001382 {
cristy233fe582012-07-07 14:00:18 +00001383 case RedPixelChannel:
1384 default:
1385 {
cristy699ae5b2013-07-03 13:41:29 +00001386 SetPixelRed(image,ClampToQuantum(QuantumRange*source_pixels[i]),q);
cristy233fe582012-07-07 14:00:18 +00001387 break;
1388 }
1389 case GreenPixelChannel:
1390 {
cristy699ae5b2013-07-03 13:41:29 +00001391 SetPixelGreen(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1392 q);
cristy233fe582012-07-07 14:00:18 +00001393 break;
1394 }
1395 case BluePixelChannel:
1396 {
cristy699ae5b2013-07-03 13:41:29 +00001397 SetPixelBlue(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1398 q);
cristy233fe582012-07-07 14:00:18 +00001399 break;
1400 }
1401 case BlackPixelChannel:
1402 {
cristy699ae5b2013-07-03 13:41:29 +00001403 SetPixelBlack(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1404 q);
cristy233fe582012-07-07 14:00:18 +00001405 break;
1406 }
1407 case AlphaPixelChannel:
1408 {
cristy699ae5b2013-07-03 13:41:29 +00001409 SetPixelAlpha(image,ClampToQuantum(QuantumRange*source_pixels[i]),
1410 q);
cristy233fe582012-07-07 14:00:18 +00001411 break;
1412 }
cristy3ed852e2009-09-05 21:47:34 +00001413 }
cristy3ed852e2009-09-05 21:47:34 +00001414 i++;
cristyed231572011-07-14 02:18:59 +00001415 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001416 }
1417 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1418 break;
1419 }
1420 image_view=DestroyCacheView(image_view);
cristy699ae5b2013-07-03 13:41:29 +00001421 source_info=RelinquishVirtualMemory(source_info);
cristy3ed852e2009-09-05 21:47:34 +00001422 return(MagickTrue);
1423}
1424
cristyc9550792009-11-13 20:05:42 +00001425static MagickBooleanType InverseFourierTransformChannel(
1426 const Image *magnitude_image,const Image *phase_image,
cristyd3090f92011-10-18 00:05:15 +00001427 const PixelChannel channel,const MagickBooleanType modulus,
cristy3ed852e2009-09-05 21:47:34 +00001428 Image *fourier_image,ExceptionInfo *exception)
1429{
cristy3ed852e2009-09-05 21:47:34 +00001430 fftw_complex
cristy699ae5b2013-07-03 13:41:29 +00001431 *inverse_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001432
1433 FourierInfo
1434 fourier_info;
1435
1436 MagickBooleanType
1437 status;
1438
cristy699ae5b2013-07-03 13:41:29 +00001439 MemoryInfo
1440 *inverse_info;
1441
cristy3ed852e2009-09-05 21:47:34 +00001442 size_t
1443 extent;
1444
cristyc9550792009-11-13 20:05:42 +00001445 fourier_info.width=magnitude_image->columns;
cristy0a69e642013-10-08 19:51:44 +00001446 fourier_info.height=magnitude_image->rows;
cristyc9550792009-11-13 20:05:42 +00001447 if ((magnitude_image->columns != magnitude_image->rows) ||
1448 ((magnitude_image->columns % 2) != 0) ||
1449 ((magnitude_image->rows % 2) != 0))
cristy3ed852e2009-09-05 21:47:34 +00001450 {
cristyc9550792009-11-13 20:05:42 +00001451 extent=magnitude_image->columns < magnitude_image->rows ?
1452 magnitude_image->rows : magnitude_image->columns;
cristy3ed852e2009-09-05 21:47:34 +00001453 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
1454 }
1455 fourier_info.height=fourier_info.width;
cristy233fe582012-07-07 14:00:18 +00001456 fourier_info.center=(ssize_t) floor((double) fourier_info.width/2L)+1L;
cristy3ed852e2009-09-05 21:47:34 +00001457 fourier_info.channel=channel;
1458 fourier_info.modulus=modulus;
cristy699ae5b2013-07-03 13:41:29 +00001459 inverse_info=AcquireVirtualMemory((size_t) fourier_info.height,
1460 fourier_info.center*sizeof(*inverse_pixels));
1461 if (inverse_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001462 {
1463 (void) ThrowMagickException(exception,GetMagickModule(),
cristyefe601c2013-01-05 17:51:12 +00001464 ResourceLimitError,"MemoryAllocationFailed","`%s'",
cristyc9550792009-11-13 20:05:42 +00001465 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001466 return(MagickFalse);
1467 }
cristy699ae5b2013-07-03 13:41:29 +00001468 inverse_pixels=(fftw_complex *) GetVirtualMemoryBlob(inverse_info);
1469 status=InverseFourier(&fourier_info,magnitude_image,phase_image,
1470 inverse_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +00001471 if (status != MagickFalse)
cristy699ae5b2013-07-03 13:41:29 +00001472 status=InverseFourierTransform(&fourier_info,inverse_pixels,fourier_image,
cristy3ed852e2009-09-05 21:47:34 +00001473 exception);
cristy699ae5b2013-07-03 13:41:29 +00001474 inverse_info=RelinquishVirtualMemory(inverse_info);
cristy3ed852e2009-09-05 21:47:34 +00001475 return(status);
1476}
1477#endif
1478
cristyc9550792009-11-13 20:05:42 +00001479MagickExport Image *InverseFourierTransformImage(const Image *magnitude_image,
1480 const Image *phase_image,const MagickBooleanType modulus,
1481 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001482{
1483 Image
1484 *fourier_image;
1485
cristyc9550792009-11-13 20:05:42 +00001486 assert(magnitude_image != (Image *) NULL);
1487 assert(magnitude_image->signature == MagickSignature);
1488 if (magnitude_image->debug != MagickFalse)
1489 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1490 magnitude_image->filename);
1491 if (phase_image == (Image *) NULL)
1492 {
1493 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
cristyefe601c2013-01-05 17:51:12 +00001494 "TwoOrMoreImagesRequired","`%s'",magnitude_image->filename);
cristy9372a152009-11-18 01:42:16 +00001495 return((Image *) NULL);
cristyc9550792009-11-13 20:05:42 +00001496 }
cristy3ed852e2009-09-05 21:47:34 +00001497#if !defined(MAGICKCORE_FFTW_DELEGATE)
1498 fourier_image=(Image *) NULL;
1499 (void) modulus;
1500 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +00001501 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (FFTW)",
cristyc9550792009-11-13 20:05:42 +00001502 magnitude_image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001503#else
1504 {
cristyc9550792009-11-13 20:05:42 +00001505 fourier_image=CloneImage(magnitude_image,magnitude_image->columns,
cristy4c9c4d02013-10-08 00:05:57 +00001506 magnitude_image->rows,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00001507 if (fourier_image != (Image *) NULL)
1508 {
1509 MagickBooleanType
1510 is_gray,
1511 status;
1512
cristy3ed852e2009-09-05 21:47:34 +00001513 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001514 is_gray=IsImageGray(magnitude_image,exception);
cristyc9550792009-11-13 20:05:42 +00001515 if (is_gray != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001516 is_gray=IsImageGray(phase_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001517#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyb34ef052010-10-07 00:12:05 +00001518 #pragma omp parallel sections
cristy3ed852e2009-09-05 21:47:34 +00001519#endif
cristy3ed852e2009-09-05 21:47:34 +00001520 {
cristyb34ef052010-10-07 00:12:05 +00001521#if defined(MAGICKCORE_OPENMP_SUPPORT)
1522 #pragma omp section
1523#endif
cristy3ed852e2009-09-05 21:47:34 +00001524 {
cristyb34ef052010-10-07 00:12:05 +00001525 MagickBooleanType
1526 thread_status;
1527
1528 if (is_gray != MagickFalse)
1529 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001530 phase_image,GrayPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001531 else
cristyc9550792009-11-13 20:05:42 +00001532 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001533 phase_image,RedPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001534 if (thread_status == MagickFalse)
1535 status=thread_status;
cristy3ed852e2009-09-05 21:47:34 +00001536 }
cristyb34ef052010-10-07 00:12:05 +00001537#if defined(MAGICKCORE_OPENMP_SUPPORT)
1538 #pragma omp section
1539#endif
1540 {
1541 MagickBooleanType
1542 thread_status;
1543
1544 thread_status=MagickTrue;
1545 if (is_gray == MagickFalse)
1546 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001547 phase_image,GreenPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001548 if (thread_status == MagickFalse)
1549 status=thread_status;
1550 }
1551#if defined(MAGICKCORE_OPENMP_SUPPORT)
1552 #pragma omp section
1553#endif
1554 {
1555 MagickBooleanType
1556 thread_status;
1557
1558 thread_status=MagickTrue;
1559 if (is_gray == MagickFalse)
1560 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001561 phase_image,BluePixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001562 if (thread_status == MagickFalse)
1563 status=thread_status;
1564 }
1565#if defined(MAGICKCORE_OPENMP_SUPPORT)
1566 #pragma omp section
1567#endif
1568 {
1569 MagickBooleanType
1570 thread_status;
1571
1572 thread_status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00001573 if (magnitude_image->colorspace == CMYKColorspace)
cristyb34ef052010-10-07 00:12:05 +00001574 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001575 phase_image,BlackPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001576 if (thread_status == MagickFalse)
1577 status=thread_status;
1578 }
1579#if defined(MAGICKCORE_OPENMP_SUPPORT)
1580 #pragma omp section
1581#endif
1582 {
1583 MagickBooleanType
1584 thread_status;
1585
1586 thread_status=MagickTrue;
cristy8a46d822012-08-28 23:32:39 +00001587 if (magnitude_image->alpha_trait == BlendPixelTrait)
cristyb34ef052010-10-07 00:12:05 +00001588 thread_status=InverseFourierTransformChannel(magnitude_image,
cristyd3090f92011-10-18 00:05:15 +00001589 phase_image,AlphaPixelChannel,modulus,fourier_image,exception);
cristyb34ef052010-10-07 00:12:05 +00001590 if (thread_status == MagickFalse)
1591 status=thread_status;
1592 }
cristy3ed852e2009-09-05 21:47:34 +00001593 }
1594 if (status == MagickFalse)
1595 fourier_image=DestroyImage(fourier_image);
1596 }
cristyb34ef052010-10-07 00:12:05 +00001597 fftw_cleanup();
cristy3ed852e2009-09-05 21:47:34 +00001598 }
1599#endif
1600 return(fourier_image);
1601}