00001 /*!@file AppDevices/app-cam-saccades.C Use a pan-tilt camera for executing saccades */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00005 // University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: Dirk Walther <walther@caltech.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/AppDevices/app-cam-saccades.C $ 00035 // $Id: app-cam-saccades.C 10982 2009-03-05 05:11:22Z itti $ 00036 // 00037 00038 #include "Channels/ChannelBase.H" 00039 #include "Channels/ChannelOpts.H" 00040 #include "Component/GlobalOpts.H" 00041 #include "Component/ModelManager.H" 00042 #include "Devices/DeviceOpts.H" 00043 #include "Devices/FrameGrabberConfigurator.H" 00044 #include "Devices/VCC4.H" 00045 #include "GUI/XWinManaged.H" 00046 #include "Image/DrawOps.H" 00047 #include "Image/Image.H" 00048 #include "Image/ShapeOps.H" 00049 #include "Image/fancynorm.H" 00050 #include "Media/MediaSimEvents.H" 00051 #include "Neuro/NeuroOpts.H" 00052 #include "Neuro/NeuroSimEvents.H" 00053 #include "Neuro/SaccadeControllers.H" 00054 #include "Neuro/ShapeEstimator.H" 00055 #include "Neuro/SimulationViewerStd.H" 00056 #include "Neuro/SpatialMetrics.H" 00057 #include "Neuro/StdBrain.H" 00058 #include "Neuro/VisualCortex.H" 00059 #include "Simulation/SimEventQueueConfigurator.H" 00060 #include "Transport/FrameIstream.H" 00061 #include "Util/log.H" 00062 00063 #include <algorithm> // for std::max() and std::min() 00064 #include <cmath> 00065 #include <iostream> 00066 #include <stdio.h> 00067 00068 //#define CAM_DISPLAY NULL 00069 #define CAM_WINDOW_NAME "Saliency Calculated" 00070 #define CAM_PREV_NAME "Captured Image" 00071 #define ANG_FACTOR (180.0/Pi) 00072 #define MAXPAN ((float)100.0) 00073 #define MINPAN ((float)-100.0) 00074 #define MAXTILT ((float)30.0) 00075 #define MINTILT ((float)-30.0) 00076 00077 // make random saccade if the salmap evolves for more than TOO_MUCH_TIME s 00078 #define TOO_MUCH_TIME SimTime::SECS(0.7) 00079 00080 00081 float sqr (float x); 00082 float randang(); 00083 00084 // give back a random angle increment/decrement 00085 inline float randang () 00086 { 00087 const float max_randang = 30.0; 00088 return ((float)(2.0 * rand() / RAND_MAX) - 1.0) * max_randang; 00089 } 00090 00091 // sqr(x) = x*x; 00092 inline float sqr (float x) 00093 { 00094 return (x * x); 00095 } 00096 00097 // ###################################################################### 00098 // ##### Main Program: 00099 // ###################################################################### 00100 /*! This program takes input from a camera, calculates 00101 the most salient spot(s) and moves the camera there 00102 according to the following rules:<p> 00103 1) take image from camera 00104 2) calculate next most salient spot 00105 3) If (spot is already in list) goto 2 00106 4) store spot in list 00107 5) move to the spot 00108 6) goto 1<p> 00109 The list is finite (small) sized and "forgets" the oldest entries. 00110 When the search for the next most salient spot in a given image 00111 takes too much time, the camera is moved by a random amount.*/ 00112 int main(const int argc, const char **argv) 00113 { 00114 LOG_FLAGS &= (~LOG_FULLTRACE); 00115 00116 // instantiate a model manager: 00117 ModelManager manager("Saccade Tester"); 00118 00119 // Instantiate our various ModelComponents: 00120 nub::soft_ref<SimEventQueueConfigurator> 00121 seqc(new SimEventQueueConfigurator(manager)); 00122 manager.addSubComponent(seqc); 00123 00124 nub::soft_ref<FrameGrabberConfigurator> 00125 gbc(new FrameGrabberConfigurator(manager)); 00126 manager.addSubComponent(gbc); 00127 00128 nub::soft_ref<VCC4> pantilt(new VCC4(manager)); 00129 manager.addSubComponent(pantilt); 00130 00131 nub::soft_ref<StdBrain> brain(new StdBrain(manager)); 00132 manager.addSubComponent(brain); 00133 00134 nub::ref<SpatialMetrics> metrics(new SpatialMetrics(manager)); 00135 manager.addSubComponent(metrics); 00136 00137 // choose a V4Lgrabber by default, and a few custom grabbing 00138 // defaults, for backward compatibility with an older version of 00139 // this program: 00140 manager.setOptionValString(&OPT_FrameGrabberType, "V4L"); 00141 manager.setOptionValString(&OPT_FrameGrabberDims, "320x240"); 00142 manager.setOptionValString(&OPT_FrameGrabberChannel, "1"); 00143 00144 // set some more parameters as defaults 00145 manager.setOptionValString(&OPT_OriInteraction,"SubtractMean"); 00146 manager.setOptionValString(&OPT_OrientComputeType,"Steerable"); 00147 manager.setOptionValString(&OPT_RawVisualCortexChans,"OIC"); 00148 manager.setOptionValString(&OPT_UseRandom,"false"); 00149 manager.setOptionValString(&OPT_ShapeEstimatorMode,"FeatureMap"); 00150 manager.setOptionValString(&OPT_ShapeEstimatorSmoothMethod,"Chamfer"); 00151 manager.setOptionValString(&OPT_IORtype,"ShapeEst"); 00152 manager.setOptionValString(&OPT_SVdisplayFOA,"true"); 00153 manager.setOptionValString(&OPT_SVdisplayPatch,"false"); 00154 manager.setOptionValString(&OPT_SVdisplayFOALinks,"false"); 00155 manager.setOptionValString(&OPT_SVdisplayAdditive,"false"); 00156 manager.setOptionValString(&OPT_SVdisplayTime,"false"); 00157 manager.setOptionValString(&OPT_SVdisplayBoring,"false"); 00158 metrics->setFOAradius(20); 00159 00160 // Parse command-line: 00161 if (manager.parseCommandLine(argc, argv, "", 0, 0) == false) return(1); 00162 00163 // do post-command-line configs: 00164 nub::soft_ref<FrameIstream> gb = gbc->getFrameGrabber(); 00165 if (gb.isInvalid()) 00166 LFATAL("You need to select a frame grabber type via the " 00167 "--fg-type=XX command-line option for this program " 00168 "to be useful"); 00169 const Dims dims = gb->peekDims(); 00170 nub::soft_ref<SimEventQueue> seq = seqc->getQ(); 00171 00172 // let's get all our ModelComponent instances started: 00173 manager.start(); 00174 00175 float hdeg = 80, wdeg = 100;; 00176 float min_dist = std::min(hdeg, wdeg) / 12; 00177 const int num_mem = 10; 00178 float mem_pan[num_mem], mem_tilt[num_mem]; 00179 int mem_ptr = 0; 00180 bool mem_filled = false; 00181 bool is_in_mem; 00182 int mem_top; 00183 00184 pantilt->CameraInitialize(true); 00185 pantilt->PlainCommand(VCC4_SetZoomingWIDE); 00186 00187 XWinManaged salWin(dims*2, -1, -1, CAM_WINDOW_NAME); 00188 XWinManaged capWin(dims*2, -1, -1, CAM_PREV_NAME); 00189 00190 //StdBrain brain(4,2,4,3,4,4,SMDfoa,VCXNORM_FANCY); 00191 Image<PixRGB <byte> > capImg, salImg, capImg2; 00192 00193 float campos_pan = 0.0, campos_tilt = 0.0; 00194 float oldpos_pan = 0.0, oldpos_tilt = 0.0; 00195 float newpos_pan, newpos_tilt; 00196 00197 // main loop 00198 while (!salWin.pressedCloseButton() && !capWin.pressedCloseButton()) 00199 { 00200 // wait until camera stops moving 00201 //while (pantilt->IsMoving()) usleep (10000); 00202 //usleep (100000); 00203 00204 brain->reset(MC_RECURSE); 00205 00206 // capture image and show in preview window 00207 for (int i = 0; i < 20; i++) capImg = gb->readRGB(); 00208 capImg2 = capImg; 00209 drawDisk(capImg2, Point2D<int>(dims/2), 00210 3, PixRGB<byte>(255,0,0)); 00211 capWin.drawImage(rescale(capImg2,dims*2)); 00212 rutz::shared_ptr<SimEventInputFrame> 00213 e(new SimEventInputFrame(brain.get(), GenericFrame(capImg), 0)); 00214 seq->post(e); // post the image to the brain 00215 00216 // evolve 00217 seq->resetTime(); 00218 while(true) 00219 { 00220 Point2D<int> winner(-1, -1); 00221 while(seq->now() < TOO_MUCH_TIME) 00222 { 00223 if (SeC<SimEventWTAwinner> e = 00224 seq->check<SimEventWTAwinner>(0)) 00225 { 00226 winner = e->winner().p; 00227 break; 00228 } 00229 if (seq->evolve() == SIM_BREAK) LFATAL("BREAK"); 00230 } 00231 00232 // too much time -> random saccade 00233 if (seq->now() >= TOO_MUCH_TIME || winner.isValid() == false) 00234 { 00235 newpos_pan = oldpos_pan + randang(); 00236 newpos_pan = std::max(newpos_pan, MINPAN); 00237 newpos_pan = std::min(newpos_pan, MAXPAN); 00238 00239 newpos_tilt = oldpos_tilt + randang(); 00240 newpos_tilt = std::max(newpos_tilt, MINTILT); 00241 newpos_tilt = std::min(newpos_tilt, MAXTILT); 00242 00243 LINFO("Using random position."); 00244 break; 00245 } 00246 00247 // calculate new camera position and make sure they are in 00248 // the right range 00249 newpos_pan = oldpos_pan + (((float)winner.i - 00250 0.5 * (float)dims.w()) / 00251 (float)dims.w() * 0.5 * wdeg); 00252 //newpos_pan = oldpos_pan + ANG_FACTOR * tan(X_FACTOR * 00253 //((float)winner.i - 0.5*dims.w())); 00254 newpos_pan = std::max(newpos_pan, MINPAN); 00255 newpos_pan = std::min(newpos_pan, MAXPAN); 00256 00257 newpos_tilt = oldpos_tilt + ((0.5 * (float)dims.h() - 00258 (float)winner.j) / 00259 (float)dims.h() * 0.5 * hdeg); 00260 //newpos_tile = oldpos_tilt + ANG_FACTOR * tan(Y_FACTOR * 00261 //((float)winner.j - 0.5*dims.h())); 00262 newpos_tilt = std::max(newpos_tilt, MINTILT); 00263 newpos_tilt = std::min(newpos_tilt, MAXTILT); 00264 00265 is_in_mem = false; 00266 mem_top = mem_filled ? num_mem : mem_ptr; 00267 for (int i = 0; i < mem_top; i++) 00268 is_in_mem |= (sqr(newpos_pan - mem_pan[i]) + 00269 sqr(newpos_tilt - mem_tilt[i]) < sqr(min_dist)); 00270 00271 if (!is_in_mem) break; 00272 00273 LDEBUG("Using next cycle."); 00274 LDEBUG("newpos = %f, %f", newpos_pan, newpos_tilt); 00275 #ifdef DEBUG 00276 std::cout << "Mark 15 \n"; 00277 for (int i = 0; i < mem_top; i++) 00278 LDEBUG("mempos[%i] = %f, %f",i, mem_pan[i], mem_tilt[i]); 00279 #endif 00280 } 00281 00282 campos_pan = newpos_pan; 00283 campos_tilt = newpos_tilt; 00284 00285 mem_pan [mem_ptr] = newpos_pan; 00286 mem_tilt [mem_ptr] = newpos_tilt; 00287 mem_ptr++; 00288 00289 if (mem_ptr >= num_mem) 00290 { 00291 mem_ptr = 0; 00292 mem_filled = true; 00293 } 00294 00295 LDEBUG("mem_ptr = %i",mem_ptr); 00296 LDEBUG("newpos = %i, %i",int(newpos_pan),int(newpos_tilt)); 00297 00298 oldpos_pan = newpos_pan; 00299 oldpos_tilt = newpos_tilt; 00300 pantilt->gotoPosition (campos_pan, campos_tilt); 00301 00302 // set the window's title bar to the ShapeEstimatorLabel 00303 //salWin.setTitle(brain->getSE()->getWinningLabel().c_str()); 00304 00305 // retrieve the image with marked salient spot and display it 00306 ////////////salImg = brain->getSV()->getTraj(seq->now()); 00307 salWin.drawImage(rescale(salImg,dims*2)); 00308 00309 // end of the main loop 00310 } 00311 pantilt->PlainCommand(VCC4_GoHome); 00312 manager.stop(); 00313 return 0; 00314 } 00315 00316 00317 00318 // ###################################################################### 00319 /* So things look consistent in everyone's emacs... */ 00320 /* Local Variables: */ 00321 /* indent-tabs-mode: nil */ 00322 /* End: */