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