45#include "MagickCore/studio.h"
46#include "MagickCore/artifact.h"
47#include "MagickCore/attribute.h"
48#include "MagickCore/blob.h"
49#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"
57#include "MagickCore/monitor-private.h"
58#include "MagickCore/pixel-accessor.h"
59#include "MagickCore/property.h"
60#include "MagickCore/quantum-private.h"
61#include "MagickCore/resource_.h"
62#include "MagickCore/string-private.h"
63#include "MagickCore/thread-private.h"
64#if defined(MAGICKCORE_FFTW_DELEGATE)
66#define ENABLE_FFTW_DELEGATE
67#elif !defined(__cplusplus) && !defined(c_plusplus)
68#define ENABLE_FFTW_DELEGATE
71#if defined(ENABLE_FFTW_DELEGATE)
72#if defined(MAGICKCORE_HAVE_COMPLEX_H)
76#if !defined(MAGICKCORE_HAVE_CABS)
77#define cabs(z) (sqrt(z[0]*z[0]+z[1]*z[1]))
79#if !defined(MAGICKCORE_HAVE_CARG)
80#define carg(z) (atan2(cimag(z),creal(z)))
82#if !defined(MAGICKCORE_HAVE_CIMAG)
83#define cimag(z) (z[1])
85#if !defined(MAGICKCORE_HAVE_CREAL)
86#define creal(z) (z[0])
134MagickExport
Image *ComplexImages(
const Image *images,
const ComplexOperator op,
137#define ComplexImageTag "Complex/Image"
179 assert(images != (
Image *) NULL);
180 assert(images->signature == MagickCoreSignature);
182 assert(exception->signature == MagickCoreSignature);
183 if (IsEventLogging() != MagickFalse)
184 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",images->filename);
185 if (images->next == (
Image *) NULL)
187 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
188 "ImageSequenceRequired",
"`%s'",images->filename);
189 return((
Image *) NULL);
191 image=CloneImage(images,0,0,MagickTrue,exception);
192 if (image == (
Image *) NULL)
193 return((
Image *) NULL);
194 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
196 image=DestroyImageList(image);
200 complex_images=NewImageList();
201 AppendImageToList(&complex_images,image);
202 image=CloneImage(images->next,0,0,MagickTrue,exception);
203 if (image == (
Image *) NULL)
205 complex_images=DestroyImageList(complex_images);
206 return(complex_images);
209 AppendImageToList(&complex_images,image);
210 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
212 complex_images=DestroyImageList(complex_images);
213 return(complex_images);
218 artifact=GetImageArtifact(images,
"complex:snr");
220 if (artifact != (
const char *) NULL)
221 snr=StringToDouble(artifact,(
char **) NULL);
223 Ai_image=images->next;
225 Bi_image=images->next;
226 if ((images->next->next != (
Image *) NULL) &&
227 (images->next->next->next != (
Image *) NULL))
229 Br_image=images->next->next;
230 Bi_image=images->next->next->next;
232 Cr_image=complex_images;
233 Ci_image=complex_images->next;
234 number_channels=MagickMin(MagickMin(MagickMin(
235 Ar_image->number_channels,Ai_image->number_channels),MagickMin(
236 Br_image->number_channels,Bi_image->number_channels)),MagickMin(
237 Cr_image->number_channels,Ci_image->number_channels));
238 Ar_view=AcquireVirtualCacheView(Ar_image,exception);
239 Ai_view=AcquireVirtualCacheView(Ai_image,exception);
240 Br_view=AcquireVirtualCacheView(Br_image,exception);
241 Bi_view=AcquireVirtualCacheView(Bi_image,exception);
242 Cr_view=AcquireAuthenticCacheView(Cr_image,exception);
243 Ci_view=AcquireAuthenticCacheView(Ci_image,exception);
246 columns=MagickMin(Cr_image->columns,Ci_image->columns);
247 rows=MagickMin(Cr_image->rows,Ci_image->rows);
248#if defined(MAGICKCORE_OPENMP_SUPPORT)
249 #pragma omp parallel for schedule(static) shared(progress,status) \
250 magick_number_threads(Cr_image,complex_images,rows,1L)
252 for (y=0; y < (ssize_t) rows; y++)
267 if (status == MagickFalse)
269 Ar=GetCacheViewVirtualPixels(Ar_view,0,y,columns,1,exception);
270 Ai=GetCacheViewVirtualPixels(Ai_view,0,y,columns,1,exception);
271 Br=GetCacheViewVirtualPixels(Br_view,0,y,columns,1,exception);
272 Bi=GetCacheViewVirtualPixels(Bi_view,0,y,columns,1,exception);
273 Cr=QueueCacheViewAuthenticPixels(Cr_view,0,y,columns,1,exception);
274 Ci=QueueCacheViewAuthenticPixels(Ci_view,0,y,columns,1,exception);
275 if ((Ar == (
const Quantum *) NULL) || (Ai == (
const Quantum *) NULL) ||
276 (Br == (
const Quantum *) NULL) || (Bi == (
const Quantum *) NULL) ||
277 (Cr == (Quantum *) NULL) || (Ci == (Quantum *) NULL))
282 for (x=0; x < (ssize_t) columns; x++)
287 for (i=0; i < (ssize_t) number_channels; i++)
290 ai = QuantumScale*(double) Ai[i],
291 ar = QuantumScale*(
double) Ar[i],
292 bi = QuantumScale*(double) Bi[i],
293 br = QuantumScale*(
double) Br[i],
299 case AddComplexOperator:
305 case ConjugateComplexOperator:
312 case DivideComplexOperator:
314 cr=PerceptibleReciprocal(br*br+bi*bi+snr)*(ar*br+ai*bi);
315 ci=PerceptibleReciprocal(br*br+bi*bi+snr)*(ai*br-ar*bi);
318 case MagnitudePhaseComplexOperator:
320 cr=sqrt(ar*ar+ai*ai);
321 ci=atan2((
double) ai,(
double) ar)/(2.0*MagickPI)+0.5;
324 case MultiplyComplexOperator:
330 case RealImaginaryComplexOperator:
332 cr=ar*cos(2.0*MagickPI*(ai-0.5));
333 ci=ar*sin(2.0*MagickPI*(ai-0.5));
336 case SubtractComplexOperator:
343 Cr[i]=(double) QuantumRange*cr;
344 Ci[i]=(double) QuantumRange*ci;
346 Ar+=(ptrdiff_t) GetPixelChannels(Ar_image);
347 Ai+=(ptrdiff_t) GetPixelChannels(Ai_image);
348 Br+=(ptrdiff_t) GetPixelChannels(Br_image);
349 Bi+=(ptrdiff_t) GetPixelChannels(Bi_image);
350 Cr+=(ptrdiff_t) GetPixelChannels(Cr_image);
351 Ci+=(ptrdiff_t) GetPixelChannels(Ci_image);
353 if (SyncCacheViewAuthenticPixels(Ci_view,exception) == MagickFalse)
355 if (SyncCacheViewAuthenticPixels(Cr_view,exception) == MagickFalse)
357 if (images->progress_monitor != (MagickProgressMonitor) NULL)
362#if defined(MAGICKCORE_OPENMP_SUPPORT)
366 proceed=SetImageProgress(images,ComplexImageTag,progress,images->rows);
367 if (proceed == MagickFalse)
371 Cr_view=DestroyCacheView(Cr_view);
372 Ci_view=DestroyCacheView(Ci_view);
373 Br_view=DestroyCacheView(Br_view);
374 Bi_view=DestroyCacheView(Bi_view);
375 Ar_view=DestroyCacheView(Ar_view);
376 Ai_view=DestroyCacheView(Ai_view);
377 if (status == MagickFalse)
378 complex_images=DestroyImageList(complex_images);
379 return(complex_images);
413#if defined(ENABLE_FFTW_DELEGATE)
415static MagickBooleanType RollFourier(
const size_t width,
const size_t height,
416 const ssize_t x_offset,
const ssize_t y_offset,
double *roll_pixels)
434 source_info=AcquireVirtualMemory(width,height*
sizeof(*source_pixels));
437 source_pixels=(
double *) GetVirtualMemoryBlob(source_info);
439 for (y=0L; y < (ssize_t) height; y++)
442 v=((y+y_offset) < 0L) ? y+y_offset+(ssize_t) height : y+y_offset;
444 v=((y+y_offset) > ((ssize_t) height-1L)) ? y+y_offset-(ssize_t) height :
446 for (x=0L; x < (ssize_t) width; x++)
449 u=((x+x_offset) < 0L) ? x+x_offset+(ssize_t) width : x+x_offset;
451 u=((x+x_offset) > ((ssize_t) width-1L)) ? x+x_offset-(ssize_t) width :
453 source_pixels[v*(ssize_t) width+u]=roll_pixels[i++];
456 (void) memcpy(roll_pixels,source_pixels,height*width*
sizeof(*source_pixels));
457 source_info=RelinquishVirtualMemory(source_info);
461static MagickBooleanType ForwardQuadrantSwap(
const size_t width,
462 const size_t height,
double *source_pixels,
double *forward_pixels)
478 status=RollFourier(center,height,0L,(ssize_t) height/2L,source_pixels);
479 if (status == MagickFalse)
481 for (y=0L; y < (ssize_t) height; y++)
482 for (x=0L; x < (ssize_t) (width/2L); x++)
483 forward_pixels[y*(ssize_t) width+x+(ssize_t) width/2L]=
484 source_pixels[y*(ssize_t) center+x];
485 for (y=1; y < (ssize_t) height; y++)
486 for (x=0L; x < (ssize_t) (width/2L); x++)
487 forward_pixels[((ssize_t) height-y)*(ssize_t) width+(ssize_t) width/2L-x-1L]=
488 source_pixels[y*(ssize_t) center+x+1L];
489 for (x=0L; x < (ssize_t) (width/2L); x++)
490 forward_pixels[(ssize_t) width/2L-x-1L]=source_pixels[x+1L];
494static void CorrectPhaseLHS(
const size_t width,
const size_t height,
495 double *fourier_pixels)
501 for (y=0L; y < (ssize_t) height; y++)
502 for (x=0L; x < (ssize_t) (width/2L); x++)
503 fourier_pixels[y*(ssize_t) width+x]*=(-1.0);
506static MagickBooleanType ForwardFourier(
const FourierInfo *fourier_info,
536 magnitude_image=GetFirstImageInList(image);
537 phase_image=GetNextImageInList(image);
538 if (phase_image == (
Image *) NULL)
540 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
541 "ImageSequenceRequired",
"`%s'",image->filename);
547 magnitude_info=AcquireVirtualMemory(fourier_info->width,fourier_info->height*
548 sizeof(*magnitude_pixels));
549 phase_info=AcquireVirtualMemory(fourier_info->width,fourier_info->height*
550 sizeof(*phase_pixels));
551 if ((magnitude_info == (
MemoryInfo *) NULL) ||
555 phase_info=RelinquishVirtualMemory(phase_info);
557 magnitude_info=RelinquishVirtualMemory(magnitude_info);
558 (void) ThrowMagickException(exception,GetMagickModule(),
559 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
562 magnitude_pixels=(
double *) GetVirtualMemoryBlob(magnitude_info);
563 (void) memset(magnitude_pixels,0,fourier_info->width*
564 fourier_info->height*
sizeof(*magnitude_pixels));
565 phase_pixels=(
double *) GetVirtualMemoryBlob(phase_info);
566 (void) memset(phase_pixels,0,fourier_info->width*
567 fourier_info->height*
sizeof(*phase_pixels));
568 status=ForwardQuadrantSwap(fourier_info->width,fourier_info->height,
569 magnitude,magnitude_pixels);
570 if (status != MagickFalse)
571 status=ForwardQuadrantSwap(fourier_info->width,fourier_info->height,phase,
573 CorrectPhaseLHS(fourier_info->width,fourier_info->height,phase_pixels);
574 if (fourier_info->modulus != MagickFalse)
577 for (y=0L; y < (ssize_t) fourier_info->height; y++)
578 for (x=0L; x < (ssize_t) fourier_info->width; x++)
580 phase_pixels[i]/=(2.0*MagickPI);
581 phase_pixels[i]+=0.5;
585 magnitude_view=AcquireAuthenticCacheView(magnitude_image,exception);
587 for (y=0L; y < (ssize_t) fourier_info->height; y++)
589 q=GetCacheViewAuthenticPixels(magnitude_view,0L,y,fourier_info->width,1UL,
591 if (q == (Quantum *) NULL)
593 for (x=0L; x < (ssize_t) fourier_info->width; x++)
595 switch (fourier_info->channel)
597 case RedPixelChannel:
600 SetPixelRed(magnitude_image,ClampToQuantum((
double) QuantumRange*
601 magnitude_pixels[i]),q);
604 case GreenPixelChannel:
606 SetPixelGreen(magnitude_image,ClampToQuantum((
double) QuantumRange*
607 magnitude_pixels[i]),q);
610 case BluePixelChannel:
612 SetPixelBlue(magnitude_image,ClampToQuantum((
double) QuantumRange*
613 magnitude_pixels[i]),q);
616 case BlackPixelChannel:
618 SetPixelBlack(magnitude_image,ClampToQuantum((
double) QuantumRange*
619 magnitude_pixels[i]),q);
622 case AlphaPixelChannel:
624 SetPixelAlpha(magnitude_image,ClampToQuantum((
double) QuantumRange*
625 magnitude_pixels[i]),q);
630 q+=(ptrdiff_t) GetPixelChannels(magnitude_image);
632 status=SyncCacheViewAuthenticPixels(magnitude_view,exception);
633 if (status == MagickFalse)
636 magnitude_view=DestroyCacheView(magnitude_view);
638 phase_view=AcquireAuthenticCacheView(phase_image,exception);
639 for (y=0L; y < (ssize_t) fourier_info->height; y++)
641 q=GetCacheViewAuthenticPixels(phase_view,0L,y,fourier_info->width,1UL,
643 if (q == (Quantum *) NULL)
645 for (x=0L; x < (ssize_t) fourier_info->width; x++)
647 switch (fourier_info->channel)
649 case RedPixelChannel:
652 SetPixelRed(phase_image,ClampToQuantum((
double) QuantumRange*
656 case GreenPixelChannel:
658 SetPixelGreen(phase_image,ClampToQuantum((
double) QuantumRange*
662 case BluePixelChannel:
664 SetPixelBlue(phase_image,ClampToQuantum((
double) QuantumRange*
668 case BlackPixelChannel:
670 SetPixelBlack(phase_image,ClampToQuantum((
double) QuantumRange*
674 case AlphaPixelChannel:
676 SetPixelAlpha(phase_image,ClampToQuantum((
double) QuantumRange*
682 q+=(ptrdiff_t) GetPixelChannels(phase_image);
684 status=SyncCacheViewAuthenticPixels(phase_view,exception);
685 if (status == MagickFalse)
688 phase_view=DestroyCacheView(phase_view);
689 phase_info=RelinquishVirtualMemory(phase_info);
690 magnitude_info=RelinquishVirtualMemory(magnitude_info);
694static MagickBooleanType ForwardFourierTransform(
FourierInfo *fourier_info,
695 const Image *image,
double *magnitude_pixels,
double *phase_pixels,
728 if ((fourier_info->width >= INT_MAX) || (fourier_info->height >= INT_MAX))
730 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
731 "WidthOrHeightExceedsLimit",
"`%s'",image->filename);
734 source_info=AcquireVirtualMemory(fourier_info->width,fourier_info->height*
735 sizeof(*source_pixels));
738 (void) ThrowMagickException(exception,GetMagickModule(),
739 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
742 source_pixels=(
double *) GetVirtualMemoryBlob(source_info);
743 memset(source_pixels,0,fourier_info->width*fourier_info->height*
744 sizeof(*source_pixels));
746 image_view=AcquireVirtualCacheView(image,exception);
747 for (y=0L; y < (ssize_t) fourier_info->height; y++)
749 p=GetCacheViewVirtualPixels(image_view,0L,y,fourier_info->width,1UL,
751 if (p == (
const Quantum *) NULL)
753 for (x=0L; x < (ssize_t) fourier_info->width; x++)
755 switch (fourier_info->channel)
757 case RedPixelChannel:
760 source_pixels[i]=QuantumScale*(double) GetPixelRed(image,p);
763 case GreenPixelChannel:
765 source_pixels[i]=QuantumScale*(double) GetPixelGreen(image,p);
768 case BluePixelChannel:
770 source_pixels[i]=QuantumScale*(double) GetPixelBlue(image,p);
773 case BlackPixelChannel:
775 source_pixels[i]=QuantumScale*(double) GetPixelBlack(image,p);
778 case AlphaPixelChannel:
780 source_pixels[i]=QuantumScale*(double) GetPixelAlpha(image,p);
785 p+=(ptrdiff_t) GetPixelChannels(image);
788 image_view=DestroyCacheView(image_view);
789 forward_info=AcquireVirtualMemory(fourier_info->width,(fourier_info->height/2+
790 1)*
sizeof(*forward_pixels));
793 (void) ThrowMagickException(exception,GetMagickModule(),
794 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
795 source_info=(
MemoryInfo *) RelinquishVirtualMemory(source_info);
798 forward_pixels=(fftw_complex *) GetVirtualMemoryBlob(forward_info);
799#if defined(MAGICKCORE_OPENMP_SUPPORT)
800 #pragma omp critical (MagickCore_ForwardFourierTransform)
802 fftw_r2c_plan=fftw_plan_dft_r2c_2d((
int) fourier_info->width,
803 (
int) fourier_info->height,source_pixels,forward_pixels,FFTW_ESTIMATE);
804 fftw_execute_dft_r2c(fftw_r2c_plan,source_pixels,forward_pixels);
805 fftw_destroy_plan(fftw_r2c_plan);
806 source_info=(
MemoryInfo *) RelinquishVirtualMemory(source_info);
807 value=GetImageArtifact(image,
"fourier:normalize");
808 if ((value == (
const char *) NULL) || (LocaleCompare(value,
"forward") == 0))
817 gamma=PerceptibleReciprocal((
double) fourier_info->width*
818 fourier_info->height);
819 for (y=0L; y < (ssize_t) fourier_info->height; y++)
820 for (x=0L; x < (ssize_t) fourier_info->center; x++)
822#if defined(MAGICKCORE_HAVE_COMPLEX_H)
823 forward_pixels[i]*=gamma;
825 forward_pixels[i][0]*=gamma;
826 forward_pixels[i][1]*=gamma;
835 if (fourier_info->modulus != MagickFalse)
836 for (y=0L; y < (ssize_t) fourier_info->height; y++)
837 for (x=0L; x < (ssize_t) fourier_info->center; x++)
839 magnitude_pixels[i]=cabs(forward_pixels[i]);
840 phase_pixels[i]=carg(forward_pixels[i]);
844 for (y=0L; y < (ssize_t) fourier_info->height; y++)
845 for (x=0L; x < (ssize_t) fourier_info->center; x++)
847 magnitude_pixels[i]=creal(forward_pixels[i]);
848 phase_pixels[i]=cimag(forward_pixels[i]);
851 forward_info=(
MemoryInfo *) RelinquishVirtualMemory(forward_info);
855static MagickBooleanType ForwardFourierTransformChannel(
const Image *image,
856 const PixelChannel channel,
const MagickBooleanType modulus,
873 fourier_info.width=image->columns;
874 fourier_info.height=image->rows;
875 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
876 ((image->rows % 2) != 0))
878 size_t extent=image->columns < image->rows ? image->rows : image->columns;
879 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
881 fourier_info.height=fourier_info.width;
882 fourier_info.center=fourier_info.width/2+1;
883 fourier_info.channel=channel;
884 fourier_info.modulus=modulus;
885 magnitude_info=AcquireVirtualMemory(fourier_info.width,(fourier_info.height/2+
886 1)*
sizeof(*magnitude_pixels));
887 phase_info=AcquireVirtualMemory(fourier_info.width,(fourier_info.height/2+1)*
888 sizeof(*phase_pixels));
889 if ((magnitude_info == (
MemoryInfo *) NULL) ||
893 phase_info=RelinquishVirtualMemory(phase_info);
895 magnitude_info=RelinquishVirtualMemory(magnitude_info);
896 (void) ThrowMagickException(exception,GetMagickModule(),
897 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
900 magnitude_pixels=(
double *) GetVirtualMemoryBlob(magnitude_info);
901 phase_pixels=(
double *) GetVirtualMemoryBlob(phase_info);
902 status=ForwardFourierTransform(&fourier_info,image,magnitude_pixels,
903 phase_pixels,exception);
904 if (status != MagickFalse)
905 status=ForwardFourier(&fourier_info,fourier_image,magnitude_pixels,
906 phase_pixels,exception);
907 phase_info=RelinquishVirtualMemory(phase_info);
908 magnitude_info=RelinquishVirtualMemory(magnitude_info);
913MagickExport
Image *ForwardFourierTransformImage(
const Image *image,
919 fourier_image=NewImageList();
920#if !defined(ENABLE_FFTW_DELEGATE)
922 (void) ThrowMagickException(exception,GetMagickModule(),
923 MissingDelegateWarning,
"DelegateLibrarySupportNotBuiltIn",
"`%s' (FFTW)",
934 width=image->columns;
936 if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
937 ((image->rows % 2) != 0))
939 size_t extent=image->columns < image->rows ? image->rows :
941 width=(extent & 0x01) == 1 ? extent+1UL : extent;
944 magnitude_image=CloneImage(image,width,height,MagickTrue,exception);
945 if (magnitude_image != (
Image *) NULL)
950 magnitude_image->storage_class=DirectClass;
951 magnitude_image->depth=32UL;
952 phase_image=CloneImage(image,width,height,MagickTrue,exception);
953 if (phase_image == (
Image *) NULL)
954 magnitude_image=DestroyImage(magnitude_image);
961 phase_image->storage_class=DirectClass;
962 phase_image->depth=32UL;
963 AppendImageToList(&fourier_image,magnitude_image);
964 AppendImageToList(&fourier_image,phase_image);
966 is_gray=IsImageGray(image);
967#if defined(MAGICKCORE_OPENMP_SUPPORT)
968 #pragma omp parallel sections
971#if defined(MAGICKCORE_OPENMP_SUPPORT)
978 if (is_gray != MagickFalse)
979 thread_status=ForwardFourierTransformChannel(image,
980 GrayPixelChannel,modulus,fourier_image,exception);
982 thread_status=ForwardFourierTransformChannel(image,
983 RedPixelChannel,modulus,fourier_image,exception);
984 if (thread_status == MagickFalse)
985 status=thread_status;
987#if defined(MAGICKCORE_OPENMP_SUPPORT)
994 thread_status=MagickTrue;
995 if (is_gray == MagickFalse)
996 thread_status=ForwardFourierTransformChannel(image,
997 GreenPixelChannel,modulus,fourier_image,exception);
998 if (thread_status == MagickFalse)
999 status=thread_status;
1001#if defined(MAGICKCORE_OPENMP_SUPPORT)
1008 thread_status=MagickTrue;
1009 if (is_gray == MagickFalse)
1010 thread_status=ForwardFourierTransformChannel(image,
1011 BluePixelChannel,modulus,fourier_image,exception);
1012 if (thread_status == MagickFalse)
1013 status=thread_status;
1015#if defined(MAGICKCORE_OPENMP_SUPPORT)
1022 thread_status=MagickTrue;
1023 if (image->colorspace == CMYKColorspace)
1024 thread_status=ForwardFourierTransformChannel(image,
1025 BlackPixelChannel,modulus,fourier_image,exception);
1026 if (thread_status == MagickFalse)
1027 status=thread_status;
1029#if defined(MAGICKCORE_OPENMP_SUPPORT)
1036 thread_status=MagickTrue;
1037 if (image->alpha_trait != UndefinedPixelTrait)
1038 thread_status=ForwardFourierTransformChannel(image,
1039 AlphaPixelChannel,modulus,fourier_image,exception);
1040 if (thread_status == MagickFalse)
1041 status=thread_status;
1044 if (status == MagickFalse)
1045 fourier_image=DestroyImageList(fourier_image);
1051 return(fourier_image);
1088#if defined(ENABLE_FFTW_DELEGATE)
1089static MagickBooleanType InverseQuadrantSwap(
const size_t width,
1090 const size_t height,
const double *source,
double *destination)
1103 for (y=1L; y < (ssize_t) height; y++)
1104 for (x=0L; x < (ssize_t) (width/2L+1L); x++)
1105 destination[((ssize_t) height-y)*(ssize_t) center-x+(ssize_t) width/2L]=
1106 source[y*(ssize_t) width+x];
1107 for (y=0L; y < (ssize_t) height; y++)
1108 destination[y*(ssize_t) center]=
1109 source[y*(ssize_t) width+(ssize_t) width/2L];
1110 for (x=0L; x < (ssize_t) center; x++)
1111 destination[x]=source[(ssize_t) center-x-1L];
1112 return(RollFourier(center,height,0L,(ssize_t) height/-2L,destination));
1115static MagickBooleanType InverseFourier(
FourierInfo *fourier_info,
1116 const Image *magnitude_image,
const Image *phase_image,
1147 magnitude_info=AcquireVirtualMemory(fourier_info->width,fourier_info->height*
1148 sizeof(*magnitude_pixels));
1149 phase_info=AcquireVirtualMemory(fourier_info->width,fourier_info->height*
1150 sizeof(*phase_pixels));
1151 inverse_info=AcquireVirtualMemory(fourier_info->width,(fourier_info->height/2+
1152 1)*
sizeof(*inverse_pixels));
1153 if ((magnitude_info == (
MemoryInfo *) NULL) ||
1158 magnitude_info=RelinquishVirtualMemory(magnitude_info);
1160 phase_info=RelinquishVirtualMemory(phase_info);
1162 inverse_info=RelinquishVirtualMemory(inverse_info);
1163 (void) ThrowMagickException(exception,GetMagickModule(),
1164 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
1165 magnitude_image->filename);
1166 return(MagickFalse);
1168 magnitude_pixels=(
double *) GetVirtualMemoryBlob(magnitude_info);
1169 phase_pixels=(
double *) GetVirtualMemoryBlob(phase_info);
1170 inverse_pixels=(
double *) GetVirtualMemoryBlob(inverse_info);
1172 magnitude_view=AcquireVirtualCacheView(magnitude_image,exception);
1173 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1175 p=GetCacheViewVirtualPixels(magnitude_view,0L,y,fourier_info->width,1UL,
1177 if (p == (
const Quantum *) NULL)
1179 for (x=0L; x < (ssize_t) fourier_info->width; x++)
1181 switch (fourier_info->channel)
1183 case RedPixelChannel:
1186 magnitude_pixels[i]=QuantumScale*(double)
1187 GetPixelRed(magnitude_image,p);
1190 case GreenPixelChannel:
1192 magnitude_pixels[i]=QuantumScale*(double)
1193 GetPixelGreen(magnitude_image,p);
1196 case BluePixelChannel:
1198 magnitude_pixels[i]=QuantumScale*(double)
1199 GetPixelBlue(magnitude_image,p);
1202 case BlackPixelChannel:
1204 magnitude_pixels[i]=QuantumScale*(double)
1205 GetPixelBlack(magnitude_image,p);
1208 case AlphaPixelChannel:
1210 magnitude_pixels[i]=QuantumScale*(double)
1211 GetPixelAlpha(magnitude_image,p);
1216 p+=(ptrdiff_t) GetPixelChannels(magnitude_image);
1219 magnitude_view=DestroyCacheView(magnitude_view);
1220 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
1221 magnitude_pixels,inverse_pixels);
1222 (void) memcpy(magnitude_pixels,inverse_pixels,fourier_info->height*
1223 fourier_info->center*
sizeof(*magnitude_pixels));
1225 phase_view=AcquireVirtualCacheView(phase_image,exception);
1226 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1228 p=GetCacheViewVirtualPixels(phase_view,0,y,fourier_info->width,1,
1230 if (p == (
const Quantum *) NULL)
1232 for (x=0L; x < (ssize_t) fourier_info->width; x++)
1234 switch (fourier_info->channel)
1236 case RedPixelChannel:
1239 phase_pixels[i]=QuantumScale*(double) GetPixelRed(phase_image,p);
1242 case GreenPixelChannel:
1244 phase_pixels[i]=QuantumScale*(double) GetPixelGreen(phase_image,p);
1247 case BluePixelChannel:
1249 phase_pixels[i]=QuantumScale*(double) GetPixelBlue(phase_image,p);
1252 case BlackPixelChannel:
1254 phase_pixels[i]=QuantumScale*(double) GetPixelBlack(phase_image,p);
1257 case AlphaPixelChannel:
1259 phase_pixels[i]=QuantumScale*(double) GetPixelAlpha(phase_image,p);
1264 p+=(ptrdiff_t) GetPixelChannels(phase_image);
1267 if (fourier_info->modulus != MagickFalse)
1270 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1271 for (x=0L; x < (ssize_t) fourier_info->width; x++)
1273 phase_pixels[i]-=0.5;
1274 phase_pixels[i]*=(2.0*MagickPI);
1278 phase_view=DestroyCacheView(phase_view);
1279 CorrectPhaseLHS(fourier_info->width,fourier_info->height,phase_pixels);
1280 if (status != MagickFalse)
1281 status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
1282 phase_pixels,inverse_pixels);
1283 (void) memcpy(phase_pixels,inverse_pixels,fourier_info->height*
1284 fourier_info->center*
sizeof(*phase_pixels));
1285 inverse_info=RelinquishVirtualMemory(inverse_info);
1290 if (fourier_info->modulus != MagickFalse)
1291 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1292 for (x=0L; x < (ssize_t) fourier_info->center; x++)
1294#if defined(MAGICKCORE_HAVE_COMPLEX_H)
1295 fourier_pixels[i]=magnitude_pixels[i]*cos(phase_pixels[i])+I*
1296 magnitude_pixels[i]*sin(phase_pixels[i]);
1298 fourier_pixels[i][0]=magnitude_pixels[i]*cos(phase_pixels[i]);
1299 fourier_pixels[i][1]=magnitude_pixels[i]*sin(phase_pixels[i]);
1304 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1305 for (x=0L; x < (ssize_t) fourier_info->center; x++)
1307#if defined(MAGICKCORE_HAVE_COMPLEX_H)
1308 fourier_pixels[i]=magnitude_pixels[i]+I*phase_pixels[i];
1310 fourier_pixels[i][0]=magnitude_pixels[i];
1311 fourier_pixels[i][1]=phase_pixels[i];
1315 magnitude_info=RelinquishVirtualMemory(magnitude_info);
1316 phase_info=RelinquishVirtualMemory(phase_info);
1320static MagickBooleanType InverseFourierTransform(
FourierInfo *fourier_info,
1349 if ((fourier_info->width >= INT_MAX) || (fourier_info->height >= INT_MAX))
1351 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
1352 "WidthOrHeightExceedsLimit",
"`%s'",image->filename);
1353 return(MagickFalse);
1355 source_info=AcquireVirtualMemory(fourier_info->width,fourier_info->height*
1356 sizeof(*source_pixels));
1359 (void) ThrowMagickException(exception,GetMagickModule(),
1360 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
1361 return(MagickFalse);
1363 source_pixels=(
double *) GetVirtualMemoryBlob(source_info);
1364 value=GetImageArtifact(image,
"fourier:normalize");
1365 if (LocaleCompare(value,
"inverse") == 0)
1374 gamma=PerceptibleReciprocal((
double) fourier_info->width*
1375 fourier_info->height);
1376 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1377 for (x=0L; x < (ssize_t) fourier_info->center; x++)
1379#if defined(MAGICKCORE_HAVE_COMPLEX_H)
1380 fourier_pixels[i]*=gamma;
1382 fourier_pixels[i][0]*=gamma;
1383 fourier_pixels[i][1]*=gamma;
1388#if defined(MAGICKCORE_OPENMP_SUPPORT)
1389 #pragma omp critical (MagickCore_InverseFourierTransform)
1391 fftw_c2r_plan=fftw_plan_dft_c2r_2d((
int) fourier_info->width,
1392 (
int) fourier_info->height,fourier_pixels,source_pixels,FFTW_ESTIMATE);
1393 fftw_execute_dft_c2r(fftw_c2r_plan,fourier_pixels,source_pixels);
1394 fftw_destroy_plan(fftw_c2r_plan);
1396 image_view=AcquireAuthenticCacheView(image,exception);
1397 for (y=0L; y < (ssize_t) fourier_info->height; y++)
1399 if (y >= (ssize_t) image->rows)
1401 q=GetCacheViewAuthenticPixels(image_view,0L,y,fourier_info->width >
1402 image->columns ? image->columns : fourier_info->width,1UL,exception);
1403 if (q == (Quantum *) NULL)
1405 for (x=0L; x < (ssize_t) fourier_info->width; x++)
1407 if (x < (ssize_t) image->columns)
1408 switch (fourier_info->channel)
1410 case RedPixelChannel:
1413 SetPixelRed(image,ClampToQuantum((
double) QuantumRange*
1414 source_pixels[i]),q);
1417 case GreenPixelChannel:
1419 SetPixelGreen(image,ClampToQuantum((
double) QuantumRange*
1420 source_pixels[i]),q);
1423 case BluePixelChannel:
1425 SetPixelBlue(image,ClampToQuantum((
double) QuantumRange*
1426 source_pixels[i]),q);
1429 case BlackPixelChannel:
1431 SetPixelBlack(image,ClampToQuantum((
double) QuantumRange*
1432 source_pixels[i]),q);
1435 case AlphaPixelChannel:
1437 SetPixelAlpha(image,ClampToQuantum((
double) QuantumRange*
1438 source_pixels[i]),q);
1443 q+=(ptrdiff_t) GetPixelChannels(image);
1445 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1448 image_view=DestroyCacheView(image_view);
1449 source_info=RelinquishVirtualMemory(source_info);
1453static MagickBooleanType InverseFourierTransformChannel(
1454 const Image *magnitude_image,
const Image *phase_image,
1455 const PixelChannel channel,
const MagickBooleanType modulus,
1470 fourier_info.width=magnitude_image->columns;
1471 fourier_info.height=magnitude_image->rows;
1472 if ((magnitude_image->columns != magnitude_image->rows) ||
1473 ((magnitude_image->columns % 2) != 0) ||
1474 ((magnitude_image->rows % 2) != 0))
1476 size_t extent=magnitude_image->columns < magnitude_image->rows ?
1477 magnitude_image->rows : magnitude_image->columns;
1478 fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
1480 fourier_info.height=fourier_info.width;
1481 fourier_info.center=fourier_info.width/2+1;
1482 fourier_info.channel=channel;
1483 fourier_info.modulus=modulus;
1484 inverse_info=AcquireVirtualMemory(fourier_info.width,(fourier_info.height/2+
1485 1)*
sizeof(*inverse_pixels));
1488 (void) ThrowMagickException(exception,GetMagickModule(),
1489 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
1490 magnitude_image->filename);
1491 return(MagickFalse);
1493 inverse_pixels=(fftw_complex *) GetVirtualMemoryBlob(inverse_info);
1494 status=InverseFourier(&fourier_info,magnitude_image,phase_image,
1495 inverse_pixels,exception);
1496 if (status != MagickFalse)
1497 status=InverseFourierTransform(&fourier_info,inverse_pixels,fourier_image,
1499 inverse_info=RelinquishVirtualMemory(inverse_info);
1504MagickExport
Image *InverseFourierTransformImage(
const Image *magnitude_image,
1505 const Image *phase_image,
const MagickBooleanType modulus,
1511 assert(magnitude_image != (
Image *) NULL);
1512 assert(magnitude_image->signature == MagickCoreSignature);
1513 if (IsEventLogging() != MagickFalse)
1514 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",
1515 magnitude_image->filename);
1516 if (phase_image == (
Image *) NULL)
1518 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
1519 "ImageSequenceRequired",
"`%s'",magnitude_image->filename);
1520 return((
Image *) NULL);
1522#if !defined(ENABLE_FFTW_DELEGATE)
1523 fourier_image=(
Image *) NULL;
1525 (void) ThrowMagickException(exception,GetMagickModule(),
1526 MissingDelegateWarning,
"DelegateLibrarySupportNotBuiltIn",
"`%s' (FFTW)",
1527 magnitude_image->filename);
1530 fourier_image=CloneImage(magnitude_image,magnitude_image->columns,
1531 magnitude_image->rows,MagickTrue,exception);
1532 if (fourier_image != (
Image *) NULL)
1539 is_gray=IsImageGray(magnitude_image);
1540 if (is_gray != MagickFalse)
1541 is_gray=IsImageGray(phase_image);
1542#if defined(MAGICKCORE_OPENMP_SUPPORT)
1543 #pragma omp parallel sections
1546#if defined(MAGICKCORE_OPENMP_SUPPORT)
1553 if (is_gray != MagickFalse)
1554 thread_status=InverseFourierTransformChannel(magnitude_image,
1555 phase_image,GrayPixelChannel,modulus,fourier_image,exception);
1557 thread_status=InverseFourierTransformChannel(magnitude_image,
1558 phase_image,RedPixelChannel,modulus,fourier_image,exception);
1559 if (thread_status == MagickFalse)
1560 status=thread_status;
1562#if defined(MAGICKCORE_OPENMP_SUPPORT)
1569 thread_status=MagickTrue;
1570 if (is_gray == MagickFalse)
1571 thread_status=InverseFourierTransformChannel(magnitude_image,
1572 phase_image,GreenPixelChannel,modulus,fourier_image,exception);
1573 if (thread_status == MagickFalse)
1574 status=thread_status;
1576#if defined(MAGICKCORE_OPENMP_SUPPORT)
1583 thread_status=MagickTrue;
1584 if (is_gray == MagickFalse)
1585 thread_status=InverseFourierTransformChannel(magnitude_image,
1586 phase_image,BluePixelChannel,modulus,fourier_image,exception);
1587 if (thread_status == MagickFalse)
1588 status=thread_status;
1590#if defined(MAGICKCORE_OPENMP_SUPPORT)
1597 thread_status=MagickTrue;
1598 if (magnitude_image->colorspace == CMYKColorspace)
1599 thread_status=InverseFourierTransformChannel(magnitude_image,
1600 phase_image,BlackPixelChannel,modulus,fourier_image,exception);
1601 if (thread_status == MagickFalse)
1602 status=thread_status;
1604#if defined(MAGICKCORE_OPENMP_SUPPORT)
1611 thread_status=MagickTrue;
1612 if (magnitude_image->alpha_trait != UndefinedPixelTrait)
1613 thread_status=InverseFourierTransformChannel(magnitude_image,
1614 phase_image,AlphaPixelChannel,modulus,fourier_image,exception);
1615 if (thread_status == MagickFalse)
1616 status=thread_status;
1619 if (status == MagickFalse)
1620 fourier_image=DestroyImage(fourier_image);
1625 return(fourier_image);