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 }