MagickCore  6.7.5
fourier.c
Go to the documentation of this file.
00001 /*
00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00003 %                                                                             %
00004 %                                                                             %
00005 %                                                                             %
00006 %               FFFFF   OOO   U   U  RRRR   IIIII  EEEEE  RRRR                %
00007 %               F      O   O  U   U  R   R    I    E      R   R               %
00008 %               FFF    O   O  U   U  RRRR     I    EEE    RRRR                %
00009 %               F      O   O  U   U  R R      I    E      R R                 %
00010 %               F       OOO    UUU   R  R   IIIII  EEEEE  R  R                %
00011 %                                                                             %
00012 %                                                                             %
00013 %                MagickCore Discrete Fourier Transform Methods                %
00014 %                                                                             %
00015 %                              Software Design                                %
00016 %                                Sean Burke                                   %
00017 %                               Fred Weinhaus                                 %
00018 %                                John Cristy                                  %
00019 %                                 July 2009                                   %
00020 %                                                                             %
00021 %                                                                             %
00022 %  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
00023 %  dedicated to making software imaging solutions freely available.           %
00024 %                                                                             %
00025 %  You may not use this file except in compliance with the License.  You may  %
00026 %  obtain a copy of the License at                                            %
00027 %                                                                             %
00028 %    http://www.imagemagick.org/script/license.php                            %
00029 %                                                                             %
00030 %  Unless required by applicable law or agreed to in writing, software        %
00031 %  distributed under the License is distributed on an "AS IS" BASIS,          %
00032 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
00033 %  See the License for the specific language governing permissions and        %
00034 %  limitations under the License.                                             %
00035 %                                                                             %
00036 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00037 %
00038 %
00039 %
00040 */
00041 
00042 /*
00043   Include declarations.
00044 */
00045 #include "MagickCore/studio.h"
00046 #include "MagickCore/attribute.h"
00047 #include "MagickCore/cache.h"
00048 #include "MagickCore/image.h"
00049 #include "MagickCore/image-private.h"
00050 #include "MagickCore/list.h"
00051 #include "MagickCore/fourier.h"
00052 #include "MagickCore/log.h"
00053 #include "MagickCore/memory_.h"
00054 #include "MagickCore/monitor.h"
00055 #include "MagickCore/pixel-accessor.h"
00056 #include "MagickCore/property.h"
00057 #include "MagickCore/quantum-private.h"
00058 #include "MagickCore/thread-private.h"
00059 #if defined(MAGICKCORE_FFTW_DELEGATE)
00060 #if defined(MAGICKCORE_HAVE_COMPLEX_H)
00061 #include <complex.h>
00062 #endif
00063 #include <fftw3.h>
00064 #if !defined(MAGICKCORE_HAVE_CABS)
00065 #define cabs(z)  (sqrt(z[0]*z[0]+z[1]*z[1]))
00066 #endif
00067 #if !defined(MAGICKCORE_HAVE_CARG)
00068 #define carg(z)  (atan2(cimag(z),creal(z)))
00069 #endif
00070 #if !defined(MAGICKCORE_HAVE_CIMAG)
00071 #define cimag(z)  (z[1])
00072 #endif
00073 #if !defined(MAGICKCORE_HAVE_CREAL)
00074 #define creal(z)  (z[0])
00075 #endif
00076 #endif
00077 
00078 /*
00079   Typedef declarations.
00080 */
00081 typedef struct _FourierInfo
00082 {
00083   PixelChannel
00084     channel;
00085 
00086   MagickBooleanType
00087     modulus;
00088 
00089   size_t
00090     width,
00091     height;
00092 
00093   ssize_t
00094     center;
00095 } FourierInfo;
00096 
00097 /*
00098 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00099 %                                                                             %
00100 %                                                                             %
00101 %                                                                             %
00102 %     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                 %
00103 %                                                                             %
00104 %                                                                             %
00105 %                                                                             %
00106 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00107 %
00108 %  ForwardFourierTransformImage() implements the discrete Fourier transform
00109 %  (DFT) of the image either as a magnitude / phase or real / imaginary image
00110 %  pair.
00111 %
00112 %  The format of the ForwadFourierTransformImage method is:
00113 %
00114 %      Image *ForwardFourierTransformImage(const Image *image,
00115 %        const MagickBooleanType modulus,ExceptionInfo *exception)
00116 %
00117 %  A description of each parameter follows:
00118 %
00119 %    o image: the image.
00120 %
00121 %    o modulus: if true, return as transform as a magnitude / phase pair
00122 %      otherwise a real / imaginary image pair.
00123 %
00124 %    o exception: return any errors or warnings in this structure.
00125 %
00126 */
00127 
00128 #if defined(MAGICKCORE_FFTW_DELEGATE)
00129 
00130 static MagickBooleanType RollFourier(const size_t width,const size_t height,
00131   const ssize_t x_offset,const ssize_t y_offset,double *fourier)
00132 {
00133   double
00134     *roll;
00135 
00136   register ssize_t
00137     i,
00138     x;
00139 
00140   ssize_t
00141     u,
00142     v,
00143     y;
00144 
00145   /*
00146     Move zero frequency (DC, average color) from (0,0) to (width/2,height/2).
00147   */
00148   roll=(double *) AcquireQuantumMemory((size_t) height,width*sizeof(*roll));
00149   if (roll == (double *) NULL)
00150     return(MagickFalse);
00151   i=0L;
00152   for (y=0L; y < (ssize_t) height; y++)
00153   {
00154     if (y_offset < 0L)
00155       v=((y+y_offset) < 0L) ? y+y_offset+(ssize_t) height : y+y_offset;
00156     else
00157       v=((y+y_offset) > ((ssize_t) height-1L)) ? y+y_offset-(ssize_t) height :
00158         y+y_offset;
00159     for (x=0L; x < (ssize_t) width; x++)
00160     {
00161       if (x_offset < 0L)
00162         u=((x+x_offset) < 0L) ? x+x_offset+(ssize_t) width : x+x_offset;
00163       else
00164         u=((x+x_offset) > ((ssize_t) width-1L)) ? x+x_offset-(ssize_t) width :
00165           x+x_offset;
00166       roll[v*width+u]=fourier[i++];
00167     }
00168   }
00169   (void) CopyMagickMemory(fourier,roll,height*width*sizeof(*roll));
00170   roll=(double *) RelinquishMagickMemory(roll);
00171   return(MagickTrue);
00172 }
00173 
00174 static MagickBooleanType ForwardQuadrantSwap(const size_t width,
00175   const size_t height,double *source,double *destination)
00176 {
00177   MagickBooleanType
00178     status;
00179 
00180   register ssize_t
00181     x;
00182 
00183   ssize_t
00184     center,
00185     y;
00186 
00187   /*
00188     Swap quadrants.
00189   */
00190   center=(ssize_t) floor((double) width/2L)+1L;
00191   status=RollFourier((size_t) center,height,0L,(ssize_t) height/2L,source);
00192   if (status == MagickFalse)
00193     return(MagickFalse);
00194   for (y=0L; y < (ssize_t) height; y++)
00195     for (x=0L; x < (ssize_t) (width/2L-1L); x++)
00196       destination[width*y+x+width/2L]=source[center*y+x];
00197   for (y=1; y < (ssize_t) height; y++)
00198     for (x=0L; x < (ssize_t) (width/2L-1L); x++)
00199       destination[width*(height-y)+width/2L-x-1L]=source[center*y+x+1L];
00200   for (x=0L; x < (ssize_t) (width/2L); x++)
00201     destination[-x+width/2L-1L]=destination[x+width/2L+1L];
00202   return(MagickTrue);
00203 }
00204 
00205 static void CorrectPhaseLHS(const size_t width,const size_t height,
00206   double *fourier)
00207 {
00208   register ssize_t
00209     x;
00210 
00211   ssize_t
00212     y;
00213 
00214   for (y=0L; y < (ssize_t) height; y++)
00215     for (x=0L; x < (ssize_t) (width/2L); x++)
00216       fourier[y*width+x]*=(-1.0);
00217 }
00218 
00219 static MagickBooleanType ForwardFourier(const FourierInfo *fourier_info,
00220   Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
00221 {
00222   CacheView
00223     *magnitude_view,
00224     *phase_view;
00225 
00226   double
00227     *magnitude_source,
00228     *phase_source;
00229 
00230   Image
00231     *magnitude_image,
00232     *phase_image;
00233 
00234   MagickBooleanType
00235     status;
00236 
00237   register ssize_t
00238     x;
00239 
00240   register Quantum
00241     *q;
00242 
00243   ssize_t
00244     i,
00245     y;
00246 
00247   magnitude_image=GetFirstImageInList(image);
00248   phase_image=GetNextImageInList(image);
00249   if (phase_image == (Image *) NULL)
00250     {
00251       (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
00252         "ImageSequenceRequired","`%s'",image->filename);
00253       return(MagickFalse);
00254     }
00255   /*
00256     Create "Fourier Transform" image from constituent arrays.
00257   */
00258   magnitude_source=(double *) AcquireQuantumMemory((size_t)
00259     fourier_info->height,fourier_info->width*sizeof(*magnitude_source));
00260   if (magnitude_source == (double *) NULL)
00261     return(MagickFalse);
00262   (void) ResetMagickMemory(magnitude_source,0,fourier_info->height*
00263     fourier_info->width*sizeof(*magnitude_source));
00264   phase_source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
00265     fourier_info->width*sizeof(*phase_source));
00266   if (phase_source == (double *) NULL)
00267     {
00268       (void) ThrowMagickException(exception,GetMagickModule(),
00269         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
00270       magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
00271       return(MagickFalse);
00272     }
00273   status=ForwardQuadrantSwap(fourier_info->height,fourier_info->height,
00274     magnitude,magnitude_source);
00275   if (status != MagickFalse)
00276     status=ForwardQuadrantSwap(fourier_info->height,fourier_info->height,phase,
00277       phase_source);
00278   CorrectPhaseLHS(fourier_info->height,fourier_info->height,phase_source);
00279   if (fourier_info->modulus != MagickFalse)
00280     {
00281       i=0L;
00282       for (y=0L; y < (ssize_t) fourier_info->height; y++)
00283         for (x=0L; x < (ssize_t) fourier_info->width; x++)
00284         {
00285           phase_source[i]/=(2.0*MagickPI);
00286           phase_source[i]+=0.5;
00287           i++;
00288         }
00289     }
00290   magnitude_view=AcquireCacheView(magnitude_image);
00291   phase_view=AcquireCacheView(phase_image);
00292   i=0L;
00293   for (y=0L; y < (ssize_t) fourier_info->height; y++)
00294   {
00295     q=GetCacheViewAuthenticPixels(magnitude_view,0L,y,fourier_info->height,1UL,
00296       exception);
00297     if (q == (Quantum *) NULL)
00298       break;
00299     for (x=0L; x < (ssize_t) fourier_info->width; x++)
00300     {
00301       switch (fourier_info->channel)
00302       {
00303         case RedPixelChannel:
00304         default:
00305         {
00306           SetPixelRed(magnitude_image,ClampToQuantum(QuantumRange*
00307             magnitude_source[i]),q);
00308           break;
00309         }
00310         case GreenPixelChannel:
00311         {
00312           SetPixelGreen(magnitude_image,ClampToQuantum(QuantumRange*
00313             magnitude_source[i]),q);
00314           break;
00315         }
00316         case BluePixelChannel:
00317         {
00318           SetPixelBlue(magnitude_image,ClampToQuantum(QuantumRange*
00319             magnitude_source[i]),q);
00320           break;
00321         }
00322         case BlackPixelChannel:
00323         {
00324           SetPixelBlack(magnitude_image,ClampToQuantum(QuantumRange*
00325             magnitude_source[i]),q);
00326           break;
00327         }
00328         case AlphaPixelChannel:
00329         {
00330           SetPixelAlpha(magnitude_image,ClampToQuantum(QuantumRange*
00331             magnitude_source[i]),q);
00332           break;
00333         }
00334       }
00335       i++;
00336       q+=GetPixelChannels(magnitude_image);
00337     }
00338     status=SyncCacheViewAuthenticPixels(magnitude_view,exception);
00339     if (status == MagickFalse)
00340       break;
00341   }
00342   i=0L;
00343   for (y=0L; y < (ssize_t) fourier_info->height; y++)
00344   {
00345     q=GetCacheViewAuthenticPixels(phase_view,0L,y,fourier_info->height,1UL,
00346       exception);
00347     if (q == (Quantum *) NULL)
00348       break;
00349     for (x=0L; x < (ssize_t) fourier_info->width; x++)
00350     {
00351       switch (fourier_info->channel)
00352       {
00353         case RedPixelChannel:
00354         default:
00355         {
00356           SetPixelRed(phase_image,ClampToQuantum(QuantumRange*
00357             phase_source[i]),q);
00358           break;
00359         }
00360         case GreenPixelChannel:
00361         {
00362           SetPixelGreen(phase_image,ClampToQuantum(QuantumRange*
00363             phase_source[i]),q);
00364           break;
00365         }
00366         case BluePixelChannel:
00367         {
00368           SetPixelBlue(phase_image,ClampToQuantum(QuantumRange*
00369             phase_source[i]),q);
00370           break;
00371         }
00372         case BlackPixelChannel:
00373         {
00374           SetPixelBlack(phase_image,ClampToQuantum(QuantumRange*
00375             phase_source[i]),q);
00376           break;
00377         }
00378         case AlphaPixelChannel:
00379         {
00380           SetPixelAlpha(phase_image,ClampToQuantum(QuantumRange*
00381             phase_source[i]),q);
00382           break;
00383         }
00384       }
00385       i++;
00386       q+=GetPixelChannels(phase_image);
00387     }
00388     status=SyncCacheViewAuthenticPixels(phase_view,exception);
00389     if (status == MagickFalse)
00390       break;
00391    }
00392   phase_view=DestroyCacheView(phase_view);
00393   magnitude_view=DestroyCacheView(magnitude_view);
00394   phase_source=(double *) RelinquishMagickMemory(phase_source);
00395   magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
00396   return(status);
00397 }
00398 
00399 static MagickBooleanType ForwardFourierTransform(FourierInfo *fourier_info,
00400   const Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
00401 {
00402   CacheView
00403     *image_view;
00404 
00405   double
00406     n,
00407     *source;
00408 
00409   fftw_complex
00410     *fourier;
00411 
00412   fftw_plan
00413     fftw_r2c_plan;
00414 
00415   register const Quantum
00416     *p;
00417 
00418   register ssize_t
00419     i,
00420     x;
00421 
00422   ssize_t
00423     y;
00424 
00425   /*
00426     Generate the forward Fourier transform.
00427   */
00428   source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
00429     fourier_info->width*sizeof(*source));
00430   if (source == (double *) NULL)
00431     {
00432       (void) ThrowMagickException(exception,GetMagickModule(),
00433         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
00434       return(MagickFalse);
00435     }
00436   ResetMagickMemory(source,0,fourier_info->height*fourier_info->width*
00437     sizeof(*source));
00438   i=0L;
00439   image_view=AcquireCacheView(image);
00440   for (y=0L; y < (ssize_t) fourier_info->height; y++)
00441   {
00442     p=GetCacheViewVirtualPixels(image_view,0L,y,fourier_info->width,1UL,
00443       exception);
00444     if (p == (const Quantum *) NULL)
00445       break;
00446     for (x=0L; x < (ssize_t) fourier_info->width; x++)
00447     {
00448       switch (fourier_info->channel)
00449       {
00450         case RedPixelChannel:
00451         default:
00452         {
00453           source[i]=QuantumScale*GetPixelRed(image,p);
00454           break;
00455         }
00456         case GreenPixelChannel:
00457         {
00458           source[i]=QuantumScale*GetPixelGreen(image,p);
00459           break;
00460         }
00461         case BluePixelChannel:
00462         {
00463           source[i]=QuantumScale*GetPixelBlue(image,p);
00464           break;
00465         }
00466         case BlackPixelChannel:
00467         {
00468           source[i]=QuantumScale*GetPixelBlack(image,p);
00469           break;
00470         }
00471         case AlphaPixelChannel:
00472         {
00473           source[i]=QuantumScale*GetPixelAlpha(image,p);
00474           break;
00475         }
00476       }
00477       i++;
00478       p+=GetPixelChannels(image);
00479     }
00480   }
00481   image_view=DestroyCacheView(image_view);
00482   fourier=(fftw_complex *) AcquireQuantumMemory((size_t) fourier_info->height,
00483     fourier_info->center*sizeof(*fourier));
00484   if (fourier == (fftw_complex *) NULL)
00485     {
00486       (void) ThrowMagickException(exception,GetMagickModule(),
00487         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
00488       source=(double *) RelinquishMagickMemory(source);
00489       return(MagickFalse);
00490     }
00491 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00492   #pragma omp critical (MagickCore_ForwardFourierTransform)
00493 #endif
00494   fftw_r2c_plan=fftw_plan_dft_r2c_2d(fourier_info->width,fourier_info->width,
00495     source,fourier,FFTW_ESTIMATE);
00496   fftw_execute(fftw_r2c_plan);
00497   fftw_destroy_plan(fftw_r2c_plan);
00498   source=(double *) RelinquishMagickMemory(source);
00499   /*
00500     Normalize Fourier transform.
00501   */
00502   n=(double) fourier_info->width*(double) fourier_info->width;
00503   i=0L;
00504   for (y=0L; y < (ssize_t) fourier_info->height; y++)
00505     for (x=0L; x < (ssize_t) fourier_info->center; x++)
00506     {
00507 #if defined(MAGICKCORE_HAVE_COMPLEX_H)
00508       fourier[i]/=n;
00509 #else
00510       fourier[i][0]/=n;
00511       fourier[i][1]/=n;
00512 #endif
00513       i++;
00514     }
00515   /*
00516     Generate magnitude and phase (or real and imaginary).
00517   */
00518   i=0L;
00519   if (fourier_info->modulus != MagickFalse)
00520     for (y=0L; y < (ssize_t) fourier_info->height; y++)
00521       for (x=0L; x < (ssize_t) fourier_info->center; x++)
00522       {
00523         magnitude[i]=cabs(fourier[i]);
00524         phase[i]=carg(fourier[i]);
00525         i++;
00526       }
00527   else
00528     for (y=0L; y < (ssize_t) fourier_info->height; y++)
00529       for (x=0L; x < (ssize_t) fourier_info->center; x++)
00530       {
00531         magnitude[i]=creal(fourier[i]);
00532         phase[i]=cimag(fourier[i]);
00533         i++;
00534       }
00535   fourier=(fftw_complex *) RelinquishMagickMemory(fourier);
00536   return(MagickTrue);
00537 }
00538 
00539 static MagickBooleanType ForwardFourierTransformChannel(const Image *image,
00540   const PixelChannel channel,const MagickBooleanType modulus,
00541   Image *fourier_image,ExceptionInfo *exception)
00542 {
00543   double
00544     *magnitude,
00545     *phase;
00546 
00547   fftw_complex
00548     *fourier;
00549 
00550   FourierInfo
00551     fourier_info;
00552 
00553   MagickBooleanType
00554     status;
00555 
00556   size_t
00557     extent;
00558 
00559   fourier_info.width=image->columns;
00560   if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
00561       ((image->rows % 2) != 0))
00562     {
00563       extent=image->columns < image->rows ? image->rows : image->columns;
00564       fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
00565     }
00566   fourier_info.height=fourier_info.width;
00567   fourier_info.center=(ssize_t) floor((double) fourier_info.width/2.0)+1L;
00568   fourier_info.channel=channel;
00569   fourier_info.modulus=modulus;
00570   magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
00571     fourier_info.center*sizeof(*magnitude));
00572   if (magnitude == (double *) NULL)
00573     {
00574       (void) ThrowMagickException(exception,GetMagickModule(),
00575         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
00576       return(MagickFalse);
00577     }
00578   phase=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
00579     fourier_info.center*sizeof(*phase));
00580   if (phase == (double *) NULL)
00581     {
00582       (void) ThrowMagickException(exception,GetMagickModule(),
00583         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
00584       magnitude=(double *) RelinquishMagickMemory(magnitude);
00585       return(MagickFalse);
00586     }
00587   fourier=(fftw_complex *) AcquireQuantumMemory((size_t) fourier_info.height,
00588     fourier_info.center*sizeof(*fourier));
00589   if (fourier == (fftw_complex *) NULL)
00590     {
00591       (void) ThrowMagickException(exception,GetMagickModule(),
00592         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
00593       phase=(double *) RelinquishMagickMemory(phase);
00594       magnitude=(double *) RelinquishMagickMemory(magnitude);
00595       return(MagickFalse);
00596     }
00597   status=ForwardFourierTransform(&fourier_info,image,magnitude,phase,exception);
00598   if (status != MagickFalse)
00599     status=ForwardFourier(&fourier_info,fourier_image,magnitude,phase,
00600       exception);
00601   fourier=(fftw_complex *) RelinquishMagickMemory(fourier);
00602   phase=(double *) RelinquishMagickMemory(phase);
00603   magnitude=(double *) RelinquishMagickMemory(magnitude);
00604   return(status);
00605 }
00606 #endif
00607 
00608 MagickExport Image *ForwardFourierTransformImage(const Image *image,
00609   const MagickBooleanType modulus,ExceptionInfo *exception)
00610 {
00611   Image
00612     *fourier_image;
00613 
00614   fourier_image=NewImageList();
00615 #if !defined(MAGICKCORE_FFTW_DELEGATE)
00616   (void) modulus;
00617   (void) ThrowMagickException(exception,GetMagickModule(),
00618     MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","`%s' (FFTW)",
00619     image->filename);
00620 #else
00621   {
00622     Image
00623       *magnitude_image;
00624 
00625     size_t
00626       extent,
00627       width;
00628 
00629     width=image->columns;
00630     if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
00631         ((image->rows % 2) != 0))
00632       {
00633         extent=image->columns < image->rows ? image->rows : image->columns;
00634         width=(extent & 0x01) == 1 ? extent+1UL : extent;
00635       }
00636     magnitude_image=CloneImage(image,width,width,MagickFalse,exception);
00637     if (magnitude_image != (Image *) NULL)
00638       {
00639         Image
00640           *phase_image;
00641 
00642         magnitude_image->storage_class=DirectClass;
00643         magnitude_image->depth=32UL;
00644         phase_image=CloneImage(image,width,width,MagickFalse,exception);
00645         if (phase_image == (Image *) NULL)
00646           magnitude_image=DestroyImage(magnitude_image);
00647         else
00648           {
00649             MagickBooleanType
00650               is_gray,
00651               status;
00652 
00653             phase_image->storage_class=DirectClass;
00654             phase_image->depth=32UL;
00655             AppendImageToList(&fourier_image,magnitude_image);
00656             AppendImageToList(&fourier_image,phase_image);
00657             status=MagickTrue;
00658             is_gray=IsImageGray(image,exception);
00659 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00660             #pragma omp parallel sections
00661 #endif
00662             {
00663 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00664               #pragma omp section
00665 #endif
00666               {
00667                 MagickBooleanType
00668                   thread_status;
00669 
00670                 if (is_gray != MagickFalse)
00671                   thread_status=ForwardFourierTransformChannel(image,
00672                     GrayPixelChannel,modulus,fourier_image,exception);
00673                 else
00674                   thread_status=ForwardFourierTransformChannel(image,
00675                     RedPixelChannel,modulus,fourier_image,exception);
00676                 if (thread_status == MagickFalse)
00677                   status=thread_status;
00678               }
00679 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00680               #pragma omp section
00681 #endif
00682               {
00683                 MagickBooleanType
00684                   thread_status;
00685 
00686                 thread_status=MagickTrue;
00687                 if (is_gray == MagickFalse)
00688                   thread_status=ForwardFourierTransformChannel(image,
00689                     GreenPixelChannel,modulus,fourier_image,exception);
00690                 if (thread_status == MagickFalse)
00691                   status=thread_status;
00692               }
00693 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00694               #pragma omp section
00695 #endif
00696               {
00697                 MagickBooleanType
00698                   thread_status;
00699 
00700                 thread_status=MagickTrue;
00701                 if (is_gray == MagickFalse)
00702                   thread_status=ForwardFourierTransformChannel(image,
00703                     BluePixelChannel,modulus,fourier_image,exception);
00704                 if (thread_status == MagickFalse)
00705                   status=thread_status;
00706               }
00707 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00708               #pragma omp section
00709 #endif
00710               {
00711                 MagickBooleanType
00712                   thread_status;
00713 
00714                 thread_status=MagickTrue;
00715                 if (image->colorspace == CMYKColorspace)
00716                   thread_status=ForwardFourierTransformChannel(image,
00717                     BlackPixelChannel,modulus,fourier_image,exception);
00718                 if (thread_status == MagickFalse)
00719                   status=thread_status;
00720               }
00721 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00722               #pragma omp section
00723 #endif
00724               {
00725                 MagickBooleanType
00726                   thread_status;
00727 
00728                 thread_status=MagickTrue;
00729                 if (image->matte != MagickFalse)
00730                   thread_status=ForwardFourierTransformChannel(image,
00731                     AlphaPixelChannel,modulus,fourier_image,exception);
00732                 if (thread_status == MagickFalse)
00733                   status=thread_status;
00734               }
00735             }
00736             if (status == MagickFalse)
00737               fourier_image=DestroyImageList(fourier_image);
00738             fftw_cleanup();
00739           }
00740       }
00741   }
00742 #endif
00743   return(fourier_image);
00744 }
00745 
00746 /*
00747 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00748 %                                                                             %
00749 %                                                                             %
00750 %                                                                             %
00751 %     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                 %
00752 %                                                                             %
00753 %                                                                             %
00754 %                                                                             %
00755 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00756 %
00757 %  InverseFourierTransformImage() implements the inverse discrete Fourier
00758 %  transform (DFT) of the image either as a magnitude / phase or real /
00759 %  imaginary image pair.
00760 %
00761 %  The format of the InverseFourierTransformImage method is:
00762 %
00763 %      Image *InverseFourierTransformImage(const Image *magnitude_image,
00764 %        const Image *phase_image,const MagickBooleanType modulus,
00765 %        ExceptionInfo *exception)
00766 %
00767 %  A description of each parameter follows:
00768 %
00769 %    o magnitude_image: the magnitude or real image.
00770 %
00771 %    o phase_image: the phase or imaginary image.
00772 %
00773 %    o modulus: if true, return transform as a magnitude / phase pair
00774 %      otherwise a real / imaginary image pair.
00775 %
00776 %    o exception: return any errors or warnings in this structure.
00777 %
00778 */
00779 
00780 #if defined(MAGICKCORE_FFTW_DELEGATE)
00781 static MagickBooleanType InverseQuadrantSwap(const size_t width,
00782   const size_t height,const double *source,double *destination)
00783 {
00784   register ssize_t
00785     x;
00786 
00787   ssize_t
00788     center,
00789     y;
00790 
00791   /*
00792     Swap quadrants.
00793   */
00794   center=(ssize_t) floor((double) width/2.0)+1L;
00795   for (y=1L; y < (ssize_t) height; y++)
00796     for (x=0L; x < (ssize_t) (width/2L+1L); x++)
00797       destination[center*(height-y)-x+width/2L]=source[y*width+x];
00798   for (y=0L; y < (ssize_t) height; y++)
00799     destination[center*y]=source[y*width+width/2L];
00800   for (x=0L; x < center; x++)
00801     destination[x]=source[center-x-1L];
00802   return(RollFourier(center,height,0L,(ssize_t) height/-2L,destination));
00803 }
00804 
00805 static MagickBooleanType InverseFourier(FourierInfo *fourier_info,
00806   const Image *magnitude_image,const Image *phase_image,fftw_complex *fourier,
00807   ExceptionInfo *exception)
00808 {
00809   CacheView
00810     *magnitude_view,
00811     *phase_view;
00812 
00813   double
00814     *magnitude,
00815     *phase,
00816     *magnitude_source,
00817     *phase_source;
00818 
00819   MagickBooleanType
00820     status;
00821 
00822   register const Quantum
00823     *p;
00824 
00825   register ssize_t
00826     i,
00827     x;
00828 
00829   ssize_t
00830     y;
00831 
00832   /*
00833     Inverse fourier - read image and break down into a double array.
00834   */
00835   magnitude_source=(double *) AcquireQuantumMemory((size_t)
00836     fourier_info->height,fourier_info->width*sizeof(*magnitude_source));
00837   if (magnitude_source == (double *) NULL)
00838     {
00839       (void) ThrowMagickException(exception,GetMagickModule(),
00840         ResourceLimitError,"MemoryAllocationFailed","`%s'",
00841         magnitude_image->filename);
00842       return(MagickFalse);
00843     }
00844   phase_source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
00845     fourier_info->width*sizeof(*phase_source));
00846   if (phase_source == (double *) NULL)
00847     {
00848       (void) ThrowMagickException(exception,GetMagickModule(),
00849         ResourceLimitError,"MemoryAllocationFailed","`%s'",
00850         magnitude_image->filename);
00851       magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
00852       return(MagickFalse);
00853     }
00854   i=0L;
00855   magnitude_view=AcquireCacheView(magnitude_image);
00856   for (y=0L; y < (ssize_t) fourier_info->height; y++)
00857   {
00858     p=GetCacheViewVirtualPixels(magnitude_view,0L,y,fourier_info->width,1UL,
00859       exception);
00860     if (p == (const Quantum *) NULL)
00861       break;
00862     for (x=0L; x < (ssize_t) fourier_info->width; x++)
00863     {
00864       switch (fourier_info->channel)
00865       {
00866         case RedPixelChannel:
00867         default:
00868         {
00869           magnitude_source[i]=QuantumScale*GetPixelRed(magnitude_image,p);
00870           break;
00871         }
00872         case GreenPixelChannel:
00873         {
00874           magnitude_source[i]=QuantumScale*GetPixelGreen(magnitude_image,p);
00875           break;
00876         }
00877         case BluePixelChannel:
00878         {
00879           magnitude_source[i]=QuantumScale*GetPixelBlue(magnitude_image,p);
00880           break;
00881         }
00882         case BlackPixelChannel:
00883         {
00884           magnitude_source[i]=QuantumScale*GetPixelBlack(magnitude_image,p);
00885           break;
00886         }
00887         case AlphaPixelChannel:
00888         {
00889           magnitude_source[i]=QuantumScale*GetPixelAlpha(magnitude_image,p);
00890           break;
00891         }
00892       }
00893       i++;
00894       p+=GetPixelChannels(magnitude_image);
00895     }
00896   }
00897   i=0L;
00898   phase_view=AcquireCacheView(phase_image);
00899   for (y=0L; y < (ssize_t) fourier_info->height; y++)
00900   {
00901     p=GetCacheViewVirtualPixels(phase_view,0,y,fourier_info->width,1,
00902       exception);
00903     if (p == (const Quantum *) NULL)
00904       break;
00905     for (x=0L; x < (ssize_t) fourier_info->width; x++)
00906     {
00907       switch (fourier_info->channel)
00908       {
00909         case RedPixelChannel:
00910         default:
00911         {
00912           phase_source[i]=QuantumScale*GetPixelRed(phase_image,p);
00913           break;
00914         }
00915         case GreenPixelChannel:
00916         {
00917           phase_source[i]=QuantumScale*GetPixelGreen(phase_image,p);
00918           break;
00919         }
00920         case BluePixelChannel:
00921         {
00922           phase_source[i]=QuantumScale*GetPixelBlue(phase_image,p);
00923           break;
00924         }
00925         case BlackPixelChannel:
00926         {
00927           phase_source[i]=QuantumScale*GetPixelBlack(phase_image,p);
00928           break;
00929         }
00930         case AlphaPixelChannel:
00931         {
00932           phase_source[i]=QuantumScale*GetPixelAlpha(phase_image,p);
00933           break;
00934         }
00935       }
00936       i++;
00937       p+=GetPixelChannels(phase_image);
00938     }
00939   }
00940   if (fourier_info->modulus != MagickFalse)
00941     {
00942       i=0L;
00943       for (y=0L; y < (ssize_t) fourier_info->height; y++)
00944         for (x=0L; x < (ssize_t) fourier_info->width; x++)
00945         {
00946           phase_source[i]-=0.5;
00947           phase_source[i]*=(2.0*MagickPI);
00948           i++;
00949         }
00950     }
00951   magnitude_view=DestroyCacheView(magnitude_view);
00952   phase_view=DestroyCacheView(phase_view);
00953   magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
00954     fourier_info->center*sizeof(*magnitude));
00955   if (magnitude == (double *) NULL)
00956     {
00957       (void) ThrowMagickException(exception,GetMagickModule(),
00958         ResourceLimitError,"MemoryAllocationFailed","`%s'",
00959         magnitude_image->filename);
00960       magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
00961       phase_source=(double *) RelinquishMagickMemory(phase_source);
00962       return(MagickFalse);
00963     }
00964   status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
00965     magnitude_source,magnitude);
00966   magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
00967   phase=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
00968     fourier_info->width*sizeof(*phase));
00969   if (phase == (double *) NULL)
00970     {
00971       (void) ThrowMagickException(exception,GetMagickModule(),
00972         ResourceLimitError,"MemoryAllocationFailed","`%s'",
00973         magnitude_image->filename);
00974       phase_source=(double *) RelinquishMagickMemory(phase_source);
00975       return(MagickFalse);
00976     }
00977   CorrectPhaseLHS(fourier_info->width,fourier_info->width,phase_source);
00978   if (status != MagickFalse)
00979     status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
00980       phase_source,phase);
00981   phase_source=(double *) RelinquishMagickMemory(phase_source);
00982   /*
00983     Merge two sets.
00984   */
00985   i=0L;
00986   if (fourier_info->modulus != MagickFalse)
00987     for (y=0L; y < (ssize_t) fourier_info->height; y++)
00988        for (x=0L; x < (ssize_t) fourier_info->center; x++)
00989        {
00990 #if defined(MAGICKCORE_HAVE_COMPLEX_H)
00991          fourier[i]=magnitude[i]*cos(phase[i])+I*magnitude[i]*sin(phase[i]);
00992 #else
00993          fourier[i][0]=magnitude[i]*cos(phase[i]);
00994          fourier[i][1]=magnitude[i]*sin(phase[i]);
00995 #endif
00996          i++;
00997       }
00998   else
00999     for (y=0L; y < (ssize_t) fourier_info->height; y++)
01000       for (x=0L; x < (ssize_t) fourier_info->center; x++)
01001       {
01002 #if defined(MAGICKCORE_HAVE_COMPLEX_H)
01003         fourier[i]=magnitude[i]+I*phase[i];
01004 #else
01005         fourier[i][0]=magnitude[i];
01006         fourier[i][1]=phase[i];
01007 #endif
01008         i++;
01009       }
01010   phase=(double *) RelinquishMagickMemory(phase);
01011   magnitude=(double *) RelinquishMagickMemory(magnitude);
01012   return(status);
01013 }
01014 
01015 static MagickBooleanType InverseFourierTransform(FourierInfo *fourier_info,
01016   fftw_complex *fourier,Image *image,ExceptionInfo *exception)
01017 {
01018   CacheView
01019     *image_view;
01020 
01021   double
01022     *source;
01023 
01024   fftw_plan
01025     fftw_c2r_plan;
01026 
01027   register Quantum
01028     *q;
01029 
01030   register ssize_t
01031     i,
01032     x;
01033 
01034   ssize_t
01035     y;
01036 
01037   source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
01038     fourier_info->width*sizeof(*source));
01039   if (source == (double *) NULL)
01040     {
01041       (void) ThrowMagickException(exception,GetMagickModule(),
01042         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
01043       return(MagickFalse);
01044     }
01045 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01046   #pragma omp critical (MagickCore_InverseFourierTransform)
01047 #endif
01048   {
01049     fftw_c2r_plan=fftw_plan_dft_c2r_2d(fourier_info->width,fourier_info->height,
01050       fourier,source,FFTW_ESTIMATE);
01051     fftw_execute(fftw_c2r_plan);
01052     fftw_destroy_plan(fftw_c2r_plan);
01053   }
01054   i=0L;
01055   image_view=AcquireCacheView(image);
01056   for (y=0L; y < (ssize_t) fourier_info->height; y++)
01057   {
01058     if (y >= (ssize_t) image->rows)
01059       break;
01060     q=GetCacheViewAuthenticPixels(image_view,0L,y,fourier_info->width >
01061       image->columns ? image->columns : fourier_info->width,1UL,exception);
01062     if (q == (Quantum *) NULL)
01063       break;
01064     for (x=0L; x < (ssize_t) fourier_info->width; x++)
01065     {
01066       switch (fourier_info->channel)
01067       {
01068         case RedPixelChannel:
01069         default:
01070         {
01071           SetPixelRed(image,ClampToQuantum(QuantumRange*source[i]),q);
01072           break;
01073         }
01074         case GreenPixelChannel:
01075         {
01076           SetPixelGreen(image,ClampToQuantum(QuantumRange*source[i]),q);
01077           break;
01078         }
01079         case BluePixelChannel:
01080         {
01081           SetPixelBlue(image,ClampToQuantum(QuantumRange*source[i]),q);
01082           break;
01083         }
01084         case BlackPixelChannel:
01085         {
01086           SetPixelBlack(image,ClampToQuantum(QuantumRange*source[i]),q);
01087           break;
01088         }
01089         case AlphaPixelChannel:
01090         {
01091           SetPixelAlpha(image,ClampToQuantum(QuantumRange*source[i]),q);
01092           break;
01093         }
01094       }
01095       i++;
01096       q+=GetPixelChannels(image);
01097     }
01098     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
01099       break;
01100   }
01101   image_view=DestroyCacheView(image_view);
01102   source=(double *) RelinquishMagickMemory(source);
01103   return(MagickTrue);
01104 }
01105 
01106 static MagickBooleanType InverseFourierTransformChannel(
01107   const Image *magnitude_image,const Image *phase_image,
01108   const PixelChannel channel,const MagickBooleanType modulus,
01109   Image *fourier_image,ExceptionInfo *exception)
01110 {
01111   double
01112     *magnitude,
01113     *phase;
01114 
01115   fftw_complex
01116     *fourier;
01117 
01118   FourierInfo
01119     fourier_info;
01120 
01121   MagickBooleanType
01122     status;
01123 
01124   size_t
01125     extent;
01126 
01127   fourier_info.width=magnitude_image->columns;
01128   if ((magnitude_image->columns != magnitude_image->rows) ||
01129       ((magnitude_image->columns % 2) != 0) ||
01130       ((magnitude_image->rows % 2) != 0))
01131     {
01132       extent=magnitude_image->columns < magnitude_image->rows ?
01133         magnitude_image->rows : magnitude_image->columns;
01134       fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
01135     }
01136   fourier_info.height=fourier_info.width;
01137   fourier_info.center=(ssize_t) floor((double) fourier_info.width/2.0)+1L;
01138   fourier_info.channel=channel;
01139   fourier_info.modulus=modulus;
01140   magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
01141     fourier_info.center*sizeof(*magnitude));
01142   if (magnitude == (double *) NULL)
01143     {
01144       (void) ThrowMagickException(exception,GetMagickModule(),
01145         ResourceLimitError,"MemoryAllocationFailed","`%s'",
01146         magnitude_image->filename);
01147       return(MagickFalse);
01148     }
01149   phase=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
01150     fourier_info.center*sizeof(*phase));
01151   if (phase == (double *) NULL)
01152     {
01153       (void) ThrowMagickException(exception,GetMagickModule(),
01154         ResourceLimitError,"MemoryAllocationFailed","`%s'",
01155         magnitude_image->filename);
01156       magnitude=(double *) RelinquishMagickMemory(magnitude);
01157       return(MagickFalse);
01158     }
01159   fourier=(fftw_complex *) AcquireQuantumMemory((size_t) fourier_info.height,
01160     fourier_info.center*sizeof(*fourier));
01161   if (fourier == (fftw_complex *) NULL)
01162     {
01163       (void) ThrowMagickException(exception,GetMagickModule(),
01164         ResourceLimitError,"MemoryAllocationFailed","`%s'",
01165         magnitude_image->filename);
01166       phase=(double *) RelinquishMagickMemory(phase);
01167       magnitude=(double *) RelinquishMagickMemory(magnitude);
01168       return(MagickFalse);
01169     }
01170   status=InverseFourier(&fourier_info,magnitude_image,phase_image,fourier,
01171    exception);
01172   if (status != MagickFalse)
01173     status=InverseFourierTransform(&fourier_info,fourier,fourier_image,
01174       exception);
01175   fourier=(fftw_complex *) RelinquishMagickMemory(fourier);
01176   phase=(double *) RelinquishMagickMemory(phase);
01177   magnitude=(double *) RelinquishMagickMemory(magnitude);
01178   return(status);
01179 }
01180 #endif
01181 
01182 MagickExport Image *InverseFourierTransformImage(const Image *magnitude_image,
01183   const Image *phase_image,const MagickBooleanType modulus,
01184   ExceptionInfo *exception)
01185 {
01186   Image
01187     *fourier_image;
01188 
01189   assert(magnitude_image != (Image *) NULL);
01190   assert(magnitude_image->signature == MagickSignature);
01191   if (magnitude_image->debug != MagickFalse)
01192     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
01193       magnitude_image->filename);
01194   if (phase_image == (Image *) NULL)
01195     {
01196       (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
01197         "ImageSequenceRequired","`%s'",magnitude_image->filename);
01198       return((Image *) NULL);
01199     }
01200 #if !defined(MAGICKCORE_FFTW_DELEGATE)
01201   fourier_image=(Image *) NULL;
01202   (void) modulus;
01203   (void) ThrowMagickException(exception,GetMagickModule(),
01204     MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","`%s' (FFTW)",
01205     magnitude_image->filename);
01206 #else
01207   {
01208     fourier_image=CloneImage(magnitude_image,magnitude_image->columns,
01209       magnitude_image->rows,MagickFalse,exception);
01210     if (fourier_image != (Image *) NULL)
01211       {
01212         MagickBooleanType
01213           is_gray,
01214           status;
01215 
01216         status=MagickTrue;
01217         is_gray=IsImageGray(magnitude_image,exception);
01218         if (is_gray != MagickFalse)
01219           is_gray=IsImageGray(phase_image,exception);
01220 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01221         #pragma omp parallel sections
01222 #endif
01223         {
01224 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01225           #pragma omp section
01226 #endif
01227           {
01228             MagickBooleanType
01229               thread_status;
01230 
01231             if (is_gray != MagickFalse)
01232               thread_status=InverseFourierTransformChannel(magnitude_image,
01233                 phase_image,GrayPixelChannel,modulus,fourier_image,exception);
01234             else
01235               thread_status=InverseFourierTransformChannel(magnitude_image,
01236                 phase_image,RedPixelChannel,modulus,fourier_image,exception);
01237             if (thread_status == MagickFalse)
01238               status=thread_status;
01239           }
01240 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01241           #pragma omp section
01242 #endif
01243           {
01244             MagickBooleanType
01245               thread_status;
01246 
01247             thread_status=MagickTrue;
01248             if (is_gray == MagickFalse)
01249               thread_status=InverseFourierTransformChannel(magnitude_image,
01250                 phase_image,GreenPixelChannel,modulus,fourier_image,exception);
01251             if (thread_status == MagickFalse)
01252               status=thread_status;
01253           }
01254 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01255           #pragma omp section
01256 #endif
01257           {
01258             MagickBooleanType
01259               thread_status;
01260 
01261             thread_status=MagickTrue;
01262             if (is_gray == MagickFalse)
01263               thread_status=InverseFourierTransformChannel(magnitude_image,
01264                 phase_image,BluePixelChannel,modulus,fourier_image,exception);
01265             if (thread_status == MagickFalse)
01266               status=thread_status;
01267           }
01268 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01269           #pragma omp section
01270 #endif
01271           {
01272             MagickBooleanType
01273               thread_status;
01274 
01275             thread_status=MagickTrue;
01276             if (magnitude_image->colorspace == CMYKColorspace)
01277               thread_status=InverseFourierTransformChannel(magnitude_image,
01278                 phase_image,BlackPixelChannel,modulus,fourier_image,exception);
01279             if (thread_status == MagickFalse)
01280               status=thread_status;
01281           }
01282 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01283           #pragma omp section
01284 #endif
01285           {
01286             MagickBooleanType
01287               thread_status;
01288 
01289             thread_status=MagickTrue;
01290             if (magnitude_image->matte != MagickFalse)
01291               thread_status=InverseFourierTransformChannel(magnitude_image,
01292                 phase_image,AlphaPixelChannel,modulus,fourier_image,exception);
01293             if (thread_status == MagickFalse)
01294               status=thread_status;
01295           }
01296         }
01297         if (status == MagickFalse)
01298           fourier_image=DestroyImage(fourier_image);
01299       }
01300     fftw_cleanup();
01301   }
01302 #endif
01303   return(fourier_image);
01304 }