GistEstimatorFFT.C

Go to the documentation of this file.
00001 /*!@file Neuro/GistEstimatorFFT.C Extract gist of image using the Fourier Transform */
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: Christian Siagian <siagian at usc dot edu>
00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Neuro/GistEstimatorFFT.C $
00035 // $Id: GistEstimatorFFT.C 13065 2010-03-28 00:01:00Z itti $
00036 //
00037 
00038 // ######################################################################
00039 /*! Extract gist of image using the Fourier transform                  */
00040 
00041 #ifndef NEURO_GISTESTIMATORFFT_C_DEFINED
00042 #define NEURO_GISTESTIMATORFFT_C_DEFINED
00043 
00044 #include "Neuro/GistEstimatorFFT.H"
00045 
00046 #include "Neuro/gistParams.H"
00047 #include "Channels/IntensityChannel.H"
00048 #include "Image/Kernels.H"     // for gaborFilter()
00049 #include "Image/CutPaste.H"    // for inplacePaste()
00050 #include "Image/MathOps.H"     // for mean(), stdev()
00051 #include "Image/DrawOps.H"     // drawGrid()
00052 #include "Raster/Raster.H"
00053 
00054 #include "Simulation/SimEventQueue.H"
00055 #include "Neuro/NeuroSimEvents.H"
00056 #include "Simulation/SimEvents.H"
00057 
00058 #define NUM_G_LEV              5  // number of gabor levels
00059 #define NUM_G_DIR              8  // number of gabor orientations
00060 
00061 #define WIN_NUM                4  // 4: 4 by 4 windows
00062 
00063 // ######################################################################
00064 GistEstimatorFFT::GistEstimatorFFT(OptionManager& mgr,
00065                                    const std::string& descrName,
00066                                    const std::string& tagName)
00067   :
00068   GistEstimatorAdapter(mgr, descrName, tagName),
00069   SIMCALLBACK_INIT(SimEventRetinaImage)
00070 {
00071   gaborMaskImg = NULL;
00072 
00073   // (4x4 grid) x (5 levels x 8 direction feature size) = 16x40 = 640
00074   itsGistVector.resize
00075     (1, WIN_NUM * WIN_NUM * NUM_G_LEV * NUM_G_DIR, NO_INIT);
00076 }
00077 
00078 // ######################################################################
00079 GistEstimatorFFT::~GistEstimatorFFT()
00080 { }
00081 
00082 // ######################################################################
00083 Image<double> GistEstimatorFFT::getGist()
00084 {
00085   return itsGistVector;
00086 }
00087 
00088 // ######################################################################
00089 void GistEstimatorFFT::
00090 onSimEventRetinaImage(SimEventQueue& q, rutz::shared_ptr<SimEventRetinaImage>& e)
00091 {
00092   Image<float> img = normalize(e->frame().grayFloat());
00093   computeGistFeatureVector(img);
00094   LINFO("Gist features are computed");
00095 
00096   // post an event so that anyone interested in gist can grab it:
00097   rutz::shared_ptr<SimEventGistOutput>
00098     ew(new SimEventGistOutput(this, itsGistVector));
00099   q.post(ew);
00100 }
00101 
00102 // ######################################################################
00103 void GistEstimatorFFT::computeGistFeatureVector(Image<float> img)
00104 {
00105   // get the current (normalized) input image
00106   // check whether it is not the same size as the previous one
00107   int w = img.getWidth()/WIN_NUM, h = img.getHeight()/WIN_NUM;
00108   Image<float> wImg(w,h, ZEROS);
00109 
00110   itsFftImage.resize(img.getDims());
00111 
00112   // reset the gabor masks initially or whenever dimension changed
00113   if((gaborMaskImg == NULL) ||
00114      (w != gaborMaskImg[0][0].getWidth() ) ||
00115      (h != gaborMaskImg[0][0].getHeight()))
00116     {
00117       //xw = new XWinManaged
00118       //  (Dims(2*img.getWidth(), img.getHeight()), 0, 0, "xw");
00119       //xw->drawImage(img,0,0); Raster::waitForKey();
00120       LDEBUG("w: %d, h: %d",w,h);
00121       setupFFTW(w,h);
00122     }
00123   //Raster::waitForKey();
00124 
00125   // get the features on each window
00126   Image<float> cImg(img.getWidth(), img.getHeight(), ZEROS);
00127   Image<float> dImg = img;
00128   float mn, mx; getMinMax(dImg,mn,mx);
00129   drawGrid(dImg, img.getWidth()/4,img.getHeight()/4,1,1, mx);
00130 
00131   Image<double>::iterator aptr = itsGistVector.beginw();
00132   for(uint ii = 0; ii < WIN_NUM; ii++)
00133     for(uint jj = 0; jj < WIN_NUM; jj++)
00134       {
00135         Point2D<int> p((ii*img.getWidth())/WIN_NUM,
00136                        (jj*img.getHeight())/WIN_NUM);
00137         Rectangle r(p, Dims(w,h));
00138         wImg = crop(img, r);
00139 
00140         //compute feature vector
00141         //Fourier Transform of the image
00142         fftCompute(wImg, itsOutFftwBuffer);
00143 
00144         // go through each gabor masks
00145         for(int i = 0; i < NUM_G_LEV; i++)
00146           for(int j = 0; j < NUM_G_DIR; j++)
00147             {
00148               // weighted sum of fft for a feature
00149               double sum = 0.0;
00150               for(int k = 0; k < h; k++)
00151                 for(int l = 0; l < w/2+1; l++)
00152                   {
00153                     sum += log(1.0 + gaborMask[i][j][k][l]) *
00154                       log(1.0 + itsOutFftwBuffer[k][l]);
00155                   }
00156               *aptr++ = sum/(h*w/2+1);
00157             }
00158 
00159         // display Fourier Transform Image
00160         Image<float> temp = getFftImage(itsOutFftwBuffer, w, h);
00161         LDEBUG("w,h:%d:%d: i,j:%d:%d [%d %d]",
00162                ii*w, jj*h, temp.getWidth(), temp.getHeight(),
00163                itsFftImage.getWidth(), itsFftImage.getHeight());
00164 
00165         inplacePaste(itsFftImage, temp, Point2D<int>(ii*w, jj*h));
00166       }
00167 }
00168 
00169 // ######################################################################
00170 // setup input and output array for FFT
00171 void GistEstimatorFFT::setupFFTW(int w, int h)
00172 {
00173   itsFftw = new FFTWWrapper(w,h);
00174   delete [] itsFftwBuffer;
00175   itsFftwBuffer = new double[w*h];
00176   itsFftw->init(itsFftwBuffer);
00177 
00178   // setup output array
00179   itsOutFftwBuffer = new double*[h];
00180   for(int i = 0; i < h; i++)
00181     itsOutFftwBuffer[i] = new double[w/2+1];
00182 
00183   // setup the log-gabor masks
00184   setupGaborMask(w,h);
00185 }
00186 
00187 // ######################################################################
00188 void GistEstimatorFFT::setupGaborMask(int w, int h)
00189 {
00190   int dim      = (w<h)?w:h;
00191   float stdev  = double(floor((dim - 3.0)/2.0/sqrt(10.0)));
00192   float period = double(stdev * 2.0);
00193   LDEBUG("w:h: %d:%d; dim -> %d== %f, %f",w,h,dim, stdev, period);
00194   itsStddev = stdev;
00195 
00196   // setup storage for gabor masks
00197   gaborMask = new double***[NUM_G_LEV];
00198   for(int i = 0; i < NUM_G_LEV; i++)
00199     gaborMask[i] = new double**[NUM_G_DIR];
00200 
00201   gaborMaskImg = new Image<float>*[NUM_G_LEV];
00202   for(int i = 0; i < NUM_G_LEV; i++)
00203     gaborMaskImg[i] = new Image<float>[NUM_G_DIR];
00204 
00205   // traverse through the different frequency and orientation
00206   float scale = 1.0;
00207   for(int  i = 0; i < NUM_G_LEV; i++)
00208   {
00209     for(int j = 0; j < NUM_G_DIR; j++)
00210     {
00211       // allocate space for the solution
00212       gaborMask[i][j] = new double*[h];
00213       for(int k = 0; k < h; k++)
00214         gaborMask[i][j][k] = new double[w/2+1];
00215 
00216       // setup the gabor kernel
00217       //Image<float> gaborF = gaborFilter<float>(stdev/scale, period/scale, 0.0,
00218       //                      (j*180.0)/NUM_G_DIR);
00219       //Image<float> temp (w, h, ZEROS); inplacePaste(temp, gaborF, Point2D<int>(0,0));
00220       //LINFO("gabor: [%d,%d]: scale: %f",gaborF.getWidth(), gaborF.getHeight(), scale);
00221       //xw->drawImage(temp,w,0); Raster::waitForKey();
00222       //fftCompute(temp, gaborMask[i][j]);
00223       //gaborMaskImg[i][j] = getFftImage(gaborMask[i][j], w, h);
00224       //xw->drawImage(gaborMaskImg[i][j],w,0);
00225 
00226       // perform the fourier transform and save the results
00227       computeFFTgabor(stdev/scale, period/scale, (j*180.0)/NUM_G_DIR, w, h,
00228                       gaborMask[i][j]);
00229 
00230       gaborMaskImg[i][j] = getFftImage(gaborMask[i][j], w, h);
00231       //xw->drawImage(gaborMaskImg[i][j],w,0);
00232 
00233       //Raster::waitForKey();
00234     }
00235     scale *= 2;
00236   }
00237 }
00238 
00239 // ######################################################################
00240 void GistEstimatorFFT::computeFFTgabor
00241 ( const float stddev, const float period,
00242   const float theta, uint w, uint h, double **gm)
00243 {
00244   float rtDeg = M_PI / 180.0F * theta;
00245   LDEBUG("std: %f, period: %f 1/p:[%f], theta: %f:%f:: cos: %f, sin: %f",
00246         stddev, period, 1.0/period, theta,rtDeg, cos(rtDeg), sin(rtDeg));
00247 
00248   double aSq = 1/(2.0*M_PI*stddev*stddev);
00249   double Kf = itsStddev*8.0;
00250   float X  = M_PI/aSq/4800.0;  //float X2 = .1*stddev/itsStddev;
00251   LDEBUG("Kf: %f X: %f, a^2: %f", Kf, X, aSq);
00252 
00253   // fourier transform cycles
00254   // so we perform the calculation four times
00255 
00256   double co = cos(rtDeg); double si = sin(rtDeg);
00257   double u0 = Kf*co/period; double v0 = Kf*si/period;
00258   LDEBUG("1: [u0,v0]:[%f,%f]", u0,v0);
00259   for(uint i = 0; i < w/2+1; i++)
00260     {
00261       for(uint j = 0; j < h/2; j++)
00262         {
00263           float ur =      (float(i) - u0)*co + (float(j) - v0)*si;
00264           float vr = -1.0*(float(i) - u0)*si + (float(j) - v0)*co;
00265           gm[j][i] = exp(-X*(ur*ur + vr*vr))/aSq;
00266           // gm[j][i] = exp((-M_PI/(aSq))*(ur*ur + vr*vr));
00267        }
00268     }
00269 
00270   co = cos(rtDeg);  si = sin(rtDeg);
00271   u0 = Kf*co/period; v0 = Kf*si/period + float(h);
00272   LDEBUG("2: [u0,v0]:[%f,%f]", u0,v0);
00273   for(uint i = 0; i < w/2+1; i++)
00274     {
00275       for(uint j = h/2; j < h; j++)
00276         {
00277           float ur =      (float(i) - u0)*co + (float(j) - v0)*si;
00278           float vr = -1.0*(float(i) - u0)*si + (float(j) - v0)*co;
00279           gm[j][i] = exp(-X*(ur*ur + vr*vr))/aSq;
00280       }
00281     }
00282 
00283   co = cos(-rtDeg);  si = sin(-rtDeg);
00284   u0 = -Kf*co/period; v0 = Kf*si/period;
00285   LDEBUG("3: [u0,v0]:[%f,%f]", u0,v0);
00286   for(uint i = 0; i < w/2+1; i++)
00287     {
00288       for(uint j = 0; j < h/2; j++)
00289         {
00290           float ur =      (float(i) - u0)*co + (float(j) - v0)*si;
00291           float vr = -1.0*(float(i) - u0)*si + (float(j) - v0)*co;
00292           gm[j][i] += exp(-X*(ur*ur + vr*vr))/aSq;
00293         }
00294     }
00295 
00296   co = cos(-rtDeg);  si = sin(-rtDeg);
00297   u0 = -Kf*co/period; v0 = Kf*si/period + float(h);
00298   LDEBUG("4: [u0,v0]:[%f,%f]", u0,v0);
00299   for(uint i = 0; i < w/2+1; i++)
00300     {
00301       for(uint j = h/2; j < h; j++)
00302         {
00303           float ur =      (float(i) - u0)*co + (float(j) - v0)*si;
00304           float vr = -1.0*(float(i) - u0)*si + (float(j) - v0)*co;
00305           gm[j][i] += exp(-X*(ur*ur + vr*vr))/aSq;
00306        }
00307     }
00308 }
00309 
00310 // ######################################################################
00311 // convert image to array to input to FFT
00312 // and compute the FFT
00313 void GistEstimatorFFT::fftCompute(Image<float> img, double **fftOut)
00314 {
00315   int w = img.getWidth(), h = img.getHeight();
00316 
00317   double *itsFftwInputData = itsFftwBuffer;
00318   for(int j = 0; j < h; j++)
00319     for(int i = 0; i < w; i++)
00320       *itsFftwInputData++ = (double)img.getVal(i,j);
00321 
00322   itsFftw->compute(fftOut);
00323 }
00324 
00325 // ######################################################################
00326 Image<float> GistEstimatorFFT::getFftImage(double **res, int w, int h)
00327 {
00328   // scaling the large dynamic range of the image Fourier Transform
00329   // using log: ln(1.0 + |F(i,j)|)
00330   // origin is centered to the middle of the image
00331 
00332   // gamma correction
00333   float gc = 1.5;
00334 
00335   // redraw the images
00336   Image<float> ftImg(w,h,ZEROS);
00337   for(int i = 0; i < w/2; i++)
00338     for(int j = 0; j < h/2; j++)
00339       ftImg.setVal(i+w/2, h/2-j,
00340                    pow(log(1.0 + res[j][i+1]), gc));
00341 
00342   for(int i = 0; i < w/2; i++)
00343     for(int j = h/2; j < h; j++)
00344       ftImg.setVal(i+w/2, 3*h/2-1-j,
00345                    pow(log(1.0 + res[j][i+1]), gc));
00346 
00347   for(int i = 0; i < w/2; i++)
00348     for(int j = 0; j < h; j++)
00349       ftImg.setVal(i, j, ftImg.getVal(w-1-i, h-1-j));
00350 
00351 
00352 //   float mn, mx; getMinMax(ftImg,mn,mx);
00353 //   for(int i = 0; i < ftImg.getWidth(); i++)
00354 //     {
00355 //       for(int j = 0; j < ftImg.getHeight(); j++)
00356 //         {
00357 //           if(ftImg.getVal(i,j) >= mx)
00358 //             {
00359 //               LINFO("coor: %d, %d",i,j);
00360 //               ftImg.setVal(i,j,0.0);
00361 //             }
00362 //           }
00363 //     }
00364 //   LINFO("min-i-max: %f, %f",mn,mx);
00365 
00366 
00367   return ftImg;
00368 }
00369 
00370 // ######################################################################
00371 // normalize image by subtracting it with mean and dividing by stdev
00372 Image<float> GistEstimatorFFT::normalize(Image<float> img)
00373 {
00374   Image<float> tImg = img;
00375   double tMean  = float(mean(img));
00376   double tStdev = float(stdev(img));
00377   tImg -= tMean;
00378   tImg /= tStdev;
00379 
00380   return tImg;
00381 }
00382 
00383 // ######################################################################
00384 Image<float> GistEstimatorFFT::getFftImage()
00385 {
00386   return itsFftImage;
00387 }
00388 
00389 // ######################################################################
00390 /* So things look consistent in everyone's emacs... */
00391 /* Local Variables: */
00392 /* indent-tabs-mode: nil */
00393 /* End: */
00394 
00395 #endif // NEURO_GISTESTIMATORFFT_C_DEFINED
Generated on Sun May 8 08:05:24 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3