app-ScorbotMultiGrab.C

00001 #include <iomanip>
00002 #include <vector>
00003 #include <pthread.h>
00004 #include <signal.h>
00005 #include <fstream>
00006 #include <boost/lexical_cast.hpp>
00007 #include <boost/algorithm/string.hpp>
00008 #include <list>
00009 
00010 #include "Devices/V4L2grabber.H"
00011 #include "Util/WorkThreadServer.H"
00012 #include "Raster/Raster.H"
00013 #include "Component/ModelManager.H"
00014 #include "Media/FrameSeries.H"
00015 #include "Raster/GenericFrame.H"
00016 #include "Transport/FrameInfo.H"
00017 #include "Image/Image.H"
00018 #include "Image/DrawOps.H"
00019 #include "GUI/PrefsWindow.H"
00020 #include "Devices/DeviceOpts.H"
00021 #include "Devices/Serial.H"
00022 #include "Image/DrawOps.H"
00023 #include "GUI/XWinManaged.H"
00024 #include "GUI/ImageDisplayStream.H"
00025 #include "GUI/PrefsWindow.H"
00026 #include "GUI/DebugWin.H"
00027 #include "GUI/PrefsWindow.H"
00028 #include "Util/StringUtil.H"
00029 #include "rutz/time.h"
00030 #include "Util/csignals.H"
00031 
00032 class ScorbotSimple;
00033 
00034 const char* dataDir = "/home2/robotscenes";
00035 std::string sceneDir = "";
00036 int pathID = 0;
00037 int sceneID = 0;
00038 
00039 nub::soft_ref<OutputFrameSeries> ofs;
00040 nub::soft_ref<ScorbotSimple> scorbot;
00041 XWinManaged *userInteractiveWindow = NULL;
00042 volatile bool record = false;
00043 volatile bool keepgoing = true;
00044 volatile bool dolivedisplay = false;
00045 
00046 // 'volatile' because we will modify this from signal handlers
00047 volatile int signum = 0;
00048 
00049 Image< PixRGB<byte> > inputImage;
00050 volatile unsigned long currentVideoFrameNumber = 0;
00051 pthread_mutex_t imgMutex = PTHREAD_MUTEX_INITIALIZER;
00052 
00053 rutz::time epoch = rutz::time::wall_clock_now();
00054 
00055 const int focusval[4] = { 10, 6, 7, 8 };
00056 
00057 // number of cameras
00058 #define NUMCAMS 4
00059 
00060 // ######################################################################
00061 //! An Async Image Writer which works on a separate thread 
00062 class WriteJob : public JobServer::Job
00063 {
00064 public:
00065   WriteJob(const Image< PixRGB<byte> > img_, const std::string fname_) :
00066     img(img_), fname(fname_) { }
00067 
00068   virtual ~WriteJob() { }
00069 
00070   virtual void run() { Raster::WriteRGB(img, fname); }
00071 
00072   virtual const char* jobType() const { return "WriteJob"; }
00073 
00074 private:
00075   const Image< PixRGB<byte> > img;
00076   const std::string fname;
00077 };
00078 
00079 WorkThreadServer writer("Write Server", 5, false); // write several files at once since png compression is bottleneck
00080 
00081 void AsyncWriteImage(const Image< PixRGB<byte> > img, size_t cameraID, unsigned long frameNumber)
00082 {
00083   const std::string fname = sformat("%s/RobotScene-s%04d-p%02d/RobotScene-s%04d-p%02d-c%02zu-f%06lu.ppm", 
00084                                     dataDir, sceneID, pathID, sceneID, pathID, cameraID, frameNumber);
00085   writer.enqueueJob(rutz::make_shared(new WriteJob(img, fname)));
00086 }
00087 
00088 // ######################################################################
00089 // grabber devices
00090 std::vector< nub::ref<V4L2grabber> > grabbers;
00091 
00092 //! A grab job (we grab from all cameras in parallel)
00093 class GrabJob : public JobServer::Job
00094 {
00095 public:
00096   GrabJob(const size_t cameraID_) :
00097     cameraID(cameraID_),
00098     frameNumber(0)
00099   {
00100     ASSERT(cameraID < grabbers.size());
00101   }
00102 
00103   virtual ~GrabJob() {}
00104 
00105   virtual void run() 
00106   {
00107     nub::ref<V4L2grabber> grabber = grabbers[cameraID];
00108     bool recording = false;
00109 
00110     while(keepgoing) {
00111       // let's grab an image:
00112       GenericFrame frame = grabber->readFrame();
00113       Image< PixRGB<byte> > img;
00114 
00115       if (record) {
00116         if (recording == false) { LINFO("Start recording camera %"ZU" at %fms...", cameraID, (rutz::time::wall_clock_now() - epoch).msec()); recording = true; }
00117         img = frame.asRgb();
00118         AsyncWriteImage(img, cameraID, frameNumber);
00119         ++frameNumber;
00120         if ((frameNumber % 10) == 0) LINFO("Camera %"ZU" got frame %"ZU" at %fms", cameraID, frameNumber, (rutz::time::wall_clock_now() - epoch).msec());
00121       } else {
00122         if (recording == true) { LINFO("Stop recording camera %"ZU" frame %"ZU" at %fms...", cameraID, frameNumber, (rutz::time::wall_clock_now() - epoch).msec()); recording = false; }
00123         frameNumber = 0;
00124       }
00125 
00126       // pass the current frame to the display thread
00127       if (cameraID == 0 && record == false) {
00128         if (img.initialized() == false) img = frame.asRgb();
00129         pthread_mutex_lock(&imgMutex);
00130         inputImage = img;
00131         ++currentVideoFrameNumber;
00132         pthread_mutex_unlock(&imgMutex);
00133       }
00134     }
00135   }
00136 
00137   virtual const char* jobType() const { return "GrabJob"; }
00138 
00139 private:
00140   const size_t cameraID;
00141   unsigned long frameNumber;
00142 };
00143 
00144 WorkThreadServer grabberThreadServer("Grab Server", NUMCAMS, false); // number of parallel grab jobs
00145 
00146 void setupGrabbers(ModelManager& manager)
00147 {
00148   size_t cameraID = 0;
00149   while(true)
00150   {
00151     std::string dev = sformat("/dev/video%"ZU, grabbers.size());
00152     LINFO("Trying to open %s", dev.c_str());
00153     int fd = open(dev.c_str(), O_RDONLY);
00154     if (fd == -1) { LINFO("%s not found -- Skipping.", dev.c_str()); break; } else close(fd);
00155 
00156     // instantiate and configure a grabber:
00157     nub::ref<V4L2grabber> grabber(new V4L2grabber(manager, dev, dev));
00158     
00159     // do not export any command-line option to avoid clashes among grabber instances:
00160     grabber->forgetExports();
00161 
00162     // let's set everything by hand here to ensure consistency:
00163     grabber->setModelParamVal("FrameGrabberDevice", dev);
00164 
00165     grabber->setModelParamVal("FrameGrabberNbuf", 2);
00166     grabber->setModelParamVal("FrameGrabberStreaming", true); 
00167     grabber->setModelParamVal("FrameGrabberByteSwap", false);
00168     grabber->setModelParamVal("FrameGrabberDims", Dims(1280,720));
00169     grabber->setModelParamVal("FrameGrabberMode", VIDFMT_YUYV);
00170     grabber->setModelParamVal("FrameGrabberChannel", 0);
00171 
00172     // to make sure we will force the hardware to set its values, first set some trash values, then set the real values:
00173     // turn all auto controls to on:
00174     grabber->setModelParamVal("FrameGrabberWhiteBalTempAuto", true);
00175     grabber->setModelParamVal("FrameGrabberPowerLineFreq", 2);
00176     grabber->setModelParamVal("FrameGrabberBacklightComp", 1);
00177     grabber->setModelParamVal("FrameGrabberFocusAuto", true);
00178  
00179     // now turn all auto controls to off:
00180     grabber->setModelParamVal("FrameGrabberWhiteBalTempAuto", false);
00181     grabber->setModelParamVal("FrameGrabberPowerLineFreq", 0);
00182     grabber->setModelParamVal("FrameGrabberBacklightComp", 0);
00183     grabber->setModelParamVal("FrameGrabberExposureAuto", 3); // that's still auto
00184     grabber->setModelParamVal("FrameGrabberFocusAuto", false);
00185     grabber->setModelParamVal("FrameGrabberExposureAuto", 1); // now we are full manual for exposure
00186  
00187     // set all manual settings 1-off from what we want:
00188     grabber->setModelParamVal("FrameGrabberBrightness", 134);
00189     grabber->setModelParamVal("FrameGrabberContrast", 6);
00190     grabber->setModelParamVal("FrameGrabberSaturation", 84);
00191     grabber->setModelParamVal("FrameGrabberWhiteBalTemp", 3101);
00192     grabber->setModelParamVal("FrameGrabberExposureAbs", 39);
00193     grabber->setModelParamVal("FrameGrabberSharpness", 26);
00194     grabber->setModelParamVal("FrameGrabberFocus", focusval[cameraID] + 1);
00195     grabber->setModelParamVal("FrameGrabberZoom", 1);
00196  
00197     // and now the real values:
00198     grabber->setModelParamVal("FrameGrabberPowerLineFreq", 0); // should be set already but just in case...
00199     grabber->setModelParamVal("FrameGrabberBacklightComp", 0);
00200     grabber->setModelParamVal("FrameGrabberExposureAuto", 1);
00201     grabber->setModelParamVal("FrameGrabberWhiteBalTempAuto", false);
00202  
00203     grabber->setModelParamVal("FrameGrabberBrightness", 133);
00204     grabber->setModelParamVal("FrameGrabberContrast", 5);
00205     grabber->setModelParamVal("FrameGrabberSaturation", 83);
00206     grabber->setModelParamVal("FrameGrabberWhiteBalTemp", 3100);
00207     grabber->setModelParamVal("FrameGrabberSharpness", 25);
00208     grabber->setModelParamVal("FrameGrabberExposureAbs", 156);
00209     grabber->setModelParamVal("FrameGrabberFocusAuto", false);
00210     grabber->setModelParamVal("FrameGrabberFocus", focusval[cameraID]);
00211     grabber->setModelParamVal("FrameGrabberZoom", 0);
00212     
00213     // keep track of it:
00214     manager.addSubComponent(grabber);
00215     grabbers.push_back(grabber);
00216     LINFO("Added V4L2grabber for %s", dev.c_str());
00217 
00218     ++cameraID;
00219   }
00220 }
00221 
00222 // ######################################################################
00223 struct SceneSetup
00224 {
00225   struct Object
00226   {
00227     std::string trayFileName;
00228     std::string trayName;
00229     int trayColumn;
00230     int trayRow;
00231     float trayX;
00232     float trayY;
00233     std::vector< Point2D<int> > outline;
00234   };
00235 
00236   std::string setupPath;
00237   int setupNum;
00238   int backgroundIdx;
00239   std::string backgroundFileName;
00240   std::vector<int> pathIndex;
00241   
00242   std::vector<SceneSetup::Object> objects;
00243 };
00244 
00245 // ######################################################################
00246 // Parse the scene setup file found in the given path
00247 SceneSetup loadSceneSetup(const int id)
00248 {
00249   SceneSetup setup;
00250   setup.setupPath = sceneDir;
00251 
00252   std::string setupFileName = setup.setupPath + "/" + sformat("%04d.txt", id);
00253   std::ifstream setupFile(setupFileName.c_str());
00254 
00255   if (!setupFile.is_open()) LFATAL("Could not open setup file: %s", setupFileName.c_str());
00256 
00257   std::string line;
00258 
00259   try
00260   {
00261     //SetupNumber
00262     getline(setupFile, line);
00263     setup.setupNum = boost::lexical_cast<int>(line.substr(line.find("=")+1));
00264 
00265     //BackgroundIndex
00266     getline(setupFile, line);
00267     setup.backgroundIdx = boost::lexical_cast<int>(line.substr(line.find("=")+1));
00268 
00269     //BackgroundFileName
00270     getline(setupFile, line);
00271     setup.backgroundFileName = line.substr(line.find("=")+1);
00272     
00273     //PathIndex
00274     getline(setupFile, line);
00275     std::vector<std::string> tok;
00276     split(line.substr(line.find("=")+1), ",", std::back_inserter(tok));
00277     for (size_t ii = 0; ii < tok.size(); ++ii)
00278       setup.pathIndex.push_back(boost::lexical_cast<int>(tok[ii]));
00279 
00280     //NumberOfObjects
00281     getline(setupFile, line);
00282     int numObjects = boost::lexical_cast<int>(line.substr(line.find("=")+1));
00283 
00284     for(int i=0; i<numObjects; ++i)
00285     {
00286       SceneSetup::Object obj;
00287 
00288       //TrayFileName
00289       getline(setupFile, line);
00290       obj.trayFileName = line.substr(line.find("=")+1);
00291 
00292       //TrayName
00293       size_t s = obj.trayFileName.find_last_of('/');
00294       size_t e = obj.trayFileName.find_last_of('.');
00295       obj.trayName = obj.trayFileName.substr(s+1, e-s-1);
00296 
00297       //TrayColumn
00298       getline(setupFile, line);
00299       obj.trayColumn = boost::lexical_cast<int>(line.substr(line.find("=")+1));
00300 
00301       //TrayRow
00302       getline(setupFile, line);
00303       obj.trayRow = boost::lexical_cast<int>(line.substr(line.find("=")+1));
00304 
00305       //XOnTray
00306       getline(setupFile, line);
00307       obj.trayX = boost::lexical_cast<float>(line.substr(line.find("=")+1));
00308 
00309       //YOnTray
00310       getline(setupFile, line);
00311       obj.trayY = boost::lexical_cast<float>(line.substr(line.find("=")+1));
00312 
00313       setup.objects.push_back(obj);
00314     }
00315   }
00316   catch(boost::bad_lexical_cast& e)
00317   {
00318     LFATAL("Error Parsing Setup File (%s) [%s] -- Offending Line: %s", setupFileName.c_str(), e.what(), line.c_str());
00319   }
00320 
00321   return setup;
00322 }
00323 
00324 // ######################################################################
00325 // Display the image and prompts to the user
00326 void *displayThreadMethod(void*)
00327 {
00328   // this is disabled for now...
00329 
00330   /*
00331   unsigned long frameNumber = 0;
00332   while(true)
00333   {
00334     Image< PixRGB<byte> > img;
00335     pthread_mutex_lock(&imgMutex);
00336     if (frameNumber != currentVideoFrameNumber) {
00337       img = inputImage;
00338       frameNumber = currentVideoFrameNumber;
00339     }
00340     pthread_mutex_unlock(&imgMutex);
00341 
00342     if (dolivedisplay && img.initialized()) {
00343       //      img = rescale(img, img.getDims() / 2);
00344       ofs->writeRGB(img, "Live Video from Main Camera");
00345       ofs->updateNext();
00346     }
00347 
00348     // go easy on the CPU:
00349     usleep(30000);
00350   }
00351   */
00352 
00353   return NULL;
00354 }
00355 
00356 // ######################################################################
00357 class ScorbotSimple: public ModelComponent
00358 {
00359   public:
00360     ScorbotSimple(OptionManager& mgr, 
00361         const std::string& descrName = "",
00362         const std::string& tagName = "") :
00363       ModelComponent(mgr, descrName, tagName),
00364       itsSerial(new Serial(mgr)) 
00365     {
00366       addSubComponent(itsSerial);
00367       itsSerial->configure("/dev/ttyUSB0", 115200, "8N1", false, false, 10000);
00368     }
00369 
00370     bool getBusyState()
00371     {
00372       char cmd = 'B';
00373       itsSerial->write(&cmd, 1);
00374       usleep(5000);
00375 
00376       char retVal = '?';
00377       itsSerial->read(&retVal, 1);
00378       //if (retVal == '?') LERROR("Serial Comminucation failed");
00379       return (retVal == '1');
00380     }
00381 
00382     bool getHomeState()
00383     {
00384       char cmd = 'H';
00385       itsSerial->write(&cmd, 1);
00386       usleep(5000);
00387 
00388       char retVal = '?';
00389       itsSerial->read(&retVal, 1);
00390       //if (retVal == '?') LERROR("Serial Comminucation failed");
00391       return (retVal == '1');
00392     }
00393     
00394     void emptyBuffer()
00395     {
00396        while(true)
00397        {
00398            usleep(10000);
00399            char retVal = '?';
00400            itsSerial->read(&retVal, 1);
00401            if(retVal == '?') break;
00402        }
00403     }
00404   
00405     void setPathNum(int pathnum)
00406     {
00407       //LINFO("Executing Path %i", pathnum);
00408       char cmd[2] = { 'P', pathnum };
00409       itsSerial->write(&cmd, 2);
00410     }
00411 
00412   private:
00413     nub::ref<Serial> itsSerial;
00414 };
00415 
00416 // ######################################################################
00417 // Run the robot arm along the given path, blocking until it is finished
00418 void executePath(int pathnum)
00419 {
00420   LINFO("Waiting for Robot HOME ...");
00421   while(!scorbot->getHomeState())
00422   {
00423      if(signum != 0) { record = false; return; }
00424      usleep(10000);
00425   }
00426   LINFO("Going To Path %d", pathnum);
00427   scorbot->setPathNum(pathnum++);
00428   LINFO("Waiting for Robot BUSY ...");
00429   while(!scorbot->getBusyState())
00430   {
00431      if(signum != 0) { record = false; return; }
00432      usleep(10000);
00433   }
00434   epoch = rutz::time::wall_clock_now();
00435   usleep(1000000); // trash old frame in camera buffer
00436   LINFO("Camera recording on.");
00437   record = true;
00438   LINFO("Waiting for Robot ~BUSY ...");
00439   while(scorbot->getBusyState())
00440   {
00441      if(signum != 0) { record = false; return; }
00442      usleep(10000);
00443   }
00444   record = false;
00445   LINFO("Camera recording off.");
00446 
00447   scorbot->emptyBuffer();
00448 }
00449 
00450 // ######################################################################
00451 // Prompt the user to place an object from a tray onto the table, and 
00452 // then present them with an interface to outline that object
00453 std::vector< Point2D<int> > promptPlaceObjectOnScene(SceneSetup const & setup, int objIdx)
00454 {
00455   LINFO("Place object %d onto table. Follow instructions in User Interactive window...", objIdx);
00456 
00457   const SceneSetup::Object &obj = setup.objects[objIdx];
00458 
00459   Image< PixRGB<byte> > trayImg = Raster::ReadRGB(setup.setupPath + "/" + obj.trayFileName);
00460   trayImg = rescale(trayImg, trayImg.getDims()/2);
00461 
00462   Point2D<int> objPos(obj.trayX*trayImg.getWidth(), obj.trayY*trayImg.getHeight());
00463 
00464   drawCircle(trayImg, objPos, trayImg.getWidth()/12, PixRGB<byte>(255, 0, 0), 2);
00465   drawCross(trayImg, objPos, PixRGB<byte>(255, 0, 0), trayImg.getWidth()/8, 2);
00466 
00467   std::ostringstream ss;
00468   ss << "Place Tray " << obj.trayName << 
00469     " (row " << obj.trayRow << 
00470     ", col " << obj.trayColumn << 
00471     ") on table and ENTER";
00472   writeText(trayImg, Point2D<int>(0, 0), ss.str().c_str());
00473 
00474   userInteractiveWindow->drawImage(trayImg, 0, 0, true);
00475   while(userInteractiveWindow->getLastKeyPress() != 36) usleep(100000);
00476   
00477   pthread_mutex_lock(&imgMutex);
00478   Image<PixRGB<byte> > cameraImg = inputImage;
00479   pthread_mutex_unlock(&imgMutex);
00480   userInteractiveWindow->drawImage(cameraImg, 0, 0, true);
00481   std::vector< Point2D<int> > poly;
00482   std::string msg = "Outline new object using 4 points. ESCAPE to undo point. SPACE to refresh image. ENTER when finished.";
00483 
00484   // eat all previous mouse clicks and key presses, just in case:
00485   while (userInteractiveWindow->getLastMouseClick() != Point2D<int>(-1, -1)) { }
00486   while (userInteractiveWindow->getLastKeyPress() != -1) { }
00487 
00488   bool finished = false;
00489   while(!finished) {
00490     Point2D<int> mouseClick = userInteractiveWindow->getLastMouseClick();
00491     if(mouseClick != Point2D<int>(-1, -1) && poly.size() < 4)
00492       poly.push_back(mouseClick);
00493 
00494     int lastKeyPress = userInteractiveWindow->getLastKeyPress();
00495     switch(lastKeyPress) {
00496     case -1: // No Key
00497       break;
00498     case 9:  // ESCAPE
00499       if(poly.size()) poly.erase(poly.end()-1);
00500       break;
00501     case 36: // ENTER
00502       if(poly.size() == 4) finished = true;
00503       break;
00504     case 65: // SPACE
00505       pthread_mutex_lock(&imgMutex);
00506       cameraImg = inputImage;
00507       pthread_mutex_unlock(&imgMutex);
00508       userInteractiveWindow->drawImage(cameraImg, 0, 0, true);
00509       break;
00510     default:
00511       LINFO("Key Pressed: %d", lastKeyPress);
00512       break;
00513     }
00514 
00515     Image< PixRGB<byte> > dispImage = cameraImg;
00516     for(size_t i=0; i<poly.size(); ++i) drawCircle(dispImage, poly[i], 5, PixRGB<byte>(255, 0, 0), 3);
00517     drawOutlinedPolygon(dispImage, poly, PixRGB<byte>(0, 0, 255), Point2D<int>(0,0),0,1,0,0,3);
00518     writeText(dispImage, Point2D<int>(0,0), msg.c_str());
00519     userInteractiveWindow->drawImage(dispImage, 0, 0, true);
00520 
00521     usleep(100000);
00522   }
00523 
00524   LINFO("Done placing object %d onto table.", objIdx);
00525 
00526   return poly;
00527 }
00528 
00529 // ######################################################################
00530 // Prompt the user to return the object back to the tray place an object
00531 void promptReturnObjectToTray(SceneSetup const & setup, int objIdx)
00532 {
00533   LINFO("Placing back object %d into its tray. Follow instructions in User Interactive window...", objIdx);
00534   const SceneSetup::Object &obj = setup.objects[objIdx];
00535 
00536   Image< PixRGB<byte> > trayImg = Raster::ReadRGB(setup.setupPath + "/" + obj.trayFileName);
00537   trayImg = rescale(trayImg, trayImg.getDims()/2);
00538 
00539   Point2D<int> objPos(obj.trayX*trayImg.getWidth(), obj.trayY*trayImg.getHeight());
00540 
00541   drawCircle(trayImg, objPos, trayImg.getWidth()/12, PixRGB<byte>(255, 0, 0), 2);
00542   drawCross(trayImg, objPos, PixRGB<byte>(255, 0, 0), trayImg.getWidth()/8, 2);
00543 
00544   std::ostringstream ss;
00545   ss << "Place back Obj(" <<
00546     "row " << obj.trayRow << 
00547     ", col " << obj.trayColumn << 
00548     ") into tray " << obj.trayName << 
00549     " and ENTER";
00550   writeText(trayImg, Point2D<int>(0, 0), ss.str().c_str());
00551   
00552   userInteractiveWindow->drawImage(trayImg, 0, 0, true);
00553 
00554   // eat all previous mouse clicks and key presses, just in case:
00555   while (userInteractiveWindow->getLastMouseClick() != Point2D<int>(-1, -1)) { }
00556   while (userInteractiveWindow->getLastKeyPress() != -1) { }
00557 
00558   // wait for ENTER:
00559   while (userInteractiveWindow->getLastKeyPress() != 36) usleep(100000);
00560 
00561   LINFO("Done placing back object %d into its tray.", objIdx);
00562 }
00563 
00564 // ######################################################################
00565 //! Get/confirm an int value:
00566 void getInt(const char *msg, int& val)
00567 {
00568   printf("%s [%d]: ", msg, val); fflush(stdout);
00569   char input[1000]; while(gets(input) == NULL) LERROR("Invalid input, try again");
00570 
00571   if (strlen(input) > 0) val = atoi(input); // updated the value
00572 };
00573 
00574 // ######################################################################
00575 int submain(const int argc, const char** argv)
00576 {
00577   // catch signals and redirect them for a clean exit (in particular, this gives us a chance to do useful things like
00578   // flush and close output files that would otherwise be left in a bogus state, like mpeg output files):
00579   catchsignals(&signum);
00580 
00581   LINFO("#############################################STARTING##############################################");
00582 
00583   ModelManager mgr("App Scorbot MultiGrab");
00584 
00585   scorbot.reset(new ScorbotSimple(mgr));
00586   mgr.addSubComponent(scorbot);
00587 
00588   setupGrabbers(mgr);
00589 
00590   ofs.reset(new OutputFrameSeries(mgr));
00591   mgr.addSubComponent(ofs);
00592 
00593   if(mgr.parseCommandLine(argc, argv, "FilePrefix SceneID", 2, 2) == false) return -1;
00594   mgr.start();
00595 
00596   if (grabbers.size() < NUMCAMS) LFATAL("Only found %"ZU" cameras instead of %d. Reboot your machine and try again.", grabbers.size(), NUMCAMS);
00597 
00598   // get our grabbers to start grabbing:  
00599   for (size_t cameraID = 0; cameraID < grabbers.size(); ++cameraID)
00600     grabberThreadServer.enqueueJob(rutz::make_shared(new GrabJob(cameraID)));
00601 
00602   sceneDir = mgr.getExtraArg(0);
00603   sceneID = boost::lexical_cast<int>(mgr.getExtraArg(1));
00604 
00605   pthread_t displayThread;
00606   pthread_create(&displayThread, NULL, &displayThreadMethod, NULL);
00607 
00608   // Create the interactive window
00609   userInteractiveWindow = new XWinManaged(Dims(640,480), -1, -1, "User Interactive");
00610   userInteractiveWindow->setVisible(false);
00611   userInteractiveWindow->setPosition(0, 0);
00612 
00613   // Main loop:
00614   int runnumber = 0;
00615   while(true) {
00616     if(signum != 0) break;
00617 
00618     // home the robot once in a while:
00619     if ((runnumber % 5) == 0) {
00620       int gogo = 0; getInt("Perform robot homing sequence and press ENTER", gogo);
00621     }
00622 
00623     // select the scene:
00624     getInt("Enter scene ID (-1 to exit):", sceneID);
00625 
00626     if (sceneID == -1) break; // abort on scene -1
00627 
00628     // STEP 1. Load the scene file
00629     SceneSetup setup = loadSceneSetup(sceneID);
00630 
00631     // STEP 2. Show the interactive window:
00632     userInteractiveWindow->setVisible(true);
00633 
00634     // STEP 3. Display background image and ask the user to place it on the scene
00635     Image< PixRGB<byte> > backgroundImage = Raster::ReadRGB(setup.setupPath + "/" + setup.backgroundFileName);
00636     backgroundImage = rescale(backgroundImage, Dims(640, 480));
00637     writeText(backgroundImage, Point2D<int>(0, 0), "Please place this background on the scene and press ENTER.");
00638     userInteractiveWindow->drawImage(backgroundImage, 0, 0, true);
00639     LINFO("Place background map on scene and add houses, trees, and other background objects. See User Interactive window for instructions...");
00640     // eat all previous mouse clicks and key presses, just in case:
00641     while (userInteractiveWindow->getLastMouseClick() != Point2D<int>(-1, -1)) { }
00642     while (userInteractiveWindow->getLastKeyPress() != -1) { }
00643     // wait for ENTER:
00644     while(userInteractiveWindow->getLastKeyPress() != 36) usleep(100000);
00645     LINFO("Background map done. Make sure you have built a nice scene.");
00646 
00647     // STEP 4. Display each object and ask user to put on the scene and specify its bounding box
00648     for (size_t i = 0; i < setup.objects.size(); ++i)
00649       setup.objects[i].outline = promptPlaceObjectOnScene(setup, i);
00650 
00651     // STEP 5. Hide the interactive window
00652     userInteractiveWindow->setVisible(false);
00653 
00654     // STEP 6. Write out outlines to a file
00655     {
00656       std::string objectFileName = sformat("%s/RobotScene-s%04d-polygons.txt", dataDir, sceneID);
00657       std::ofstream objectFile(objectFileName.c_str());
00658 
00659       LINFO("Saving the object bounding boxes into: %s", objectFileName.c_str());
00660  
00661       for(size_t i=0; i<setup.objects.size(); ++i)
00662         {
00663           for(size_t j=0; j<setup.objects[i].outline.size(); ++j)
00664             {
00665               Point2D<int> &pnt = setup.objects[i].outline[j];
00666               if(j != 0) objectFile << ',';
00667               objectFile << pnt.i << ',' << pnt.j;
00668             }
00669           objectFile << std::endl;
00670         }
00671     }
00672 
00673     // STEP 7. Execute the path and record the videos
00674     for (pathID = 0; pathID < int(setup.pathIndex.size()); ++pathID) {
00675       if(signum != 0) break;
00676       // create a directory for this scene / light:
00677       const std::string dir = sformat("%s/RobotScene-s%04d-p%02d", dataDir, sceneID, pathID);
00678       const std::string cmd = sformat("/bin/mkdir -p %s", dir.c_str());
00679       if (system(cmd.c_str()) == -1) PLFATAL("Could not create directory %s", dir.c_str());
00680 
00681       int gogo = pathID; getInt("Set light and press ENTER to start video recording", gogo);
00682 
00683       // make sure we don't have too many pending disk writes:
00684       while(writer.size() > 1000) {
00685         LINFO("Waiting for image writer thread, queue size = %"ZU"...", writer.size());
00686         usleep(1000000);
00687       }
00688 
00689       LINFO("Running Scene %04d Path %02d ...", sceneID, setup.pathIndex[pathID]);
00690 
00691       executePath(setup.pathIndex[pathID]);
00692     }
00693     if(signum != 0) break;
00694 
00695     // STEP 8. Instruct users to place the objects back into the bins
00696     userInteractiveWindow->setVisible(true);
00697     userInteractiveWindow->setPosition(0, 0);
00698     for(size_t i=0; i<setup.objects.size(); ++i) promptReturnObjectToTray(setup, i);
00699     userInteractiveWindow->setVisible(false);
00700 
00701     // STEP 9. Ready for next scene
00702     ++sceneID;
00703     ++runnumber;
00704   }
00705 
00706   // stop grabbing:
00707   keepgoing = false;
00708 
00709   // wait for all pics to be written (note: this just waits until the queue of pending jobs is empty, the writer's
00710   // destructor will wait until all jobs are complete):
00711   while(writer.size()) {
00712     LINFO("Waiting for image writer thread, queue size = %"ZU"...", writer.size());
00713     usleep(500000);
00714   }
00715   writer.flushQueue(250000, true);
00716 
00717   // make sure all is done
00718   LINFO("Cleaning up... Stand by...");
00719   usleep(2000000);
00720 
00721   // stop all our ModelComponents
00722   mgr.stop();
00723 
00724   LINFO("Finished.");
00725 
00726   return 0;
00727 }
00728 
00729 // ######################################################################
00730 int main(int argc, const char** argv)
00731 {
00732   int ret = -1;
00733 
00734   try { ret = submain(argc, argv); }
00735   catch(...) { REPORT_CURRENT_EXCEPTION; }
00736 
00737   return ret;
00738 }
Generated on Sun May 8 08:05:56 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3