00001 /*!@file BeoSub/BeoSubSaliency.C A class for quick-and-dirty saliency mapping */ 00002 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: Zack Gossman <gossman@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/BeoSub/BeoSubSaliency.C $ 00035 // $Id: BeoSubSaliency.C 12074 2009-11-24 07:51:51Z itti $ 00036 // 00037 00038 #include "BeoSub/BeoSubSaliency.H" 00039 #include "Demo/DemoOpts.H" //Is this worth it? FIX? 00040 00041 // ###################################################################### 00042 void *BeoSubSaliency_CMAP(void *c) 00043 { 00044 BeoSubSaliency *d = (BeoSubSaliency *)c; 00045 d->computeCMAP(); 00046 return NULL; 00047 } 00048 00049 // ###################################################################### 00050 BeoSubSaliency::BeoSubSaliency(OptionManager& mgr, 00051 const std::string& descrName, 00052 const std::string& tagName): 00053 ModelComponent(mgr, descrName, tagName), 00054 itsNumThreads(&OPT_SMTnumThreads, this) 00055 { 00056 MYLOGVERB = LOG_INFO; 00057 00058 win = Point2D<int>(IMAGEWIDTH/2, IMAGEHEIGHT/2); // coordinates of attended location 00059 debugmode = false; 00060 hasRun = false; 00061 //start threads. They should go to sleep on the condition since no jobs have ben queued up yet 00062 pthread_mutex_init(&jobLock, NULL); 00063 pthread_mutex_init(&condLock, NULL); 00064 pthread_mutex_init(&mapLock, NULL); 00065 pthread_cond_init(&jobCond, NULL); 00066 00067 // get our processing threads started: 00068 worker = new pthread_t[itsNumThreads.getVal()]; 00069 for (uint i = 0; i < itsNumThreads.getVal(); i ++){ 00070 pthread_create(&worker[i], NULL, BeoSubSaliency_CMAP, (void *)this); 00071 00072 // all threads should go and lock against our job condition. Sleep a 00073 // bit to make sure this really happens: HACKY 00074 usleep(100000); 00075 } 00076 00077 } 00078 00079 // ###################################################################### 00080 BeoSubSaliency::~BeoSubSaliency(){ 00081 00082 pthread_cond_destroy(&jobCond); 00083 delete [] worker; 00084 } 00085 00086 Point2D<int> BeoSubSaliency::run(Image< PixRGB<byte> > img, bool debug){ 00087 00088 debugmode = debug; 00089 dataReady = false; 00090 totalJobs = 0; 00091 jobsDone = 0; 00092 colima = img; 00093 00094 if(debugmode && !hasRun){ 00095 hasRun = true; 00096 wini.reset( new XWindow(img.getDims(), -1, -1, "input window") ); 00097 wini->setPosition(0, 0); 00098 wino.reset( new XWindow(img.getDims(), -1, -1, "output window") ); 00099 wino->setPosition(370, 0); 00100 } 00101 00102 gotLum = false; gotRGBY = false; 00103 00104 //a queue builder for the threads' job queue 00105 pthread_mutex_lock(&jobLock); 00106 jobQueue.clear(); 00107 00108 jobQueue.push_back(jobData(INTENSITY, Gaussian5, IWEIGHT, 0.0F)); 00109 00110 jobQueue.push_back(jobData(REDGREEN, Gaussian5, CWEIGHT, 0.0F)); 00111 00112 jobQueue.push_back(jobData(SKINHUE, Gaussian5, SWEIGHT, 0.0F)); 00113 00114 //jobQueue.push_back(jobData(ORI0, Oriented5, OWEIGHT, 0.0F)); 00115 //jobQueue.push_back(jobData(ORI45, Oriented5, OWEIGHT, 45.0F)); 00116 //jobQueue.push_back(jobData(ORI90, Oriented5, OWEIGHT, 90.0F)); 00117 //jobQueue.push_back(jobData(ORI135, Oriented5, OWEIGHT, 135.0F)); 00118 00119 jobQueue.push_back(jobData(FLICKER, Gaussian5, FWEIGHT, 0.0F)); 00120 00121 jobQueue.push_back(jobData(BLUEYELLOW, Gaussian5, CWEIGHT, 0.0F)); 00122 00123 totalJobs = jobQueue.size(); 00124 00125 pthread_mutex_unlock(&jobLock); 00126 00127 //broadcast on job queue condition to wake up worker threads 00128 pthread_cond_broadcast(&jobCond); 00129 00130 //Make setup() busy-wait until a bool is set by the finishing thread NOTE: may be better to run this in a thread that sleeps on a condition 00131 while(!dataReady){//NEEDS TO BE FIXED SO THAT THIS CAN BE A WAIT ON A CONDITION (MAKE THIS A THREAD)! 00132 usleep(100); 00133 } 00134 00135 //find the point of highest saliency 00136 findMax(outmap, winsm, maxval); 00137 00138 float minOut, maxOut; 00139 00140 //get values for display 00141 getMinMax(outmap, minOut, maxOut); 00142 LINFO("Min: %f Max: %f\n", minOut, maxOut); 00143 00144 // rescale winner coordinates according to PRESCALE: 00145 win.i = winsm.i << sml; 00146 win.i += int(((1<<(sml-1)) * float(rand()))/RAND_MAX); 00147 win.j = winsm.j << sml; 00148 win.j += int(((1<<(sml-1)) * float(rand()))/RAND_MAX); 00149 00150 //return position 00151 if(debugmode){ 00152 Image<float> tmp = quickInterpolate(outmap, (1<<sml)); 00153 inplaceNormalize(tmp, 0.0F, 255.0F); 00154 Image< PixRGB<byte> > temp = tmp; 00155 drawDisk(temp, win, 4, PixRGB<byte>(225, 225, 20)); 00156 drawDisk(img, win, 4, PixRGB<byte>(225, 225, 20)); 00157 wini->drawImage(img); 00158 wino->drawImage(temp); 00159 } 00160 00161 LINFO("The point of highest saliency is: %d, %d\n", win.i, win.j); 00162 outmap.clear(); 00163 return win; 00164 } 00165 00166 // ###################################################################### 00167 //The threaded function 00168 void BeoSubSaliency::computeCMAP() 00169 { 00170 00171 while(true){ 00172 jobData current(0, Gaussian5, 0.0F, 0.0F); 00173 bool jobsEmpty = true; 00174 00175 //check for another job in the queue. If there is one, process it 00176 pthread_mutex_lock(&jobLock); 00177 if(!jobQueue.empty()){ 00178 current = jobQueue.front(); 00179 jobQueue.pop_front(); 00180 jobsEmpty = false; 00181 } 00182 else{ 00183 jobsEmpty = true; 00184 } 00185 00186 if(!jobsEmpty){ 00187 //read next entry in job queue and perform desired action on current image and record result in output image (accumulative) 00188 Image<float> curImage; 00189 00190 //The case statement on this end parses the desired action from the job queue and performs the needed image pre-processing 00191 pthread_mutex_lock(&mapLock); 00192 switch(current.jobType) 00193 { 00194 //While shared resources are used here, they are only read, so they should not need to be protected by mutexers 00195 case REDGREEN: // ############################## 00196 { 00197 if (gotRGBY == false){ 00198 getRGBY(colima, r, g, b, y, byte(25)); gotRGBY = true; 00199 } 00200 curImage = r-g; 00201 } 00202 break; 00203 case BLUEYELLOW: // ############################## 00204 { 00205 if (gotRGBY == false){ 00206 getRGBY(colima, r, g, b, y, byte(25)); gotRGBY = true; 00207 } 00208 curImage = b-y; 00209 } 00210 break; 00211 case SKINHUE: // ############################## 00212 { 00213 skinima = hueDistance(colima, COL_SKIN_MUR, COL_SKIN_MUG, 00214 COL_SKIN_SIGR, COL_SKIN_SIGG, 00215 COL_SKIN_RHO); 00216 00217 curImage = skinima; 00218 break; 00219 } 00220 case ORI0: // ############################## 00221 { 00222 } 00223 case ORI45: // ############################## 00224 { 00225 } 00226 case ORI90: // ############################## 00227 { 00228 } 00229 case ORI135: // ############################## 00230 { 00231 } 00232 case FLICKER: // ############################## 00233 { 00234 if (gotLum == false){ 00235 lum = Image<float>(luminance(colima)); gotLum = true; 00236 } 00237 // compute flicker consp map and send to collector: 00238 if (!previma.initialized()){ 00239 previma = lum; 00240 curImage.resize(lum.getDims(), true); // clear 00241 } 00242 else{ 00243 curImage = lum - previma; 00244 previma = lum; 00245 } 00246 } 00247 break; 00248 case INTENSITY: // ############################# 00249 { 00250 if (gotLum == false){ 00251 lum = Image<float>(luminance(colima)); gotLum = true; 00252 } 00253 curImage = lum; 00254 } 00255 break; 00256 default: // ############################# 00257 { 00258 //should never get here 00259 LERROR("Attempt to pass an invalid jobtype DENIED"); 00260 curImage = lum; 00261 } 00262 break; 00263 //add additional cases as more channels are added 00264 00265 } 00266 pthread_mutex_unlock(&mapLock); 00267 00268 // compute pyramid: 00269 ImageSet<float> pyr = buildPyrGeneric(curImage, 0, maxdepth, 00270 current.ptyp, 00271 current.orientation); 00272 00273 // alloc conspicuity map and clear it: 00274 Image<float> cmap(pyr[sml].getDims(), ZEROS); 00275 00276 // intensities is the max-normalized weighted sum of IntensCS: 00277 for (int delta = delta_min; delta <= delta_max; delta ++) 00278 for (int lev = level_min; lev <= level_max; lev ++) 00279 { 00280 Image<float> tmp = centerSurround(pyr, lev, lev + delta, true); 00281 tmp = downSize(tmp, cmap.getWidth(), cmap.getHeight()); 00282 inplaceAddBGnoise(tmp, 255.0); 00283 tmp = maxNormalize(tmp, MAXNORMMIN, MAXNORMMAX, normtyp); 00284 cmap += tmp; 00285 } 00286 00287 inplaceAddBGnoise(cmap, 25.0F); 00288 00289 if (normtyp == VCXNORM_MAXNORM) 00290 cmap = maxNormalize(cmap, MAXNORMMIN, MAXNORMMAX, normtyp); 00291 else 00292 cmap = maxNormalize(cmap, 0.0f, 0.0f, normtyp); 00293 00294 // multiply by conspicuity coefficient: 00295 if (current.weight != 1.0F) cmap *= current.weight; 00296 00297 // inject newly received saliency map: 00298 pthread_mutex_lock(&mapLock);//lock outmap access 00299 00300 /*Uncomment if statement to use exclusivity cmap*/ 00301 00302 //if (current.jobType == FLICKER) 00303 // { 00304 00305 if (outmap.initialized()){ 00306 outmap += cmap; 00307 } 00308 else{ 00309 outmap = cmap; 00310 } 00311 00312 // } 00313 00314 jobsDone++; 00315 pthread_mutex_unlock(&mapLock);//unlock outmap access 00316 00317 } 00318 else{ 00319 //Set counter var used by run. Hacky since it needs to be changed every time a job is added. 00320 //pthread_mutex_lock(&condLock); 00321 if(jobsDone >= totalJobs){ 00322 dataReady = true; 00323 } 00324 00325 pthread_mutex_unlock(&jobLock); 00326 00327 pthread_cond_wait(&jobCond, &condLock); 00328 00329 pthread_mutex_unlock(&condLock); 00330 00331 pthread_mutex_lock(&jobLock); 00332 00333 } 00334 pthread_mutex_unlock(&jobLock); 00335 } 00336 return; 00337 } 00338 00339 // ###################################################################### 00340 /* So things look consistent in everyone's emacs... */ 00341 /* Local Variables: */ 00342 /* indent-tabs-mode: nil */ 00343 /* End: */