FourierEngine.C

Go to the documentation of this file.
00001 /*!@file Image/FourierEngine.C Thin wrapper around the fftw3 library */
00002 
00003 // //////////////////////////////////////////////////////////////////// //
00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005   //
00005 // by the University of Southern California (USC) and the iLab at USC.  //
00006 // See http://iLab.usc.edu for information about this project.          //
00007 // //////////////////////////////////////////////////////////////////// //
00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00010 // in Visual Environments, and Applications'' by Christof Koch and      //
00011 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00012 // pending; application number 09/912,225 filed July 23, 2001; see      //
00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00014 // //////////////////////////////////////////////////////////////////// //
00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00016 //                                                                      //
00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00018 // redistribute it and/or modify it under the terms of the GNU General  //
00019 // Public License as published by the Free Software Foundation; either  //
00020 // version 2 of the License, or (at your option) any later version.     //
00021 //                                                                      //
00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00025 // PURPOSE.  See the GNU General Public License for more details.       //
00026 //                                                                      //
00027 // You should have received a copy of the GNU General Public License    //
00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00030 // Boston, MA 02111-1307 USA.                                           //
00031 // //////////////////////////////////////////////////////////////////// //
00032 //
00033 // Primary maintainer for this file:
00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Image/FourierEngine.C $
00035 // $Id: FourierEngine.C 9993 2008-07-29 00:04:18Z lior $
00036 //
00037 
00038 #ifndef IMAGE_FOURIERENGINE_C_DEFINED
00039 #define IMAGE_FOURIERENGINE_C_DEFINED
00040 
00041 #include "Image/FourierEngine.H"
00042 
00043 #include "rutz/trace.h"
00044 
00045 #ifdef HAVE_FFTW3_H
00046 
00047 namespace
00048 {
00049   // see comments in FourierEngine::fft() about USE_FFTW_GURU
00050   const bool USE_FFTW_GURU = true;
00051 
00052 // #define USE_FFTW_THREADS
00053 
00054   void init_threads()
00055   {
00056     // in order to use threads, we need to also link against
00057     // -lfftw3_threads and -lpthread on the command-line
00058 #ifdef USE_FFTW_THREADS
00059     static bool inited = false;
00060 
00061     if (!inited)
00062       {
00063         inited = true;
00064         if (fftw_init_threads() == 0)
00065           LFATAL("fftw_init_threads() failed");
00066 
00067         fftw_plan_with_nthreads(4);
00068       }
00069 #endif
00070   }
00071 
00072   template <class T>
00073   struct fftw;
00074 
00075   template <>
00076   struct fftw<float>
00077   {
00078     static float* alloc_real(size_t n)
00079     {
00080       return static_cast<float*>(fftwf_malloc(sizeof(float)*n));
00081     }
00082 
00083     static complexf* alloc_complex(size_t n)
00084     {
00085       return static_cast<complexf*>(fftwf_malloc(sizeof(complexf)*n));
00086     }
00087 
00088     static void dealloc(void* mem)
00089     {
00090       fftwf_free(mem);
00091     }
00092 
00093     static void* plan_dft_r2c_2d(const Dims& d,
00094                                  float* src,
00095                                  complexf* dst)
00096     {
00097       GVX_TRACE(__PRETTY_FUNCTION__);
00098 
00099       return fftwf_plan_dft_r2c_2d
00100         (d.h(), d.w(),
00101          src,
00102          // this reinterpret_cast is 'safe', because according to fftw
00103          // documentation, the fftwf_complex type (just a typedef for
00104          // float[2]) should be bit-compatible with both the C99
00105          // complex type as well as the c++ std::complex<float> type
00106          reinterpret_cast<fftwf_complex*>(dst),
00107          FFTW_MEASURE);
00108     }
00109 
00110     static void* plan_dft_c2r_2d(const Dims& d,
00111                                  complexf* src,
00112                                  float* dst)
00113     {
00114       GVX_TRACE(__PRETTY_FUNCTION__);
00115 
00116       return fftwf_plan_dft_c2r_2d
00117         (d.h(), d.w(),
00118          reinterpret_cast<fftwf_complex*>(src),
00119          dst,
00120          FFTW_MEASURE);
00121     }
00122 
00123     static void destroy_plan(void* p)
00124     {
00125       fftwf_destroy_plan(static_cast<fftwf_plan>(p));
00126     }
00127 
00128     static void execute_dft_r2c(void* p,
00129                                 const float* src,
00130                                 complexf* dst)
00131     {
00132       fftwf_execute_dft_r2c
00133         (static_cast<fftwf_plan>(p),
00134          const_cast<float*>(src),
00135          reinterpret_cast<fftwf_complex*>(dst));
00136     }
00137 
00138     static void execute_dft_c2r(void* p,
00139                                 const complexf* src,
00140                                 float* dst)
00141     {
00142       fftwf_execute_dft_c2r
00143         (static_cast<fftwf_plan>(p),
00144          reinterpret_cast<fftwf_complex*>(const_cast<complexf*>(src)),
00145          dst);
00146     }
00147 
00148     static void execute(void* p)
00149     {
00150       fftwf_execute(static_cast<fftwf_plan>(p));
00151     }
00152   };
00153 
00154   template <>
00155   struct fftw<double>
00156   {
00157     static double* alloc_real(size_t n)
00158     {
00159       return static_cast<double*>(fftw_malloc(sizeof(double)*n));
00160     }
00161 
00162     static complexd* alloc_complex(size_t n)
00163     {
00164       return static_cast<complexd*>(fftw_malloc(sizeof(complexd)*n));
00165     }
00166 
00167     static void dealloc(void* mem)
00168     {
00169       fftw_free(mem);
00170     }
00171 
00172     static void* plan_dft_r2c_2d(const Dims& d,
00173                                  double* src,
00174                                  complexd* dst)
00175     {
00176       GVX_TRACE(__PRETTY_FUNCTION__);
00177 
00178       return fftw_plan_dft_r2c_2d
00179         (d.h(), d.w(),
00180          src,
00181          // this reinterpret_cast is 'safe', because according to fftw
00182          // documentation, the fftw_complex type (just a typedef for
00183          // double[2]) should be bit-compatible with both the C99
00184          // complex type as well as the c++ std::complex<double> type
00185          reinterpret_cast<fftw_complex*>(dst),
00186          FFTW_MEASURE);
00187     }
00188 
00189     static void* plan_dft_c2r_2d(const Dims& d,
00190                                  complexd* src,
00191                                  double* dst)
00192     {
00193       GVX_TRACE(__PRETTY_FUNCTION__);
00194 
00195       return fftw_plan_dft_c2r_2d
00196         (d.h(), d.w(),
00197          reinterpret_cast<fftw_complex*>(src),
00198          dst,
00199          FFTW_MEASURE);
00200     }
00201 
00202     static void destroy_plan(void* p)
00203     {
00204       fftw_destroy_plan(static_cast<fftw_plan>(p));
00205     }
00206 
00207     static void execute_dft_r2c(void* p,
00208                                 const double* src,
00209                                 complexd* dst)
00210     {
00211       fftw_execute_dft_r2c
00212         (static_cast<fftw_plan>(p),
00213          const_cast<double*>(src),
00214          reinterpret_cast<fftw_complex*>(dst));
00215     }
00216 
00217     static void execute_dft_c2r(void* p,
00218                                 const complexd* src,
00219                                 double* dst)
00220     {
00221       fftw_execute_dft_c2r
00222         (static_cast<fftw_plan>(p),
00223          reinterpret_cast<fftw_complex*>(const_cast<complexd*>(src)),
00224          dst);
00225     }
00226 
00227     static void execute(void* p)
00228     {
00229       fftw_execute(static_cast<fftw_plan>(p));
00230     }
00231   };
00232 }
00233 
00234 #endif // HAVE_FFTW3_H
00235 
00236 // ##############################################################
00237 template <class T>
00238 FourierEngine<T>::FourierEngine(const Dims& d)
00239   :
00240   itsInputDims(d),
00241   itsSrc(0),
00242   itsDst(0),
00243   itsPlan(0)
00244 {
00245 GVX_TRACE(__PRETTY_FUNCTION__);
00246 
00247 #ifndef HAVE_FFTW3_H
00248   LFATAL("you must have fftw3 installed to use this function");
00249 #else
00250   init_threads();
00251 
00252   // Use fftw_malloc() here instead of plain malloc() or new[],
00253   // because fftw_malloc() is supposed to provide stronger alignment
00254   // guarantees, so that more efficient 'aligned' SIMD operations can
00255   // be used.
00256   itsSrc = fftw<T>::alloc_real(itsInputDims.h() * itsInputDims.w());
00257 
00258   if (itsSrc == 0)
00259     LFATAL("memory allocation failure");
00260 
00261   itsDst = fftw<T>::alloc_complex(itsInputDims.h() * (itsInputDims.w()/2+1));
00262 
00263   if (itsDst == 0)
00264     {
00265       fftw<T>::dealloc(itsSrc);
00266       LFATAL("memory allocation failure");
00267     }
00268 
00269   // delay initialization of itsPlan until the first call to fft()
00270 #endif // HAVE_FFTW3_H
00271 }
00272 
00273 // ##############################################################
00274 template <class T>
00275 FourierEngine<T>::~FourierEngine()
00276 {
00277 GVX_TRACE(__PRETTY_FUNCTION__);
00278 #ifndef HAVE_FFTW3_H
00279   // don't LFATAL() in a destructor
00280   LERROR("you must have fftw3 installed to use this function");
00281 #else
00282   if (itsPlan != 0)
00283     fftw<T>::destroy_plan(itsPlan);
00284   fftw<T>::dealloc(itsDst);
00285   fftw<T>::dealloc(itsSrc);
00286 #endif
00287 }
00288 
00289 // ##############################################################
00290 template <class T>
00291 Image<std::complex<T> > FourierEngine<T>::fft(const Image<T>& x)
00292 {
00293 GVX_TRACE(__PRETTY_FUNCTION__);
00294 #ifndef HAVE_FFTW3_H
00295   LFATAL("you must have fftw3 installed to use this function");
00296   /* can't happen */ return Image<std::complex<T> >();
00297 #else
00298   ASSERT(x.getDims() == itsInputDims);
00299 
00300   if (itsPlan == 0)
00301     {
00302       itsPlan = fftw<T>::plan_dft_r2c_2d(itsInputDims, itsSrc, itsDst);
00303 
00304       if (itsPlan == 0)
00305         {
00306           fftw<T>::dealloc(itsSrc); fftw<T>::dealloc(itsDst);
00307           LFATAL("couldn't construct fftw_plan");
00308         }
00309     }
00310 
00311   if (USE_FFTW_GURU)
00312     {
00313       // It's possible to use fftw's guru interface to get some
00314       // speed-up (~5-8% faster) by avoiding copying the input+output
00315       // arrays; note that this relies on the input+output arrays
00316       // having the maximal alignment that fftw expects (16-byte
00317       // boundary), which is now guaranteed by the fact that
00318       // ArrayData<T> uses invt_allocate() to create its T array.
00319 
00320       Image<std::complex<T> > result(itsInputDims.w()/2 + 1,
00321                                      itsInputDims.h(), NO_INIT);
00322 
00323       fftw<T>::execute_dft_r2c
00324         (itsPlan, x.getArrayPtr(), result.getArrayPtr());
00325 
00326       return result;
00327     }
00328   else
00329     {
00330       std::copy(x.begin(), x.end(), itsSrc);
00331 
00332       {
00333         GVX_TRACE("fftw_execute-forward");
00334         fftw<T>::execute(itsPlan);
00335       }
00336 
00337       return Image<std::complex<T> >(itsDst,
00338                              itsInputDims.w()/2 + 1, itsInputDims.h());
00339     }
00340 #endif // HAVE_FFTW3_H
00341 }
00342 
00343 // ##############################################################
00344 template <class T>
00345 FourierInvEngine<T>::FourierInvEngine(const Dims& d)
00346   :
00347   itsOutputDims(d),
00348   itsSrc(0),
00349   itsDst(0),
00350   itsPlan(0)
00351 {
00352 GVX_TRACE(__PRETTY_FUNCTION__);
00353 #ifndef HAVE_FFTW3_H
00354   LFATAL("you must have fftw3 installed to use this function");
00355 #else
00356   init_threads();
00357 
00358   // See comments in FourierEngine::FourierEngine()
00359 
00360   itsSrc = fftw<T>::alloc_complex(itsOutputDims.h() * (itsOutputDims.w()/2+1));
00361 
00362   if (itsSrc == 0)
00363     LFATAL("memory allocation failure");
00364 
00365   itsDst = fftw<T>::alloc_real(itsOutputDims.h() * itsOutputDims.w());
00366 
00367   if (itsDst == 0)
00368     {
00369       fftw<T>::dealloc(itsSrc);
00370       LFATAL("memory allocation failure");
00371     }
00372 
00373   // delay initialization of itsPlan until the first call to ifft()
00374 #endif // HAVE_FFTW3_H
00375 }
00376 
00377 // ##############################################################
00378 template <class T>
00379 FourierInvEngine<T>::~FourierInvEngine()
00380 {
00381 GVX_TRACE(__PRETTY_FUNCTION__);
00382 #ifndef HAVE_FFTW3_H
00383   // don't LFATAL() in a destructor
00384   LERROR("you must have fftw3 installed to use this function");
00385 #else
00386   if (itsPlan != 0)
00387     fftw<T>::destroy_plan(itsPlan);
00388   fftw<T>::dealloc(itsDst);
00389   fftw<T>::dealloc(itsSrc);
00390 #endif
00391 }
00392 
00393 // ##############################################################
00394 template <class T>
00395 Image<T> FourierInvEngine<T>::ifft(const Image<std::complex<T> >& x)
00396 {
00397 GVX_TRACE(__PRETTY_FUNCTION__);
00398 #ifndef HAVE_FFTW3_H
00399   LFATAL("you must have fftw3 installed to use this function");
00400   /* can't happen */ return Image<T>();
00401 #else
00402   ASSERT(x.getWidth() == itsOutputDims.w()/2+1);
00403   ASSERT(x.getHeight() == itsOutputDims.h());
00404 
00405   if (itsPlan == 0)
00406     {
00407       itsPlan = fftw<T>::plan_dft_c2r_2d(itsOutputDims, itsSrc, itsDst);
00408 
00409       if (itsPlan == 0)
00410         {
00411           fftw<T>::dealloc(itsSrc); fftw<T>::dealloc(itsDst);
00412           LFATAL("couldn't construct fftw_plan");
00413         }
00414     }
00415 
00416   if (USE_FFTW_GURU)
00417     {
00418       Image<T> result(itsOutputDims, NO_INIT);
00419 
00420       // need a copy of the input, because fftw_execute will destroy
00421       // its input when operating in the complex->real direction
00422       Image<std::complex<T> > xcopy(x);
00423 
00424       fftw<T>::execute_dft_c2r
00425         (itsPlan,
00426          xcopy.getArrayPtr(),
00427          result.getArrayPtr());
00428 
00429       return result;
00430     }
00431   else
00432     {
00433       std::copy(x.begin(), x.end(), itsSrc);
00434 
00435       {
00436         GVX_TRACE("fftw_execute-reverse");
00437         fftw<T>::execute(itsPlan);
00438       }
00439 
00440       return Image<T>(itsDst, itsOutputDims.w(), itsOutputDims.h());
00441     }
00442 #endif
00443 }
00444 
00445 // ##############################################################
00446 template <class T>
00447 Image<T> magnitude(const Image<std::complex<T> >& img)
00448 {
00449 GVX_TRACE(__PRETTY_FUNCTION__);
00450   const std::complex<T>* c = img.getArrayPtr();
00451 
00452   Image<T> result(img.getDims(), NO_INIT);
00453   for (typename Image<T>::iterator
00454          itr = result.beginw(), stop = result.endw();
00455        itr != stop;
00456        ++itr)
00457     {
00458       *itr = std::abs(*c);
00459       ++c;
00460     }
00461   return result;
00462 }
00463 
00464 // ##############################################################
00465 template <class T>
00466 Image<T> logmagnitude(const Image<std::complex<T> >& img)
00467 {
00468 GVX_TRACE(__PRETTY_FUNCTION__);
00469   const std::complex<T>* c = img.getArrayPtr();
00470 
00471   Image<T> result(img.getDims(), NO_INIT);
00472   for (typename Image<T>::iterator
00473          itr = result.beginw(), stop = result.endw();
00474        itr != stop;
00475        ++itr)
00476     {
00477       const double a = std::abs(*c);
00478       *itr = log(a > 0.0 // avoid doing log(0)
00479                  ? a
00480                  : std::numeric_limits<double>::min());
00481       ++c;
00482     }
00483   return result;
00484 }
00485 
00486 // ##############################################################
00487 template <class T>
00488 Image<T> phase(const Image<std::complex<T> >& img)
00489 {
00490 GVX_TRACE(__PRETTY_FUNCTION__);
00491   const std::complex<T>* c = img.getArrayPtr();
00492 
00493   Image<T> result(img.getDims(), NO_INIT);
00494   for (typename Image<T>::iterator
00495          itr = result.beginw(), stop = result.endw();
00496        itr != stop;
00497        ++itr)
00498     {
00499       *itr = std::arg(*c);
00500       ++c;
00501     }
00502   return result;
00503 }
00504 
00505 // ##############################################################
00506 template <class T>
00507 Image<T> cartesian(const Image<T>& in,
00508                         const Dims& dims)
00509 {
00510 GVX_TRACE(__PRETTY_FUNCTION__);
00511   Image<T> result(dims, ZEROS);
00512   Image<int> counts(dims, ZEROS);
00513 
00514   const int nfreq = dims.w();
00515   const int nori = dims.h();
00516 
00517   const double theta_max = M_PI;
00518   const double rad_max =
00519     sqrt(in.getWidth()*in.getWidth() +
00520          in.getHeight()*in.getHeight()/4);
00521 
00522   const int h = in.getHeight(), h2 = in.getHeight()/2;
00523 
00524   const Point2D<int> left(-1, 0);
00525   const Point2D<int> right(1, 0);
00526   const Point2D<int> up(0, -1);
00527   const Point2D<int> down(0, 1);
00528 
00529   for (int y = 0; y < h; ++y)
00530     if (y < h2)
00531       for (int x = 0; x < in.getWidth(); ++x)
00532         {
00533           const double th = atan2(y, x);
00534           const double r = sqrt(x*x+y*y);
00535 
00536           Point2D<int> pos(int(nfreq*r/rad_max),
00537                       int(nori*th/theta_max));
00538 
00539           result[pos] += in[Point2D<int>(x,y)];
00540           ++counts[pos];
00541 
00542           if (result.coordsOk(pos+left))
00543             { result[pos+left] += in[Point2D<int>(x,y)]; ++counts[pos+left]; }
00544           if (result.coordsOk(pos+right))
00545             { result[pos+right] += in[Point2D<int>(x,y)]; ++counts[pos+right]; }
00546           if (result.coordsOk(pos+up))
00547             { result[pos+up] += in[Point2D<int>(x,y)]; ++counts[pos+up]; }
00548           if (result.coordsOk(pos+down))
00549             { result[pos+down] += in[Point2D<int>(x,y)]; ++counts[pos+down]; }
00550         }
00551     else
00552       for (int x = 0; x < in.getWidth(); ++x)
00553         {
00554           int y2 = h - y;
00555 
00556           const double th = atan2(y2, -x);
00557           const double r = sqrt(x*x+y2*y2);
00558 
00559           Point2D<int> pos(int(nfreq*r/rad_max),
00560                       int(nori*th/theta_max));
00561 
00562           result[pos] += in[Point2D<int>(x,y)];
00563           ++counts[pos];
00564 
00565           if (result.coordsOk(pos+left))
00566             { result[pos+left] += in[Point2D<int>(x,y)]; ++counts[pos+left]; }
00567           if (result.coordsOk(pos+right))
00568             { result[pos+right] += in[Point2D<int>(x,y)]; ++counts[pos+right]; }
00569           if (result.coordsOk(pos+up))
00570             { result[pos+up] += in[Point2D<int>(x,y)]; ++counts[pos+up]; }
00571           if (result.coordsOk(pos+down))
00572             { result[pos+down] += in[Point2D<int>(x,y)]; ++counts[pos+down]; }
00573         }
00574 
00575   for (int i = 0; i < result.getSize(); ++i)
00576     if (counts[i] > 0)
00577       result[i] /= counts[i];
00578 
00579   return result;
00580 }
00581 
00582 #if 0
00583 template class FourierEngine<float>;
00584 template class FourierInvEngine<float>;
00585 #endif
00586 
00587 template class FourierEngine<double>;
00588 template class FourierInvEngine<double>;
00589 
00590 template Image<float> magnitude(const Image<std::complex<float> >&);
00591 template Image<float> logmagnitude(const Image<std::complex<float> >&);
00592 template Image<float> phase(const Image<std::complex<float> >&);
00593 template Image<float> cartesian(const Image<float>&, const Dims&);
00594 
00595 template Image<double> magnitude(const Image<std::complex<double> >&);
00596 template Image<double> logmagnitude(const Image<std::complex<double> >&);
00597 template Image<double> phase(const Image<std::complex<double> >&);
00598 template Image<double> cartesian(const Image<double>&, const Dims&);
00599 
00600 // ##############################################################
00601 /* So things look consistent in everyone's emacs... */
00602 /* Local Variables: */
00603 /* indent-tabs-mode: nil */
00604 /* End: */
00605 
00606 #endif // IMAGE_FOURIERENGINE_C_DEFINED
Generated on Sun May 8 08:05:11 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3