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