00001 /*!@file Parallel/pvisionTCP3-nerdcam.C Grab, process over beowulf, move camera */ 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: Laurent Itti <itti@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Parallel/pvisionTCP3-nerdcam.C $ 00035 // $Id: pvisionTCP3-nerdcam.C 9412 2008-03-10 23:10:15Z farhan $ 00036 // 00037 00038 /*! This parallel vision processing master is for use with 00039 pvisionTCP3. See the pvisionTCP3go script in bin/ for how to launch 00040 the slaves. The main difference between the TCP2 and TCP3 versions 00041 is that TCP3 uses load-balancing to send images to the slaves, and 00042 the master node is not only in charge of grabbing the video and 00043 displaying the results, but also of collecting the various 00044 conspicuity maps and assembling them into the saliency map. The 00045 difference between this and pvisionTCP3-master is that here we also 00046 move a pan/tilt camera towards the most salient location in the 00047 display. */ 00048 00049 #include "Beowulf/Beowulf.H" 00050 #include "Component/ModelManager.H" 00051 #include "Devices/BeoChip.H" 00052 #include "Devices/DeviceOpts.H" 00053 #include "Devices/FrameGrabberConfigurator.H" 00054 #include "Image/ColorOps.H" 00055 #include "Image/CutPaste.H" // for inplacePaste() 00056 #include "Image/DrawOps.H" 00057 #include "Image/FilterOps.H" 00058 #include "Image/Image.H" 00059 #include "Image/ImageSet.H" 00060 #include "Image/MathOps.H" 00061 #include "Image/ShapeOps.H" // for decX() etc. 00062 #include "Neuro/NeuroOpts.H" 00063 #include "Neuro/SaccadeController.H" 00064 #include "Neuro/SaccadeControllerConfigurator.H" 00065 #include "Parallel/pvisionTCP-defs.H" 00066 #include "Simulation/SimEventQueue.H" 00067 #include "Simulation/SimEventQueueConfigurator.H" 00068 #include "Transport/FrameIstream.H" 00069 #ifdef HAVE_SDL_SDL_H 00070 #include "Psycho/PsychoDisplay.H" 00071 #endif 00072 #include "Util/Assert.H" 00073 #include "Util/Timer.H" 00074 #include "Util/sformat.H" 00075 #include "Video/RgbConversion.H" // for toVideoYUV422() 00076 00077 #include <signal.h> 00078 #include <unistd.h> 00079 00080 00081 //! servo to use for pan 00082 #define SERVO_PAN 6 00083 00084 //! servo to use for tilt 00085 #define SERVO_TILT 7 00086 00087 //! Number of frames over which average framerate is computed 00088 #define NAVG 20 00089 00090 //! prescale level by which we downsize images before sending them off 00091 #define PRESCALE 2 00092 00093 static bool goforever = true; //!< Will turn false on interrupt signal 00094 00095 //! Signal handler (e.g., for control-C) 00096 void terminate(int s) 00097 { LERROR("*** INTERRUPT ***"); goforever = false; exit(1); } 00098 00099 //! function to receive results from slaves 00100 void receiveCMAPS(nub::soft_ref<Beowulf>& beo, Image<float> *cmap, 00101 int32 *cmapframe); 00102 00103 // ###################################################################### 00104 extern "C" int main(const int argc, char** argv) 00105 { 00106 #ifndef HAVE_SDL_SDL_H 00107 00108 LFATAL("<SDL/SDL.h> must be installed to use this program"); 00109 00110 #else 00111 00112 MYLOGVERB = LOG_INFO; 00113 00114 // instantiate a model manager: 00115 ModelManager manager("Parallel Vision TCP Version 3"); 00116 00117 // Instantiate our various ModelComponents: 00118 nub::soft_ref<SimEventQueueConfigurator> 00119 seqc(new SimEventQueueConfigurator(manager)); 00120 manager.addSubComponent(seqc); 00121 00122 nub::soft_ref<FrameGrabberConfigurator> 00123 gbc(new FrameGrabberConfigurator(manager)); 00124 manager.addSubComponent(gbc); 00125 00126 nub::soft_ref<Beowulf> 00127 beo(new Beowulf(manager, "Beowulf Master", "BeowulfMaster", true)); 00128 manager.addSubComponent(beo); 00129 00130 nub::soft_ref<PsychoDisplay> 00131 d(new PsychoDisplay(manager)); 00132 manager.addSubComponent(d); 00133 00134 nub::soft_ref<SaccadeControllerEyeConfigurator> 00135 scc(new SaccadeControllerEyeConfigurator(manager)); 00136 manager.addSubComponent(scc); 00137 00138 nub::soft_ref<BeoChip> 00139 bc(new BeoChip(manager)); 00140 manager.addSubComponent(bc); 00141 00142 // let's set a bunch of defauls: 00143 manager.exportOptions(MC_RECURSE); 00144 manager.setOptionValString(&OPT_SaccadeControllerEyeType, "Threshfric"); 00145 manager.setOptionValString(&OPT_FrameGrabberType, "V4L"); 00146 manager.setOptionValString(&OPT_SCeyeMaxIdleSecs, "1000.0"); 00147 manager.setOptionValString(&OPT_SCeyeThreshMinOvert, "4.0"); 00148 manager.setOptionValString(&OPT_SCeyeThreshMaxCovert, "3.0"); 00149 // manager.setOptionValString(&OPT_SCeyeSpringK, "1000000.0"); 00150 bc->calibrateServo(SERVO_PAN, 128, 0, 255); 00151 bc->calibrateServo(SERVO_TILT, 128, 0, 255); 00152 bc->setServo(SERVO_PAN, 0.0f); 00153 bc->setServo(SERVO_TILT, 0.0f); 00154 00155 // Parse command-line: 00156 if (manager.parseCommandLine(argc, argv, "", 0, 0) == false) return(1); 00157 00158 // do post-command-line configs: 00159 nub::soft_ref<SimEventQueue> seq = seqc->getQ(); 00160 00161 nub::soft_ref<FrameIstream> gb = gbc->getFrameGrabber(); 00162 if (gb.isInvalid()) 00163 LFATAL("You need to select a frame grabber type via the " 00164 "--fg-type=XX command-line option for this program " 00165 "to be useful"); 00166 int w = gb->getWidth(), h = gb->getHeight(); 00167 00168 nub::ref<SaccadeController> sc = scc->getSC(); 00169 00170 int foa_size = std::min(w, h) / 12; 00171 manager.setModelParamVal("InputFrameDims", Dims(w, h), 00172 MC_RECURSE | MC_IGNORE_MISSING); 00173 manager.setModelParamVal("SCeyeStartAtIP", true, 00174 MC_RECURSE | MC_IGNORE_MISSING); 00175 manager.setModelParamVal("SCeyeInitialPosition",Point2D<int>(w/2,h/2), 00176 MC_RECURSE | MC_IGNORE_MISSING); 00177 manager.setModelParamVal("FOAradius", foa_size, 00178 MC_RECURSE | MC_IGNORE_MISSING); 00179 manager.setModelParamVal("FoveaRadius", foa_size, 00180 MC_RECURSE | MC_IGNORE_MISSING); 00181 00182 // catch signals and redirect them to terminate for clean exit: 00183 signal(SIGHUP, terminate); signal(SIGINT, terminate); 00184 signal(SIGQUIT, terminate); signal(SIGTERM, terminate); 00185 signal(SIGALRM, terminate); 00186 00187 // get prepared to grab, communicate, display, etc: 00188 int32 frame = 0; // count the frames 00189 PixRGB<byte> pix(255, 255, 0); // yellow color for fixations 00190 TCPmessage smsg; // buffer to send messages to nodes 00191 00192 uint64 avgtime = 0; int avgn = 0; // for average framerate 00193 float fps = 0.0F; // to display framerate 00194 Timer tim; // for computation of framerate 00195 Timer masterclock; // master clock for simulations 00196 00197 Image<float> cmap[NBCMAP2]; // array of conspicuity maps 00198 int32 cmapframe[NBCMAP2]; // array of cmap frame numbers 00199 for (int i = 0; i < NBCMAP2; i ++) cmapframe[i] = -1; 00200 int sml = 4; // pyramid level of saliency map 00201 Image<float> sm(w >> sml, h >> sml, ZEROS); // saliency map 00202 Point2D<int> fixation(-1, -1); // coordinates of eye fixation 00203 00204 // image buffer for display: 00205 Image<PixRGB<byte> > disp(w * 2, h + 20, ZEROS); 00206 disp += d->getGrey(); 00207 int dw = d->getDims().w(), dh = d->getDims().h(); 00208 ASSERT(dw == w * 2); ASSERT(dh >= disp.getHeight()); 00209 int ovlyoff = (w * 2) * ((dh - disp.getHeight()) / 2); 00210 int ovluvoff = ovlyoff / 4; 00211 00212 // let's get all our ModelComponent instances started: 00213 manager.start(); 00214 00215 // get our BeoChip going: 00216 bc->lcdClear(); //01234567890123456789 00217 bc->lcdPrintf(0, 0, "pvisionTCP3-nerdcam "); 00218 bc->lcdPrintf(0, 1, "Pan: --- Tilt:---"); 00219 bc->lcdPrintf(0, 2, "xFOA:--- yFOA:---"); 00220 bc->lcdPrintf(0, 3, "xFOV:--- yFOV:---"); 00221 00222 // clear screen 00223 d->clearScreen(); 00224 d->displayText("<SPACE> to start - <SPACE> again to quit"); 00225 while(d->waitForKey() != ' ') ; 00226 d->clearScreen(); 00227 00228 // create an overlay: 00229 d->createYUVoverlay(SDL_YV12_OVERLAY); 00230 00231 // get the frame grabber to start streaming: 00232 gb->startStream(); 00233 00234 // initialize the timers: 00235 tim.reset(); masterclock.reset(); 00236 00237 // ########## MAIN LOOP: grab, process, display: 00238 while(goforever) 00239 { 00240 // receive conspicuity maps: 00241 receiveCMAPS(beo, cmap, cmapframe); 00242 00243 // grab an image: 00244 Image< PixRGB<byte> > ima = gb->readRGB(); 00245 00246 // display image in window: 00247 inplacePaste(disp, ima, Point2D<int>(0, 0)); 00248 Image<float> dispsm(sm); inplaceNormalize(dispsm, 0.0F, 255.0F); 00249 inplacePaste(disp, 00250 Image<PixRGB<byte> >(toRGB(quickInterpolate(dispsm, 1 << sml))), 00251 Point2D<int>(w, 0)); 00252 00253 sc->evolve(*seq); 00254 Point2D<int> eye = sc->getDecision(*seq); 00255 if (eye.i >= 0) fixation = eye; 00256 if (fixation.i >= 0) 00257 { 00258 drawPatch(disp, fixation, 2, pix); 00259 drawCircle(disp, fixation, foa_size, pix, 2); 00260 } 00261 writeText(disp, Point2D<int>(w, 0), sformat("%.1ffps", fps).c_str(), 00262 PixRGB<byte>(255), PixRGB<byte>(0)); 00263 00264 SDL_Overlay* ovl = d->lockYUVoverlay(); 00265 toVideoYUV422(disp, ovl->pixels[0] + ovlyoff, 00266 ovl->pixels[2] + ovluvoff, 00267 ovl->pixels[1] + ovluvoff); 00268 d->unlockYUVoverlay(); 00269 d->displayYUVoverlay(-1, SDLdisplay::NO_WAIT); 00270 00271 // check for space bar pressed; note: will abort us violently if 00272 // ESC is pressed instead: 00273 if (d->checkForKey() == ' ') goforever = false; 00274 00275 // receive conspicuity maps: 00276 receiveCMAPS(beo, cmap, cmapframe); 00277 00278 // send every other frame to processing: 00279 if (frame & 1) 00280 { 00281 // prescale image: 00282 Image<PixRGB<byte> > ima2 = 00283 decY(lowPass5y(decX(lowPass5x(ima),1<<PRESCALE)),1<<PRESCALE); 00284 00285 // we do the job of BEO_RETINA on the master to reduce latency: 00286 // compute luminance and send it off: 00287 Image<byte> lum = luminance(ima2); 00288 00289 // first, send off luminance to orientation slaves: 00290 smsg.reset(frame, BEO_ORI0); smsg.addImage(lum); beo->send(smsg); 00291 smsg.setAction(BEO_ORI45); beo->send(smsg); 00292 smsg.setAction(BEO_ORI90); beo->send(smsg); 00293 smsg.setAction(BEO_ORI135); beo->send(smsg); 00294 00295 // and also send to flicker slave: 00296 smsg.setAction(BEO_FLICKER); beo->send(smsg); 00297 00298 // finally, send to luminance slave: 00299 smsg.setAction(BEO_LUMINANCE); beo->send(smsg); 00300 00301 // compute RG and BY and send them off: 00302 Image<byte> r, g, b, y; getRGBY(ima2, r, g, b, y, (byte)25); 00303 smsg.reset(frame, BEO_REDGREEN); 00304 smsg.addImage(r); smsg.addImage(g); beo->send(smsg); 00305 smsg.reset(frame, BEO_BLUEYELLOW); 00306 smsg.addImage(b); smsg.addImage(y); beo->send(smsg); 00307 } 00308 00309 // receive conspicuity maps: 00310 receiveCMAPS(beo, cmap, cmapframe); 00311 00312 // build our current saliency map: 00313 std::string info = sformat("%06d / ", frame); 00314 Image<float> sminput; 00315 for (int i = 0; i < NBCMAP2; i ++) 00316 if (cmap[i].initialized()) 00317 { 00318 if (sminput.initialized()) sminput += cmap[i]; 00319 else sminput = cmap[i]; 00320 info += sformat("%06d ", cmapframe[i]); 00321 } 00322 else 00323 info += "------ "; 00324 writeText(disp, Point2D<int>(0, h), info.c_str(), 00325 PixRGB<byte>(255), d->getGrey()); 00326 00327 // inject saliency map input into saliency map: 00328 if (sminput.initialized()) sm = sm * 0.7F + sminput * 0.3F; 00329 00330 // evolve our saccade controller up to now: 00331 while(seq->now() < masterclock.getSimTime()) seq->evolve(); 00332 sc->evolve(*seq); 00333 00334 // find most salient location and feed saccade controller: 00335 float maxval; Point2D<int> currwin; findMax(sm, currwin, maxval); 00336 WTAwinner newwin = 00337 WTAwinner::buildFromSMcoords(currwin, sml, true, 00338 masterclock.getSimTime(), 00339 maxval, false); 00340 if (newwin.isValid()) sc->setPercept(newwin, *seq); 00341 00342 // receive conspicuity maps: 00343 receiveCMAPS(beo, cmap, cmapframe); 00344 00345 // compute and show framerate and stats over the last NAVG frames: 00346 avgtime += tim.getReset(); avgn ++; 00347 if (avgn == NAVG) 00348 { 00349 fps = 1000.0F / float(avgtime) * float(avgn); 00350 avgtime = 0; avgn = 0; 00351 } 00352 00353 // update BeoChip display every other frame: 00354 if ((frame & 1) == 0) 00355 { 00356 bc->lcdPrintf(0, 1, "Pan: %03d Tilt:%03d", 00357 bc->getServoRaw(SERVO_PAN), 00358 bc->getServoRaw(SERVO_TILT)); 00359 bc->lcdPrintf(0, 2, "xFOA:%03d yFOA:%03d", 00360 newwin.p.i, newwin.p.j); 00361 bc->lcdPrintf(0, 3, "xFOV:%03d yFOV:%03d", 00362 fixation.i, fixation.j); 00363 } 00364 00365 // move the nerdcam so as to bring eye to the center: 00366 float pan = bc->getServo(SERVO_PAN) + 00367 float(fixation.i)/float(w) - 0.5f; 00368 float tilt = bc->getServo(SERVO_TILT) + 00369 float(fixation.j)/float(h) - 0.5f; 00370 if (pan < -1.0f) pan = -1.0f; else if (pan > 1.0f) pan = 1.0f; 00371 if (tilt < -1.0f) tilt = -1.0f; else if (tilt > 1.0f) tilt = 1.0f; 00372 00373 bc->setServo(SERVO_PAN, pan); 00374 bc->setServo(SERVO_TILT, tilt); 00375 00376 // ready for next frame: 00377 frame++; 00378 while(seq->now() < masterclock.getSimTime()) seq->evolve(); 00379 } 00380 00381 // got interrupted; let's cleanup and exit: 00382 d->destroyYUVoverlay(); 00383 LINFO("Normal exit"); 00384 manager.stop(); 00385 return 0; 00386 00387 #endif 00388 00389 } 00390 00391 // ###################################################################### 00392 void receiveCMAPS(nub::soft_ref<Beowulf>& beo, Image<float> *cmap, 00393 int32 *cmapframe) 00394 { 00395 TCPmessage rmsg; // buffer to receive messages from nodes 00396 int32 rframe, raction, rnode = -1, recnb=0; // receive from any node 00397 while(beo->receive(rnode, rmsg, rframe, raction)) // no wait 00398 { 00399 //LINFO("received %d/%d from %d while at %d", 00400 // rframe, raction, rnode, frame); 00401 switch(raction & 0xffff) 00402 { 00403 case BEO_CMAP: // ############################## 00404 { 00405 // get the map: 00406 Image<float> ima = rmsg.getElementFloatIma(); 00407 00408 // the map number is stored in the high 16 bits of the 00409 // raction field: 00410 int32 mapn = raction >> 16; 00411 if (mapn < 0 || mapn >= NBCMAP2) { 00412 LERROR("Bogus cmap number ignored"); 00413 break; 00414 } 00415 00416 // here is a totally asynchronous system example: we 00417 // just update our current value of a given cmap if 00418 // the one we just received is more recent than the 00419 // one we had so far: 00420 if (cmapframe[mapn] < rframe) 00421 { cmap[mapn] = ima; cmapframe[mapn] = rframe; } 00422 } 00423 00424 break; 00425 default: // ############################## 00426 LERROR("Bogus action %d -- IGNORING.", raction); 00427 break; 00428 } 00429 // limit number of receives, so we don't hold CPU too long: 00430 recnb ++; if (recnb > NBCMAP2 * 2) break; 00431 } 00432 } 00433 00434 // ###################################################################### 00435 /* So things look consistent in everyone's emacs... */ 00436 /* Local Variables: */ 00437 /* indent-tabs-mode: nil */ 00438 /* End: */