]> granicus.if.org Git - imagemagick/blobdiff - MagickCore/fourier.c
(no commit message)
[imagemagick] / MagickCore / fourier.c
index 625622ac6d77ea56a1551b0b94d301d8d3ce5e4c..a649eca2f53c54d77987d1ba80e19eca51fcdeea 100644 (file)
 %                              Software Design                                %
 %                                Sean Burke                                   %
 %                               Fred Weinhaus                                 %
-%                                John Cristy                                  %
+%                                   Cristy                                    %
 %                                 July 2009                                   %
 %                                                                             %
 %                                                                             %
-%  Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization      %
+%  Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization      %
 %  dedicated to making software imaging solutions freely available.           %
 %                                                                             %
 %  You may not use this file except in compliance with the License.  You may  %
@@ -43,6 +43,7 @@
   Include declarations.
 */
 #include "MagickCore/studio.h"
+#include "MagickCore/artifact.h"
 #include "MagickCore/attribute.h"
 #include "MagickCore/blob.h"
 #include "MagickCore/cache.h"
 #include "MagickCore/log.h"
 #include "MagickCore/memory_.h"
 #include "MagickCore/monitor.h"
+#include "MagickCore/monitor-private.h"
 #include "MagickCore/pixel-accessor.h"
+#include "MagickCore/pixel-private.h"
 #include "MagickCore/property.h"
 #include "MagickCore/quantum-private.h"
 #include "MagickCore/resource_.h"
+#include "MagickCore/string-private.h"
 #include "MagickCore/thread-private.h"
 #if defined(MAGICKCORE_FFTW_DELEGATE)
 #if defined(MAGICKCORE_HAVE_COMPLEX_H)
@@ -101,6 +105,262 @@ typedef struct _FourierInfo
 %                                                                             %
 %                                                                             %
 %                                                                             %
