BeoSubSaliency.C

Go to the documentation of this file.
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: */
Generated on Sun May 8 08:04:32 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3