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