00001 /*!@file CMapDemo/SaliencyCMapMT.C A class for quick-and-dirty 00002 saliency mapping integrated with cmap CORBA object */ 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/CMapDemo/SaliencyCMapMT.C $ 00035 // $Id: SaliencyCMapMT.C 14376 2011-01-11 02:44:34Z pez $ 00036 // 00037 00038 #include "CMapDemo/SaliencyCMapMT.H" 00039 #include "Demo/DemoOpts.H" 00040 #include "Corba/CorbaUtil.H" 00041 #include "Image/PyrBuilder.H" 00042 #include "Image/OpenCVUtil.H" 00043 00044 #include <sys/time.h> 00045 #include <errno.h> 00046 00047 #ifdef HAVE_OPENCV_CV_H 00048 #include <opencv/cv.h> 00049 #endif 00050 00051 //are we using a local linked cmap server or a remote 00052 //set this if we are running on only one computer 00053 #ifdef LocalCMapServer 00054 //#include "Corba/Objects/CMapServer.H" 00055 #endif 00056 // ###################################################################### 00057 // ##### Global options: 00058 // ###################################################################### 00059 00060 00061 // relative feature weights: 00062 #define IWEIGHT 1.0 00063 #define CWEIGHT 1.0 00064 #define OWEIGHT 1.0 00065 #define FWEIGHT 4.0 00066 #define SWEIGHT 0.7 00067 00068 // image size vars 00069 #define IMAGEWIDTH 160 00070 #define IMAGEHEIGHT 120 00071 00072 00073 #define numthreads 1 00074 00075 // ###################################################################### 00076 void *SaliencyMT_CMAP(void *c) 00077 { 00078 SaliencyMT *d = (SaliencyMT *)c; 00079 d->computeCMAP(); 00080 return NULL; 00081 } 00082 00083 // ###################################################################### 00084 SaliencyMT::SaliencyMT(OptionManager& mgr,CORBA::ORB_ptr orb, short saliencyMapLevel, 00085 const std::string& descrName, 00086 const std::string& tagName): 00087 ModelComponent(mgr, descrName, tagName), nCmapObj(0), 00088 itsNumThreads(&OPT_SMTnumThreads, this), // see Demo/DemoOpts.{H,C} 00089 SMBias(ImageSet<float>(14)) 00090 00091 { 00092 numWorkers = 0U; 00093 00094 if (!getMultiObjectRef(orb, "saliency.CMapServers", CMap_ref, nCmapObj)){ 00095 LFATAL("Can not find any object to bind with"); 00096 } 00097 for(int i=0; i<nCmapObj; i++) 00098 CMap::_narrow(CMap_ref[i])->setSaliencyMapLevel(saliencyMapLevel); 00099 biasSM = false; 00100 } 00101 00102 void SaliencyMT::setBiasSM(bool val){ 00103 biasSM = val; 00104 } 00105 00106 // ###################################################################### 00107 void SaliencyMT::setSaliencyMapLevel(const short saliencyMapLevel){ 00108 //set the saliency map level to return 00109 for(int i=0; i<nCmapObj; i++) 00110 CMap::_narrow(CMap_ref[i])->setSaliencyMapLevel(saliencyMapLevel); 00111 } 00112 00113 00114 // ###################################################################### 00115 //!The current cmap object to send the request to 00116 CMap_ptr SaliencyMT::getCmapRef(){ 00117 static int current_obj = 0; 00118 00119 //just do a round robin 00120 current_obj = (current_obj+1)%nCmapObj; 00121 00122 LDEBUG("Using cmap object number %i\n", current_obj); 00123 00124 return CMap::_narrow(CMap_ref[current_obj]); 00125 } 00126 00127 // ###################################################################### 00128 void SaliencyMT::start1() 00129 { 00130 // start threads. They should go to sleep on the condition since no 00131 // jobs have ben queued up yet: 00132 pthread_mutex_init(&jobLock, NULL); 00133 pthread_mutex_init(&mapLock, NULL); 00134 pthread_mutex_init(&jobStatusLock, NULL); 00135 pthread_cond_init(&jobCond, NULL); 00136 pthread_cond_init(&jobDone, NULL); 00137 00138 LINFO("Starting with %u threads...", itsNumThreads.getVal()); 00139 00140 // get our processing threads started: 00141 worker = new pthread_t[itsNumThreads.getVal()]; 00142 for (uint i = 0; i < itsNumThreads.getVal(); i ++) 00143 { 00144 pthread_create(&worker[i], NULL, SaliencyMT_CMAP, (void *)this); 00145 00146 // all threads should go and lock against our job condition. Sleep a 00147 // bit to make sure this really happens: 00148 usleep(100000); 00149 } 00150 } 00151 00152 // ###################################################################### 00153 void SaliencyMT::stop2() 00154 { 00155 // should cleanup the threads, mutexes, etc... 00156 pthread_cond_destroy(&jobCond); 00157 00158 //for (uint i = 0; i < numthreads; i ++) 00159 // pthread_delete(&worker[i]..... 00160 00161 delete [] worker; 00162 } 00163 00164 // ###################################################################### 00165 SaliencyMT::~SaliencyMT(){ } 00166 00167 // ###################################################################### 00168 void SaliencyMT::newInput(Image< PixRGB<byte> > img) 00169 { 00170 //LINFO("new input....."); 00171 // store current color image: 00172 pthread_mutex_lock(&mapLock); 00173 colima = img; 00174 00175 // also kill any old output and internals: 00176 outmap.freeMem(); 00177 gotLum = false; gotRGBY = false; gotSkin = false; 00178 pthread_mutex_unlock(&mapLock); 00179 00180 // setup job queue: 00181 pthread_mutex_lock(&jobLock); 00182 jobQueue.clear(); 00183 00184 jobQueue.push_back(jobData(INTENSITY, Gaussian5, IWEIGHT, 0.0F)); 00185 00186 jobQueue.push_back(jobData(REDGREEN, Gaussian5, CWEIGHT, 0.0F)); 00187 jobQueue.push_back(jobData(BLUEYELLOW, Gaussian5, CWEIGHT, 0.0F)); 00188 00189 ////jobQueue.push_back(jobData(SKINHUE, Gaussian5, SWEIGHT, 0.0F)); 00190 00191 // jobQueue.push_back(jobData(ORI0, Oriented5, OWEIGHT, 0.0F)); 00192 // jobQueue.push_back(jobData(ORI45, Oriented5, OWEIGHT, 45.0F)); 00193 // jobQueue.push_back(jobData(ORI90, Oriented5, OWEIGHT, 90.0F)); 00194 // jobQueue.push_back(jobData(ORI135, Oriented5, OWEIGHT, 135.0F)); 00195 00196 //jobQueue.push_back(jobData(FLICKER, Gaussian5, FWEIGHT, 0.0F)); 00197 00198 jobsTodo = jobQueue.size(); 00199 pthread_mutex_unlock(&jobLock); 00200 00201 // broadcast on job queue condition to wake up worker threads: 00202 pthread_cond_broadcast(&jobCond); 00203 //LINFO("new input ok....."); 00204 } 00205 00206 // ###################################################################### 00207 bool SaliencyMT::outputReady() 00208 { 00209 bool ret = false; 00210 00211 pthread_mutex_lock(&jobLock); 00212 if (jobsTodo == 0U) ret = true; 00213 pthread_mutex_unlock(&jobLock); 00214 00215 return ret; 00216 } 00217 00218 // ###################################################################### 00219 Image<float> SaliencyMT::getOutput() 00220 { 00221 Image<float> ret; 00222 00223 pthread_mutex_lock(&mapLock); 00224 ret = outmap; 00225 pthread_mutex_unlock(&mapLock); 00226 00227 return ret; 00228 } 00229 00230 // ###################################################################### 00231 Image<float> SaliencyMT::getSMap(Image< PixRGB<byte> > img) 00232 { 00233 Image<float> ret; 00234 00235 newInput(img); 00236 00237 LINFO("Getting smap"); 00238 //wait for done signal 00239 pthread_mutex_lock(&jobStatusLock); 00240 00241 LINFO("Waiting for smap"); 00242 struct timeval abstime_tv; 00243 gettimeofday(&abstime_tv, NULL); 00244 00245 struct timespec abstime; 00246 abstime.tv_sec = abstime_tv.tv_sec; 00247 abstime.tv_sec += 3; //wait 3 seconds for condition 00248 abstime.tv_nsec = 0; 00249 00250 //pthread_cond_wait(&jobDone, &jobStatusLock); 00251 if (pthread_cond_timedwait(&jobDone, &jobStatusLock, &abstime) == ETIMEDOUT){ 00252 LINFO("TIme out"); 00253 } 00254 pthread_mutex_unlock(&jobStatusLock); 00255 00256 LINFO("Getting smap "); 00257 00258 pthread_mutex_lock(&mapLock); 00259 ret = outmap; 00260 pthread_mutex_unlock(&mapLock); 00261 00262 return ret; 00263 } 00264 00265 // ###################################################################### 00266 //Bias the main saliency map 00267 void SaliencyMT::setSMBias(ImageSet<float> &bias){ 00268 00269 for(unsigned int i=0; i<bias.size(); i++){ 00270 if (bias[i].initialized()) 00271 SMBias[i] = bias[i]; 00272 } 00273 } 00274 00275 // ###################################################################### 00276 //Bias the CMap 00277 void SaliencyMT::setBias(int type, std::vector<float> &bias) 00278 { 00279 00280 CMap::BiasSeq *curBias = NULL; 00281 switch (type) { 00282 case REDGREEN: 00283 curBias = &cmapBias.redgreen; 00284 break; 00285 case BLUEYELLOW: 00286 curBias = &cmapBias.blueyellow; 00287 break; 00288 case SKINHUE: 00289 curBias = &cmapBias.skinhue; 00290 break; 00291 case ORI0: 00292 curBias = &cmapBias.ori0; 00293 break; 00294 case ORI45: 00295 curBias = &cmapBias.ori45; 00296 break; 00297 case ORI90: 00298 curBias = &cmapBias.ori90; 00299 break; 00300 case ORI135: 00301 curBias = &cmapBias.ori135; 00302 break; 00303 case INTENSITY: 00304 curBias = &cmapBias.intensity; 00305 break; 00306 case FLICKER: 00307 curBias = &cmapBias.flicker; 00308 break; 00309 default: 00310 LINFO("Unknown type"); 00311 } 00312 00313 00314 //assign the bias 00315 if (curBias != NULL){ 00316 curBias->length(bias.size()); 00317 for(unsigned int i=0; i<bias.size(); i++) 00318 (*curBias)[i] = bias[i]; 00319 } 00320 00321 } 00322 00323 // ###################################################################### 00324 // Get the CMap 00325 void SaliencyMT::getBias(Image< PixRGB<byte> > &ima, 00326 std::vector<float> &bias, int type, Point2D<int> &loc) 00327 { 00328 PyramidType ptype = Gaussian5; 00329 float weight = 0; 00330 float ori = 0; 00331 Image<byte> curImage; 00332 00333 Image<float> local_lum; //curent luminance image 00334 Image<byte> local_r, local_g, local_b, local_y; //curent RGBY images 00335 Image<float> local_skinima; //skin hue map 00336 00337 switch (type) { 00338 case REDGREEN: 00339 ptype = Gaussian5; 00340 ori = 0.0F; 00341 getRGBY(ima, local_r, local_g, local_b, local_y, byte(25)); 00342 curImage = local_r - local_g; 00343 break; 00344 00345 // ################################################## 00346 case BLUEYELLOW: 00347 ptype = Gaussian5; 00348 ori = 0.0F; 00349 getRGBY(ima, local_r, local_g, local_b, local_y, byte(25)); 00350 curImage = local_b - local_y; 00351 break; 00352 00353 // ################################################## 00354 case SKINHUE: 00355 ptype = Gaussian5; 00356 ori = 0.0F; 00357 local_skinima = hueDistance(ima, COL_SKIN_MUR, COL_SKIN_MUG, 00358 COL_SKIN_SIGR, COL_SKIN_SIGG, 00359 COL_SKIN_RHO); 00360 curImage = local_skinima; 00361 break; 00362 00363 // ################################################## 00364 case ORI0: 00365 ptype = Oriented5; 00366 ori = 0.0F; 00367 00368 curImage = Image<byte>(luminance(ima)); 00369 break; 00370 case ORI45: 00371 ptype = Oriented5; 00372 ori = 45.0F; 00373 curImage = Image<byte>(luminance(ima)); 00374 break; 00375 case ORI90: 00376 ptype = Oriented5; 00377 ori = 90.0F; 00378 curImage = Image<byte>(luminance(ima)); 00379 break; 00380 case ORI135: 00381 ptype = Oriented5; 00382 ori = 135.0F; 00383 curImage = Image<byte>(luminance(ima)); 00384 break; 00385 case INTENSITY: 00386 ptype = Gaussian5; 00387 ori = 0.0F; 00388 curImage = Image<byte>(luminance(ima)); 00389 break; 00390 00391 // ################################################## 00392 case FLICKER: 00393 ptype = Gaussian5; 00394 ori = 0.0F; 00395 curImage = Image<byte>(luminance(ima)); 00396 break; 00397 00398 // ################################################## 00399 default: 00400 LERROR("What is going on around here?"); 00401 00402 } 00403 00404 CMap_ptr CMap = getCmapRef(); 00405 Point2DOrb locOrb; 00406 locOrb.i = loc.i; locOrb.j = loc.j; 00407 00408 ImageOrb *curImageOrb = image2Orb(curImage); 00409 CMap::BiasSeq *curBias = CMap->getBiasCMAP(*curImageOrb, ptype, ori, weight, locOrb); 00410 delete curImageOrb; 00411 00412 //assign the bias 00413 if (curBias != NULL){ 00414 curBias->length(bias.size()); 00415 for(unsigned int i=0; i<bias.size(); i++) 00416 bias[i] = (*curBias)[i]; 00417 } 00418 delete curBias; 00419 00420 } 00421 00422 // ###################################################################### 00423 // the threaded function 00424 void SaliencyMT::computeCMAP() 00425 { 00426 #ifndef HAVE_OPENCV 00427 LFATAL("OpenCV must be installed to use this function"); 00428 #else 00429 pthread_mutex_lock(&mapLock); 00430 uint myNum = numWorkers ++; 00431 pthread_mutex_unlock(&mapLock); 00432 LINFO(" ... worker %u ready.", myNum); 00433 00434 while(true) 00435 { 00436 // wait until there are jobs in the queue that we can process: 00437 pthread_mutex_lock(&jobLock); 00438 jobData current; bool nojobs = true; 00439 if (jobQueue.empty() == false) 00440 { 00441 current = jobQueue.front(); 00442 jobQueue.pop_front(); 00443 nojobs = false; 00444 } 00445 else 00446 pthread_cond_wait(&jobCond, &jobLock); 00447 pthread_mutex_unlock(&jobLock); 00448 00449 // if we don't have a job to do, just wait more: 00450 if (nojobs) continue; 00451 LDEBUG("[%u] GOT: job %d", myNum, int(current.jobType)); 00452 00453 // read next entry in job queue and perform desired action on 00454 // current image and record result in output image 00455 // (accumulative) 00456 Image<byte> curImage; 00457 00458 // The case statement on this end parses the desired action from 00459 // the job queue and performs the needed image pre-processing 00460 pthread_mutex_lock(&mapLock); 00461 00462 switch(current.jobType) 00463 { 00464 // While shared resources are used here, they are only read, 00465 // so they should not need to be protected by mutexers 00466 00467 // ################################################## 00468 case REDGREEN: 00469 if (gotRGBY == false) 00470 { getRGBY(colima, r, g, b, y, byte(25)); gotRGBY = true; } 00471 curImage = r - g; 00472 break; 00473 00474 // ################################################## 00475 case BLUEYELLOW: 00476 if (gotRGBY == false) 00477 { getRGBY(colima, r, g, b, y, byte(25)); gotRGBY = true; } 00478 curImage = b - y; 00479 break; 00480 00481 // ################################################## 00482 case SKINHUE: 00483 if (gotSkin == false) 00484 { 00485 skinima = hueDistance(colima, COL_SKIN_MUR, COL_SKIN_MUG, 00486 COL_SKIN_SIGR, COL_SKIN_SIGG, 00487 COL_SKIN_RHO); 00488 gotSkin = true; 00489 } 00490 curImage = skinima; 00491 break; 00492 00493 // ################################################## 00494 case ORI0: 00495 if (gotLum == false) 00496 { lum = Image<byte>(luminance(colima)); gotLum = true; } 00497 curImage = lum; 00498 break; 00499 case ORI45: 00500 if (gotLum == false) 00501 { lum = Image<byte>(luminance(colima)); gotLum = true; } 00502 curImage = lum; 00503 break; 00504 case ORI90: 00505 if (gotLum == false) 00506 { lum = Image<byte>(luminance(colima)); gotLum = true; } 00507 curImage = lum; 00508 break; 00509 case ORI135: 00510 if (gotLum == false) 00511 { lum = Image<byte>(luminance(colima)); gotLum = true; } 00512 curImage = lum; 00513 break; 00514 case INTENSITY: 00515 if (gotLum == false) 00516 { lum = Image<byte>(luminance(colima)); gotLum = true; } 00517 curImage = lum; 00518 break; 00519 00520 // ################################################## 00521 case FLICKER: 00522 if (gotLum == false) 00523 { lum = Image<byte>(luminance(colima)); gotLum = true; } 00524 // compute flicker consp map and send to collector: 00525 if (prev.initialized() == false) 00526 { 00527 prev = lum; 00528 curImage.resize(lum.getDims(), true); // clear 00529 } 00530 else 00531 { 00532 curImage = lum - prev; 00533 prev = lum; 00534 } 00535 break; 00536 00537 // ################################################## 00538 default: 00539 LERROR("What is going on around here?"); 00540 curImage = lum; 00541 } 00542 pthread_mutex_unlock(&mapLock); 00543 00544 /* 00545 CMap_var CMapObj = getCmapRef(); //get the object to send to 00546 00547 ImageOrb *imgOrb; 00548 ImageOrb *curImageOrb = image2Orb(curImage); 00549 00550 if (biasSM && curBias != NULL && curBias->length()){ 00551 imgOrb = CMapObj->computeBiasCMAP(*curImageOrb, 00552 current.ptyp, current.orientation, current.weight, *curBias); 00553 } else { 00554 imgOrb = CMapObj->computeCMAP(*curImageOrb, 00555 current.ptyp, current.orientation, current.weight); 00556 } 00557 delete curImageOrb; 00558 00559 Image<float> cmap; 00560 orb2Image(*imgOrb, cmap); 00561 delete imgOrb; 00562 */ 00563 00564 Image<float> cmap = curImage; 00565 00566 //Image<float> biasedCMap; 00567 Image<float> biasedCMap(cmap.getWidth()-SMBias[current.jobType].getWidth()+1, 00568 cmap.getHeight()-SMBias[current.jobType].getHeight()+1, 00569 NO_INIT); 00570 00571 if (biasSM){ 00572 if (SMBias[current.jobType].initialized()) 00573 cvMatchTemplate(img2ipl(cmap), 00574 img2ipl(SMBias[current.jobType]), 00575 img2ipl(biasedCMap), 00576 //CV_TM_CCOEFF); 00577 CV_TM_SQDIFF); 00578 00579 // biasedCMap = correlation(cmap, SMBias[current.jobType]); 00580 } 00581 00582 00583 // Add to saliency map: 00584 pthread_mutex_lock(&mapLock); 00585 cmaps[current.jobType] = cmap; //save the cmap 00586 00587 if (biasSM){ 00588 if (outmap.initialized()) outmap += biasedCMap; 00589 else outmap = biasedCMap; 00590 } else { 00591 if (outmap.initialized()) outmap += cmap; 00592 else outmap = cmap; 00593 } 00594 00595 pthread_mutex_unlock(&mapLock); 00596 00597 pthread_mutex_lock(&jobLock); 00598 -- jobsTodo; 00599 LDEBUG("done with job %d, %u todo...", int(current.jobType),jobsTodo); 00600 00601 if (jobsTodo == 0U) //Last job, let know that we are done for block calls 00602 pthread_cond_signal(&jobDone); 00603 00604 00605 pthread_mutex_unlock(&jobLock); 00606 } 00607 #endif 00608 } 00609 00610 // ###################################################################### 00611 /* So things look consistent in everyone's emacs... */ 00612 /* Local Variables: */ 00613 /* indent-tabs-mode: nil */ 00614 /* End: */