00001 /*!@file Neuro/ShapeEstimator.C Estimate the shape/size of an attended object */ 00002 00003 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2003 // 00004 // by the University of Southern California (USC) and the iLab at USC. // 00005 // See http://iLab.usc.edu for information about this project. // 00006 // //////////////////////////////////////////////////////////////////// // 00007 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00008 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00009 // in Visual Environments, and Applications'' by Christof Koch and // 00010 // Laurent Itti, California Institute of Technology, 2001 (patent // 00011 // pending; application number 09/912,225 filed July 23, 2001; see // 00012 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00013 // //////////////////////////////////////////////////////////////////// // 00014 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00015 // // 00016 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00017 // redistribute it and/or modify it under the terms of the GNU General // 00018 // Public License as published by the Free Software Foundation; either // 00019 // version 2 of the License, or (at your option) any later version. // 00020 // // 00021 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00022 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00023 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00024 // PURPOSE. See the GNU General Public License for more details. // 00025 // // 00026 // You should have received a copy of the GNU General Public License // 00027 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00028 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00029 // Boston, MA 02111-1307 USA. // 00030 // //////////////////////////////////////////////////////////////////// // 00031 // 00032 // Primary maintainer for this file: Laurent Itti <itti@usc.edu> 00033 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Neuro/ShapeEstimator.C $ 00034 // $Id: ShapeEstimator.C 14762 2011-05-03 01:13:16Z siagian $ 00035 // 00036 00037 #include "Neuro/ShapeEstimator.H" 00038 00039 #include "Component/ModelOptionDef.H" 00040 #include "Component/GlobalOpts.H" 00041 #include "Channels/ChannelMaps.H" 00042 #include "Component/OptionManager.H" 00043 #include "Image/DrawOps.H" 00044 #include "Image/FilterOps.H" 00045 #include "Image/ImageSet.H" 00046 #include "Image/MathOps.H" 00047 #include "Image/MorphOps.H" // for openImg() 00048 #include "Image/Pixels.H" 00049 #include "Image/Point2D.H" 00050 #include "Image/Transforms.H" 00051 #include "Neuro/NeuroOpts.H" 00052 #include "Neuro/NeuroSimEvents.H" 00053 #include "Neuro/VisualCortex.H" 00054 #include "Neuro/SpatialMetrics.H" 00055 #include "Simulation/SimEventQueue.H" 00056 #include "Transport/FrameInfo.H" 00057 #include "Transport/FrameOstream.H" 00058 #include "Util/Assert.H" 00059 #include "Util/log.H" 00060 #include "Util/sformat.H" 00061 #include "Util/TextLog.H" 00062 00063 #include "Raster/Raster.H" 00064 #include "Image/ShapeOps.H" 00065 #include "Image/CutPaste.H" 00066 #include "Util/Timer.H" 00067 00068 #define SIGMA 20 00069 #define CLAMP 0.4f 00070 00071 const ModelOptionDef OPT_ShapeEstimatorUseLargeNeigh = 00072 { MODOPT_FLAG, "ShapeEstimatorUseLargeNeigh", &MOC_BRAIN, OPTEXP_CORE, 00073 "Use a larger 3x3 neighborhood to track down a local maximum across " 00074 "scales when true, otherwise use a 2x2 neighborhood.", 00075 "shape-estim-largeneigh", '\0', "", "true" }; 00076 00077 // ###################################################################### 00078 ShapeEstimator::ShapeEstimator(OptionManager& mgr, 00079 const std::string& descrName, 00080 const std::string& tagName, 00081 const nub::soft_ref<VisualCortex> vcx) : 00082 SimModule(mgr, descrName, tagName), 00083 SIMCALLBACK_INIT(SimEventWTAwinner), 00084 SIMCALLBACK_INIT(SimEventSaveOutput), 00085 itsMetrics(new SpatialMetrics(mgr)), 00086 itsLogFile(&OPT_TextLogFile, this), 00087 itsMode(&OPT_ShapeEstimatorMode, this), 00088 itsSmMethod(&OPT_ShapeEstimatorSmoothMethod, this), 00089 itsSaveObjMask(&OPT_BrainSaveObjMask, this), // Neuro/NeuroOpts.{H,C} 00090 itsUseLargeNeigh(&OPT_ShapeEstimatorUseLargeNeigh, this), 00091 structEl(), itsSmoothMask(), itsCumMask(), 00092 itsSalientRegionSegmenter(new SalientRegionSegmenter()) 00093 { 00094 this->addSubComponent(itsMetrics); 00095 } 00096 00097 00098 // ###################################################################### 00099 void ShapeEstimator::reset1() 00100 { 00101 // reset some stuff for ShapeEstimator 00102 itsSmoothMask.freeMem(); 00103 itsCumMask.freeMem(); 00104 00105 // propagate to our base class: 00106 ModelComponent::reset1(); 00107 } 00108 00109 // ###################################################################### 00110 void ShapeEstimator::onSimEventWTAwinner 00111 (SimEventQueue& q, rutz::shared_ptr<SimEventWTAwinner>& e) 00112 { 00113 // nothing to do? 00114 if (itsMode.getVal() == SEMnone) return; 00115 00116 // any new covert attention shift? 00117 const Point2D<int> winner = e->winner().p; // in input coordinates 00118 00119 // in the following, we are going to try to find good values for 00120 // winmap (the winning map or submap), and winloc (location of 00121 // winner in winmap's coordinates): 00122 NamedImage<float> winmap; Point2D<int> winloc(-1, -1); 00123 00124 // grab the latest retina image, just to know its dims: 00125 Dims indims; 00126 if (SeC<SimEventRetinaImage> ee = q.check<SimEventRetinaImage>(this, SEQ_ANY)) 00127 indims = ee->frame().getDims(); 00128 else 00129 LFATAL("Oooops, we have a WTA winner " 00130 "but no retina image in the SimEventQueue"); 00131 00132 // invalidate any previous object mask: 00133 itsSmoothMask.freeMem(); 00134 00135 // grab all the VisualCortex maps: 00136 rutz::shared_ptr<SimReqVCXmaps> vcxm(new SimReqVCXmaps(this)); 00137 q.request(vcxm); // VisualCortex is now filling-in the maps... 00138 rutz::shared_ptr<ChannelMaps> chm = vcxm->channelmaps(); 00139 00140 // in case we use MT features map 00141 // we have to have a MotionSpatioTemporalChannel 00142 if(itsMode.getVal() == SEMMTfeatureMap) 00143 { 00144 getMotionShapeEstimatorMap(q,e); 00145 return; 00146 } 00147 // NOTE: THIS MAY NEED TO BE COMMENTED OUT 00148 getShapeEstimatorMap(q,e); 00149 00150 // in case we use saliency map, just store the SM as the winning map: 00151 if (itsMode.getVal() == SEMsaliencyMap) 00152 { 00153 winmap = chm->getMap(); 00154 locateLocalMax(winmap, winner, indims, winloc); 00155 } 00156 else 00157 { 00158 // loop over the channels: 00159 float mx = 0.0F; rutz::shared_ptr<ChannelMaps> chan; 00160 00161 for (uint i = 0; i < chm->numSubchans(); ++i) 00162 { 00163 rutz::shared_ptr<ChannelMaps> ch = chm->subChanMaps(i); 00164 NamedImage<float> output = ch->getMap(); 00165 if (output.initialized()) 00166 { 00167 Point2D<int> w; 00168 const float val = locateLocalMax(output, winner, indims, w); 00169 LINFO("Examining: %s, val = %g", output.name().c_str(), val); 00170 00171 // do we have a new max? 00172 if (val > mx) { winmap = output; mx = val; chan = ch; winloc = w; } 00173 } 00174 } 00175 00176 // we did not find a max here? -> resort back to saliency map: 00177 if (mx == 0.0F) 00178 { 00179 winmap = chm->getMap(); 00180 locateLocalMax(winmap, winner, indims, winloc); 00181 } 00182 else 00183 { 00184 // ok, we found a max in one of our channel conspicuity 00185 // maps. If we are using the conspicuity map mode, we 00186 // are done. Otherwise, let's go one level further, 00187 // looking at individual feature maps: 00188 00189 // NOTE: this code should be truly recursive, instead here 00190 // we are going to flatten the channel hierarchy... 00191 00192 if (itsMode.getVal() != SEMconspicuityMap) 00193 { 00194 // loop over the submaps of the winning channel: 00195 mx = 0.0F; Point2D<int> winloc2(0, 0); NamedImage<float> winmap2; 00196 for (uint i = 0; i < chan->numSubmaps(); ++i) 00197 { 00198 NamedImage<float> submap = chan->getSubmap(i); 00199 if (submap.initialized()) 00200 { 00201 Point2D<int> w; 00202 const float val = locateLocalMax(submap, winloc, winmap.getDims(), w); 00203 LINFO("Examining: %s, val = %g", submap.name().c_str(), val); 00204 00205 // do we have a new max? 00206 if (val > mx) { winmap2 = submap; mx = val; winloc2 = w; } 00207 } 00208 } 00209 00210 // did not find a max here? then just keep our old 00211 // values for winmap, winloc, and winlabel (which 00212 // are at the conspicuity map level). Otherwise, 00213 // update to the feature map level: 00214 if (mx > 0.0F) { winmap = winmap2; winloc = winloc2; } 00215 } 00216 } 00217 } 00218 postShapeEstimatorMask(winmap, winloc, winner, indims, q); 00219 } 00220 00221 00222 // ###################################################################### 00223 void ShapeEstimator::postShapeEstimatorMask 00224 (NamedImage<float> winmap, Point2D<int> winloc, 00225 Point2D<int> winner, Dims indims, SimEventQueue& q) 00226 { 00227 // since we had a winner to start with, one of the detections 00228 // should have worked... So we have winmap, winlabel, and winloc 00229 // all ready to go: 00230 ASSERT(winmap.initialized()); 00231 00232 // Now compute the objectmask: 00233 Image<byte> objmask; 00234 Image<float> winmapnormalized = winmap; 00235 inplaceNormalize(winmapnormalized, 0.0F, 1.0F); 00236 00237 // if we found a good seed point, use it to segment the 00238 // object. Otherwise, we failed, so let's just return a disk: 00239 const bool goodseed = (winmap.getVal(winloc) > 0.0F); 00240 if (goodseed) 00241 { 00242 LDEBUG("Segmenting object around (%d, %d) in %s.", 00243 winner.i, winner.j, winmap.name().c_str()); 00244 objmask = segmentObjectClean(winmapnormalized, winloc); 00245 } 00246 else 00247 { 00248 LDEBUG("Drawing disk object around (%d, %d) in %s.", 00249 winner.i, winner.j, winmap.name().c_str()); 00250 objmask.resize(winmap.getDims(), true); 00251 drawDisk(objmask, winloc, 00252 (itsMetrics->getFOAradius() * objmask.getWidth()) / indims.w(), 00253 byte(255)); 00254 } 00255 00256 // all right, compute the smooth mask: 00257 switch(itsSmMethod.getVal()) 00258 { 00259 case SESMgaussian: 00260 { 00261 Image<float> temp = scaleBlock(objmask, indims); 00262 itsSmoothMask = convGauss<float>(temp, SIGMA, SIGMA, 5); 00263 inplaceNormalize(itsSmoothMask, 0.0F, 3.0F); 00264 inplaceClamp(itsSmoothMask, 0.0F, 1.0F); 00265 break; 00266 } 00267 00268 case SESMchamfer: 00269 { 00270 if (structEl.initialized() == false) 00271 { 00272 //create the structuring element for the opening 00273 const int ss = 8; 00274 structEl = Image<byte>(ss+ss,ss+ss, ZEROS); 00275 drawDisk(structEl, Point2D<int>(ss,ss), ss, byte(1)); 00276 } 00277 00278 const byte cutoff = 100; 00279 Image<byte> temp = scaleBlock(objmask, indims); 00280 temp = chamfer34(openImg(temp, structEl), cutoff); 00281 itsSmoothMask = binaryReverse(Image<float>(temp), 255.0F); 00282 inplaceNormalize(itsSmoothMask, 0.0F, 1.0F); 00283 break; 00284 } 00285 00286 case SESMnone: 00287 { 00288 itsSmoothMask = scaleBlock(objmask, indims) / 255.0F; 00289 break; 00290 } 00291 00292 default: LFATAL("Unknown Smoothing Method"); 00293 } 00294 00295 // update the cumulative smooth mask: 00296 if (itsCumMask.initialized()) itsCumMask = takeMax(itsCumMask, itsSmoothMask); 00297 else itsCumMask = itsSmoothMask; 00298 00299 // finally compute the IOR mask. Basically, we want to take the 00300 // dot product between the binary object mask and the winning 00301 // map, so that we will inhibit stronger the locations which are 00302 // more active. However, let's add some default level of 00303 // inhibition inside the whole object, so that every location 00304 // within the object gets suppressed at least some. Also note 00305 // that we need to make sure the WTA winner will actually get 00306 // inhibited, so that attention can shift. Indeed, sometimes the 00307 // max of the saliency map (which is the WTAwinner) may not 00308 // belong to the object that has been segmented, e.g., from some 00309 // feature map; so we just force it into the objmask first. 00310 Image<byte> objmask2(objmask); 00311 objmask2.setVal((winner.i * objmask.getWidth()) / indims.w(), 00312 (winner.j * objmask.getHeight()) / indims.h(), 00313 byte(255)); 00314 Image<byte> iormask = lowPass3(objmask2) * (winmapnormalized + 0.25F); 00315 00316 // great, let's post an event with all our results: 00317 rutz::shared_ptr<SimEventShapeEstimatorOutput> 00318 e(new SimEventShapeEstimatorOutput(this, winmap, objmask, iormask, itsSmoothMask, itsCumMask, 00319 winmap.name(), goodseed)); 00320 00321 00322 00323 00324 Raster::waitForKey(); 00325 uint width = itsSmoothMask.getWidth(); 00326 uint height = itsSmoothMask.getHeight(); 00327 uint scale = 4; 00328 if(itsWin.is_invalid()) 00329 itsWin.reset(new XWinManaged(Dims(width*3,height*2), 0, 0, "ShapeEst")); 00330 itsWin->setDims(Dims(width*3, height*2)); 00331 00332 LINFO("%d %d", winmap.getWidth(), winmap.getHeight()); 00333 LINFO("%d %d", objmask.getWidth(), objmask.getHeight()); 00334 LINFO("%d %d", iormask.getWidth(), iormask.getHeight()); 00335 LINFO("%d %d", itsSmoothMask.getWidth(), itsSmoothMask.getHeight()); 00336 LINFO("%d %d", itsCumMask.getWidth(), itsCumMask.getHeight()); 00337 00338 Image<float> disp(width*3, height*2, NO_INIT); 00339 00340 Image<float> dwinmap = zoomXY(winmap,scale); 00341 inplaceNormalize(dwinmap, 0.0F, 1.0F); 00342 inplacePaste(disp, dwinmap, Point2D<int>(0,0)); 00343 00344 Image<float> dobjmask = zoomXY(objmask,scale); 00345 inplaceNormalize(dobjmask, 0.0F, 1.0F); 00346 inplacePaste(disp, dobjmask, Point2D<int>(width,0)); 00347 00348 Image<float> diormask = zoomXY(iormask,scale); 00349 inplaceNormalize(diormask, 0.0F, 1.0F); 00350 inplacePaste(disp, diormask, Point2D<int>(2*width,0)); 00351 00352 Image<float> dsmmask = itsSmoothMask; 00353 inplaceNormalize(dsmmask, 0.0F, 1.0F); 00354 inplacePaste(disp, dsmmask, Point2D<int>(0,height)); 00355 00356 Image<float> dcummask = itsCumMask; 00357 inplaceNormalize(dcummask, 0.0F, 1.0F); 00358 inplacePaste(disp, dcummask, Point2D<int>(width, height)); 00359 00360 itsWin->drawImage(disp,0,0); 00361 00362 Raster::waitForKey(); 00363 00364 00365 00366 00367 q.post(e); 00368 00369 // log it: 00370 const std::string msg = 00371 sformat("(%d,%d) %s %s", 00372 winner.i, winner.j, winmap.name().c_str(), 00373 goodseed ? "[Shape]" : "[Disk]"); 00374 00375 textLog(itsLogFile.getVal(), "ShapeEstimator", msg); 00376 LINFO("Shape estimated %s", msg.c_str()); 00377 } 00378 00379 // ###################################################################### 00380 float ShapeEstimator::locateLocalMax(const Image<float>& submap, 00381 const Point2D<int>& winner, 00382 const Dims& fulldims, 00383 Point2D<int>& winloc, 00384 int mini, int maxi) 00385 { 00386 // Scale down the winner coordinates, from input image dims to the 00387 // dims of submap, rounding down: 00388 winloc.i = (winner.i * submap.getWidth()) / fulldims.w(); 00389 winloc.j = (winner.j * submap.getHeight()) / fulldims.h(); 00390 00391 // Explore the local neighborhood. Note that here we are only 00392 // interested in positive values: 00393 float maxval = 0.0F; std::vector<Point2D<int> > surr; 00394 00395 if (itsUseLargeNeigh.getVal()) mini = -1; 00396 00397 for (int j = mini; j <= maxi; ++j) 00398 for (int i = mini; i <= maxi; ++i) 00399 surr.push_back(winloc + Point2D<int>(i, j)); 00400 00401 for (uint i = 0; i < surr.size(); ++i) 00402 { 00403 surr[i].clampToDims(submap.getDims()); 00404 const float val = submap.getVal(surr[i]); 00405 if (val > maxval) { winloc = surr[i]; maxval = val; } 00406 } 00407 return maxval; 00408 } 00409 00410 // ###################################################################### 00411 void ShapeEstimator:: 00412 onSimEventSaveOutput(SimEventQueue& q, rutz::shared_ptr<SimEventSaveOutput>& e) 00413 { 00414 if (itsSaveObjMask.getVal()) 00415 { 00416 // get the OFS to save to, assuming sinfo is of type 00417 // SimModuleSaveInfo (will throw a fatal exception otherwise): 00418 nub::ref<FrameOstream> ofs = dynamic_cast<const SimModuleSaveInfo&>(e->sinfo()).ofs; 00419 00420 if (itsSmoothMask.initialized()) 00421 { 00422 const Image<byte> om = itsSmoothMask * 255.0F; 00423 ofs->writeGray(om, "OBJ", FrameInfo("ShapeEstimator object mask", SRC_POS)); 00424 } 00425 else 00426 { 00427 if (SeC<SimEventRetinaImage> ee = q.check<SimEventRetinaImage>(this, SEQ_ANY)) 00428 ofs->writeGray(Image<byte>(ee->frame().getDims(), ZEROS), "OBJ", 00429 FrameInfo("ShapeEstimator object mask", SRC_POS)); 00430 } 00431 } 00432 } 00433 00434 // ###################################################################### 00435 void ShapeEstimator::getMotionShapeEstimatorMap 00436 (SimEventQueue& q, rutz::shared_ptr<SimEventWTAwinner>& e) 00437 { 00438 // any new covert attention shift? 00439 const Point2D<int> winner = e->winner().p; // in input coordinates 00440 LINFO("SEMMTfeatureMap: winner: %d %d", winner.i, winner.j); 00441 00442 // in the following, we are going to try to find good values for 00443 // winmap (the winning map or submap), and winloc (location of 00444 // winner in winmap's coordinates): 00445 NamedImage<float> winmap; Point2D<int> winloc(-1, -1); 00446 00447 // grab the latest retina image, just to know its dims: 00448 Dims indims; 00449 if (SeC<SimEventRetinaImage> ee = q.check<SimEventRetinaImage>(this, SEQ_ANY)) 00450 indims = ee->frame().getDims(); 00451 else LFATAL("Oooops, we have a WTA winner but no retina image in the SimEventQueue"); 00452 00453 SeC<SimEventRetinaImage> ee = q.check<SimEventRetinaImage>(this, SEQ_ANY); 00454 Image<PixRGB<byte> > image = ee->frame().colorByte(); 00455 00456 //Raster::waitForKey(); 00457 uint width = image.getWidth(); 00458 uint height = image.getHeight(); 00459 //uint scale = 16; 00460 if(itsWin.is_invalid()) 00461 itsWin.reset(new XWinManaged(Dims(width*3,height*2), 0, 0, "ShapeEstObj")); 00462 itsWin->setDims(Dims(width*3, height*2)); 00463 00464 Image<PixRGB<byte> > disp = image; 00465 drawCross(disp, winner, PixRGB<byte>(255,0,0), 10, 2); 00466 itsWin->drawImage(disp,0,0); 00467 LINFO("Input Image: %d %d", image.getWidth(), image.getHeight()); 00468 Raster::waitForKey(); 00469 00470 // invalidate any previous object mask: 00471 itsSmoothMask.freeMem(); 00472 00473 // grab all the VisualCortex maps: 00474 rutz::shared_ptr<SimReqVCXmaps> vcxm(new SimReqVCXmaps(this)); 00475 q.request(vcxm); // VisualCortex is now filling-in the maps... 00476 rutz::shared_ptr<ChannelMaps> chm = vcxm->channelmaps(); 00477 00478 00479 // Image<float> dwinmap = zoomXY(winmap,scale); 00480 // inplaceNormalize(dwinmap, 0.0F, 1.0F); 00481 // itsWin->drawImage(dwinmap,0,0); 00482 // LINFO("Saliency Map"); 00483 // Raster::waitForKey(); 00484 00485 00486 00487 00488 00489 00490 00491 00492 00493 00494 00495 00496 // loop over the channels: 00497 float mx = 0.0F; rutz::shared_ptr<ChannelMaps> chan; 00498 uint jmax = 0; 00499 00500 // get the MotionSpatioTemporalChannel 00501 for (uint i = 0; i < chm->numSubchans(); ++i) 00502 { 00503 rutz::shared_ptr<ChannelMaps> ch = chm->subChanMaps(i); 00504 NamedImage<float> output = ch->getMap(); 00505 00506 // Image<float> doutput = zoomXY(output,16); 00507 // inplaceNormalize(doutput, 0.0F, 1.0F); 00508 // itsWin->drawImage(doutput,0,0); 00509 // LINFO("CMAP[%3d]: %s", i, output.name().c_str()); 00510 // Raster::waitForKey(); 00511 00512 if(output.name().find(std::string("motionSpatioTemporal")) != 00513 std::string::npos) 00514 { 00515 // go through each sub channels 00516 uint nSubchans = ch->numSubchans(); 00517 LINFO("num subchans: %d", nSubchans); 00518 00519 int cDir = -1; 00520 for(uint j = 0; j < nSubchans; j++) 00521 { 00522 rutz::shared_ptr<ChannelMaps> sch = ch->subChanMaps(j); 00523 NamedImage<float> rawcsmap = sch->getRawCSmap(0); 00524 00525 std::string rcsname = rawcsmap.name(); 00526 int fupos = rcsname.find_first_of('_') +1; 00527 std::string rcsname2 = rcsname.substr(fupos); 00528 int supos = rcsname2.find_first_of('_')+ fupos; 00529 std::string number = rcsname.substr(fupos,supos-fupos).c_str(); 00530 //LINFO("%s %s -> %s", rcsname.c_str(), rcsname2.c_str(), number.c_str()); 00531 int dir = atoi(number.c_str()); 00532 00533 if(cDir < dir) 00534 { 00535 cDir = dir; 00536 00537 Image<float> drawcsmap = zoomXY(rawcsmap, 4); 00538 inplaceNormalize(drawcsmap, 0.0F, 1.0F); 00539 itsWin->drawImage(drawcsmap, 0,0); 00540 LINFO("RCSMAP[%3d][%3d]:[%d %d]: %s", 00541 i,j, rawcsmap.getWidth(), rawcsmap.getHeight(), 00542 rawcsmap.name().c_str()); 00543 Raster::waitForKey(); 00544 00545 // check to see if it's a higher saliency value 00546 if (output.initialized()) 00547 { 00548 Point2D<int> w; 00549 const float val = locateLocalMax(rawcsmap, winner, indims, w, -1, 1); 00550 LINFO("Examining: %s, val = %g", rawcsmap.name().c_str(), val); 00551 00552 // do we have a new max? 00553 if (val > mx) 00554 { 00555 LINFO("j: %d", j); jmax = j; 00556 winmap = rawcsmap; mx = val; chan = ch; winloc = w; 00557 } 00558 } 00559 } 00560 } 00561 } 00562 } 00563 00564 // we did not find a max here? -> resort back to saliency map: 00565 if (mx == 0.0F) 00566 { 00567 winmap = chm->getMap(); 00568 locateLocalMax(winmap, winner, indims, winloc); 00569 LINFO("did not find a max in the map"); 00570 } 00571 00572 LINFO("jmax: %d mx: %f", jmax, mx); 00573 postShapeEstimatorMask(winmap, winloc, winner, indims, q); 00574 00575 Raster::waitForKey(); 00576 } 00577 00578 // ###################################################################### 00579 void ShapeEstimator::getShapeEstimatorMap 00580 (SimEventQueue& q, rutz::shared_ptr<SimEventWTAwinner>& e) 00581 { 00582 Timer t(1000000); t.reset(); 00583 00584 // any new covert attention shift? 00585 const Point2D<int> winner = e->winner().p; // in input coordinates 00586 00587 // in the following, we are going to try to find good values for 00588 // winmap (the winning map or submap), and winloc (location of 00589 // winner in winmap's coordinates): 00590 NamedImage<float> winmap; Point2D<int> winloc(-1, -1); 00591 00592 // grab the latest retina image, just to know its dims: 00593 Dims indims; 00594 if (SeC<SimEventRetinaImage> ee = q.check<SimEventRetinaImage>(this, SEQ_ANY)) 00595 indims = ee->frame().getDims(); 00596 else LFATAL("Oooops, we have a WTA winner but no retina image in the SimEventQueue"); 00597 00598 SeC<SimEventRetinaImage> ee = q.check<SimEventRetinaImage>(this, SEQ_ANY); 00599 Image<PixRGB<byte> > image = ee->frame().colorByte(); 00600 00601 uint width = image.getWidth(); 00602 uint height = image.getHeight(); 00603 uint scale = 16; 00604 if(itsWin.is_invalid()) 00605 itsWin.reset(new XWinManaged(Dims(width*3,height*2), 0, 0, "ShapeEstObj")); 00606 itsWin->setDims(Dims(width*3, height*2)); 00607 00608 Image<PixRGB<byte> > disp = image; 00609 drawCross(disp, winner, PixRGB<byte>(255,0,0), 10, 1); 00610 drawRect(disp, 00611 Rectangle(Point2D<int>(winner.i-8, winner.j-8), Dims(16, 16)), 00612 PixRGB<byte>(255,255,0)); 00613 00614 itsWin->drawImage(disp,0,0); 00615 LINFO("winner: %d %d in [%3d %3d]", 00616 winner.i, winner.j, image.getWidth(), image.getHeight()); 00617 00618 // invalidate any previous object mask: 00619 itsSmoothMask.freeMem(); 00620 00621 // grab all the VisualCortex maps: 00622 rutz::shared_ptr<SimReqVCXmaps> vcxm(new SimReqVCXmaps(this)); 00623 q.request(vcxm); // VisualCortex is now filling-in the maps... 00624 rutz::shared_ptr<ChannelMaps> chm = vcxm->channelmaps(); 00625 00626 winmap = chm->getMap(); 00627 locateLocalMax(winmap, winner, indims, winloc); 00628 00629 Image<float> dwinmap = zoomXY(winmap,scale); 00630 inplaceNormalize(dwinmap, 0.0F, 1.0F); 00631 itsWin->drawImage(dwinmap,0,0); 00632 //inplacePaste(disp, dwinmap, Point2D<int>(0,0)); 00633 00634 // loop over the channels: 00635 rutz::shared_ptr<ChannelMaps> chan; 00636 00637 // // go through each channel 00638 // for (uint i = 0; i < chm->numSubchans(); ++i) 00639 // { 00640 // rutz::shared_ptr<ChannelMaps> ch = chm->subChanMaps(i); 00641 // NamedImage<float> output = ch->getMap(); 00642 00643 // Image<float> doutput = zoomXY(output,scale); 00644 // inplaceNormalize(doutput, 0.0F, 1.0F); 00645 // itsWin->drawImage(doutput,0,0); 00646 // LINFO("CMAP[%3d]: %s", i, output.name().c_str()); 00647 00648 // Raster::waitForKey(); 00649 00650 // // if (output.initialized()) 00651 // // { 00652 // // Point2D<int> w; 00653 // // const float val = locateLocalMax(output, winner, indims, w); 00654 // // LINFO("Examining: %s, val = %g", output.name().c_str(), val); 00655 // // } 00656 00657 // // go through each sub channels 00658 // uint nSubchans = ch->numSubchans(); 00659 // LINFO("num subchans: %d", nSubchans); 00660 00661 // for(uint j = 0; j < nSubchans; j++) 00662 // { 00663 // rutz::shared_ptr<ChannelMaps> sch = ch->subChanMaps(j); 00664 00665 // // // loop over the submaps of the winning channel: 00666 // // //float mx2 = 0.0F; Point2D<int> winloc2(0, 0); NamedImage<float> winmap2; 00667 // // for (uint k = 0; k < sch->numSubmaps(); ++k) 00668 // // { 00669 // // //NamedImage<float> submap = sch->getRawCSmap(k); 00670 // // NamedImage<float> submap = sch->getSubmap(k); 00671 // // // if (submap2.initialized()) 00672 // // // { 00673 // // // Point2D<int> w2; 00674 // // // const float val2 = locateLocalMax(submap2, winloc2, winmap.getDims(), w2); 00675 // // // LINFO("Examining: %s, val = %g", submap2.name().c_str(), val2); 00676 // // // } 00677 00678 // // Image<float> dsubmap = zoomXY(submap, scale); 00679 // // inplaceNormalize(dsubmap, 0.0F, 1.0F); 00680 // // itsWin->drawImage(dsubmap, 0,0); 00681 // // LINFO("FMAP[%3d][%3d][%3d]:[%d %d]: %s", 00682 // // i,j,k, submap.getWidth(), submap.getHeight(), 00683 // // submap.name().c_str()); 00684 // // Raster::waitForKey(); 00685 // // } 00686 00687 // uint scale2 = 4; 00688 // NamedImage<float> rawcsmap = sch->getRawCSmap(0); 00689 // Image<float> drawcsmap = zoomXY(rawcsmap, scale2); 00690 // inplaceNormalize(drawcsmap, 0.0F, 1.0F); 00691 // itsWin->drawImage(drawcsmap, 0,0); 00692 // LINFO("RCSMAP[%3d][%3d]:[%d %d]: %s", 00693 // i,j, rawcsmap.getWidth(), rawcsmap.getHeight(), 00694 // rawcsmap.name().c_str()); 00695 // Raster::waitForKey(); 00696 00697 // // get all the features: 00698 // ImageSet<float> pyr = sch->getPyramid(); 00699 // LINFO("Rsize: %d", pyr.size()); 00700 // for(uint k = 0; k < pyr.size(); k++) 00701 // { 00702 // Image<float> rmap = pyr[k]; 00703 // uint nscale = uint(pow(2.0,k)); 00704 // Image<float> drmap = zoomXY(rmap,nscale); 00705 // inplaceNormalize(drmap, 0.0F, 1.0F); 00706 // itsWin->drawImage(drmap,0,0); 00707 // LINFO("RMAP[%3d][%3d][%3d]: (%3d)-> %d %d --> %d %d", i,j,k, nscale, 00708 // rmap.getWidth(), rmap.getHeight(), 00709 // drmap.getWidth(), drmap.getHeight()); 00710 // Raster::waitForKey(); 00711 // } 00712 // } 00713 // } 00714 00715 // since we had a winner to start with, one of the detections 00716 // should have worked... So we have winmap, winlabel, and winloc 00717 // all ready to go: 00718 ASSERT(winmap.initialized()); 00719 00720 // =============================================================== 00721 // =============================================================== 00722 // Have: most salient point 00723 // winning channel, feature map, etc. 00724 00725 // =============================================================== 00726 // start at the point: 00727 // list all the possible reason why this point is selected 00728 // maybe different strategy for different channels 00729 // intensity/color vs. orientation channel 00730 // ---> learning ???? 00731 00732 // localize the location of saliency point better: goodseed 00733 00734 // =============================================================== 00735 // grow in hierarachical manner 00736 // center surround manner (grow both center & surround region) 00737 00738 // consider both: 00739 // contour: ---> take boundary evidence into account 00740 // region uniformity --> keep growing histogram 00741 // --> may have GMM (Gaussian Mixture Model) 00742 00743 // how about motion!!! 00744 00745 // try the PAMI 2011 center surround 00746 00747 // GROWING SHOULD BE FAST: 100 - 200 ms target 00748 00749 // does this spread too much ---> IOR but no Object 00750 // check out how that affect the 00751 00752 // what scale to spread??? 00753 00754 if(!itsSalientRegionSegmenter->getImage().initialized()) 00755 itsSalientRegionSegmenter->setImage(image); 00756 00757 Image<float> segObjImage = 00758 itsSalientRegionSegmenter->getSalientRegion(winner); 00759 00760 00761 00762 // =============================================================== 00763 // =============================================================== 00764 00765 // LINFO("%d %d", winmap.getWidth(), winmap.getHeight()); 00766 // LINFO("%d %d", objmask.getWidth(), objmask.getHeight()); 00767 // LINFO("%d %d", iormask.getWidth(), iormask.getHeight()); 00768 // LINFO("%d %d", itsSmoothMask.getWidth(), itsSmoothMask.getHeight()); 00769 // LINFO("%d %d", itsCumMask.getWidth(), itsCumMask.getHeight()); 00770 00771 // Image<float> disp(width*3, height*2, NO_INIT); 00772 00773 // Image<float> dwinmap = zoomXY(winmap,scale); 00774 // inplaceNormalize(dwinmap, 0.0F, 1.0F); 00775 // inplacePaste(disp, dwinmap, Point2D<int>(0,0)); 00776 00777 // Image<float> dobjmask = zoomXY(objmask,scale); 00778 // inplaceNormalize(dobjmask, 0.0F, 1.0F); 00779 // inplacePaste(disp, dobjmask, Point2D<int>(width,0)); 00780 00781 // Image<float> diormask = zoomXY(iormask,scale); 00782 // inplaceNormalize(diormask, 0.0F, 1.0F); 00783 // inplacePaste(disp, diormask, Point2D<int>(2*width,0)); 00784 00785 // Image<float> dsmmask = itsSmoothMask; 00786 // inplaceNormalize(dsmmask, 0.0F, 1.0F); 00787 // inplacePaste(disp, dsmmask, Point2D<int>(0,height)); 00788 00789 // Image<float> dcummask = itsCumMask; 00790 // inplaceNormalize(dcummask, 0.0F, 1.0F); 00791 // inplacePaste(disp, dcummask, Point2D<int>(width, height)); 00792 00793 // itsWin->drawImage(disp,0,0); 00794 00795 // Raster::waitForKey(); 00796 00797 00798 00799 00800 00801 // LINFO("%d %d", winmap.getWidth(), winmap.getHeight()); 00802 // LINFO("%d %d", objmask.getWidth(), objmask.getHeight()); 00803 // LINFO("%d %d", iormask.getWidth(), iormask.getHeight()); 00804 // LINFO("%d %d", itsSmoothMask.getWidth(), itsSmoothMask.getHeight()); 00805 // LINFO("%d %d", itsCumMask.getWidth(), itsCumMask.getHeight()); 00806 00807 // winning map : 1/16x1/16 --> probably has no more use 00808 // objmask : 1/16x1/16 --> coarse version 00809 // iormask : 1/16x1/16 --> 00810 // its smooth mask : org size 00811 // its cumulative mask : org size 00812 00813 // difference between objmask & iormask is that 00814 // if we realize that this is a background, 00815 // we have small (or none) obj mask 00816 // but large iormask 00817 00818 // in prev implementation is performing lowpass3 00819 00820 // great, let's post an event with all our results: 00821 // rutz::shared_ptr<SimEventShapeEstimatorOutput> 00822 // e(new SimEventShapeEstimatorOutput 00823 // (this, winmap, objmask, iormask, itsSmoothMask, itsCumMask, 00824 // winmap.name(), goodseed)); 00825 00826 00827 00828 uint64 t2 = t.get(); 00829 LINFO("time: %f", t2/1000.0); 00830 } 00831 00832 // ###################################################################### 00833 /* So things look consistent in everyone's emacs... */ 00834 /* Local Variables: */ 00835 /* indent-tabs-mode: nil */ 00836 /* End: */