00001 /*!@file Demo/SaliencyMT.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/Demo/SaliencyMT.C $ 00035 // $Id: SaliencyMT.C 8095 2007-03-12 01:14:23Z lior $ 00036 // 00037 00038 #include "Demo/SaliencyMT.H" 00039 #include "Demo/DemoOpts.H" 00040 00041 // ###################################################################### 00042 // ##### Global options: 00043 // ###################################################################### 00044 00045 #define sml 2 00046 #define delta_min 3 00047 #define delta_max 4 00048 #define level_min 0 00049 #define level_max 2 00050 #define maxdepth (level_max + delta_max + 1) 00051 #define normtyp (VCXNORM_MAXNORM) 00052 00053 // relative feature weights: 00054 #define IWEIGHT 0.7 00055 #define CWEIGHT 1.0 00056 #define OWEIGHT 1.0 00057 #define FWEIGHT 1.0 00058 #define SWEIGHT 0.7 00059 00060 // image size vars 00061 #define IMAGEWIDTH 160 00062 #define IMAGEHEIGHT 120 00063 00064 // action definitions CHANGE TO ENUM? FIX? 00065 #define RETINA 1 00066 #define WINNER 2 00067 #define LUMINANCE 3 00068 #define REDGREEN 4 00069 #define BLUEYELLOW 5 00070 #define ORI0 6 00071 #define ORI45 7 00072 #define ORI90 8 00073 #define ORI135 9 00074 #define CMAP 10 00075 #define FLICKER 11 00076 #define INTENSITY 12 00077 #define SKINHUE 13 00078 00079 #define numthreads 1 00080 00081 // ###################################################################### 00082 void *SaliencyMT_CMAP(void *c) 00083 { 00084 SaliencyMT *d = (SaliencyMT *)c; 00085 d->computeCMAP(); 00086 return NULL; 00087 } 00088 00089 // ###################################################################### 00090 SaliencyMT::SaliencyMT(OptionManager& mgr, 00091 const std::string& descrName, 00092 const std::string& tagName): 00093 ModelComponent(mgr, descrName, tagName), 00094 itsNumThreads(&OPT_SMTnumThreads, this) // see Demo/DemoOpts.{H,C} 00095 00096 { 00097 00098 numWorkers = 0U; 00099 00100 } 00101 00102 // ###################################################################### 00103 void SaliencyMT::start1() 00104 { 00105 // start threads. They should go to sleep on the condition since no 00106 // jobs have ben queued up yet: 00107 pthread_mutex_init(&jobLock, NULL); 00108 pthread_mutex_init(&mapLock, NULL); 00109 pthread_cond_init(&jobCond, NULL); 00110 00111 LINFO("Starting with %u threads...", itsNumThreads.getVal()); 00112 00113 // get our processing threads started: 00114 worker = new pthread_t[itsNumThreads.getVal()]; 00115 for (uint i = 0; i < itsNumThreads.getVal(); i ++) 00116 { 00117 pthread_create(&worker[i], NULL, SaliencyMT_CMAP, (void *)this); 00118 00119 // all threads should go and lock against our job condition. Sleep a 00120 // bit to make sure this really happens: 00121 usleep(100000); 00122 } 00123 } 00124 00125 // ###################################################################### 00126 void SaliencyMT::stop2() 00127 { 00128 // should cleanup the threads, mutexes, etc... 00129 pthread_cond_destroy(&jobCond); 00130 00131 //for (uint i = 0; i < numthreads; i ++) 00132 // pthread_delete(&worker[i]..... 00133 00134 delete [] worker; 00135 } 00136 00137 // ###################################################################### 00138 SaliencyMT::~SaliencyMT() 00139 { } 00140 00141 // ###################################################################### 00142 void SaliencyMT::newInput(Image< PixRGB<byte> > img, bool procFlicker) 00143 { 00144 //LINFO("new input....."); 00145 // store current color image: 00146 pthread_mutex_lock(&mapLock); 00147 colima = img; 00148 00149 // also kill any old output and internals: 00150 outmap.freeMem(); 00151 gotLum = false; gotRGBY = false; gotSkin = false; 00152 pthread_mutex_unlock(&mapLock); 00153 00154 // setup job queue: 00155 pthread_mutex_lock(&jobLock); 00156 jobQueue.clear(); 00157 00158 jobQueue.push_back(jobData(INTENSITY, Gaussian5, IWEIGHT, 0.0F)); 00159 00160 jobQueue.push_back(jobData(REDGREEN, Gaussian5, CWEIGHT, 0.0F)); 00161 00162 // jobQueue.push_back(jobData(SKINHUE, Gaussian5, SWEIGHT, 0.0F)); 00163 00164 jobQueue.push_back(jobData(ORI0, Oriented5, OWEIGHT, 0.0F)); 00165 jobQueue.push_back(jobData(ORI45, Oriented5, OWEIGHT, 45.0F)); 00166 jobQueue.push_back(jobData(ORI90, Oriented5, OWEIGHT, 90.0F)); 00167 jobQueue.push_back(jobData(ORI135, Oriented5, OWEIGHT, 135.0F)); 00168 00169 if (procFlicker) 00170 jobQueue.push_back(jobData(FLICKER, Gaussian5, FWEIGHT, 0.0F)); 00171 00172 jobQueue.push_back(jobData(BLUEYELLOW, Gaussian5, CWEIGHT, 0.0F)); 00173 00174 jobsTodo = jobQueue.size(); 00175 pthread_mutex_unlock(&jobLock); 00176 00177 // broadcast on job queue condition to wake up worker threads: 00178 pthread_cond_broadcast(&jobCond); 00179 //LINFO("new input ok....."); 00180 } 00181 00182 // ###################################################################### 00183 bool SaliencyMT::outputReady() 00184 { 00185 bool ret = false; 00186 00187 pthread_mutex_lock(&jobLock); 00188 if (jobsTodo == 0U) ret = true; 00189 pthread_mutex_unlock(&jobLock); 00190 00191 return ret; 00192 } 00193 00194 // ###################################################################### 00195 Image<float> SaliencyMT::getOutput() 00196 { 00197 Image<float> ret; 00198 00199 pthread_mutex_lock(&mapLock); 00200 ret = outmap; 00201 pthread_mutex_unlock(&mapLock); 00202 00203 return ret; 00204 } 00205 00206 // ###################################################################### 00207 //The threaded function 00208 void SaliencyMT::computeCMAP() 00209 { 00210 pthread_mutex_lock(&mapLock); 00211 uint myNum = numWorkers ++; 00212 pthread_mutex_unlock(&mapLock); 00213 LINFO(" ... worker %u ready.", myNum); 00214 00215 while(true) 00216 { 00217 // wait until there are jobs in the queue that we can process: 00218 pthread_mutex_lock(&jobLock); 00219 jobData current(0, Gaussian5, 0.0F, 0.0F); bool nojobs = true; 00220 if (jobQueue.empty() == false) 00221 { 00222 current = jobQueue.front(); 00223 jobQueue.pop_front(); 00224 nojobs = false; 00225 } 00226 else 00227 pthread_cond_wait(&jobCond, &jobLock); 00228 pthread_mutex_unlock(&jobLock); 00229 00230 // if we don't have a job to do, just wait more: 00231 if (nojobs) continue; 00232 //LINFO("[%u] GOT: job %d", myNum, int(current.jobType)); 00233 00234 // read next entry in job queue and perform desired action on 00235 // current image and record result in output image 00236 // (accumulative) 00237 Image<float> curImage; 00238 00239 // The case statement on this end parses the desired action from 00240 // the job queue and performs the needed image pre-processing 00241 pthread_mutex_lock(&mapLock); 00242 switch(current.jobType) 00243 { 00244 // While shared resources are used here, they are only read, 00245 // so they should not need to be protected by mutexers 00246 00247 // ################################################## 00248 case REDGREEN: 00249 if (gotRGBY == false) 00250 { getRGBY(colima, r, g, b, y, byte(25)); gotRGBY = true; } 00251 curImage = r - g; 00252 break; 00253 00254 // ################################################## 00255 case BLUEYELLOW: 00256 if (gotRGBY == false) 00257 { getRGBY(colima, r, g, b, y, byte(25)); gotRGBY = true; } 00258 curImage = b - y; 00259 break; 00260 00261 // ################################################## 00262 case SKINHUE: 00263 if (gotSkin == false) 00264 { 00265 skinima = hueDistance(colima, COL_SKIN_MUR, COL_SKIN_MUG, 00266 COL_SKIN_SIGR, COL_SKIN_SIGG, 00267 COL_SKIN_RHO); 00268 gotSkin = true; 00269 } 00270 curImage = skinima; 00271 break; 00272 00273 // ################################################## 00274 case ORI0: 00275 case ORI45: 00276 case ORI90: 00277 case ORI135: 00278 case INTENSITY: 00279 if (gotLum == false) 00280 { lum = Image<float>(luminance(colima)); gotLum = true; } 00281 curImage = lum; 00282 break; 00283 00284 // ################################################## 00285 case FLICKER: 00286 if (gotLum == false) 00287 { lum = Image<float>(luminance(colima)); gotLum = true; } 00288 // compute flicker consp map and send to collector: 00289 if (prev.initialized() == false) 00290 { 00291 prev = lum; 00292 curImage.resize(lum.getDims(), true); // clear 00293 } 00294 else 00295 { 00296 curImage = lum - prev; 00297 prev = lum; 00298 } 00299 break; 00300 00301 // ################################################## 00302 default: 00303 LERROR("What is going on around here?"); 00304 curImage = lum; 00305 } 00306 pthread_mutex_unlock(&mapLock); 00307 00308 // compute pyramid: 00309 ImageSet<float> pyr = 00310 buildPyrGeneric(curImage, 0, maxdepth, 00311 current.ptyp, current.orientation); 00312 00313 // alloc conspicuity map and clear it: 00314 Image<float> cmap(pyr[sml].getDims(), ZEROS); 00315 00316 // intensities is the max-normalized weighted sum of IntensCS: 00317 for (int delta = delta_min; delta <= delta_max; delta ++) 00318 for (int lev = level_min; lev <= level_max; lev ++) 00319 { 00320 Image<float> tmp = centerSurround(pyr, lev, lev + delta, true); 00321 tmp = downSize(tmp, cmap.getWidth(), cmap.getHeight()); 00322 tmp = maxNormalize(tmp, MAXNORMMIN, MAXNORMMAX, normtyp); 00323 cmap += tmp; 00324 } 00325 00326 inplaceAddBGnoise(cmap, 25.0F); 00327 00328 if (normtyp == VCXNORM_MAXNORM) 00329 cmap = maxNormalize(cmap, MAXNORMMIN, MAXNORMMAX, normtyp); 00330 else 00331 cmap = maxNormalize(cmap, 0.0f, 0.0f, normtyp); 00332 00333 // multiply by conspicuity coefficient: 00334 if (current.weight != 1.0F) cmap *= current.weight; 00335 00336 // Add to saliency map: 00337 pthread_mutex_lock(&mapLock); 00338 if (outmap.initialized()) outmap += cmap; 00339 else outmap = cmap; 00340 pthread_mutex_unlock(&mapLock); 00341 00342 pthread_mutex_lock(&jobLock); 00343 -- jobsTodo; 00344 //LINFO("done with job %d, %u todo...", int(current.jobType),jobsTodo); 00345 pthread_mutex_unlock(&jobLock); 00346 } 00347 } 00348 00349 // ###################################################################### 00350 /* So things look consistent in everyone's emacs... */ 00351 /* Local Variables: */ 00352 /* indent-tabs-mode: nil */ 00353 /* End: */