00001 /*!@file Beobot/BeobotBrainMT.C efficient implementation of the 00002 feature pyramids, Saliency, Gist, Shape Estimator for Beobot */ 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: Christian Siagian <siagian@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Beobot/BeobotBrainMT.C $ 00035 // $Id: BeobotBrainMT.C 12127 2009-12-06 03:05:23Z siagian $ 00036 // 00037 00038 // ###################################################################### 00039 00040 #include "Beobot/BeobotBrainMT.H" 00041 #include "Neuro/gistParams.H" 00042 #include "Neuro/GistEstimatorStd.H" 00043 00044 #include "Image/MathOps.H" 00045 #include "Image/DrawOps.H" 00046 #include "Image/ShapeOps.H" 00047 #include "Image/Transforms.H" 00048 #include "Image/fancynorm.H" 00049 #include "Image/FilterOps.H" 00050 #include "Image/PyrBuilder.H" 00051 #include "Image/ColorOps.H" 00052 #include "Util/Timer.H" 00053 #include "Image/CutPaste.H" // for inplacePaste() 00054 #include "Image/MorphOps.H" // for openImg() 00055 00056 // ###################################################################### 00057 void *BeobotBrainMT_threadCompute(void *c) 00058 { 00059 BeobotBrainMT *d = (BeobotBrainMT *)c; 00060 d->threadCompute(); 00061 return NULL; 00062 } 00063 00064 // ###################################################################### 00065 BeobotBrainMT::BeobotBrainMT(OptionManager& mgr, 00066 const std::string& descrName, 00067 const std::string& tagName) 00068 : 00069 ModelComponent(mgr, descrName, tagName) 00070 { 00071 numWorkers = 0U; 00072 00073 // the number of channels we are going to compute 00074 itsNumChannels = NUM_CHANNELS; 00075 00076 // initialize the channel weights 00077 itsChanWeight.resize(itsNumChannels); 00078 itsChanWeight[REDGREEN ] = CWEIGHT; 00079 itsChanWeight[BLUEYELLOW] = CWEIGHT; 00080 itsChanWeight[INTENSITY ] = IWEIGHT; 00081 itsChanWeight[ORI0 ] = OWEIGHT; 00082 itsChanWeight[ORI45 ] = OWEIGHT; 00083 itsChanWeight[ORI90 ] = OWEIGHT; 00084 itsChanWeight[ORI135 ] = OWEIGHT; 00085 00086 // conspicuity maps 00087 itsCMaps.resize(itsNumChannels); 00088 00089 // initialize CSmaps, raw CS maps 00090 uint cscount = 00091 (level_max - level_min + 1) * (delta_max - delta_min + 1); 00092 itsCSMaps.resize(itsNumChannels); 00093 itsRawCSMaps.resize(itsNumChannels); 00094 for(uint i = 0; i < itsNumChannels; i++) 00095 { 00096 itsCSMaps[i].resize(cscount); 00097 itsRawCSMaps[i].resize(cscount); 00098 } 00099 00100 // feature maps storage 00101 itsImgPyrs.resize(itsNumChannels); 00102 00103 //create the structuring element for Chamfer smoothing 00104 const int ss = 8; 00105 structEl = Image<byte>(ss+ss,ss+ss,ZEROS); 00106 drawDisk(structEl, Point2D<int>(ss,ss),ss,(byte)1); 00107 00108 // initializing gist vector 00109 itsGistFeatSize = NUM_GIST_FEAT_SIZE; 00110 itsGistVector = Image<double>(1, itsGistFeatSize, NO_INIT); 00111 LINFO("initialized"); 00112 00113 // window to debug: default 320 x 240 00114 //itsWin = 00115 // new XWinManaged(Dims(2*IMAGE_WIDTH,2*IMAGE_HEIGHT), 0, 0, "bbmtWin"); 00116 00117 itsTimer.reset(new Timer(1000000)); 00118 itsProcessTime = -1.0F; 00119 } 00120 00121 // ###################################################################### 00122 void BeobotBrainMT::start1() 00123 { 00124 // start threads. They should go to sleep on the condition since no 00125 // jobs have ben queued up yet: 00126 pthread_mutex_init(&jobLock, NULL); 00127 pthread_mutex_init(&mapLock, NULL); 00128 pthread_cond_init(&jobCond, NULL); 00129 00130 LINFO("Starting with %d threads...", numBBMTthreads); 00131 00132 // get our processing threads started: 00133 worker = new pthread_t[numBBMTthreads]; 00134 for (uint i = 0; i < numBBMTthreads; i ++) 00135 { 00136 pthread_create(&worker[i], NULL, BeobotBrainMT_threadCompute, 00137 (void *)this); 00138 00139 // all threads should go and lock against our job condition. 00140 // Sleep a bit to make sure this really happens: 00141 usleep(100000); 00142 } 00143 } 00144 00145 // ###################################################################### 00146 void BeobotBrainMT::stop2() 00147 { 00148 // should cleanup the threads, mutexes, etc... 00149 pthread_cond_destroy(&jobCond); 00150 00151 //for (uint i = 0; i < numBBMTthreads; i ++) 00152 // pthread_delete(&worker[i]..... 00153 00154 delete [] worker; 00155 } 00156 00157 // ###################################################################### 00158 BeobotBrainMT::~BeobotBrainMT() 00159 { } 00160 00161 // ###################################################################### 00162 bool BeobotBrainMT::outputReady() 00163 { 00164 bool ret = false; 00165 00166 pthread_mutex_lock(&jobLock); 00167 if (jobsTodo == 0U) ret = true; 00168 //LINFO("jobs left: %d", jobsTodo); 00169 00170 // for the initial condition 00171 pthread_mutex_lock(&mapLock); 00172 if(!itsCurrImg.initialized()) ret = false; 00173 pthread_mutex_unlock(&mapLock); 00174 00175 pthread_mutex_unlock(&jobLock); 00176 00177 return ret; 00178 } 00179 00180 // ###################################################################### 00181 void BeobotBrainMT::input(Image< PixRGB<byte> > img) 00182 { 00183 itsTimer->reset(); 00184 computeCIOpyr(img); 00185 } 00186 00187 // ###################################################################### 00188 void BeobotBrainMT::computeCIOpyr(Image< PixRGB<byte> > img) 00189 { 00190 pthread_mutex_lock(&mapLock); 00191 00192 //LINFO("new input....."); 00193 // store current color image: 00194 itsCurrImg = img; 00195 itsCurrImgWidth = img.getWidth(); 00196 itsCurrImgHeight = img.getHeight(); 00197 //itsWin->setDims(Dims(2*itsCurrImgWidth, 2*itsCurrImgHeight)); 00198 00199 // also kill any old output and internals: 00200 itsSalmap.freeMem(); 00201 gotLum = false; gotRGBY = false; 00202 pthread_mutex_unlock(&mapLock); 00203 00204 // setup job queue: 00205 pthread_mutex_lock(&jobLock); 00206 jobQueue.clear(); 00207 00208 // color channel 00209 jobQueue.push_back(jobData(REDGREEN, Gaussian5, CWEIGHT, 0.0F)); 00210 jobQueue.push_back(jobData(BLUEYELLOW, Gaussian5, CWEIGHT, 0.0F)); 00211 00212 // intensity channel 00213 jobQueue.push_back(jobData(INTENSITY, Gaussian5, IWEIGHT, 0.0F)); 00214 00215 // orientation channel 00216 jobQueue.push_back(jobData(ORI0, Oriented5, OWEIGHT, 0.0F)); 00217 jobQueue.push_back(jobData(ORI45, Oriented5, OWEIGHT, 45.0F)); 00218 jobQueue.push_back(jobData(ORI90, Oriented5, OWEIGHT, 90.0F)); 00219 jobQueue.push_back(jobData(ORI135, Oriented5, OWEIGHT, 135.0F)); 00220 00221 jobsTodo = jobQueue.size(); 00222 pthread_mutex_unlock(&jobLock); 00223 00224 // broadcast on job queue condition to wake up worker threads: 00225 pthread_cond_broadcast(&jobCond); 00226 //LINFO("new input ok....."); 00227 } 00228 00229 // ###################################################################### 00230 Image<float> BeobotBrainMT::getSalMap() 00231 { 00232 Image<float> ret; 00233 00234 pthread_mutex_lock(&mapLock); 00235 ret = itsSalmap; 00236 pthread_mutex_unlock(&mapLock); 00237 00238 return ret; 00239 } 00240 00241 // ###################################################################### 00242 Image<double> BeobotBrainMT::getGist() 00243 { 00244 Image<double> ret; 00245 pthread_mutex_lock(&gistLock); 00246 ret = itsGistVector; 00247 pthread_mutex_unlock(&gistLock); 00248 00249 return ret; 00250 } 00251 00252 // ###################################################################### 00253 // The threaded function 00254 void BeobotBrainMT::threadCompute() 00255 { 00256 pthread_mutex_lock(&mapLock); 00257 uint myNum = numWorkers ++; 00258 pthread_mutex_unlock(&mapLock); 00259 LINFO(" ... worker %u ready.", myNum); 00260 00261 while(true) 00262 { 00263 // wait until there are jobs in the queue that we can process: 00264 pthread_mutex_lock(&jobLock); 00265 jobData current(0, Gaussian5, 0.0F, 0.0F); bool nojobs = true; 00266 if (jobQueue.empty() == false) 00267 { 00268 current = jobQueue.front(); 00269 jobQueue.pop_front(); 00270 nojobs = false; 00271 } 00272 else 00273 pthread_cond_wait(&jobCond, &jobLock); 00274 pthread_mutex_unlock(&jobLock); 00275 00276 // if we don't have a job to do, just wait more: 00277 if (nojobs) continue; 00278 //LINFO("[%u] GOT: job type %d", myNum, int(current.jobType)); 00279 00280 // read next entry in job queue and perform desired action on 00281 // current image and record result in output image 00282 // (accumulative) 00283 Image<float> curImage; 00284 00285 // The case statement on this end parses the desired action from 00286 // the job queue and performs the needed image pre-processing 00287 pthread_mutex_lock(&mapLock); 00288 switch(current.jobType) 00289 { 00290 // While shared resources are used here, they are only read, 00291 // so they should not need to be protected by mutexers 00292 00293 // ################################################## 00294 case REDGREEN: 00295 if (gotRGBY == false) 00296 { 00297 getRGBY(itsCurrImg, rgimg, byimg, 25.5f); 00298 gotRGBY = true; 00299 } 00300 curImage = rgimg; 00301 break; 00302 00303 // ################################################## 00304 case BLUEYELLOW: 00305 if (gotRGBY == false) 00306 { 00307 getRGBY(itsCurrImg, rgimg, byimg, 25.5f); 00308 gotRGBY = true; 00309 } 00310 curImage = byimg; 00311 break; 00312 00313 // ################################################## 00314 case ORI0: 00315 case ORI45: 00316 case ORI90: 00317 case ORI135: 00318 case INTENSITY: 00319 if (gotLum == false) 00320 { 00321 itsCurrLumImg = 00322 Image<float>(luminance(Image<PixRGB<float> >(itsCurrImg))); 00323 gotLum = true; 00324 } 00325 curImage = itsCurrLumImg; 00326 break; 00327 00328 // ################################################## 00329 default: 00330 LERROR("invalid job type"); 00331 curImage = itsCurrLumImg; 00332 } 00333 pthread_mutex_unlock(&mapLock); 00334 00335 ImageSet<float> pyr; 00336 00337 // alloc conspicuity map and clear it: 00338 int cW = itsCurrImgWidth /(int)(pow(2,sml)); 00339 int cH = itsCurrImgHeight/(int)(pow(2,sml)); 00340 Image<float> cmap(cW,cH, ZEROS); 00341 00342 // get the proper conspicuity map and get its corresponding gist vector 00343 // The case statement on this end parses the desired action from 00344 // the job queue and performs the needed image pre-processing 00345 int count = 0; uint cscount = 0; 00346 int s_index = 0; int offset = 0; 00347 switch(current.jobType) 00348 { 00349 // ################################################## 00350 case INTENSITY: s_index += 6; 00351 case BLUEYELLOW: s_index += 6; 00352 case REDGREEN: s_index += 16; 00353 00354 // compute & store oriented pyramid: 00355 pyr = buildPyrGaussian(curImage, 0, maxdepth, 5); 00356 itsImgPyrs[current.jobType] = pyr; 00357 00358 // intensities is the max-normalized weighted sum of IntensCS: 00359 // we also add the gist extraction line 00360 for (int lev = level_min; lev <= level_max; lev ++) 00361 for (int delta = delta_min; delta <= delta_max; delta ++) 00362 { 00363 Image<float> tmp = centerSurround(pyr, lev, lev + delta, true); 00364 00365 // extract and store the gist 00366 LDEBUG("%d_%d_%d",current.jobType,lev,lev+delta); 00367 pthread_mutex_lock(&gistLock); 00368 inplacePaste(itsGistVector, getSubSum(tmp), 00369 Point2D<int>(0, (s_index + count)*NUM_GIST_FEAT)); 00370 pthread_mutex_unlock(&gistLock); 00371 count++; 00372 00373 // LINFO("tmp(%d,%d) : cmap(%d,%d)", 00374 // tmp.getWidth(), tmp.getHeight(), 00375 // cmap.getWidth(), cmap.getHeight()); 00376 // accumulate the CMaps 00377 tmp = rescale(tmp, cmap.getWidth(), cmap.getHeight()); 00378 //tmp = downSize(tmp, cmap.getWidth(), cmap.getHeight()); 00379 float minv, maxv; getMinMax(tmp, minv, maxv); 00380 itsRawCSMaps[current.jobType][cscount] = tmp; 00381 tmp = maxNormalize(tmp, MAXNORMMIN, MAXNORMMAX, normtyp); 00382 cmap += tmp; 00383 itsCSMaps[current.jobType][cscount] = tmp; cscount++; 00384 } 00385 // store the center surround maps 00386 break; 00387 00388 // ################################################## 00389 00390 // ################################################## 00391 case ORI135: offset += 1; 00392 case ORI90: offset += 1; 00393 case ORI45: offset += 1; 00394 case ORI0: 00395 00396 // compute & store oriented pyramid: 00397 pyr = buildPyrOriented(curImage, 0, maxdepth, 9, 00398 offset*45.0f, 10.0f); 00399 itsImgPyrs[current.jobType] = pyr; 00400 00401 // extract the gist for the orientation channel 00402 for(int i = 0; i < NUM_GIST_LEV; i++) 00403 { 00404 LDEBUG("or_%f_%d",offset*45.0f,i); 00405 pthread_mutex_lock(&gistLock); 00406 inplacePaste 00407 (itsGistVector, getSubSum(pyr[i]), 00408 Point2D<int>(0, (i * NUM_GIST_LEV + offset) * NUM_GIST_FEAT)); 00409 pthread_mutex_unlock(&gistLock); 00410 } 00411 00412 // intensities is the max-normalized weighted sum of IntensCS: 00413 for (int lev = level_min; lev <= level_max; lev ++) 00414 for (int delta = delta_min; delta <= delta_max; delta ++) 00415 { 00416 Image<float> tmp = centerSurround(pyr, lev, lev + delta, true); 00417 //tmp = downSize(tmp, cmap.getWidth(), cmap.getHeight()); 00418 tmp = rescale(tmp, cmap.getWidth(), cmap.getHeight()); 00419 itsRawCSMaps[current.jobType][cscount] = tmp; 00420 tmp = maxNormalize(tmp, MAXNORMMIN, MAXNORMMAX, normtyp); 00421 cmap += tmp; 00422 00423 itsCSMaps[current.jobType][cscount] = tmp; cscount++; 00424 } 00425 // store the center surround maps 00426 00427 break; 00428 // ################################################## 00429 default: 00430 LERROR("invalid job type"); 00431 } 00432 00433 // store the Conspicuity maps 00434 itsCMaps[current.jobType] = cmap; 00435 00436 // add noise to the saliency map 00437 inplaceAddBGnoise(cmap, 25.0F); 00438 00439 if (normtyp == VCXNORM_MAXNORM) 00440 cmap = maxNormalize(cmap, MAXNORMMIN, MAXNORMMAX, normtyp); 00441 else 00442 cmap = maxNormalize(cmap, 0.0f, 0.0f, normtyp); 00443 00444 // multiply by conspicuity coefficient: 00445 if (current.weight != 1.0F) cmap *= current.weight; 00446 00447 // Add to saliency map: 00448 pthread_mutex_lock(&mapLock); 00449 if (itsSalmap.initialized()) itsSalmap += cmap; 00450 else itsSalmap = cmap; 00451 pthread_mutex_unlock(&mapLock); 00452 00453 pthread_mutex_lock(&jobLock); 00454 jobsTodo--; 00455 00456 //LINFO("done with job type %d, %u todo...", 00457 // int(current.jobType),jobsTodo); 00458 if(jobsTodo == 0) 00459 { 00460 // get the objects found 00461 findObjects(); 00462 00463 // compute the salient features of the objects 00464 computeSalientFeatures(); 00465 00466 itsProcessTime = itsTimer->get()/1000.0F; 00467 } 00468 pthread_mutex_unlock(&jobLock); 00469 } 00470 } 00471 00472 // ###################################################################### 00473 void BeobotBrainMT::findObjects() 00474 { 00475 // reset everything 00476 itsWinner.clear(); 00477 itsObjRect.clear(); 00478 itsWinningChan.clear(); 00479 itsWinningSubmapNum.clear(); 00480 itsWinningMap.clear(); 00481 itsObjectMask.clear(); 00482 00483 int scale = (int)(pow(2,sml)); 00484 Image<float> currSalMap = itsSalmap; 00485 Image<byte> accMask(currSalMap.getDims(),ZEROS); 00486 int area = 0; 00487 00488 // find most salient location 00489 float maxval; Point2D<int> currwin; findMax(currSalMap, currwin, maxval); 00490 float cmval = maxval; 00491 int maxArea = currSalMap.getSize(); 00492 LDEBUG("max val: %f", maxval); 00493 // criteria of keep searching for objects 00494 // space to segment < 50% 00495 // number of objects < 5 00496 // strength of value is higher than 5% 00497 uint i = 0; 00498 while((.05 *maxval < cmval) && (i < 5) && (float(area)/maxArea < .5)) 00499 { 00500 // get the next object 00501 Point2D<int> cwins = currwin*scale; 00502 itsWinner.push_back(cwins); 00503 setWinningMap(cwins); 00504 Image<float> cobjmask = getObjectMask(); 00505 itsObjectMask.push_back(cobjmask); 00506 Rectangle crect = getSEBoundingBox(); 00507 itsObjRect.push_back(crect); 00508 00509 // suppress the previous salient region 00510 Image<float> temp(cobjmask); 00511 temp = convGauss<float>(temp,4,4,2); 00512 inplaceNormalize(temp, 0.0F,3.0F); 00513 inplaceClamp(temp, 0.0F, 1.0F); 00514 Image<float> supMask(temp); 00515 00516 //Image<float> supMask(cobjmask); 00517 inplaceNormalize(supMask, 0.0F, 1.0F); 00518 supMask = (supMask * -1.0F) + 1.0F; 00519 currSalMap *= supMask; 00520 00521 // get the next salient location 00522 findMax(currSalMap, currwin, cmval); 00523 00524 // get amount of space segmented out 00525 accMask += cobjmask; 00526 area = int(sum(cobjmask)/255.0); 00527 00528 //int w = itsCMaps[0].getWidth()*scale; 00529 //int h = itsCMaps[0].getHeight()*scale; 00530 //Image<float> disp(w*2, h*2, ZEROS); 00531 //Image<float> damask(accMask); inplaceNormalize(damask, 0.0F, 255.0F); 00532 //Image<float> dsupmask(supMask); inplaceNormalize(dsupmask, 0.0F, 255.0F); 00533 //Image<float> dcsm(currSalMap); inplaceNormalize(dcsm, 0.0F, 255.0F); 00534 //Image<float> dsm(itsSalmap); inplaceNormalize(dsm, 0.0F, 255.0F); 00535 LDEBUG("accMask: %d cmval: %f", area, cmval); 00536 //inplacePaste(disp, zoomXY(damask, scale), Point2D<int>(0, 0)); 00537 //inplacePaste(disp, zoomXY(dsupmask, scale), Point2D<int>(w, 0)); 00538 //inplacePaste(disp, zoomXY(dcsm, scale), Point2D<int>(0, h)); 00539 //inplacePaste(disp, zoomXY(dsm, scale), Point2D<int>(w, h)); 00540 //itsWin->drawImage(disp,0,0); 00541 00542 // check for rectangle overlap 00543 float maxovl = 0.0; 00544 for(uint j = 0; j < itsObjRect.size()-1; j++) 00545 { 00546 Rectangle covl = crect.getOverlap(itsObjRect[j]); 00547 float pcnt = 0.0; 00548 if(covl.isValid()) 00549 { 00550 pcnt = (float(covl.area())/crect.area() + 00551 float(covl.area())/itsObjRect[j].area())/2.0; 00552 if(maxovl < pcnt) maxovl = pcnt; 00553 //LINFO("area: %d pcnt[%d]: %f", covl.area(), j, pcnt); 00554 } 00555 //else LINFO("area: 0 pcnt[%d]: 0.0",j); 00556 } 00557 00558 // too much overlap 00559 if(maxovl > .66) 00560 { 00561 // pop back the last inserted object 00562 itsWinner.pop_back(); 00563 itsObjRect.pop_back(); 00564 itsWinningChan.pop_back(); 00565 itsWinningSubmapNum.pop_back(); 00566 itsWinningMap.pop_back(); 00567 itsObjectMask.pop_back(); 00568 LDEBUG("object overlap with previous ones > .66"); 00569 } 00570 else 00571 { 00572 LDEBUG("sal pt %u done",i); i++; 00573 } 00574 //Raster::waitForKey(); 00575 } 00576 } 00577 00578 // ###################################################################### 00579 bool BeobotBrainMT::setWinningMap(Point2D<int> winner) 00580 { 00581 Point2D<int> scwin = downScaleCoords(winner, itsCMaps[0].getDims()); 00582 00583 // loop over the channels 00584 uint wchan; float mx = -1.0F; 00585 for (uint i = 0; i < itsNumChannels; ++i) 00586 { 00587 Image<float> output = itsCMaps[i] * itsChanWeight[i]; 00588 float curr_val = output.getVal(scwin); 00589 if (curr_val >= mx){mx = curr_val; wchan = i;} 00590 } 00591 //if (mx <= 0.0F) return false; 00592 00593 // loop over the csmaps 00594 uint wschan; mx = -1.0F; 00595 for (uint j = 0; j < itsCSMaps[wchan].size(); j++) 00596 { 00597 Image<float> csmap = itsCSMaps[wchan][j]; 00598 float curr_val = csmap.getVal(scwin); 00599 if (curr_val >= mx) { mx = curr_val; wschan = j; } 00600 00601 //imean = float(mean(csmap)); istdev = float(stdev(csmap)); 00602 //LINFO(" csmap[%d][%d]: val: %f [%f,%f] -> %f Min: %f, Max: %f", 00603 // i,j, curr_val, imean, istdev, 00604 // (curr_val - imean)/istdev, min, max); 00605 } 00606 00607 // // loop over the submaps of the winning channel 00608 // mx = -1.0F; 00609 // for (uint i = 0; i < itsImgPyrs[wchan].size(); ++i) 00610 // { 00611 // Image<float> submap = (itsImgPyrs[wchan])[i]; 00612 // float curr_val = 00613 // submap.getVal(downScaleCoords(winner,submap.getDims())); 00614 // LINFO("submap = %i -> val = %f", i, curr_val); 00615 // if (curr_val >= mx) { mx = curr_val; wschan = i; } 00616 // } 00617 // if (mx <= 0.0F) return false; 00618 00619 // store the results 00620 itsWinningChan.push_back(wchan); 00621 itsWinningSubmapNum.push_back(wschan); 00622 itsWinningMap.push_back(itsCSMaps[wchan][wschan]); 00623 00624 LDEBUG("[%d,%d] winner: chan(%d), sub-chan(%d)", 00625 winner.i, winner.j, wchan, wschan); 00626 return true; 00627 } 00628 00629 // ###################################################################### 00630 Point2D<int> BeobotBrainMT::downScaleCoords(Point2D<int> winner, Dims targetDims) 00631 { 00632 00633 int i = (winner.i * targetDims.w()) / itsCurrImgWidth; 00634 int j = (winner.j * targetDims.h()) / itsCurrImgHeight; 00635 return Point2D<int>(i,j); 00636 } 00637 00638 // ###################################################################### 00639 Image<byte> BeobotBrainMT::getObjectMask() 00640 { 00641 int index = itsWinningMap.size() - 1; 00642 Image<float> winMapNormalized = itsWinningMap[index]; 00643 inplaceNormalize(winMapNormalized, 0.0F, 1.0F); 00644 00645 Point2D<int> wp = downScaleCoords(itsWinner[index], 00646 itsWinningMap[index].getDims()); 00647 00648 // the winner usually falls at the junction of four pixels in the 00649 // saliency/conspicuity/feature map - have a look at all of the pixels 00650 std::vector<Point2D<int> > surr(4); 00651 surr[0] = Point2D<int>(0,0); surr[1] = Point2D<int>(-1,0); 00652 surr[2] = Point2D<int>(0,1); surr[3] = Point2D<int>(-1,1); 00653 00654 bool haveValue = false; 00655 for (uint i = 0; i < surr.size(); ++i) 00656 { 00657 Point2D<int> wp2 = wp + surr[i]; 00658 wp2.clampToDims(itsWinningMap[index].getDims()); 00659 if (itsWinningMap[index].getVal(wp2) > 0.0F) 00660 { 00661 wp = wp2; haveValue = true; break; 00662 } 00663 } 00664 00665 Image<byte> objMask; 00666 if (haveValue) objMask = segmentObjectClean(winMapNormalized, wp, 4); 00667 return objMask; 00668 } 00669 00670 // Dims dims(IMAGE_WIDTH, IMAGE_HEIGHT); 00671 // case SESMgaussian: 00672 // Image<float> temp = 00673 // (Image<float>)scaleBlock(itsObjectMask, dims); 00674 // itsSmoothMask = convGauss<float>(temp,SIGMA,SIGMA,5); 00675 // inplaceNormalize(itsSmoothMask, 0.0F,3.0F); 00676 // inplaceClamp(itsSmoothMask, 0.0F, 1.0F); 00677 00678 // Timer tim2(1000000); tim2.reset(); 00679 // // case SESMchamfer: 00680 // const byte cutoff = 100; 00681 // Image<byte> temp = scaleBlock(itsObjectMask, dims); 00682 // temp = chamfer34(openImg(temp,structEl),cutoff); 00683 // itsSEmask = binaryReverse((Image<float>)temp,255.0F); 00684 // inplaceNormalize(itsSEmask, 0.0F,1.0F); 00685 // uint64 t2 = tim2.get(); 00686 // LINFO("---->tim2: %8.3f",t2/1000.0); 00687 00688 // ###################################################################### 00689 Rectangle BeobotBrainMT::getSEBoundingBox() 00690 { 00691 int scale = (int)(pow(2,sml)); 00692 00693 // get bounding box and add 1 pixel padding 00694 int index = itsObjectMask.size() - 1; 00695 Rectangle r = findBoundingRect(itsObjectMask[index], byte(255)); 00696 int tr = r.top(); if(tr > 0) tr--; 00697 int lr = r.left(); if(lr > 0) lr--; 00698 int br = r.bottomO(); if(br < itsCurrImgHeight/scale) br++; 00699 int rr = r.rightO(); if(rr < itsCurrImgWidth/scale) rr++; 00700 Rectangle rsc = 00701 Rectangle::tlbrO(tr*scale, lr*scale, br*scale, rr*scale); 00702 00703 // correct the salient region to optimize SIFT recognition 00704 int w = itsCMaps[0].getWidth()*scale; 00705 int h = itsCMaps[0].getHeight()*scale; 00706 Rectangle rscc = correctBB(rsc, itsWinner[index], w, h); 00707 //} 00708 00709 00710 //// ###################################################################### 00711 //void BeobotBrainMT::display() 00712 //{ 00713 // int index = itsWinningMap.size() - 1; 00714 // int scale = (int)(pow(2,sml)); 00715 // int w = itsCMaps[0].getWidth()*scale; 00716 // int h = itsCMaps[0].getHeight()*scale; 00717 00718 // // loop over the channels 00719 // Image<float> disp(w*2, h*2, ZEROS); 00720 // Image<float> mask = zoomXY(itsObjectMask[index], scale); 00721 // inplaceNormalize(mask, 0.0F, 1.0F); 00722 // drawRect(mask, rsc, 1.0F); 00723 // inplacePaste(disp, mask, Point2D<int>(w, 0)); 00724 // for (uint i = 0; i < itsNumChannels; i++) 00725 // { 00726 // Image<float> output = itsCMaps[i] * itsChanWeight[i]; 00727 // Point2D<int> winPt = downScaleCoords(itsWinner[index], output.getDims()); 00728 // float curr_val = output.getVal(winPt); 00729 // LINFO("channel = %i -> val(%d,%d) = %f", i, winPt.i, winPt.j, curr_val); 00730 00731 // Image<float> disp2 = zoomXY(output,scale); 00732 // float min,max; getMinMax(disp2, min,max); 00733 // float imean = float(mean(output)); 00734 // float istdev = float(stdev(output)); 00735 // if(itsWinningChan[index] == i) 00736 // LINFO("WIN Chan[%d]: val: %f [%f,%f] -> %f === Min: %f, Max: %f", 00737 // i, curr_val, imean, istdev, (curr_val - imean)/istdev, min, max); 00738 // else 00739 // LINFO(" Chan[%d]: val: %f [%f,%f] -> %f === Min: %f, Max: %f", 00740 // i, curr_val, imean, istdev, (curr_val - imean)/istdev, min, max); 00741 // //drawDisk(disp2, itsWinner[index], 2, max); 00742 // //drawRect(disp2, rsc, max); 00743 // //drawRect(disp2, rscc, max/2.0F); 00744 // inplaceNormalize(disp2, 0.0F, 1.0F); 00745 // if(itsWinningChan[index] == i) 00746 // inplacePaste(disp, disp2, Point2D<int>(0, 0)); 00747 00748 // //itsWin->drawImage(disp,0,0); 00749 // //Raster::waitForKey(); 00750 00751 // // loop over the feature maps 00752 // //if(i == itsWinningChan) 00753 // for (uint j = 0; j < 8; j++) 00754 // { 00755 // Image<float> submap = itsImgPyrs[i][j]; 00756 // float curr_val = 00757 // submap.getVal(downScaleCoords(itsWinner[index], submap.getDims())); 00758 // Image<float> disp3(w,h,ZEROS); 00759 // getMinMax(submap, min,max); 00760 // LINFO(" fmap[%d][%d]: [%d,%d] val: %f Min: %f, Max: %f", 00761 // i,j, submap.getWidth(), submap.getHeight(), curr_val,min,max); 00762 00763 // if(j < 6) 00764 // { 00765 // inplacePaste(disp3, zoomXY(submap, int(pow(2,j))), Point2D<int>(0,0)); 00766 // //drawDisk(disp3, itsWinner[index], 2, max); 00767 // //drawRect(disp3, rsc, max); 00768 // //drawRect(disp3, rscc, max/2.0F); 00769 // inplaceNormalize(disp3, 0.0F, 1.0F); 00770 // drawGrid(disp3, w/4,h/4,1,1, 1.0F); 00771 // inplacePaste(disp, disp3, Point2D<int>(0, h)); 00772 // itsWin->drawImage(disp,0,0); 00773 // Raster::waitForKey(); 00774 // } 00775 // } 00776 00777 // // loop over the csmaps 00778 // //if(i == itsWinningChan) 00779 // for (uint j = 0; j < itsCSMaps[i].size(); j++) 00780 // { 00781 // Image<float> csmap = itsCSMaps[i][j]; 00782 // float curr_val = 00783 // csmap.getVal(downScaleCoords(itsWinner[index], csmap.getDims())); 00784 // imean = float(mean(csmap)); 00785 // istdev = float(stdev(csmap)); 00786 // Image<float> disp4; 00787 00788 // for(uint k = 0; k < 6; k++) 00789 // { 00790 // disp4 = toPower(zoomXY(csmap, scale), (1.0+k*.2)); 00791 // getMinMax(disp4, min,max); 00792 // LINFO(" csmap[%d][%d]: val: %f [%f,%f] -> %f = {%f %f}", 00793 // i,j, curr_val, imean, istdev, 00794 // (curr_val - imean)/istdev, min, max); 00795 00796 // //drawDisk(disp4, itsWinner[index], 2, max); 00797 // //drawRect(disp4, rsc, max); 00798 // //drawRect(disp4, rscc, max/2.0F); 00799 // inplaceNormalize(disp4, 0.0F, 1.0F); 00800 // drawGrid(disp4, w/4,h/4,1,1, 1.0F); 00801 // //if(j == itsWinningSubmapNum[index]) 00802 // inplacePaste(disp, disp4, Point2D<int>(w, h)); 00803 // itsWin->drawImage(disp,0,0); 00804 // Raster::waitForKey(); 00805 // } 00806 // } 00807 // } 00808 // itsWin->drawImage(disp,0,0); 00809 // Raster::waitForKey(); 00810 00811 return rscc; 00812 } 00813 00814 // ###################################################################### 00815 Rectangle BeobotBrainMT::correctBB(Rectangle r, Point2D<int> p, int w, int h) 00816 { 00817 float wlow = .35; float whigh = .5; 00818 float hlow = .35; float hhigh = .5; 00819 00820 // pixel slack 00821 int pslack = 5; 00822 00823 int tr = r.top(); int lr = r.left(); 00824 int br = r.bottomI(); int rr = r.rightI(); 00825 int wr = r.width(); int hr = r.height(); 00826 LDEBUG("SE bb [%3d,%3d,%3d,%d](%dx%d): p:[%d,%d]", 00827 tr, lr, br, rr, wr, hr, p.i, p.j); 00828 00829 // before we start make sure that 00830 // the salient point is within the rectangle 00831 if(((lr + pslack) > p.i) || ((rr - pslack) < p.i) || 00832 ((tr + pslack) > p.j) || ((br - pslack) < p.j) ) 00833 { 00834 LDEBUG("point is out of or near border of box"); 00835 00836 // expand window to get the points 00837 // to be more in the center 00838 if((lr + pslack) > p.i) 00839 { lr = p.i - pslack; if(lr < 0 ) lr = 0; LDEBUG("left "); } 00840 if((rr - pslack) < p.i) 00841 { rr = p.i + pslack; if(rr > w-1) rr = w-1;LDEBUG("right "); } 00842 if((tr + pslack) > p.j) 00843 { tr = p.j - pslack; if(tr < 0 ) tr = 0; LDEBUG("top "); } 00844 if((br - pslack) < p.j) 00845 { br = p.j + pslack; if(br > h-1) br = h-1;LDEBUG("bottom"); } 00846 00847 wr = rr - lr; 00848 hr = br - tr; 00849 00850 LDEBUG("CNT SE bb [%3d,%3d,%3d,%d](%dx%d) p: [%d,%d]", 00851 tr, lr, br, rr, wr, hr, p.i, p.j); 00852 } 00853 00854 // correct for size 00855 00856 // correct the width: make sure it's not too small 00857 if(wr < wlow * w) 00858 { 00859 int wdiff = int(wlow * w) - wr; 00860 float pnt = float(wdiff)/(wr + 0.0f); 00861 LDEBUG("enlarge width (%d -> %d) by: %d (%f)", 00862 wr, int(wlow * w), wdiff, pnt); 00863 00864 int ls = 0, rs = 0; 00865 if((p.i - lr)/(wr+0.0) > .7) 00866 { 00867 ls = int(.7 * wr); rs = int(.3 * wr); LDEBUG("left skew"); 00868 } 00869 else if((rr - p.i)/(wr+0.0) > .7) 00870 { 00871 ls = int(.3 * wr); rs = int(.7 * wr); LDEBUG("right skew"); 00872 } 00873 else 00874 { 00875 ls = p.i - lr; rs = rr - p.i; LDEBUG("horz centered"); 00876 } 00877 int ladd = int(ls * pnt); 00878 int radd = int(rs * pnt); 00879 LDEBUG("add: l: %d, r: %d", ladd, radd); 00880 00881 if(lr < ladd) 00882 { 00883 rr += radd + ladd - lr; lr = 0; 00884 LDEBUG("hit left border: lr: %d, rr:%d", lr, rr); 00885 } 00886 else if(w - 1 < (rr + radd)) 00887 { 00888 ladd += rr + radd - w + 1; 00889 lr = lr - ladd; 00890 rr = w - 1; 00891 LDEBUG("hit right border: lr: %d, rr:%d", lr, rr); 00892 } 00893 else 00894 { 00895 lr = lr - ladd; rr = rr + radd; 00896 LDEBUG("centered: lr: %d, rr:%d", lr, rr); 00897 } 00898 } 00899 00900 // or too large 00901 else if(wr > whigh * w) 00902 { 00903 int wdiff = wr - int(whigh * w); 00904 float pnt = float(wdiff)/(wr + 0.0f); 00905 LDEBUG("decrease width (%d -> %d) by: %d (%f)", 00906 wr, int(whigh * w), wdiff, pnt); 00907 int mid = (lr + rr)/2; 00908 LDEBUG("mid horz: %d", mid); 00909 00910 // if the sal pt is to the left of mid point 00911 if(mid > p.i) 00912 { 00913 // check sal point slack before cutting 00914 int ldiff = p.i - lr; 00915 LDEBUG("Point more left %d, right %d", ldiff, rr - p.i); 00916 if(ldiff >= pslack) 00917 { 00918 int cut = int (ldiff * pnt); 00919 lr = lr + cut; 00920 wdiff-= cut; 00921 LDEBUG("cut left slack %d, lr %d, wdiff now: %d", 00922 cut, lr, wdiff); 00923 } 00924 else LDEBUG("left is not cut"); 00925 00926 rr-= wdiff; LDEBUG("rr: %d", rr); 00927 } 00928 // if it's to the right 00929 else 00930 { 00931 // check sal point slack before cutting 00932 int rdiff = rr - p.i; 00933 LDEBUG("Point more right %d left: %d", rdiff, p.i - lr); 00934 if(rdiff >= pslack) 00935 { 00936 int cut = int (rdiff * pnt); 00937 rr = rr - cut; 00938 wdiff-= cut; 00939 LDEBUG("cut right slack %d, rr %d, wdiff now: %d", 00940 cut, rr, wdiff); 00941 } 00942 else LDEBUG("right is not cut"); 00943 00944 lr+= wdiff; LDEBUG("lr: %d", lr); 00945 } 00946 } 00947 00948 // correct the height: make sure it's not too small 00949 if(hr < hlow * h) 00950 { 00951 int hdiff = int(hlow * h) - hr; 00952 float pnt = float(hdiff)/(hr + 0.0f); 00953 LDEBUG("increase the height (%d -> %d) by: %d (%f)", 00954 hr, int(hlow * h), hdiff, pnt); 00955 00956 int ts = 0, bs = 0; 00957 if((p.j - tr)/(hr+0.0) > .7) 00958 { 00959 ts = int(.7 * hr); bs = int(.3 * hr); LDEBUG("top skew");} 00960 else if((br - p.j)/(hr+0.0) > .7) 00961 { 00962 ts = int(.3 * hr); bs = int(.7 * hr); LDEBUG("bottom skew"); 00963 } 00964 else 00965 { 00966 ts = p.j - tr; bs = br - p.j; LDEBUG("vert centered"); 00967 } 00968 00969 int tadd = int(ts * pnt); 00970 int badd = int(bs * pnt); 00971 LDEBUG("adding: t: %d, b: %d", tadd, badd); 00972 00973 if(tr < tadd) 00974 { 00975 br += badd + tadd - tr; tr = 0; 00976 LDEBUG("hit top border: tr: %d, br:%d", tr, br); 00977 } 00978 else if(h - 1 < (br + badd)) 00979 { 00980 tadd += br + badd - h + 1; 00981 tr = tr - tadd; 00982 br = h - 1; 00983 LDEBUG("hit bottom border: tr: %d, br:%d", tr, br); 00984 } 00985 else 00986 { 00987 tr = tr - tadd; br = br + badd; 00988 LDEBUG("centered vertically: lr: %d, rr:%d", tr, br); 00989 } 00990 } 00991 // or too large 00992 else if(hr > hhigh * h) 00993 { 00994 int hdiff = hr - int(hhigh * h); 00995 float pnt = float(hdiff)/(hr + 0.0f); 00996 LDEBUG("enlarge the heigth (%d -> %d) by: %d (%f)", 00997 hr, int(hhigh * h), hdiff, pnt); 00998 int mid = (tr + br)/2; 00999 LDEBUG("mid vert: %d", mid); 01000 01001 // if the sal pt is on top of midpoint 01002 if(mid > p.j) 01003 { 01004 // check sal point slack before cutting 01005 int tdiff = p.j - tr; 01006 LDEBUG("Point more top %d, bottom %d", tdiff, br - p.j); 01007 if(tdiff >= pslack) 01008 { 01009 int cut = int (tdiff * pnt); 01010 tr = tr + cut; 01011 hdiff-= cut; 01012 LDEBUG("cut top slack %d, tr %d, hdiff now: %d", 01013 cut, tr, hdiff); 01014 } 01015 else LDEBUG("top not cut"); 01016 01017 br-= hdiff; LDEBUG("br: %d", br); 01018 } 01019 // if it's lower than the midpoint 01020 else 01021 { 01022 // check sal point slack before cutting 01023 int bdiff = br - p.j; 01024 LDEBUG("Point more bottom %d top: %d", bdiff, p.j - tr); 01025 if(bdiff >= pslack) 01026 { 01027 int cut = int (bdiff * pnt); 01028 br = br - cut; 01029 hdiff-= cut; 01030 LDEBUG("cut bottom slack %d, br %d, wdiff now: %d", 01031 cut, br, hdiff); 01032 } 01033 else LDEBUG("bottom not cut"); 01034 01035 tr+= hdiff; LDEBUG("tr: %d", tr); 01036 } 01037 } 01038 01039 // also, after we are finished make sure that 01040 // the salient point is within the rectangle 01041 if(((lr + pslack) > p.i) || ((rr - pslack) < p.i) || 01042 ((tr + pslack) > p.j) || ((br - pslack) < p.j) ) 01043 { 01044 LDEBUG("point is out of or near border of box"); 01045 if((lr + pslack) > p.i) 01046 { lr = p.i - pslack; if(lr < 0 ) lr = 0; LDEBUG("left "); } 01047 if((rr - pslack) < p.i) 01048 { rr = p.i + pslack; if(rr > w-1) rr = w-1; LDEBUG("right "); } 01049 if((tr + pslack) > p.j) 01050 { tr = p.j - pslack; if(tr < 0 ) tr = 0; LDEBUG("top "); } 01051 if((br - pslack) < p.j) 01052 { br = p.j + pslack; if(br > h-1) br = h-1; LDEBUG("bottom"); } 01053 } 01054 01055 Rectangle newR = Rectangle::tlbrI(tr, lr, br, rr); 01056 LDEBUG("SE bb [%3d,%3d,%3d,%d](%dx%d): p:[%d,%d]", 01057 tr, lr, br, rr, newR.width(), newR.height(), p.i, p.j); 01058 return newR; 01059 } 01060 01061 // ###################################################################### 01062 void BeobotBrainMT::computeSalientFeatures() 01063 { 01064 itsSalientFeatures.resize(itsWinner.size()); 01065 for(uint i = 0; i < itsWinner.size(); i++) 01066 { 01067 // The coordinates we receive are at the scale of the original 01068 // image, and we will need to rescale them to the size of the 01069 // various submaps we read from. The first image in our first 01070 // pyramid has the dims of the input: 01071 //float iw = float(itsImgPyrs[0][0].getWidth()); 01072 //float ih = float(itsImgPyrs[0][0].getHeight()); 01073 itsSalientFeatures[i].clear(); 01074 Point2D<int> locn = itsWinner[i]; 01075 for(uint c = 0; c < itsNumChannels; c++) 01076 { 01077 for(int di = -2; di < 3; di++) 01078 for(int dj = -2; dj < 3; dj++) 01079 { 01080 uint fidx = 0; 01081 for (int lev = level_min; lev <= level_max; lev ++) 01082 for (int delta = delta_min; delta <= delta_max; delta ++) 01083 { 01084 // ImageSet<float> pyr = itsImgPyrs[c]; 01085 // uint clev = lev, slev = lev + delta; 01086 01087 // // read center value with bilinear interpolation: 01088 // float cval = pyr[clev].getValInterpScaled(locn, itsImgPyrs[0][0].getDims()); 01089 01090 // // read surround value with bilinear interpolation: 01091 // float cval = pyr[slev].getValInterpScaled(locn, itsImgPyrs[0][0].getDims()); 01092 01093 // cval+=0.0F; sval+= 0.0F; 01094 // compute center - surround and take absolute value 01095 // float rval = fabs(cval - sval); 01096 // float minv, maxv; 01097 // getMinMax(itsRawCSMaps[c][fidx], minv, maxv); 01098 // LINFO("rawcs min: %f max: %f ", minv, maxv); 01099 // float val = (rval - minv)/(maxv - minv); 01100 // if(val < 0.0F) val = 0.0F; else if(val > 1.0F) val = 1.0F; 01101 // LINFO("(%d,%d:%d,%d) [c(%f,%f): %f |s(%f,%f): %f]" 01102 // "[%f %f] ->%f -> %f", 01103 // locn.i, locn.j, clev, slev, cx, cy, cval, sx, sy, sval, 01104 // minv, maxv, rval, val); 01105 01106 // CS maps implementations 01107 // Features are all normalized [ 0.0 ... 1.0 ] 01108 // but if range is < 1.0 (no dominant peak) val = 0.0 01109 int csi = locn.i/4, csj = locn.j/4; 01110 float rval = 0.0; 01111 if(itsCSMaps[c][fidx].coordsOk(csi + di, csj + dj)) 01112 rval = fabs(itsCSMaps[c][fidx].getVal(csi, csj)); 01113 float minv, maxv; 01114 getMinMax(itsCSMaps[c][fidx], minv, maxv); 01115 float val; 01116 if((maxv - minv) < 1.0) val = 0.0F; 01117 else val = (rval - minv)/(maxv - minv); 01118 //LINFO("(%d,%d:%d,%d):[%f %f] %f -> %f", 01119 // csi, csj ,clev, slev, minv, maxv, rval, val); 01120 01121 itsSalientFeatures[i].push_back(double(val)); 01122 fidx++; 01123 } 01124 } 01125 } 01126 } 01127 } 01128 01129 // ###################################################################### 01130 /* So things look consistent in everyone's emacs... */ 01131 /* Local Variables: */ 01132 /* indent-tabs-mode: nil */ 01133 /* End: */