00001 /*!@file Neuro/Retina.C a human retina */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2003 // 00005 // by the 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: Laurent Itti <itti@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Neuro/Retina.C $ 00035 // $Id: Retina.C 14634 2011-03-24 00:33:16Z dberg $ 00036 // 00037 00038 #include "Neuro/Retina.H" 00039 00040 #include "Component/OptionManager.H" 00041 #include "Component/ModelOptionDef.H" 00042 #include "Image/CutPaste.H" 00043 #include "Image/DrawOps.H" 00044 #include "Image/ColorOps.H"//for luminance() 00045 #include "Image/MatrixOps.H" 00046 #include "Image/Point2DT.H" 00047 #include "Image/PyramidOps.H" // for buildPyrGaussian(), foveate() 00048 #include "SpaceVariant/SpaceVariantOpts.H" 00049 #include "Media/MediaSimEvents.H" 00050 #include "Neuro/NeuroOpts.H" 00051 #include "Neuro/NeuroSimEvents.H" 00052 #include "Neuro/SpatialMetrics.H" 00053 #include "Channels/ChannelOpts.H" 00054 #include "Psycho/EyeData.H" 00055 #include "Raster/Raster.H" 00056 #include "Simulation/SimEventQueue.H" 00057 #include "Transport/FrameInfo.H" 00058 #include "Transport/FrameOstream.H" 00059 #include "Util/sformat.H" // for sformat() 00060 00061 #include <cstdlib> 00062 #include <iostream> 00063 #include <exception> 00064 #include <vector> 00065 00066 // ###################################################################### 00067 00068 static const ModelOptionDef OPT_ClipMaskFname = 00069 { MODOPT_ARG_STRING, "ClipMaskFname", &MOC_BRAIN, OPTEXP_CORE, 00070 "Name of a grayscale image file to be loaded and used as a " 00071 "clipmask for Brain", 00072 "clip-mask", '\0', "<filename>", "" }; 00073 00074 static const ModelOptionDef OPT_RawInpRectBorder = 00075 { MODOPT_ARG(int), "RawInpRectBorder", &MOC_BRAIN, OPTEXP_CORE, 00076 "Border size to use for the Retina's raw input rectangle (used to " 00077 "select random samples in SimulationViewerCompress), in pixels.", 00078 "rawinput-rect-border", '\0', "<int>", "128" }; 00079 00080 static const ModelOptionDef OPT_EnablePyramidCaches = 00081 { MODOPT_FLAG, "EnablePyramidCaches", &MOC_BRAIN, OPTEXP_CORE, 00082 "Whether to allow caching of commonly-needed image pyramids based " 00083 "on the current input image, such as the intensity pyramid shared " 00084 "between the intensity channel and the motion channels, or the " 00085 "laplacian pyramid shared among the oriented gabor channels. There " 00086 "should be no reason to disable pyramid caching except for " 00087 "debugging or profiling.", 00088 "enable-pyramid-caches", '\0', "", "true" }; 00089 00090 // ###################################################################### 00091 // ###################################################################### 00092 // ########## Retina implementation 00093 // ###################################################################### 00094 // ###################################################################### 00095 Retina::Retina(OptionManager& mgr, const std::string& descrName, const std::string& tagName) : 00096 SimModule(mgr, descrName, tagName) 00097 { } 00098 00099 Retina::~Retina() 00100 { } 00101 00102 // ###################################################################### 00103 // ###################################################################### 00104 // ########## RetinaAdapter implementation 00105 // ###################################################################### 00106 // ###################################################################### 00107 RetinaAdapter::RetinaAdapter(OptionManager& mgr, const std::string& descrName, const std::string& tagName) : 00108 Retina(mgr, descrName, tagName), 00109 SIMCALLBACK_INIT(SimEventInputFrame), 00110 SIMCALLBACK_INIT(SimEventSaccadeStatusEye), 00111 SIMCALLBACK_INIT(SimEventSaveOutput), 00112 itsClipMaskFname(&OPT_ClipMaskFname, this), 00113 itsRawInpRectBorder(&OPT_RawInpRectBorder, this), 00114 itsInitialEyePosition(&OPT_SCeyeInitialPosition, this), 00115 itsEnablePyramidCaches(&OPT_EnablePyramidCaches, this), 00116 itsFoveaRadius(&OPT_FoveaRadius, this), 00117 itsSaveInput(&OPT_RetinaSaveInput, this), // see Neuro/NeuroOpts.{H,C} 00118 itsSaveOutput(&OPT_RetinaSaveOutput, this), 00119 itsFramingImageName(&OPT_InputFramingImageName, this), 00120 itsFramingImagePos(&OPT_InputFramingImagePos, this), 00121 itsFoveateInputDepth(&OPT_FoveateInputDepth, this), // idem 00122 itsShiftInput(&OPT_ShiftInputToEye, this), // see Neuro/NeuroOpts.{H,C} 00123 itsShiftInputBGcol(&OPT_ShiftInputToEyeBGcol, this), // idem 00124 itsInputFOV(&OPT_InputFOV, this), // see Neuro/NeuroOpts.{H,C} 00125 itsSavePyr(&OPT_RetinaStdSavePyramid, this), 00126 itsBlankBlink(&OPT_BlankBlink, this), 00127 itsRetMaskFname(&OPT_RetinaMaskFname, this), 00128 itsFlipHoriz(&OPT_RetinaFlipHoriz, this), 00129 itsFlipVertic(&OPT_RetinaFlipVertic, this), 00130 itsClipMask(), 00131 itsEyePos(-1, -1), 00132 itsEyeBlinkStatus(false), 00133 itsRawInput(), 00134 itsOutput(), 00135 itsFramingImage(), 00136 itsRetinalShift(0, 0), 00137 itsRetMask() 00138 { } 00139 00140 // ###################################################################### 00141 RetinaAdapter::~RetinaAdapter() 00142 { } 00143 00144 // ###################################################################### 00145 void RetinaAdapter::onSimEventSaccadeStatusEye(SimEventQueue& q, rutz::shared_ptr<SimEventSaccadeStatusEye>& e) 00146 { 00147 // update our blink status and eye position: 00148 const TransientStatus bs = e->blinkStatus(); 00149 if (transientStatusIsOn(bs)) itsEyeBlinkStatus = true; 00150 else if (transientStatusIsOff(bs)) itsEyeBlinkStatus = false; 00151 itsEyePos = e->position(); // in retinal coordinates 00152 00153 if (itsEyePos.isValid()) LDEBUG("Using eye position (%d, %d).", itsEyePos.i, itsEyePos.j); 00154 00155 // if we are shifting inputs according to eye position, then we here need to output a new retina every time the eye 00156 // moves; otherwise, we will do it only every time the input frame changes, in onSimEventInputFrame(). Of course if 00157 // there is no input image yet, we output nothing, also time 0 is handled by onSimEventInputFrame(): 00158 if (itsShiftInput.getVal() && itsRawInput.initialized() && q.now() != SimTime::ZERO()) { 00159 // all right, let's process the frame! 00160 const Image<PixRGB<byte> > outimg = getOutput(itsRawInput, itsEyePos, itsEyeBlinkStatus); 00161 00162 // post an event with our output: 00163 InputFrame ifr = InputFrame::fromRgb(&outimg, q.now(), &itsClipMask, 00164 InputFrame::emptyCache, !itsEnablePyramidCaches.getVal()); 00165 postInputFrame(q, ifr); 00166 } 00167 } 00168 00169 // ###################################################################### 00170 void RetinaAdapter::onSimEventInputFrame(SimEventQueue& q, rutz::shared_ptr<SimEventInputFrame>& e) 00171 { 00172 // here is the inputs image: 00173 const Image<PixRGB<byte> > inimg = e->frame().asRgb(); 00174 00175 // keep a copy of input in case we want to later save or use it: 00176 itsRawInput = inimg; 00177 00178 // NOTE that when we are just starting a simulation, the saccade controller will not have had a chance to post an eye 00179 // position yet. In this case, we will get it from our command-line: 00180 if (q.now() == SimTime::ZERO()) { 00181 itsEyePos = itsInitialEyePosition.getVal(); 00182 if (itsEyePos.i == -2 && itsEyePos.j == -2) { 00183 // we want to start at center: 00184 itsEyePos.i = inimg.getWidth() / 2; itsEyePos.j = inimg.getHeight() / 2; 00185 00186 // convert to retinal coords: 00187 itsEyePos += getRawToRetinalOffset(); 00188 } 00189 if (itsEyePos.isValid()) LDEBUG("Using eye position (%d, %d).", itsEyePos.i, itsEyePos.j); 00190 } 00191 00192 // if we are shifting inputs accordint to eye position, then we need to output a new retina every time the eye moves, 00193 // so that's done in onSimEventSaccadeStatusEye; otherwise, here, we will do it only every time the input frame 00194 // changes (except at time 0): 00195 if (itsShiftInput.getVal() == false || q.now() == SimTime::ZERO()) { 00196 // all right, let's process the frame! 00197 const Image<PixRGB<byte> > outimg = getOutput(inimg, itsEyePos, itsEyeBlinkStatus); 00198 00199 // post an event with our output: 00200 InputFrame ifr = 00201 InputFrame::fromRgb(&outimg, q.now(), &itsClipMask, InputFrame::emptyCache, !itsEnablePyramidCaches.getVal()); 00202 postInputFrame(q, ifr); 00203 } 00204 } 00205 00206 // ###################################################################### 00207 void RetinaAdapter::postInputFrame(SimEventQueue& q, InputFrame& ifr) 00208 { 00209 rutz::shared_ptr<SimEventRetinaImage> 00210 eri(new SimEventRetinaImage(this, ifr, getRawInputRectangle(itsRawInput.getDims(), ifr.getDims()), 00211 getRawToRetinalOffset())); 00212 q.post(eri); 00213 } 00214 00215 // ###################################################################### 00216 Rectangle RetinaAdapter::getRawInputRectangle(const Dims& indims, const Dims& outdims) const 00217 { 00218 const int border = itsRawInpRectBorder.getVal(); 00219 Point2D<int> fpos = itsFramingImagePos.getVal(); 00220 Rectangle r = Rectangle::tlbrI(itsRetinalShift.j + fpos.j + border, 00221 itsRetinalShift.i + fpos.i + border, 00222 itsRetinalShift.j + fpos.j + indims.h()-1 - border, 00223 itsRetinalShift.i + fpos.i + indims.w()-1 - border); 00224 return r.getOverlap(Rectangle(Point2D<int>(0,0), outdims)); 00225 } 00226 00227 // ###################################################################### 00228 Point2D<int> RetinaAdapter::getRawToRetinalOffset() const 00229 { return itsFramingImagePos.getVal() + itsRetinalShift; } 00230 00231 // ###################################################################### 00232 void RetinaAdapter::start1() 00233 { 00234 if (!itsClipMaskFname.getVal().empty()) { 00235 itsClipMask = Raster::ReadGray(itsClipMaskFname.getVal()); 00236 LINFO("Using clipmask from image file %s", itsClipMaskFname.getVal().c_str()); 00237 } 00238 00239 // if doing framing, read the framing image: 00240 if (itsFramingImageName.getVal().empty() == false) { 00241 itsFramingImage = Raster::ReadRGB(itsFramingImageName.getVal()); 00242 LINFO("Using %dx%d framing image %s", itsFramingImage.getWidth(), 00243 itsFramingImage.getHeight(), itsFramingImageName.getVal().c_str()); 00244 } 00245 00246 // if doing input masking, read the mask image: 00247 if (itsRetMaskFname.getVal().empty() == false) { 00248 itsRetMask = Raster::ReadGray(itsRetMaskFname.getVal()); 00249 LINFO("Using %dx%d retinal mask %s", itsRetMask.getWidth(), 00250 itsRetMask.getHeight(), itsRetMaskFname.getVal().c_str()); 00251 } 00252 00253 Retina::start1(); 00254 } 00255 00256 // ###################################################################### 00257 void RetinaAdapter:: 00258 onSimEventSaveOutput(SimEventQueue& q, rutz::shared_ptr<SimEventSaveOutput>& e) 00259 { 00260 this->save1(e->sinfo()); 00261 } 00262 00263 // ###################################################################### 00264 void RetinaAdapter::save1(const ModelComponentSaveInfo& sinfo) 00265 { 00266 // get the OFS to save to, assuming sinfo is of type 00267 // SimModuleSaveInfo (will throw a fatal exception otherwise): 00268 nub::ref<FrameOstream> ofs = dynamic_cast<const SimModuleSaveInfo&>(sinfo).ofs; 00269 00270 // save input? 00271 if (itsSaveInput.getVal() && itsRawInput.initialized()) 00272 ofs->writeRGB(itsRawInput, "RETIN-", FrameInfo("retinal input", SRC_POS)); 00273 00274 // save output? 00275 if (itsSaveOutput.getVal() && itsOutput.initialized()) 00276 ofs->writeRGB(itsOutput, "RETOUT-", FrameInfo("retinal output", SRC_POS)); 00277 00278 // save pyramid? 00279 if (itsSavePyr.getVal() && itsOutput.initialized()) 00280 for (uint i = 0; i < itsMultiRetina.size(); i ++) 00281 ofs->writeRGB(itsMultiRetina[i], sformat("RET%d-", i), 00282 FrameInfo(sformat("pyramid level %d in RetinaStd", i), SRC_POS)); 00283 } 00284 00285 // ###################################################################### 00286 Image< PixRGB<byte> > RetinaAdapter::getOutput(const Image<PixRGB<byte> >& inp, 00287 const Point2D<int>& eye, const bool inBlink) 00288 { 00289 // start with our latest input: 00290 Image< PixRGB<byte> > ret = inp; 00291 00292 // if we have a retinal mask image, apply it: 00293 if (itsRetMask.initialized()) 00294 { 00295 if (ret.isSameSize(itsRetMask) == false) 00296 LFATAL("Retina (%dx%d) and retinal mask (%dx%d) dim mismatch", 00297 ret.getWidth(), ret.getHeight(), itsRetMask.getWidth(), itsRetMask.getHeight()); 00298 00299 // we rely on automatic promotions (ret * itsRetMask is Image< PixRGB<int> >) and demotion (assigment to result): 00300 ret = (ret * itsRetMask) / 255; 00301 } 00302 00303 // any flipping? 00304 if (itsFlipHoriz.getVal()) ret = flipHoriz(ret); 00305 if (itsFlipVertic.getVal()) ret = flipVertic(ret); 00306 00307 // embed our latest raw input within a larger framing image if any: 00308 if (itsFramingImage.initialized()) 00309 { 00310 ret = itsFramingImage; 00311 inplacePaste(ret, inp, itsFramingImagePos.getVal()); 00312 } 00313 00314 // do we want to shift the input so that it is centered at the current eye position? 00315 if (itsShiftInput.getVal()) 00316 { 00317 // do we have a valid eye position yet? 00318 if (eye.isValid()) 00319 { 00320 LINFO("Shifting input to eye position (%d, %d)", eye.i, eye.j); 00321 00322 itsRetinalShift.i += ret.getWidth() / 2 - eye.i; 00323 itsRetinalShift.j += ret.getHeight() / 2 - eye.j; 00324 00325 ret = shiftClean(ret, itsRetinalShift.i, itsRetinalShift.j, itsShiftInputBGcol.getVal()); 00326 } 00327 00328 // in addition, do we want to crop the shifted input to a central field of view? 00329 Dims fovd(itsInputFOV.getVal()); 00330 if (fovd.isEmpty() == false) 00331 { 00332 Point2D<int> fovoff( (ret.getWidth() - fovd.w()) / 2, (ret.getHeight() - fovd.h()) / 2); 00333 if (eye.i != -1 || eye.j != -1) itsRetinalShift -= fovoff; 00334 ret = crop(ret, fovoff, fovd); 00335 } 00336 } 00337 00338 // if doing foveation, create color pyramid for itsMultiRetina, and foveate the input image, storing the result into 00339 // itsRetina; otherwise, itsMultiRetina remains unitialized and itsRetina is a straight copy of the input frame: 00340 const uint fid = itsFoveateInputDepth.getVal(); 00341 if (fid > 0) 00342 { 00343 LINFO("Initializing multiretina (depth %d)", fid); 00344 itsMultiRetina = buildPyrGaussian(ret, 0, fid, 9); 00345 00346 // create a mask with a disk at current overt fixation: 00347 Image<byte> mask(ret.getDims(), ZEROS); 00348 00349 // do we have a valid eye position yet? 00350 if (eye.isValid()) 00351 { 00352 drawDisk(mask, eye, itsFoveaRadius.getVal(), byte(255)); 00353 LINFO("Foveating input at (%d, %d)", eye.i, eye.j); 00354 } 00355 else 00356 LINFO("Foveating input with uniform medium blur"); 00357 00358 // use the mask to foveate; if we don't have a SaccadeController but have nevertheless requested 00359 // itsFoveateInputDepth > 0, the behavior of foveate() is to apply a uniform medium blur to the entire image. If 00360 // we do have a SaccadeController, this will only happen as long as no overt fixation has been made yet: 00361 ret = foveate(mask, itsMultiRetina); 00362 } 00363 00364 // if we are in a blink, assume that inputs are blanked out: 00365 if (inBlink && itsBlankBlink.getVal()) { 00366 LINFO("#### Visual input blanked out while in blink ####"); 00367 ret.clear(PixRGB<byte>(0)); 00368 } 00369 00370 // derived classes may want to transform the retinal image at this point: 00371 ret = transform(ret); 00372 00373 // keep a copy of output in case we want to later save or use it: 00374 itsOutput = ret; 00375 00376 // ready to return: 00377 return ret; 00378 } 00379 00380 // ###################################################################### 00381 // ###################################################################### 00382 // ########## RetinaConfigurator implementation 00383 // ###################################################################### 00384 // ###################################################################### 00385 RetinaConfigurator::RetinaConfigurator(OptionManager& mgr, const std::string& descrName, const std::string& tagName) : 00386 ModelComponent(mgr, descrName, tagName), 00387 itsType(&OPT_RetinaType, this), 00388 itsRET(new RetinaStub(mgr)) 00389 { 00390 addSubComponent(itsRET); 00391 } 00392 00393 // ###################################################################### 00394 RetinaConfigurator::~RetinaConfigurator() 00395 { } 00396 00397 // ###################################################################### 00398 nub::ref<Retina> RetinaConfigurator::getRET() const 00399 { return itsRET; } 00400 00401 // ###################################################################### 00402 void RetinaConfigurator::paramChanged(ModelParamBase* const param, 00403 const bool valueChanged, 00404 ParamClient::ChangeStatus* status) 00405 { 00406 ModelComponent::paramChanged(param, valueChanged, status); 00407 00408 // was that a change of our baby's name? 00409 if (param == &itsType) { 00410 // let's unregister our existing Retina: 00411 removeSubComponent(*itsRET); 00412 00413 // instantiate a Retina of the appropriate type (when the old 00414 // Retina is destroyed, it will un-export its command-line 00415 // options): 00416 if (itsType.getVal().compare("Stub") == 0) // stub 00417 itsRET.reset(new RetinaStub(getManager())); 00418 else if (itsType.getVal().compare("Std") == 0) // standard 00419 itsRET.reset(new RetinaStd(getManager())); 00420 else if (itsType.getVal().compare("CT") == 0) // Cortical or collicular - Transform 00421 itsRET.reset(new RetinaCT(getManager())); 00422 else 00423 LFATAL("Unknown Retina type %s", itsType.getVal().c_str()); 00424 00425 // add our baby as a subcomponent of us so that it will become 00426 // linked to the manager through us (hopefully we are registered 00427 // with the manager), which in turn will allow it to export its 00428 // command-line options and get configured: 00429 addSubComponent(itsRET); 00430 00431 // tell the controller to export its options: 00432 itsRET->exportOptions(MC_RECURSE); 00433 00434 // some info message: 00435 LINFO("Selected RET of type %s", itsType.getVal().c_str()); 00436 } 00437 } 00438 00439 // ###################################################################### 00440 // ###################################################################### 00441 // ########## RetinaStub implementation 00442 // ###################################################################### 00443 // ###################################################################### 00444 RetinaStub::RetinaStub(OptionManager& mgr, const std::string& descrName, const std::string& tagName) : 00445 Retina(mgr, descrName, tagName), 00446 SIMCALLBACK_INIT(SimEventInputFrame) 00447 { } 00448 00449 // ###################################################################### 00450 RetinaStub::~RetinaStub() 00451 { } 00452 00453 // ###################################################################### 00454 void RetinaStub::onSimEventInputFrame(SimEventQueue& q, rutz::shared_ptr<SimEventInputFrame>& e) 00455 { 00456 GenericFrame fr = e->frame(); 00457 InputFrame ifr; 00458 00459 switch(fr.nativeType()) { 00460 case GenericFrame::RGBD: 00461 { 00462 const Image<PixRGB<byte> > inimg = fr.asRgb(); 00463 const Image<uint16> dimg = fr.asGrayU16(); 00464 ifr = InputFrame::fromRgbDepth(&inimg, &dimg, q.now()); 00465 } 00466 break; 00467 default: 00468 { 00469 const Image<PixRGB<byte> > inimg = fr.asRgb(); 00470 ifr = InputFrame::fromRgb(&inimg, q.now()); 00471 } 00472 break; 00473 } 00474 00475 // post an event with our output: 00476 Rectangle rect(Point2D<int>(0,0), ifr.getDims()); 00477 rutz::shared_ptr<SimEventRetinaImage> eri(new SimEventRetinaImage(this, ifr, rect, Point2D<int>(0,0))); 00478 q.post(eri); 00479 } 00480 00481 // ###################################################################### 00482 // ###################################################################### 00483 // ########## RetinaStd implementation 00484 // ###################################################################### 00485 // ###################################################################### 00486 RetinaStd::RetinaStd(OptionManager& mgr, const std::string& descrName, 00487 const std::string& tagName) : 00488 RetinaAdapter(mgr, descrName, tagName) 00489 { } 00490 00491 // ###################################################################### 00492 RetinaStd::~RetinaStd() 00493 { } 00494 00495 // ###################################################################### 00496 Image<PixRGB<byte> > RetinaStd::transform(const Image<PixRGB<byte> >& image) 00497 { return image; } 00498 00499 // ###################################################################### 00500 // ###################################################################### 00501 // ########## RetinaCT implementation 00502 // ###################################################################### 00503 // ###################################################################### 00504 RetinaCT::RetinaCT(OptionManager& mgr, const std::string& descrName, const std::string& tagName) : 00505 RetinaAdapter(mgr, descrName, tagName), 00506 itsSurrFac(&OPT_SpaceVariantDogSize, this), 00507 itsLevels(&OPT_SpaceVariantChanScales, this), 00508 itsTransform(new SpaceVariantModule(mgr)), 00509 itsRgbCache(new PyramidCache<PixRGB<float> >), itsFloatCache(new PyramidCache<float>) 00510 { 00511 this->addSubComponent(itsTransform); 00512 } 00513 00514 // ###################################################################### 00515 RetinaCT::~RetinaCT() 00516 { } 00517 00518 // ###################################################################### 00519 void RetinaCT::start1() 00520 { 00521 getRootObject()->setModelParamVal("UseSpaceVariantBoundary", true, MC_RECURSE | MC_IGNORE_MISSING); 00522 RetinaAdapter::start1(); 00523 } 00524 00525 // ###################################################################### 00526 Rectangle RetinaCT::getRawInputRectangle(const Dims& indims, const Dims& outdims) const 00527 { 00528 Rectangle r(Point2D<int>(0,0), indims); 00529 return r; 00530 } 00531 00532 // ###################################################################### 00533 void RetinaCT::postInputFrame(SimEventQueue& q, InputFrame& ifr) 00534 { 00535 ifr.setPyrCacheRgb(itsRgbCache); 00536 ifr.setPyrCache(itsFloatCache); 00537 00538 rutz::shared_ptr<SimEventRetinaImage> 00539 eri(new SimEventRetinaImage(this, ifr, getRawInputRectangle(itsRawInput.getDims(), ifr.getDims()), 00540 getRawToRetinalOffset(),itsTransform->getTransform(),itsTransform->getMapTransform())); 00541 q.post(eri); 00542 } 00543 00544 // ###################################################################### 00545 Image<PixRGB<byte> > RetinaCT::transform(const Image<PixRGB<byte> >& inp) 00546 { 00547 //transform image and cache 00548 ImageSet<PixRGB<float> > rgbcache; 00549 rutz::mutex_lock_class lock; 00550 if (itsRgbCache->gaussian5.beginSet(inp, &lock)) 00551 { 00552 const float maxrf = itsTransform->getMaxRf(itsLevels.getVal().getMaxVariance(), itsSurrFac.getVal()); 00553 rgbcache = itsTransform->getScaleSpace(inp, maxrf); 00554 itsRgbCache->gaussian5.endSet(inp, rgbcache, &lock); 00555 } 00556 00557 Image<float> inpl = luminance(inp); 00558 if (itsFloatCache->gaussian5.beginSet(inpl, &lock)) 00559 { 00560 ImageSet<float> floatcache(rgbcache.size()); 00561 for (uint ii = 0; ii < floatcache.size(); ++ii) 00562 floatcache[ii] = luminance(rgbcache[ii]); 00563 00564 itsFloatCache->gaussian5.endSet(inpl, floatcache, &lock); 00565 } 00566 00567 Image<PixRGB<byte> > output = itsTransform->transform(inp, &rgbcache); 00568 return output; 00569 } 00570 00571 // ###################################################################### 00572 /* So things look consistent in everyone's emacs... */ 00573 /* Local Variables: */ 00574 /* indent-tabs-mode: nil */ 00575 /* End: */