+%     C o m p l e x I m a g e s                                               %
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  ComplexImages() performs complex mathematics on an image sequence.
+%
+%  The format of the ComplexImages method is:
+%
+%      MagickBooleanType ComplexImages(Image *images,
+%        const ComplexOperator op,ExceptionInfo *exception)
+%
+%  A description of each parameter follows:
+%
+%    o image: the image.
+%
+%    o op: A complex op.
+%
+%    o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *ComplexImages(const Image *images,const ComplexOperator op,
+  ExceptionInfo *exception)
+{
+#define ComplexImageTag  "Complex/Image"
+
+  CacheView
+    *Ai_view,
+    *Ar_view,
+    *Bi_view,
+    *Br_view,
+    *Ci_view,
+    *Cr_view;
+
+  const char
+    *artifact;
+
+  const Image
+    *Ai_image,
+    *Ar_image,
+    *Bi_image,
+    *Br_image;
+
+  double
+    snr;
+
+  Image
+    *Ci_image,
+    *complex_images,
+    *Cr_image,
+    *image;
+
+  MagickBooleanType
+    status;
+
+  MagickOffsetType
+    progress;
+
+  ssize_t
+    y;
+
+  assert(images != (Image *) NULL);
+  assert(images->signature == MagickSignature);
+  if (images->debug != MagickFalse)
+    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
+  assert(exception != (ExceptionInfo *) NULL);
+  assert(exception->signature == MagickSignature);
+  if (images->next == (Image *) NULL)
+    {
+      (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
+        "ImageSequenceRequired","`%s'",images->filename);
+      return((Image *) NULL);
+    }
+  image=CloneImage(images,images->columns,images->rows,MagickTrue,exception);
+  if (image == (Image *) NULL)
+    return((Image *) NULL);
+  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
+    {
+      image=DestroyImageList(image);
+      return(image);
+    }
+  complex_images=NewImageList();
+  AppendImageToList(&complex_images,image);
+  image=CloneImage(images,images->columns,images->rows,MagickTrue,exception);
+  if (image == (Image *) NULL)
+    {
+      complex_images=DestroyImageList(complex_images);
+      return(complex_images);
+    }
+  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
+    {
+      complex_images=DestroyImageList(complex_images);
+      return(complex_images);
+    }
+  AppendImageToList(&complex_images,image);
+  /*
+    Apply complex mathematics to image pixels.
+  */
+  artifact=GetImageArtifact(image,"complex:snr");
+  snr=0.0;
+  if (artifact != (const char *) NULL)
+    snr=StringToDouble(artifact,(char **) NULL);
+  Ar_image=images;
+  Ai_image=images->next;
+  Br_image=images;
+  Bi_image=images->next;
+  if ((images->next->next != (Image *) NULL) &&
+      (images->next->next->next != (Image *) NULL))
+    {
+      Br_image=images->next->next;
+      Bi_image=images->next->next->next;
+    }
+  Cr_image=complex_images;
+  Ci_image=complex_images->next;
+  Ar_view=AcquireVirtualCacheView(Ar_image,exception);
+  Ai_view=AcquireVirtualCacheView(Ai_image,exception);
+  Br_view=AcquireVirtualCacheView(Br_image,exception);
+  Bi_view=AcquireVirtualCacheView(Bi_image,exception);
+  Cr_view=AcquireAuthenticCacheView(Cr_image,exception);
+  Ci_view=AcquireAuthenticCacheView(Ci_image,exception);
+  status=MagickTrue;
+  progress=0;
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+    #pragma omp parallel for schedule(static,4) shared(progress,status) \
+      magick_threads(images,complex_images,images->rows,1L)
+#endif
+  for (y=0; y < (ssize_t) images->rows; y++)
+  {
+    register const Quantum
+      *restrict Ai,
+      *restrict Ar,
+      *restrict Bi,
+      *restrict Br;
+
+    register Quantum
+      *restrict Ci,
+      *restrict Cr;
+
+    register ssize_t
+      x;
+
+    if (status == MagickFalse)
+      continue;
+    Ar=GetCacheViewVirtualPixels(Ar_view,0,y,images->columns,1,exception);
+    Ai=GetCacheViewVirtualPixels(Ai_view,0,y,images->columns,1,exception);
+    Br=GetCacheViewVirtualPixels(Br_view,0,y,images->columns,1,exception);
+    Bi=GetCacheViewVirtualPixels(Bi_view,0,y,images->columns,1,exception);
+    Cr=QueueCacheViewAuthenticPixels(Cr_view,0,y,images->columns,1,exception);
+    Ci=QueueCacheViewAuthenticPixels(Ci_view,0,y,images->columns,1,exception);
+    if ((Ar == (const Quantum *) NULL) || (Ai == (const Quantum *) NULL) || 
+        (Br == (const Quantum *) NULL) || (Bi == (const Quantum *) NULL) ||
+        (Cr == (Quantum *) NULL) || (Ci == (Quantum *) NULL))
+      {
+        status=MagickFalse;
+        continue;
+      }
+    for (x=0; x < (ssize_t) images->columns; x++)
+    {
+      register ssize_t
+        i;
+
+      for (i=0; i < (ssize_t) GetPixelChannels(images); i++)
+      {
+        switch (op)
+        {
+          case AddComplexOperator:
+          {
+            Cr[i]=Ar[i]+Br[i];
+            Ci[i]=Ai[i]+Bi[i];
+            break;
+          }
+          case ConjugateComplexOperator:
+          default:
+          {
+            Cr[i]=Ar[i];
+            Ci[i]=(-Bi[i]);
+            break;
+          }
+          case DivideComplexOperator:
+          {
+            double
+              gamma;
+
+            gamma=PerceptibleReciprocal(Br[i]*Br[i]+Bi[i]*Bi[i]+snr);
+            Cr[i]=gamma*(Ar[i]*Br[i]+Ai[i]*Bi[i]);
+            Ci[i]=gamma*(Ai[i]*Br[i]-Ar[i]*Bi[i]);
+            break;
+          }
+          case MagnitudePhaseComplexOperator:
+          {
+            Cr[i]=sqrt(Ar[i]*Ar[i]+Ai[i]*Ai[i]);
+            Ci[i]=atan2(Ai[i],Ar[i])/(2.0*MagickPI)+0.5;
+            break;
+          }
+          case MultiplyComplexOperator:
+          {
+            Cr[i]=QuantumScale*(Ar[i]*Br[i]-Ai[i]*Bi[i]);
+            Ci[i]=QuantumScale*(Ai[i]*Br[i]+Ar[i]*Bi[i]);
+            break;
+          }
+          case RealImaginaryComplexOperator:
+          {
+            Cr[i]=Ar[i]*cos(2.0*MagickPI*(Ai[i]-0.5));
+            Ci[i]=Ar[i]*sin(2.0*MagickPI*(Ai[i]-0.5));
+            break;
+          }
+          case SubtractComplexOperator:
+          {
+            Cr[i]=Ar[i]-Br[i];
+            Ci[i]=Ai[i]-Bi[i];
+            break;
+          }
+        }
+        Ar+=GetPixelChannels(Ar_image);
+        Ai+=GetPixelChannels(Ai_image);
+        Br+=GetPixelChannels(Br_image);
+        Bi+=GetPixelChannels(Bi_image);
+        Cr+=GetPixelChannels(Cr_image);
+        Ci+=GetPixelChannels(Ci_image);
+      }
+    }
+    if (SyncCacheViewAuthenticPixels(Ci_view,exception) == MagickFalse)
+      status=MagickFalse;
+    if (SyncCacheViewAuthenticPixels(Cr_view,exception) == MagickFalse)
+      status=MagickFalse;
+    if (images->progress_monitor != (MagickProgressMonitor) NULL)
+      {
+        MagickBooleanType
+          proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+        #pragma omp critical (MagickCore_ComplexImages)
+#endif
+        proceed=SetImageProgress(images,ComplexImageTag,progress++,
+          images->rows);
+        if (proceed == MagickFalse)
+          status=MagickFalse;
+      }
+  }
+  Cr_view=DestroyCacheView(Cr_view);
+  Ci_view=DestroyCacheView(Ci_view);
+  Br_view=DestroyCacheView(Br_view);
+  Bi_view=DestroyCacheView(Bi_view);
+  Ar_view=DestroyCacheView(Ar_view);
+  Ai_view=DestroyCacheView(Ai_view);
+  if (status == MagickFalse)
+    complex_images=DestroyImageList(complex_images);
+  return(complex_images);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
 %     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                 %
 %                                                                             %
 %                                                                             %
@@ -422,8 +682,10 @@ static MagickBooleanType ForwardFourierTransform(FourierInfo *fourier_info,
   CacheView
     *image_view;
 
+  const char
+    *value;
+
   double
-    gamma,
     *source_pixels;
 
   fftw_complex
@@ -522,22 +784,29 @@ static MagickBooleanType ForwardFourierTransform(FourierInfo *fourier_info,
   fftw_execute(fftw_r2c_plan);
   fftw_destroy_plan(fftw_r2c_plan);
   source_info=(MemoryInfo *) RelinquishVirtualMemory(source_info);
-  /*
-    Normalize Fourier transform.
-  */
-  i=0L;
-  gamma=PerceptibleReciprocal((double) fourier_info->width*
-    fourier_info->height);
-  for (y=0L; y < (ssize_t) fourier_info->height; y++)
-    for (x=0L; x < (ssize_t) fourier_info->center; x++)
+  value=GetImageArtifact(image,"fourier:normalize");
+  if ((value == (const char *) NULL) || (LocaleCompare(value,"forward") == 0))
     {
+      double
+        gamma;
+
+      /*
+        Normalize Fourier transform.
+      */
+      i=0L;
+      gamma=PerceptibleReciprocal((double) fourier_info->width*
+        fourier_info->height);
+      for (y=0L; y < (ssize_t) fourier_info->height; y++)
+        for (x=0L; x < (ssize_t) fourier_info->center; x++)
+        {
 #if defined(MAGICKCORE_HAVE_COMPLEX_H)
-      forward_pixels[i]*=gamma;
+          forward_pixels[i]*=gamma;
 #else
-      forward_pixels[i][0]*=gamma;
-      forward_pixels[i][1]*=gamma;
+          forward_pixels[i][0]*=gamma;
+          forward_pixels[i][1]*=gamma;
 #endif
-      i++;
+          i++;
+        }
     }
   /*
     Generate magnitude and phase (or real and imaginary).
@@ -585,6 +854,7 @@ static MagickBooleanType ForwardFourierTransformChannel(const Image *image,
     extent;
 
   fourier_info.width=image->columns;
+  fourier_info.height=image->rows;
   if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
       ((image->rows % 2) != 0))
     {
@@ -642,16 +912,19 @@ MagickExport Image *ForwardFourierTransformImage(const Image *image,
 
     size_t
       extent,
+      height,
       width;
 
     width=image->columns;
+    height=image->rows;
     if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
         ((image->rows % 2) != 0))
       {
         extent=image->columns < image->rows ? image->rows : image->columns;
         width=(extent & 0x01) == 1 ? extent+1UL : extent;
       }
-    magnitude_image=CloneImage(image,width,width,MagickFalse,exception);
+    height=width;
+    magnitude_image=CloneImage(image,width,height,MagickTrue,exception);
     if (magnitude_image != (Image *) NULL)
       {
         Image
@@ -659,7 +932,7 @@ MagickExport Image *ForwardFourierTransformImage(const Image *image,
 
         magnitude_image->storage_class=DirectClass;
         magnitude_image->depth=32UL;
-        phase_image=CloneImage(image,width,width,MagickFalse,exception);
+        phase_image=CloneImage(image,width,height,MagickTrue,exception);
         if (phase_image == (Image *) NULL)
           magnitude_image=DestroyImage(magnitude_image);
         else
@@ -1028,6 +1301,9 @@ static MagickBooleanType InverseFourierTransform(FourierInfo *fourier_info,
   CacheView
     *image_view;
 
+  const char
+    *value;
+
   double
     *source_pixels;
 
@@ -1056,6 +1332,30 @@ static MagickBooleanType InverseFourierTransform(FourierInfo *fourier_info,
       return(MagickFalse);
     }
   source_pixels=(double *) GetVirtualMemoryBlob(source_info);
+  value=GetImageArtifact(image,"fourier:normalize");
+  if (LocaleCompare(value,"inverse") == 0)
+    {
+      double
+        gamma;
+
+      /*
+        Normalize inverse transform.
+      */
+      i=0L;
+      gamma=PerceptibleReciprocal((double) fourier_info->width*
+        fourier_info->height);
+      for (y=0L; y < (ssize_t) fourier_info->height; y++)
+        for (x=0L; x < (ssize_t) fourier_info->center; x++)
+        {
+#if defined(MAGICKCORE_HAVE_COMPLEX_H)
+          fourier_pixels[i]*=gamma;
+#else
+          fourier_pixels[i][0]*=gamma;
+          fourier_pixels[i][1]*=gamma;
+#endif
+          i++;
+        }
+    }
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   #pragma omp critical (MagickCore_InverseFourierTransform)
 #endif
@@ -1143,6 +1443,7 @@ static MagickBooleanType InverseFourierTransformChannel(
     extent;
 
   fourier_info.width=magnitude_image->columns;
+  fourier_info.height=magnitude_image->rows;
   if ((magnitude_image->columns != magnitude_image->rows) ||
       ((magnitude_image->columns % 2) != 0) ||
       ((magnitude_image->rows % 2) != 0))
@@ -1202,7 +1503,7 @@ MagickExport Image *InverseFourierTransformImage(const Image *magnitude_image,
 #else
   {
     fourier_image=CloneImage(magnitude_image,magnitude_image->columns,
-      magnitude_image->rows,MagickFalse,exception);
+      magnitude_image->rows,MagickTrue,exception);
     if (fourier_image != (Image *) NULL)
       {
         MagickBooleanType