00001 /*!@file Neuro/GistEstimatorGen.C extract estimated gist 00002 using available features of the image */ 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00005 // 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: Zhicheng Li <zhicheng@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Neuro/GistEstimatorGen.C $ 00035 // $Id: GistEstimatorGen.C 13065 2010-03-28 00:01:00Z itti $ 00036 // 00037 00038 // ###################################################################### 00039 /*! Extract gist of image */ 00040 00041 00042 #include "Neuro/GistEstimatorGen.H" 00043 #include "Component/ModelManager.H" 00044 #include "Channels/ChannelMaps.H" 00045 #include "Channels/BlueYellowChannel.H" 00046 #include "Channels/ColorChannel.H" 00047 #include "Channels/GaborChannel.H" 00048 #include "Channels/IntensityChannel.H" 00049 #include "Channels/OrientationChannel.H" 00050 #include "Channels/RedGreenChannel.H" 00051 #include "Channels/Hue1Channel.H" 00052 #include "Channels/Hue2Channel.H" 00053 #include "Channels/HueChannel.H" 00054 #include "Channels/H2SVChannel.H" 00055 #include "Channels/CIELabChannel.H" 00056 #include "Channels/JunctionChannel.H" 00057 #include "Channels/EndPointChannel.H" 00058 #include "Channels/TJunctionChannel.H" 00059 #include "Channels/LJunctionChannel.H" 00060 #include "Channels/XJunctionChannel.H" 00061 #include "GUI/XWinManaged.H" 00062 #include "Image/CutPaste.H" 00063 #include "Image/DrawOps.H" 00064 #include "Image/FilterOps.H" 00065 #include "Image/MathOps.H" 00066 #include "Image/ShapeOps.H" 00067 #include "Neuro/gistParams.H" 00068 #include "Raster/Raster.H" 00069 #include "Simulation/SimEventQueue.H" 00070 #include "Neuro/NeuroSimEvents.H" 00071 #include "Simulation/SimEvents.H" 00072 #include "Channels/ChannelOpts.H" 00073 #include "Channels/SingleChannel.H" 00074 #include "Util/Timer.H" 00075 #include "Util/StringUtil.H" 00076 #include "Neuro/NeuroOpts.H" 00077 00078 #define PYR_LEVEL 5 // if without center surround then use 5 levels pyramid 00079 00080 00081 // ###################################################################### 00082 // get subsum of an image - more efficient implementation 00083 Image<float> getSubSumGen(Image<float> img) 00084 { 00085 Image<float> res(1, 21, ZEROS); 00086 00087 int w = img.getWidth(); int h = img.getHeight(); 00088 std::vector<float> tempRes(21); 00089 for (int i = 0; i < 21; i++) tempRes[i] = 0.0; 00090 std::vector<int> counts(16); 00091 for (int i = 0 ; i < 16; i++) counts[i] = 0; 00092 00093 Image<float>::const_iterator itr = img.begin(); 00094 for (int y = 0; y < h; ++y) 00095 { 00096 int suby = (4*y)/h; 00097 for (int x = 0; x < w; ++x) 00098 { 00099 int subx = (4*x)/w; 00100 int subpos = 4*suby + subx; 00101 tempRes[subpos+5] += *itr; 00102 00103 ++(counts[subpos]); 00104 ++itr; 00105 } 00106 00107 } 00108 00109 int order[] = { 5,6,9,10, 7,8,11,12, 13,14,17,18, 15,16,19,20 }; 00110 for (int i = 0 ; i < 16; ++i) 00111 if (counts[i] > 0) 00112 res[i+5] = tempRes[order[i]] / (counts[order[i] - 5]+ 0.0); 00113 00114 float tre1 = tempRes[5] + tempRes[6] + tempRes[9] + tempRes[10]; 00115 int ct1 = counts[0] + counts[1] + counts[4] + counts[5]; 00116 00117 float tre2 = tempRes[7] + tempRes[8] + tempRes[11] + tempRes[12]; 00118 int ct2 = counts[2] + counts[3] + counts[6] + counts[7]; 00119 00120 float tre3 = tempRes[13] + tempRes[14] + tempRes[17] + tempRes[18]; 00121 int ct3 = counts[8] + counts[9] + counts[12] + counts[13]; 00122 00123 float tre4 = tempRes[15] + tempRes[16] + tempRes[19] + tempRes[20]; 00124 int ct4 = counts[10] + counts[11] + counts[14] + counts[15]; 00125 00126 res[1] = tre1/ct1; 00127 res[2] = tre2/ct2; 00128 res[3] = tre3/ct3; 00129 res[4] = tre4/ct4; 00130 00131 res[0] = (tre1 + tre2 + tre3 + tre4)/(ct1 + ct2 + ct3 + ct4); 00132 00133 LDEBUG("lev1 : %14.4f", res[0]); 00134 LDEBUG("lev2 : %14.4f, %14.4f", res[1], res[2]); 00135 LDEBUG(" : %14.4f, %14.4f", res[3], res[4]); 00136 LDEBUG("lev3 : %14.4f, %14.4f, %14.4f, %14.4f", 00137 res[ 5], res[ 6], res[ 9], res[10]); 00138 LDEBUG(" : %14.4f, %14.4f, %14.4f, %14.4f", 00139 res[ 7], res[ 8], res[11], res[12]); 00140 LDEBUG(" : %14.4f, %14.4f, %14.4f, %14.4f", 00141 res[13], res[14], res[17], res[18]); 00142 LDEBUG(" : %14.4f, %14.4f, %14.4f, %14.4f", 00143 res[15], res[16], res[19], res[20]); 00144 00145 return res; 00146 } 00147 00148 00149 // ###################################################################### 00150 GistEstimatorGen::GistEstimatorGen(OptionManager& mgr, 00151 const std::string& descrName, 00152 const std::string& tagName) : 00153 GistEstimatorAdapter(mgr, descrName, tagName), 00154 SIMCALLBACK_INIT(SimEventVisualCortexOutput), 00155 itsLevelSpec(&OPT_LevelSpec, this), 00156 itsUseCS(&OPT_GistCenterSurroundFlag,this) 00157 { 00158 itsGistSize = 0; 00159 } 00160 00161 // ###################################################################### 00162 GistEstimatorGen::~GistEstimatorGen() 00163 { } 00164 00165 // ###################################################################### 00166 void GistEstimatorGen:: 00167 onSimEventVisualCortexOutput(SimEventQueue& q, rutz::shared_ptr<SimEventVisualCortexOutput>& e) 00168 { 00169 //Grab the channel maps from the visual cortex 00170 rutz::shared_ptr<SimReqVCXmaps> vcxm(new SimReqVCXmaps(this)); 00171 q.request(vcxm); // VisualCortex is now filling-in the maps... 00172 rutz::shared_ptr<ChannelMaps> chm = vcxm->channelmaps(); 00173 00174 //Compute the full size gist feature vector 00175 getFeatureVector(chm); 00176 00177 // post an event so that anyone interested in gist can grab it: 00178 rutz::shared_ptr<SimEventGistOutput> 00179 ew(new SimEventGistOutput(this, itsGistVector)); 00180 q.post(ew); 00181 } 00182 00183 // ###################################################################### 00184 Image<double> GistEstimatorGen::getGist() 00185 { 00186 return itsGistVector; 00187 } 00188 00189 // ###################################################################### 00190 void GistEstimatorGen::start1() 00191 { 00192 getManager().setOptionValString(&OPT_SingleChannelComputeFullPyramidForGist,"true"); 00193 GistEstimatorAdapter::start1(); 00194 } 00195 00196 // ###################################################################### 00197 void GistEstimatorGen::getFeatureVector(rutz::shared_ptr<ChannelMaps> chanMaps) 00198 { 00199 //! first get the gist feature size and allocate the gist vector size 00200 int sz = 0, sz_cs=0, sz_nocs = 0; 00201 if(itsUseCS.getVal() == 1 || itsUseCS.getVal() == 2) 00202 sz_cs += chanMaps->numSubmaps(); 00203 00204 // sz_nocs is the number of how many raw pyramid types 00205 if(itsUseCS.getVal() == 0 || itsUseCS.getVal() == 2) 00206 for(uint i=0; i < chanMaps->numSubchans(); i++) 00207 { 00208 rutz::shared_ptr<ChannelMaps> currChan = chanMaps->subChanMaps(i); 00209 if(currChan->numSubchans() == 0) 00210 sz_nocs++; 00211 else 00212 sz_nocs += currChan->numSubchans(); 00213 } 00214 sz_nocs *= PYR_LEVEL; 00215 00216 sz = sz_cs + sz_nocs; 00217 LINFO("there are in total %4d gist feature chans", sz); 00218 itsGistVector.resize(1,NUM_GIST_FEAT * sz, NO_INIT); 00219 00220 int count = 0; 00221 00222 //! get the center-surround feature values 00223 if(itsUseCS.getVal() == 1 || itsUseCS.getVal() == 2) 00224 for(int i = 0; i<sz_cs; i++) 00225 { 00226 inplacePaste(itsGistVector,getSubSumGen(chanMaps->getRawCSmap(i)), 00227 Point2D<int>(0, count*NUM_GIST_FEAT)); 00228 count++; 00229 } 00230 00231 //! get the non center-surround feature values 00232 if(itsUseCS.getVal() == 0 || itsUseCS.getVal() == 2) 00233 for(uint i=0; i<chanMaps->numSubchans(); i++) 00234 { 00235 rutz::shared_ptr<ChannelMaps> currChan = chanMaps->subChanMaps(i); 00236 if(currChan->numSubchans() == 0) 00237 { 00238 ASSERT(currChan->hasPyramid()); 00239 for(uint j=0; j<PYR_LEVEL; j++) 00240 { 00241 inplacePaste(itsGistVector,getSubSumGen 00242 (currChan->getPyramid().getImage(j)), 00243 Point2D<int>(0,count*NUM_GIST_FEAT)); 00244 count++; 00245 } 00246 } 00247 else 00248 { 00249 for(uint i=0; i<currChan->numSubchans(); i++) 00250 { 00251 rutz::shared_ptr<ChannelMaps> currSubChan = currChan->subChanMaps(i); 00252 ASSERT(currSubChan->hasPyramid()); 00253 for(uint j=0; j<PYR_LEVEL; j++) 00254 { 00255 inplacePaste(itsGistVector,getSubSumGen 00256 (currSubChan->getPyramid().getImage(j)), 00257 Point2D<int>(0,count*NUM_GIST_FEAT)); 00258 count++; 00259 } 00260 } 00261 } 00262 } 00263 ASSERT(count == sz); 00264 itsGistSize = sz; 00265 } 00266 00267 // ###################################################################### 00268 // get gist histogram to visualize the data 00269 Image<float> GistEstimatorGen::getGistImage(int sqSize, 00270 float minO, float maxO, 00271 float minC, float maxC, 00272 float minI, float maxI) 00273 { 00274 // square size 00275 int s = sqSize; 00276 Image<float> img(NUM_GIST_COL * s, NUM_GIST_FEAT * s, ZEROS); 00277 float range; 00278 00279 // setup range for orientation channel if necessary 00280 if(maxO == minO) 00281 { 00282 minO = itsGistVector.getVal(0); 00283 maxO = itsGistVector.getVal(0); 00284 for(int i = 0; i < 16; i++) 00285 for(int j = 0; j < NUM_GIST_FEAT; j++) 00286 { 00287 float val = itsGistVector.getVal(i*NUM_GIST_FEAT+j); 00288 if(val < minO) 00289 minO = val; 00290 else if(val > maxO) 00291 maxO = val; 00292 } 00293 LDEBUG("Orientation Channel Min: %f, max: %f", minO, maxO); 00294 } 00295 range = maxO - minO; 00296 00297 // orientation channel 00298 for(int a = 0; a < 4; a++) 00299 for(int b = 0; b < 4; b++) 00300 for(int j = 0; j < NUM_GIST_FEAT; j++) 00301 { 00302 int i = b*4 + a; 00303 int ii = a*4 + b; 00304 float val = itsGistVector.getVal(ii*NUM_GIST_FEAT+j); 00305 //float val = log(itsGistVector.getVal(i*NUM_GIST_FEAT+j)+1); 00306 //val = val * val; 00307 drawPatch(img, Point2D<int>(i*s+s/2,j*s+s/2),s/2,(val-minO)/range); 00308 //LINFO("val[%d]: %f",j,val); 00309 } 00310 00311 // setup range for color channel if necessary 00312 if(maxC == minC) 00313 { 00314 minC = itsGistVector.getVal(16*NUM_GIST_FEAT); 00315 maxC = itsGistVector.getVal(16*NUM_GIST_FEAT); 00316 for(int i = 16; i < 28; i++) 00317 for(int j = 0; j < NUM_GIST_FEAT; j++) 00318 { 00319 float val = itsGistVector.getVal(i*NUM_GIST_FEAT+j); 00320 if(val < minC) 00321 minC = val; 00322 else if(val > maxC) 00323 maxC = val; 00324 } 00325 LDEBUG("Color Channel Min: %f, max: %f", minC, maxC); 00326 } 00327 range = maxC - minC; 00328 00329 // color channel 00330 for(int i = 16; i < 28; i++) 00331 for(int j = 0; j < NUM_GIST_FEAT; j++) 00332 { 00333 float val = itsGistVector.getVal(i*NUM_GIST_FEAT+j); 00334 //float val = log(itsGistVector.getVal(i*NUM_GIST_FEAT+j)+1); 00335 //val = val * val; 00336 drawPatch(img, Point2D<int>(i*s+s/2,j*s+s/2),s/2,(val-minC)/range); 00337 //LINFO("val[%d]: %f",j,val); 00338 } 00339 00340 // setup range for intensity channel if necessary 00341 if(maxI == minI) 00342 { 00343 minI = itsGistVector.getVal(28*NUM_GIST_FEAT); 00344 maxI = itsGistVector.getVal(28*NUM_GIST_FEAT); 00345 for(int i = 28; i < 34; i++) 00346 for(int j = 0; j < NUM_GIST_FEAT; j++) 00347 { 00348 float val = itsGistVector.getVal(i*NUM_GIST_FEAT+j); 00349 if(val < minI) 00350 minI = val; 00351 else if(val > maxI) 00352 maxI = val; 00353 } 00354 LDEBUG("Intensity Channel Min: %f, max: %f", minI, maxI); 00355 } 00356 range = maxI - minI; 00357 00358 // intensity channel 00359 for(int i = 28; i < NUM_GIST_COL; i++) 00360 for(int j = 0; j < NUM_GIST_FEAT; j++) 00361 { 00362 float val = itsGistVector.getVal(i*NUM_GIST_FEAT+j); 00363 //float val = log(itsGistVector.getVal(i*NUM_GIST_FEAT+j)+1); 00364 //val = val * val; 00365 drawPatch(img, Point2D<int>(i*s+s/2,j*s+s/2),s/2,(val-minI)/range); 00366 //LINFO("val[%d]: %f",j,val); 00367 } 00368 00369 // draw the delineation 00370 // spatially 00371 float max = 1.0f; 00372 drawLine(img, Point2D<int>(0, 1*s), Point2D<int>(NUM_GIST_COL*s, 1*s), max, 1); 00373 drawLine(img, Point2D<int>(0, 5*s), Point2D<int>(NUM_GIST_COL*s, 5*s), max, 1); 00374 drawLine(img, Point2D<int>(0, 9*s), Point2D<int>(NUM_GIST_COL*s, 9*s), max/2, 1); 00375 drawLine(img, Point2D<int>(0, 13*s), Point2D<int>(NUM_GIST_COL*s, 13*s), max/2, 1); 00376 drawLine(img, Point2D<int>(0, 17*s), Point2D<int>(NUM_GIST_COL*s, 17*s), max/2, 1); 00377 00378 // channelwise 00379 drawLine(img, Point2D<int>( 4*s, 0), Point2D<int>( 4*s, NUM_GIST_FEAT*s), max, 1); 00380 drawLine(img, Point2D<int>( 8*s, 0), Point2D<int>( 8*s, NUM_GIST_FEAT*s), max, 1); 00381 drawLine(img, Point2D<int>(12*s, 0), Point2D<int>(12*s, NUM_GIST_FEAT*s), max, 1); 00382 drawLine(img, Point2D<int>(16*s, 0), Point2D<int>(16*s, NUM_GIST_FEAT*s), max, 1); 00383 drawLine(img, Point2D<int>(22*s, 0), Point2D<int>(22*s, NUM_GIST_FEAT*s), max, 1); 00384 drawLine(img, Point2D<int>(28*s, 0), Point2D<int>(28*s, NUM_GIST_FEAT*s), max, 1); 00385 00386 return img; 00387 } 00388 00389 // ###################################################################### 00390 // get gist difference: Jeffrey Divergence 00391 Image<float> GistEstimatorGen::diffGist(Image<float> in) 00392 { 00393 LFATAL("fix"); 00394 float total = 0.0; 00395 float a,b,c,d; 00396 00397 for(int i = 0; i < NUM_GIST_COL; i++) 00398 { 00399 for(int j = 0; j < NUM_GIST_FEAT; j++) 00400 { 00401 a = itsGistVector.getVal(i*NUM_GIST_FEAT +j) / 00402 itsGistVector.getVal(i*NUM_GIST_FEAT); 00403 b = in.getVal(i*NUM_GIST_FEAT +j) / in.getVal(i*NUM_GIST_FEAT); 00404 c = itsGistVector.getVal(i*NUM_GIST_FEAT) + 00405 in.getVal(i*NUM_GIST_FEAT); 00406 d = (a - b) * c/2; 00407 LINFO("%6.3f - %6.3f = %f",a,b,fabs(d)); 00408 00409 if((j-5)%4 == 3) LINFO("-:-:-"); 00410 total += sqrt((d*d)); 00411 } 00412 LINFO(" "); 00413 } 00414 LINFO("Diff = %f -> %f\n",total,total/NUM_GIST_COL/NUM_GIST_FEAT); 00415 Raster::waitForKey(); 00416 00417 return Image<float>(); 00418 } 00419 00420 00421 // ###################################################################### 00422 // get the gist size at the begining to allocate the size for itsGistVector 00423 int GistEstimatorGen::getGistSize() 00424 { 00425 return itsGistSize; 00426 } 00427 00428 00429 00430 // ###################################################################### 00431 /* So things look consistent in everyone's emacs... */ 00432 /* Local Variables: */ 00433 /* indent-tabs-mode: nil */ 00434 /* End: */