45#include "magick/studio.h"
46#include "magick/artifact.h"
47#include "magick/attribute.h"
48#include "magick/blob.h"
49#include "magick/cache.h"
50#include "magick/image.h"
51#include "magick/image-private.h"
52#include "magick/list.h"
53#include "magick/fourier.h"
54#include "magick/log.h"
55#include "magick/memory_.h"
56#include "magick/monitor.h"
57#include "magick/monitor-private.h"
58#include "magick/pixel-accessor.h"
59#include "magick/pixel-private.h"
60#include "magick/property.h"
61#include "magick/quantum-private.h"
62#include "magick/resource_.h"
63#include "magick/string-private.h"
64#include "magick/thread-private.h"
65#if defined(MAGICKCORE_FFTW_DELEGATE)
67#define ENABLE_FFTW_DELEGATE
68#elif !defined(__cplusplus) && !defined(c_plusplus)
69#define ENABLE_FFTW_DELEGATE
72#if defined(ENABLE_FFTW_DELEGATE)
73#if defined(MAGICKCORE_HAVE_COMPLEX_H)
77#if !defined(MAGICKCORE_HAVE_CABS)
78#define cabs(z) (sqrt(z[0]*z[0]+z[1]*z[1]))
80#if !defined(MAGICKCORE_HAVE_CARG)
81#define carg(z) (atan2(cimag(z),creal(z)))
83#if !defined(MAGICKCORE_HAVE_CIMAG)
84#define cimag(z) (z[1])
86#if !defined(MAGICKCORE_HAVE_CREAL)
87#define creal(z) (z[0])
137MagickExport
Image *ComplexImages(
const Image *images,
const ComplexOperator op,
140#define ComplexImageTag "Complex/Image"
181 assert(images != (
Image *) NULL);
182 assert(images->signature == MagickCoreSignature);
184 assert(exception->signature == MagickCoreSignature);
185 if (IsEventLogging() != MagickFalse)
186 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",images->filename);
187 if (images->next == (
Image *) NULL)
189 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
190 "ImageSequenceRequired",
"`%s'",images->filename);
191 return((
Image *) NULL);
193 image=CloneImage(images,0,0,MagickTrue,exception);
194 if (image == (
Image *) NULL)
195 return((
Image *) NULL);
196 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
198 image=DestroyImageList(image);
202 complex_images=NewImageList();
203 AppendImageToList(&complex_images,image);
204 image=CloneImage(images->next,0,0,MagickTrue,exception);
205 if (image == (
Image *) NULL)
207 complex_images=DestroyImageList(complex_images);
208 return(complex_images);
210 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
212 image=DestroyImageList(image);
216 AppendImageToList(&complex_images,image);
220 artifact=GetImageArtifact(image,
"complex:snr");
222 if (artifact != (
const char *) NULL)
223 snr=StringToDouble(artifact,(
char **) NULL);
225 Ai_image=images->next;
227 Bi_image=images->next;
228 if ((images->next->next != (
Image *) NULL) &&
229 (images->next->next->next != (
Image *) NULL))
231 Br_image=images->next->next;
232 Bi_image=images->next->next->next;
234 Cr_image=complex_images;
235 Ci_image=complex_images->next;
236 Ar_view=AcquireVirtualCacheView(Ar_image,exception);
237 Ai_view=AcquireVirtualCacheView(Ai_image,exception);
238 Br_view=AcquireVirtualCacheView(Br_image,exception);
239 Bi_view=AcquireVirtualCacheView(Bi_image,exception);
240 Cr_view=AcquireAuthenticCacheView(Cr_image,exception);
241 Ci_view=AcquireAuthenticCacheView(Ci_image,exception);
244 columns=MagickMin(Cr_image->columns,Ci_image->columns);
245 rows=MagickMin(Cr_image->rows,Ci_image->rows);
246#if defined(MAGICKCORE_OPENMP_SUPPORT)
247 #pragma omp parallel for schedule(static) shared(progress,status) \
248 magick_number_threads(Cr_image,complex_images,rows,1L)
250 for (y=0; y < (ssize_t) rows; y++)
265 if (status == MagickFalse)
267 Ar=GetCacheViewVirtualPixels(Ar_view,0,y,columns,1,exception);
268 Ai=GetCacheViewVirtualPixels(Ai_view,0,y,columns,1,exception);
269 Br=GetCacheViewVirtualPixels(Br_view,0,y,columns,1,exception);
270 Bi=GetCacheViewVirtualPixels(Bi_view,0,y,columns,1,exception);
271 Cr=QueueCacheViewAuthenticPixels(Cr_view,0,y,columns,1,exception);
272 Ci=QueueCacheViewAuthenticPixels(Ci_view,0,y,columns,1,exception);
282 for (x=0; x < (ssize_t) columns; x++)
285 ai = { (double) (QuantumScale*(
double) GetPixelRed(Ai)),
286 (double) (QuantumScale*(
double) GetPixelGreen(Ai)),
287 (double) (QuantumScale*(
double) GetPixelBlue(Ai)),
288 (double) (image->matte != MagickFalse ? QuantumScale*
289 (
double) GetPixelOpacity(Ai) : (
double) OpaqueOpacity), 0 },
290 ar = { (double) (QuantumScale*(
double) GetPixelRed(Ar)),
291 (double) (QuantumScale*(
double) GetPixelGreen(Ar)),
292 (double) (QuantumScale*(
double) GetPixelBlue(Ar)),
293 (double) (image->matte != MagickFalse ? QuantumScale*
294 (
double) GetPixelOpacity(Ar) : (
double) OpaqueOpacity), 0 },
295 bi = { (double) (QuantumScale*(
double) GetPixelRed(Bi)),
296 (double) (QuantumScale*(
double) GetPixelGreen(Bi)),
297 (double) (QuantumScale*(
double) GetPixelBlue(Bi)),
298 (double) (image->matte != MagickFalse ? QuantumScale*
299 (
double) GetPixelOpacity(Bi) : (
double) OpaqueOpacity), 0 },
300 br = { (double) (QuantumScale*(
double) GetPixelRed(Br)),
301 (double) (QuantumScale*(
double) GetPixelGreen(Br)),
302 (double) (QuantumScale*(
double) GetPixelBlue(Br)),
303 (double) (image->matte != MagickFalse ? QuantumScale*
304 (
double) GetPixelOpacity(Br) : (
double) OpaqueOpacity), 0 },
310 case AddComplexOperator:
312 cr.red=ar.red+br.red;
313 ci.red=ai.red+bi.red;
314 cr.green=ar.green+br.green;
315 ci.green=ai.green+bi.green;
316 cr.blue=ar.blue+br.blue;
317 ci.blue=ai.blue+bi.blue;
318 cr.opacity=ar.opacity+br.opacity;
319 ci.opacity=ai.opacity+bi.opacity;
322 case ConjugateComplexOperator:
328 ci.green=(-ai.green);
331 cr.opacity=ar.opacity;
332 ci.opacity=(-ai.opacity);
335 case DivideComplexOperator:
340 gamma=PerceptibleReciprocal((
double) br.red*(
double) br.red+
341 (
double) bi.red*(
double) bi.red+snr);
342 cr.red=gamma*((double) ar.red*(double) br.red+(double) ai.red*
344 ci.red=gamma*((double) ai.red*(double) br.red-(double) ar.red*
346 gamma=PerceptibleReciprocal((
double) br.green*(
double) br.green+
347 (
double) bi.green*(
double) bi.green+snr);
348 cr.green=gamma*((double) ar.green*(double) br.green+
349 (double) ai.green*(double) bi.green);
350 ci.green=gamma*((double) ai.green*(double) br.green-
351 (double) ar.green*(double) bi.green);
352 gamma=PerceptibleReciprocal((
double) br.blue*(
double) br.blue+
353 (
double) bi.blue*(
double) bi.blue+snr);
354 cr.blue=gamma*((double) ar.blue*(double) br.blue+
355 (double) ai.blue*(double) bi.blue);
356 ci.blue=gamma*((double) ai.blue*(double) br.blue-
357 (double) ar.blue*(double) bi.blue);
358 gamma=PerceptibleReciprocal((
double) br.opacity*(
double) br.opacity+
359 (
double) bi.opacity*(
double) bi.opacity+snr);
360 cr.opacity=gamma*((double) ar.opacity*(double) br.opacity+
361 (double) ai.opacity*(double) bi.opacity);
362 ci.opacity=gamma*((double) ai.opacity*(double) br.opacity-
363 (double) ar.opacity*(double) bi.opacity);
366 case MagnitudePhaseComplexOperator:
368 cr.red=sqrt((
double) ar.red*(
double) ar.red+(
double) ai.red*
370 ci.red=atan2((
double) ai.red,(
double) ar.red)/(2.0*MagickPI)+0.5;
371 cr.green=sqrt((
double) ar.green*(
double) ar.green+(
double) ai.green*
373 ci.green=atan2((
double) ai.green,(
double) ar.green)/(2.0*MagickPI)+
375 cr.blue=sqrt((
double) ar.blue*(
double) ar.blue+(
double) ai.blue*
377 ci.blue=atan2((
double) ai.blue,(
double) ar.blue)/(2.0*MagickPI)+0.5;
378 cr.opacity=sqrt((
double) ar.opacity*(
double) ar.opacity+
379 (
double) ai.opacity*(
double) ai.opacity);
380 ci.opacity=atan2((
double) ai.opacity,(
double) ar.opacity)/
384 case MultiplyComplexOperator:
386 cr.red=((double) ar.red*(double) br.red-(double) ai.red*
388 ci.red=((double) ai.red*(double) br.red+(double) ar.red*
390 cr.green=((double) ar.green*(double) br.green-(double) ai.green*
392 ci.green=((double) ai.green*(double) br.green+(double) ar.green*
394 cr.blue=((double) ar.blue*(double) br.blue-(double) ai.blue*
396 ci.blue=((double) ai.blue*(double) br.blue+(double) ar.blue*
398 cr.opacity=((double) ar.opacity*(double) br.opacity-
399 (double) ai.opacity*(double) bi.opacity);
400 ci.opacity=((double) ai.opacity*(double) br.opacity+
401 (double) ar.opacity*(double) bi.opacity);
404 case RealImaginaryComplexOperator:
406 cr.red=(double) ar.red*cos(2.0*MagickPI*((
double) ai.red-0.5));
407 ci.red=(double) ar.red*sin(2.0*MagickPI*((
double) ai.red-0.5));
408 cr.green=(double) ar.green*cos(2.0*MagickPI*((
double) ai.green-0.5));
409 ci.green=(double) ar.green*sin(2.0*MagickPI*((
double) ai.green-0.5));
410 cr.blue=(double) ar.blue*cos(2.0*MagickPI*((
double) ai.blue-0.5));
411 ci.blue=(double) ar.blue*sin(2.0*MagickPI*((
double) ai.blue-0.5));
412 cr.opacity=(double) ar.opacity*cos(2.0*MagickPI*((
double) ai.opacity-
414 ci.opacity=(double) ar.opacity*sin(2.0*MagickPI*((
double) ai.opacity-
418 case SubtractComplexOperator:
420 cr.red=ar.red-br.red;
421 ci.red=ai.red-bi.red;
422 cr.green=ar.green-br.green;
423 ci.green=ai.green-bi.green;
424 cr.blue=ar.blue-br.blue;
425 ci.blue=ai.blue-bi.blue;
426 cr.opacity=ar.opacity-br.opacity;
427 ci.opacity=ai.opacity-bi.opacity;
431 Cr->red=(MagickRealType) QuantumRange*(MagickRealType) cr.red;
432 Ci->red=(MagickRealType) QuantumRange*(MagickRealType) ci.red;
433 Cr->green=(MagickRealType) QuantumRange*(MagickRealType) cr.green;
434 Ci->green=(MagickRealType) QuantumRange*(MagickRealType) ci.green;
435 Cr->blue=(MagickRealType) QuantumRange*(MagickRealType) cr.blue;
436 Ci->blue=(MagickRealType) QuantumRange*(MagickRealType) ci.blue;
437 if (images->matte != MagickFalse)
439 Cr->opacity=(MagickRealType) QuantumRange*(MagickRealType) cr.opacity;
440 Ci->opacity=(MagickRealType) QuantumRange*(MagickRealType) ci.opacity;
449 if (SyncCacheViewAuthenticPixels(Ci_view,exception) == MagickFalse)
451 if (SyncCacheViewAuthenticPixels(Cr_view,exception) == MagickFalse)
453 if (images->progress_monitor != (MagickProgressMonitor) NULL)
458#if defined(MAGICKCORE_OPENMP_SUPPORT)
462 proceed=SetImageProgress(images,ComplexImageTag,progress,images->rows);
463 if (proceed == MagickFalse)
467 Cr_view=DestroyCacheView(Cr_view);
468 Ci_view=DestroyCacheView(Ci_view);
469 Br_view=DestroyCacheView(Br_view);
470 Bi_view=DestroyCacheView(Bi_view);
471 Ar_view=DestroyCacheView(Ar_view);
472 Ai_view=DestroyCacheView(Ai_view);
473 if (status == MagickFalse)
474 complex_images=DestroyImageList(complex_images);
475 return(complex_images);
509#if defined(ENABLE_FFTW_DELEGATE)
511static MagickBooleanType RollFourier(
const size_t width,
const size_t height,
512 const ssize_t x_offset,
const ssize_t y_offset,
double *roll_pixels)
530 source_info=AcquireVirtualMemory(width,height*
sizeof(*source_pixels));
533 source_pixels=(
double *) GetVirtualMemoryBlob(source_info);
535 for (y=0L; y < (ssize_t) height; y++)
538 v=((y+y_offset) < 0L) ? y+y_offset+(ssize_t) height : y+y_offset;
540 v=((y+y_offset) > ((ssize_t) height-1L)) ? y+y_offset-(ssize_t) height :
542 for (x=0L; x < (ssize_t) width; x++)
545 u=((x+x_offset) < 0L) ? x+x_offset+(ssize_t) width : x+x_offset;
547 u=((x+x_offset) > ((ssize_t) width-1L)) ? x+x_offset-(ssize_t) width :
549 source_pixels[v*width+u]=roll_pixels[i++];
552 (void) memcpy(roll_pixels,source_pixels,height*width*
sizeof(*source_pixels));
553 source_info=RelinquishVirtualMemory(source_info);
557static MagickBooleanType ForwardQuadrantSwap(
const size_t width,
558 const size_t height,
double *source_pixels,
double *forward_pixels)
571 center=(ssize_t) (width/2L)+1L;
572 status=RollFourier((
size_t) center,height,0L,(ssize_t) height/2L,
574 if (status == MagickFalse)
576 for (y=0L; y < (ssize_t) height; y++)
577 for (x=0L; x < (ssize_t) (width/2L); x++)
578 forward_pixels[y*width+x+width/2L]=source_pixels[y*center+x];
579 for (y=1; y < (ssize_t) height; y++)
580 for (x=0L; x < (ssize_t) (width/2L); x++)
581 forward_pixels[(height-y)*width+width/2L-x-1L]=
582 source_pixels[y*center+x+1L];
583 for (x=0L; x < (ssize_t) (width/2L); x++)
584 forward_pixels[width/2L-x-1L]=source_pixels[x+1L];
588static void CorrectPhaseLHS(
const size_t width,
const size_t height,
589 double *fourier_pixels)
595 for (y=0L; y < (ssize_t) height; y++)
596 for (x=0L; x < (ssize_t) (width/2L); x++)
597 fourier_pixels[y*width+x]*=(-1.0);
600static MagickBooleanType ForwardFourier(
const FourierInfo *fourier_info,
633 magnitude_image=GetFirstImageInList(image);
634 phase_image=GetNextImageInList(image);
635 if (phase_image == (
Image *) NULL)
637 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
638 "ImageSequenceRequired",
"`%s'",image->filename);
644 magnitude_info=AcquireVirtualMemory(fourier_info->width,fourier_info->height*
645 sizeof(*magnitude_pixels));
646 phase_info=AcquireVirtualMemory(fourier_info->width,fourier_info->height*
647 sizeof(*phase_pixels));
648 if ((magnitude_info == (
MemoryInfo *) NULL) ||
652 phase_info=RelinquishVirtualMemory(phase_info);
654 magnitude_info=RelinquishVirtualMemory(magnitude_info);
655 (void) ThrowMagickException(exception,GetMagickModule(),
656 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
659 magnitude_pixels=(
double *) GetVirtualMemoryBlob(magnitude_info);
660 (void) memset(magnitude_pixels,0,fourier_info->width*
661 fourier_info->height*
sizeof(*magnitude_pixels));
662 phase_pixels=(
double *) GetVirtualMemoryBlob(phase_info);
663 (void) memset(phase_pixels,0,fourier_info->width*
664 fourier_info->height*
sizeof(*phase_pixels));
665 status=ForwardQuadrantSwap(fourier_info->width,fourier_info->height,
666 magnitude,magnitude_pixels);
667 if (status != MagickFalse)
668 status=ForwardQuadrantSwap(fourier_info->width,fourier_info->height,phase,
670 CorrectPhaseLHS(fourier_info->width,fourier_info->height,phase_pixels);
671 if (fourier_info->modulus != MagickFalse)
674 for (y=0L; y < (ssize_t) fourier_info->height; y++)
675 for (x=0L; x < (ssize_t) fourier_info->width; x++)
677 phase_pixels[i]/=(2.0*MagickPI);
678 phase_pixels[i]+=0.5;
682 magnitude_view=AcquireAuthenticCacheView(magnitude_image,exception);
684 for (y=0L; y < (ssize_t) fourier_info->height; y++)
686 q=GetCacheViewAuthenticPixels(magnitude_view,0L,y,fourier_info->width,1UL,
690 indexes=GetCacheViewAuthenticIndexQueue(magnitude_view);
691 for (x=0L; x < (ssize_t) fourier_info->width; x++)
693 switch (fourier_info->channel)
698 SetPixelRed(q,ClampToQuantum((MagickRealType) QuantumRange*magnitude_pixels[i]));
703 SetPixelGreen(q,ClampToQuantum((MagickRealType) QuantumRange*magnitude_pixels[i]));
708 SetPixelBlue(q,ClampToQuantum((MagickRealType) QuantumRange*magnitude_pixels[i]));
713 SetPixelOpacity(q,ClampToQuantum((MagickRealType) QuantumRange*magnitude_pixels[i]));
718 SetPixelIndex(indexes+x,ClampToQuantum((MagickRealType) QuantumRange*
719 magnitude_pixels[i]));
724 SetPixelGray(q,ClampToQuantum((MagickRealType) QuantumRange*magnitude_pixels[i]));
731 status=SyncCacheViewAuthenticPixels(magnitude_view,exception);
732 if (status == MagickFalse)
735 magnitude_view=DestroyCacheView(magnitude_view);
737 phase_view=AcquireAuthenticCacheView(phase_image,exception);
738 for (y=0L; y < (ssize_t) fourier_info->height; y++)
740 q=GetCacheViewAuthenticPixels(phase_view,0L,y,fourier_info->width,1UL,
744 indexes=GetCacheViewAuthenticIndexQueue(phase_view);
745 for (x=0L; x < (ssize_t) fourier_info->width; x++)
747 switch (fourier_info->channel)
752 SetPixelRed(q,ClampToQuantum((MagickRealType) QuantumRange*phase_pixels[i]));
757 SetPixelGreen(q,ClampToQuantum((MagickRealType) QuantumRange*phase_pixels[i]));
762 SetPixelBlue(q,ClampToQuantum((MagickRealType) QuantumRange*phase_pixels[i]));
767 SetPixelOpacity(q,ClampToQuantum((MagickRealType) QuantumRange*phase_pixels[i]));
772 SetPixelIndex(indexes+x,ClampToQuantum((MagickRealType) QuantumRange*phase_pixels[i]));
777 SetPixelGray(q,ClampToQuantum((MagickRealType) QuantumRange*phase_pixels[i]));
784 status=SyncCacheViewAuthenticPixels(phase_view,exception);
785 if (status == MagickFalse)
788 phase_view=DestroyCacheView(phase_view);
789 phase_info=RelinquishVirtualMemory(phase_info);
790 magnitude_info=RelinquishVirtualMemory(magnitude_info);
794static MagickBooleanType ForwardFourierTransform(
FourierInfo *fourier_info,
795 const Image *image,
double *magnitude_pixels,
double *phase_pixels,
832 if ((fourier_info->width >= INT_MAX) || (fourier_info->height >= INT_MAX))
834 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
835 "WidthOrHeightExceedsLimit",
"`%s'",image->filename);
838 source_info=AcquireVirtualMemory(fourier_info->width,fourier_info->height*
839 sizeof(*source_pixels));
842 (void) ThrowMagickException(exception,GetMagickModule(),
843 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
846 source_pixels=(
double *) GetVirtualMemoryBlob(source_info);
847 memset(source_pixels,0,fourier_info->width*fourier_info->height*
848 sizeof(*source_pixels));
850 image_view=AcquireVirtualCacheView(image,exception);
851 for (y=0L; y < (ssize_t) fourier_info->height; y++)
853 p=GetCacheViewVirtualPixels(image_view,0L,y,fourier_info->width,1UL,
857 indexes=GetCacheViewVirtualIndexQueue(image_view);
858 for (x=0L; x < (ssize_t) fourier_info->width; x++)
860 switch (fourier_info->channel)
865 source_pixels[i]=QuantumScale*(MagickRealType) GetPixelRed(p);
870 source_pixels[i]=QuantumScale*(MagickRealType) GetPixelGreen(p);
875 source_pixels[i]=QuantumScale*(MagickRealType) GetPixelBlue(p);
880 source_pixels[i]=QuantumScale*(MagickRealType) GetPixelOpacity(p);
885 source_pixels[i]=QuantumScale*(MagickRealType)
886 GetPixelIndex(indexes+x);
891 source_pixels[i]=QuantumScale*(MagickRealType) GetPixelGray(p);
899 image_view=DestroyCacheView(image_view);
900 forward_info=AcquireVirtualMemory(fourier_info->width,(fourier_info->height/2+
901 1)*
sizeof(*forward_pixels));
904 (void) ThrowMagickException(exception,GetMagickModule(),
905 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
906 source_info=(
MemoryInfo *) RelinquishVirtualMemory(source_info);
909 forward_pixels=(fftw_complex *) GetVirtualMemoryBlob(forward_info);
910#if defined(MAGICKCORE_OPENMP_SUPPORT)
911 #pragma omp critical (MagickCore_ForwardFourierTransform)
913 fftw_r2c_plan=fftw_plan_dft_r2c_2d((
int) fourier_info->width,
914 (
int) fourier_info->height,source_pixels,forward_pixels,FFTW_ESTIMATE);
915 fftw_execute_dft_r2c(fftw_r2c_plan,source_pixels,forward_pixels);
916 fftw_destroy_plan(fftw_r2c_plan);
917 source_info=(
MemoryInfo *) RelinquishVirtualMemory(source_info);
918 value=GetImageArtifact(image,
"fourier:normalize");
919 if ((value == (
const char *) NULL) || (LocaleCompare(value,
"forward") == 0))
928 gamma=PerceptibleReciprocal((
double) fourier_info->width*
929 fourier_info->height);
930 for (y=0L; y < (ssize_t) fourier_info->height; y++)
931 for (x=0L; x < (ssize_t) fourier_info->center; x++)
933#if defined(MAGICKCORE_HAVE_COMPLEX_H)
934 forward_pixels[i]*=gamma;
936 forward_pixels[i][0]*=gamma;
937 forward_pixels[i][1]*=gamma;
946 if (fourier_info->modulus != MagickFalse)
947 for (y=0L; y < (ssize_t) fourier_info->height; y++)
948 for (x=0L; x < (ssize_t) fourier_info->center; x++)
950 magnitude_pixels[i]=cabs(forward_pixels[i]);
951 phase_pixels[i]=carg(forward_pixels[i]);
955 for (y=0L; y < (ssize_t) fourier_info->height; y++)
956 for (x=0L; x < (ssize_t) fourier_info->center; x++)
958 magnitude_pixels[i]=creal(forward_pixels[i]);
959 phase_pixels[i]=cimag(forward_pixels[i]);
962 forward_info=(
MemoryInfo *) RelinquishVirtualMemory(forward_info);
966static MagickBooleanType ForwardFourierTransformChannel(
const Image *image,
967 const ChannelType channel,
const MagickBooleanType modulus,
984 fourier_info.width=image->columns;
985 fourier_info.height=image->rows;
986 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
987 ((image->rows % 2) != 0))
989 size_t extent=image->columns < image->rows ? image->rows : image->columns;
990 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
992 fourier_info.height=fourier_info.width;
993 fourier_info.center=(ssize_t) (fourier_info.width/2L)+1L;
994 fourier_info.channel=channel;
995 fourier_info.modulus=modulus;
996 magnitude_info=AcquireVirtualMemory(fourier_info.width,(fourier_info.height/2+
997 1)*
sizeof(*magnitude_pixels));
998 phase_info=AcquireVirtualMemory(fourier_info.width,(fourier_info.height/2+1)*
999 sizeof(*phase_pixels));
1000 if ((magnitude_info == (
MemoryInfo *) NULL) ||
1004 phase_info=RelinquishVirtualMemory(phase_info);
1006 magnitude_info=RelinquishVirtualMemory(magnitude_info);
1007 (void) ThrowMagickException(exception,GetMagickModule(),
1008 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
1009 return(MagickFalse);
1011 magnitude_pixels=(
double *) GetVirtualMemoryBlob(magnitude_info);
1012 phase_pixels=(
double *) GetVirtualMemoryBlob(phase_info);
1013 status=ForwardFourierTransform(&fourier_info,image,magnitude_pixels,
1014 phase_pixels,exception);
1015 if (status != MagickFalse)
1016 status=ForwardFourier(&fourier_info,fourier_image,magnitude_pixels,
1017 phase_pixels,exception);
1018 phase_info=RelinquishVirtualMemory(phase_info);
1019 magnitude_info=RelinquishVirtualMemory(magnitude_info);
1024MagickExport
Image *ForwardFourierTransformImage(
const Image *image,
1030 fourier_image=NewImageList();
1031#if !defined(ENABLE_FFTW_DELEGATE)
1033 (void) ThrowMagickException(exception,GetMagickModule(),
1034 MissingDelegateWarning,
"DelegateLibrarySupportNotBuiltIn",
"`%s' (FFTW)",
1045 width=image->columns;
1047 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
1048 ((image->rows % 2) != 0))
1050 size_t extent=image->columns < image->rows ? image->rows :
1052 width=(extent & 0x01) == 1 ? extent+1UL : extent;
1055 magnitude_image=CloneImage(image,width,height,MagickTrue,exception);
1056 if (magnitude_image != (
Image *) NULL)
1061 magnitude_image->storage_class=DirectClass;
1062 magnitude_image->depth=32UL;
1063 phase_image=CloneImage(image,width,height,MagickTrue,exception);
1064 if (phase_image == (
Image *) NULL)
1065 magnitude_image=DestroyImage(magnitude_image);
1072 phase_image->storage_class=DirectClass;
1073 phase_image->depth=32UL;
1074 AppendImageToList(&fourier_image,magnitude_image);
1075 AppendImageToList(&fourier_image,phase_image);
1077 is_gray=IsGrayImage(image,exception);
1078#if defined(MAGICKCORE_OPENMP_SUPPORT)
1079 #pragma omp parallel sections
1082#if defined(MAGICKCORE_OPENMP_SUPPORT)
1089 if (is_gray != MagickFalse)
1090 thread_status=ForwardFourierTransformChannel(image,
1091 GrayChannels,modulus,fourier_image,exception);
1093 thread_status=ForwardFourierTransformChannel(image,RedChannel,
1094 modulus,fourier_image,exception);
1095 if (thread_status == MagickFalse)
1096 status=thread_status;
1098#if defined(MAGICKCORE_OPENMP_SUPPORT)
1105 thread_status=MagickTrue;
1106 if (is_gray == MagickFalse)
1107 thread_status=ForwardFourierTransformChannel(image,
1108 GreenChannel,modulus,fourier_image,exception);
1109 if (thread_status == MagickFalse)
1110 status=thread_status;
1112#if defined(MAGICKCORE_OPENMP_SUPPORT)
1119 thread_status=MagickTrue;
1120 if (is_gray == MagickFalse)
1121 thread_status=ForwardFourierTransformChannel(image,
1122 BlueChannel,modulus,fourier_image,exception);
1123 if (thread_status == MagickFalse)
1124 status=thread_status;
1126#if defined(MAGICKCORE_OPENMP_SUPPORT)
1133 thread_status=MagickTrue;
1134 if (image->matte != MagickFalse)
1135 thread_status=ForwardFourierTransformChannel(image,
1136 OpacityChannel,modulus,fourier_image,exception);
1137 if (thread_status == MagickFalse)
1138 status=thread_status;
1140#if defined(MAGICKCORE_OPENMP_SUPPORT)
1147 thread_status=MagickTrue;
1148 if (image->colorspace == CMYKColorspace)
1149 thread_status=ForwardFourierTransformChannel(image,
1150 IndexChannel,modulus,fourier_image,exception);
1151 if (thread_status == MagickFalse)
1152 status=thread_status;
1155 if (status == MagickFalse)
1156 fourier_image=DestroyImageList(fourier_image);
1162 return(fourier_image);
1199#if defined(ENABLE_FFTW_DELEGATE)
1200static MagickBooleanType InverseQuadrantSwap(
const size_t width,
1201 const size_t height,
const double *source,
double *destination)
1211 center=(ssize_t) (width/2L)+1L;
1212 for (y=1L; y < (ssize_t) height; y++)
1213 for (x=0L; x < (ssize_t) (width/2L+1L); x++)
1214 destination[(height-y)*center-x+width/2L]=source[y*width+x];
1215 for (y=0L; y < (ssize_t) height; y++)
1216 destination[y*center]=source[y*width+width/2L];
1217 for (x=0L; x < center; x++)
1218 destination[x]=source[center-x-1L];
1219 return(RollFourier(center,height,0L,(ssize_t) height/-2L,destination));
1222static MagickBooleanType InverseFourier(
FourierInfo *fourier_info,
1223 const Image *magnitude_image,
const Image *phase_image,
1257 magnitude_info=AcquireVirtualMemory(fourier_info->width,fourier_info->height*
1258 sizeof(*magnitude_pixels));
1259 phase_info=AcquireVirtualMemory(fourier_info->width,fourier_info->height*
1260 sizeof(*phase_pixels));
1261 inverse_info=AcquireVirtualMemory(fourier_info->width,(fourier_info->height/
1262 2+1)*
sizeof(*inverse_pixels));
1263 if ((magnitude_info == (
MemoryInfo *) NULL) ||
1268 magnitude_info=RelinquishVirtualMemory(magnitude_info);
1270 phase_info=RelinquishVirtualMemory(phase_info);
1272 inverse_info=RelinquishVirtualMemory(inverse_info);
1273 (void) ThrowMagickException(exception,GetMagickModule(),
1274 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
1275 magnitude_image->filename);
1276 return(MagickFalse);
1278 magnitude_pixels=(
double *) GetVirtualMemoryBlob(magnitude_info);
1279 phase_pixels=(
double *) GetVirtualMemoryBlob(phase_info);
1280 inverse_pixels=(
double *) GetVirtualMemoryBlob(inverse_info);
1282 magnitude_view=AcquireVirtualCacheView(magnitude_image,exception);
1283 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1285 p=GetCacheViewVirtualPixels(magnitude_view,0L,y,fourier_info->width,1UL,
1289 indexes=GetCacheViewAuthenticIndexQueue(magnitude_view);
1290 for (x=0L; x < (ssize_t) fourier_info->width; x++)
1292 switch (fourier_info->channel)
1297 magnitude_pixels[i]=QuantumScale*(MagickRealType) GetPixelRed(p);
1302 magnitude_pixels[i]=QuantumScale*(MagickRealType) GetPixelGreen(p);
1307 magnitude_pixels[i]=QuantumScale*(MagickRealType) GetPixelBlue(p);
1310 case OpacityChannel:
1312 magnitude_pixels[i]=QuantumScale*(MagickRealType) GetPixelOpacity(p);
1317 magnitude_pixels[i]=QuantumScale*(MagickRealType)
1318 GetPixelIndex(indexes+x);
1323 magnitude_pixels[i]=QuantumScale*(MagickRealType) GetPixelGray(p);
1331 magnitude_view=DestroyCacheView(magnitude_view);
1332 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
1333 magnitude_pixels,inverse_pixels);
1334 (void) memcpy(magnitude_pixels,inverse_pixels,fourier_info->height*
1335 fourier_info->center*
sizeof(*magnitude_pixels));
1337 phase_view=AcquireVirtualCacheView(phase_image,exception);
1338 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1340 p=GetCacheViewVirtualPixels(phase_view,0,y,fourier_info->width,1,
1344 indexes=GetCacheViewAuthenticIndexQueue(phase_view);
1345 for (x=0L; x < (ssize_t) fourier_info->width; x++)
1347 switch (fourier_info->channel)
1352 phase_pixels[i]=QuantumScale*(MagickRealType) GetPixelRed(p);
1357 phase_pixels[i]=QuantumScale*(MagickRealType) GetPixelGreen(p);
1362 phase_pixels[i]=QuantumScale*(MagickRealType) GetPixelBlue(p);
1365 case OpacityChannel:
1367 phase_pixels[i]=QuantumScale*(MagickRealType) GetPixelOpacity(p);
1372 phase_pixels[i]=QuantumScale*(MagickRealType)
1373 GetPixelIndex(indexes+x);
1378 phase_pixels[i]=QuantumScale*(MagickRealType) GetPixelGray(p);
1386 if (fourier_info->modulus != MagickFalse)
1389 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1390 for (x=0L; x < (ssize_t) fourier_info->width; x++)
1392 phase_pixels[i]-=0.5;
1393 phase_pixels[i]*=(2.0*MagickPI);
1397 phase_view=DestroyCacheView(phase_view);
1398 CorrectPhaseLHS(fourier_info->width,fourier_info->height,phase_pixels);
1399 if (status != MagickFalse)
1400 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
1401 phase_pixels,inverse_pixels);
1402 (void) memcpy(phase_pixels,inverse_pixels,fourier_info->height*
1403 fourier_info->center*
sizeof(*phase_pixels));
1404 inverse_info=RelinquishVirtualMemory(inverse_info);
1409 if (fourier_info->modulus != MagickFalse)
1410 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1411 for (x=0L; x < (ssize_t) fourier_info->center; x++)
1413#if defined(MAGICKCORE_HAVE_COMPLEX_H)
1414 fourier_pixels[i]=magnitude_pixels[i]*cos(phase_pixels[i])+I*
1415 magnitude_pixels[i]*sin(phase_pixels[i]);
1417 fourier_pixels[i][0]=magnitude_pixels[i]*cos(phase_pixels[i]);
1418 fourier_pixels[i][1]=magnitude_pixels[i]*sin(phase_pixels[i]);
1423 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1424 for (x=0L; x < (ssize_t) fourier_info->center; x++)
1426#if defined(MAGICKCORE_HAVE_COMPLEX_H)
1427 fourier_pixels[i]=magnitude_pixels[i]+I*phase_pixels[i];
1429 fourier_pixels[i][0]=magnitude_pixels[i];
1430 fourier_pixels[i][1]=phase_pixels[i];
1434 magnitude_info=RelinquishVirtualMemory(magnitude_info);
1435 phase_info=RelinquishVirtualMemory(phase_info);
1439static MagickBooleanType InverseFourierTransform(
FourierInfo *fourier_info,
1471 if ((fourier_info->width >= INT_MAX) || (fourier_info->height >= INT_MAX))
1473 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
1474 "WidthOrHeightExceedsLimit",
"`%s'",image->filename);
1475 return(MagickFalse);
1477 source_info=AcquireVirtualMemory(fourier_info->width,fourier_info->height*
1478 sizeof(*source_pixels));
1481 (void) ThrowMagickException(exception,GetMagickModule(),
1482 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
1483 return(MagickFalse);
1485 source_pixels=(
double *) GetVirtualMemoryBlob(source_info);
1486 value=GetImageArtifact(image,
"fourier:normalize");
1487 if (LocaleCompare(value,
"inverse") == 0)
1496 gamma=PerceptibleReciprocal((
double) fourier_info->width*
1497 fourier_info->height);
1498 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1499 for (x=0L; x < (ssize_t) fourier_info->center; x++)
1501#if defined(MAGICKCORE_HAVE_COMPLEX_H)
1502 fourier_pixels[i]*=gamma;
1504 fourier_pixels[i][0]*=gamma;
1505 fourier_pixels[i][1]*=gamma;
1510#if defined(MAGICKCORE_OPENMP_SUPPORT)
1511 #pragma omp critical (MagickCore_InverseFourierTransform)
1513 fftw_c2r_plan=fftw_plan_dft_c2r_2d((
int) fourier_info->width,
1514 (
int) fourier_info->height,fourier_pixels,source_pixels,FFTW_ESTIMATE);
1515 fftw_execute_dft_c2r(fftw_c2r_plan,fourier_pixels,source_pixels);
1516 fftw_destroy_plan(fftw_c2r_plan);
1518 image_view=AcquireAuthenticCacheView(image,exception);
1519 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1521 if (y >= (ssize_t) image->rows)
1523 q=GetCacheViewAuthenticPixels(image_view,0L,y,fourier_info->width >
1524 image->columns ? image->columns : fourier_info->width,1UL,exception);
1527 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1528 for (x=0L; x < (ssize_t) fourier_info->width; x++)
1530 if (x < (ssize_t) image->columns)
1531 switch (fourier_info->channel)
1536 SetPixelRed(q,ClampToQuantum((MagickRealType) QuantumRange*
1542 SetPixelGreen(q,ClampToQuantum((MagickRealType) QuantumRange*
1548 SetPixelBlue(q,ClampToQuantum((MagickRealType) QuantumRange*
1552 case OpacityChannel:
1554 SetPixelOpacity(q,ClampToQuantum((MagickRealType) QuantumRange*
1560 SetPixelIndex(indexes+x,ClampToQuantum((MagickRealType)
1561 QuantumRange*source_pixels[i]));
1566 SetPixelGray(q,ClampToQuantum((MagickRealType) QuantumRange*
1574 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1577 image_view=DestroyCacheView(image_view);
1578 source_info=RelinquishVirtualMemory(source_info);
1582static MagickBooleanType InverseFourierTransformChannel(
1583 const Image *magnitude_image,
const Image *phase_image,
1584 const ChannelType channel,
const MagickBooleanType modulus,
1599 fourier_info.width=magnitude_image->columns;
1600 fourier_info.height=magnitude_image->rows;
1601 if ((magnitude_image->columns != magnitude_image->rows) ||
1602 ((magnitude_image->columns % 2) != 0) ||
1603 ((magnitude_image->rows % 2) != 0))
1605 size_t extent=magnitude_image->columns < magnitude_image->rows ?
1606 magnitude_image->rows : magnitude_image->columns;
1607 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
1609 fourier_info.height=fourier_info.width;
1610 fourier_info.center=(ssize_t) (fourier_info.width/2L)+1L;
1611 fourier_info.channel=channel;
1612 fourier_info.modulus=modulus;
1613 inverse_info=AcquireVirtualMemory(fourier_info.width,(fourier_info.height/2+
1614 1)*
sizeof(*inverse_pixels));
1617 (void) ThrowMagickException(exception,GetMagickModule(),
1618 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
1619 magnitude_image->filename);
1620 return(MagickFalse);
1622 inverse_pixels=(fftw_complex *) GetVirtualMemoryBlob(inverse_info);
1623 status=InverseFourier(&fourier_info,magnitude_image,phase_image,
1624 inverse_pixels,exception);
1625 if (status != MagickFalse)
1626 status=InverseFourierTransform(&fourier_info,inverse_pixels,fourier_image,
1628 inverse_info=RelinquishVirtualMemory(inverse_info);
1633MagickExport
Image *InverseFourierTransformImage(
const Image *magnitude_image,
1634 const Image *phase_image,
const MagickBooleanType modulus,
1640 assert(magnitude_image != (
Image *) NULL);
1641 assert(magnitude_image->signature == MagickCoreSignature);
1642 if (IsEventLogging() != MagickFalse)
1643 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",
1644 magnitude_image->filename);
1645 if (phase_image == (
Image *) NULL)
1647 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
1648 "ImageSequenceRequired",
"`%s'",magnitude_image->filename);
1649 return((
Image *) NULL);
1651#if !defined(ENABLE_FFTW_DELEGATE)
1652 fourier_image=(
Image *) NULL;
1654 (void) ThrowMagickException(exception,GetMagickModule(),
1655 MissingDelegateWarning,
"DelegateLibrarySupportNotBuiltIn",
"`%s' (FFTW)",
1656 magnitude_image->filename);
1659 fourier_image=CloneImage(magnitude_image,magnitude_image->columns,
1660 magnitude_image->rows,MagickTrue,exception);
1661 if (fourier_image != (
Image *) NULL)
1668 is_gray=IsGrayImage(magnitude_image,exception);
1669 if (is_gray != MagickFalse)
1670 is_gray=IsGrayImage(phase_image,exception);
1671#if defined(MAGICKCORE_OPENMP_SUPPORT)
1672 #pragma omp parallel sections
1675#if defined(MAGICKCORE_OPENMP_SUPPORT)
1682 if (is_gray != MagickFalse)
1683 thread_status=InverseFourierTransformChannel(magnitude_image,
1684 phase_image,GrayChannels,modulus,fourier_image,exception);
1686 thread_status=InverseFourierTransformChannel(magnitude_image,
1687 phase_image,RedChannel,modulus,fourier_image,exception);
1688 if (thread_status == MagickFalse)
1689 status=thread_status;
1691#if defined(MAGICKCORE_OPENMP_SUPPORT)
1698 thread_status=MagickTrue;
1699 if (is_gray == MagickFalse)
1700 thread_status=InverseFourierTransformChannel(magnitude_image,
1701 phase_image,GreenChannel,modulus,fourier_image,exception);
1702 if (thread_status == MagickFalse)
1703 status=thread_status;
1705#if defined(MAGICKCORE_OPENMP_SUPPORT)
1712 thread_status=MagickTrue;
1713 if (is_gray == MagickFalse)
1714 thread_status=InverseFourierTransformChannel(magnitude_image,
1715 phase_image,BlueChannel,modulus,fourier_image,exception);
1716 if (thread_status == MagickFalse)
1717 status=thread_status;
1719#if defined(MAGICKCORE_OPENMP_SUPPORT)
1726 thread_status=MagickTrue;
1727 if (magnitude_image->matte != MagickFalse)
1728 thread_status=InverseFourierTransformChannel(magnitude_image,
1729 phase_image,OpacityChannel,modulus,fourier_image,exception);
1730 if (thread_status == MagickFalse)
1731 status=thread_status;
1733#if defined(MAGICKCORE_OPENMP_SUPPORT)
1740 thread_status=MagickTrue;
1741 if (magnitude_image->colorspace == CMYKColorspace)
1742 thread_status=InverseFourierTransformChannel(magnitude_image,
1743 phase_image,IndexChannel,modulus,fourier_image,exception);
1744 if (thread_status == MagickFalse)
1745 status=thread_status;
1748 if (status == MagickFalse)
1749 fourier_image=DestroyImage(fourier_image);
1754 return(fourier_image);