00001 /** 00002 \file Robots/LoBot/control/LoRenderResults.C 00003 \brief This file defines the non-inline member functions of the 00004 lobot::RenderResults class. 00005 */ 00006 00007 // //////////////////////////////////////////////////////////////////// // 00008 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005 // 00009 // by the University of Southern California (USC) and the iLab at USC. // 00010 // See http://iLab.usc.edu for information about this project. // 00011 // //////////////////////////////////////////////////////////////////// // 00012 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00013 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00014 // in Visual Environments, and Applications'' by Christof Koch and // 00015 // Laurent Itti, California Institute of Technology, 2001 (patent // 00016 // pending; application number 09/912,225 filed July 23, 2001; see // 00017 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00018 // //////////////////////////////////////////////////////////////////// // 00019 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00020 // // 00021 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00022 // redistribute it and/or modify it under the terms of the GNU General // 00023 // Public License as published by the Free Software Foundation; either // 00024 // version 2 of the License, or (at your option) any later version. // 00025 // // 00026 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00027 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00028 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00029 // PURPOSE. See the GNU General Public License for more details. // 00030 // // 00031 // You should have received a copy of the GNU General Public License // 00032 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00033 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00034 // Boston, MA 02111-1307 USA. // 00035 // //////////////////////////////////////////////////////////////////// // 00036 // 00037 // Primary maintainer for this file: mviswana usc edu 00038 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Robots/LoBot/control/LoRenderResults.C $ 00039 // $Id: LoRenderResults.C 14019 2010-09-23 08:35:03Z mviswana $ 00040 // 00041 00042 //------------------------------ HEADERS -------------------------------- 00043 00044 // lobot headers 00045 #include "Robots/LoBot/control/LoRenderResults.H" 00046 #include "Robots/LoBot/ui/LoMainWindow.H" 00047 00048 #include "Robots/LoBot/LoApp.H" 00049 #include "Robots/LoBot/slam/LoMap.H" 00050 #include "Robots/LoBot/slam/LoOccGrid.H" 00051 #include "Robots/LoBot/slam/LoSlamParams.H" 00052 00053 #include "Robots/LoBot/config/LoConfigHelpers.H" 00054 00055 // DEVNOTE: We must supply the definition of YY_EXTRA_TYPE prior to 00056 // including LoMetlogParser.H, which is generated by flex. Otherwise, the 00057 // generated header will define YY_EXTRA_TYPE as void* and the linker 00058 // will fail to find the lomet_parser_set_extra function, which will end 00059 // up being declared in this module as taking a void* rather than a 00060 // lobot::Experiment* as it actually does. 00061 // 00062 // Moreover, we must include the flex-generated LoMetlogParser.H header 00063 // after LoExperiment.H. Otherwise, the lobot::Experiment class will not 00064 // be properly declared or defined and the compiler really detests that. 00065 #define YY_EXTRA_TYPE lobot::Experiment* 00066 #include "Robots/LoBot/metlog/LoExperiment.H" 00067 #include "Robots/LoBot/metlog/LoMetlogParser.H" 00068 00069 #include "Robots/LoBot/thread/LoPause.H" 00070 00071 #include "Robots/LoBot/misc/LoExcept.H" 00072 #include "Robots/LoBot/misc/LoRegistry.H" 00073 #include "Robots/LoBot/misc/LoTypes.H" 00074 #include "Robots/LoBot/misc/singleton.hh" 00075 00076 #include "Robots/LoBot/util/LoGL.H" 00077 #include "Robots/LoBot/util/LoFile.H" 00078 #include "Robots/LoBot/util/LoMath.H" 00079 #include "Robots/LoBot/util/triple.hh" 00080 00081 // INVT headers 00082 #include "Util/log.H" 00083 00084 // OpenGL headers 00085 #ifdef INVT_HAVE_LIBGLU 00086 #include <GL/glu.h> 00087 #endif 00088 00089 #ifdef INVT_HAVE_LIBGL 00090 #include <GL/gl.h> 00091 #endif 00092 00093 // Boost headers 00094 #include <boost/algorithm/string.hpp> 00095 00096 // Standard C++ headers 00097 #include <fstream> 00098 #include <sstream> 00099 #include <string> 00100 00101 // Standard C headers 00102 #include <stdio.h> 00103 00104 //----------------------------- NAMESPACE ------------------------------- 00105 00106 namespace lobot { 00107 00108 //-------------------------- KNOB TWIDDLING ----------------------------- 00109 00110 namespace { 00111 00112 // Retrieve settings from goal section of config file 00113 template<typename T> 00114 inline T conf(const std::string& key, const T& default_value) 00115 { 00116 return get_conf<T>(LOBE_RENDER_RESULTS, key, default_value) ; 00117 } 00118 00119 // Overload for retrieving triples 00120 template<typename T> 00121 inline triple<T, T, T> 00122 conf(const std::string& key, const triple<T, T, T>& default_value) 00123 { 00124 return get_conf<T>(LOBE_RENDER_RESULTS, key, default_value) ; 00125 } 00126 00127 /// This local class encapsulates various parameters that can be used to 00128 /// tweak different aspects of the render_results behaviour. 00129 class RenderResultsParams : public singleton<RenderResultsParams> { 00130 /// This setting specifies the name of the directory under which all 00131 /// of the lobot and lomet programs' metrics logs and results files 00132 /// are stored. By default, this string is empty. If the 00133 /// render_results behaviour is active and this setting is not 00134 /// explicitly provided, the Robolocust application will report an 00135 /// error but continue to function (although it would not fulfill its 00136 /// intended purpose). 00137 std::string m_results_dir ; 00138 00139 /// This setting specifies a regex pattern for the names of metrics 00140 /// log and dataset analysis results files. We assume that the lobot 00141 /// and lomet programs were used to output metrics logs and results 00142 /// files for different experiments/datasets under different 00143 /// subdirectories of a root data directory. If this assumption is not 00144 /// valid, the render_results behaviour cannot and will not work 00145 /// properly. 00146 /// 00147 /// When this behaviour starts up, it will search the data directory 00148 /// recursively for all files matching the name specified by this 00149 /// setting. The value of this setting should be a string and it is 00150 /// interpreted as an extended regular expression (try: man 7 regex). 00151 /// Thus, all files under the data directory whose names (relative to 00152 /// the data directory) match the regex specified by this setting will 00153 /// be included in the list of files to be loaded and visualized. 00154 /// 00155 /// After finding the list of files to be visualized, the 00156 /// render_results behaviour will then proceed to load the first of 00157 /// these files. You may use the 'n' (next), 'f' (forward), '+' or 00158 /// space keys to advance to the next file in the list. To visit the 00159 /// previous file in the list, press 'b' (back) or '-'. When either 00160 /// end of the list is reached, the behaviour will continue by simply 00161 /// wrapping around to the other end. 00162 /// 00163 /// This behaviour also supports saving the visualizations using the 00164 /// 's' (screenshot) key. When the 's' key is pressed, the behaviour 00165 /// will write an image to the same location as the metlog or results 00166 /// file currently being visualized. In fact, the screenshot's file 00167 /// name is simply the metlog or results file's name plus an 00168 /// extension. 00169 /// 00170 /// The default extension is ".png". To save the screenshot as a JPEG 00171 /// file, set the "screen_capture_fmt" option in the "[ui]" section of 00172 /// the config file to "jpg". Other image formats are also supported; 00173 /// check the documentation of the screen_capture_fmt option for more 00174 /// information. 00175 std::string m_results_file_name ; 00176 00177 /// This field holds the screenshot image file name extension to be 00178 /// used. When the user presses the 's' key, a screenshot is saved to 00179 /// the same location as the results file being visualized with the 00180 /// same basename as the results file with this extension added. 00181 std::string m_ss_fmt ; 00182 00183 /// In addition to the robot's trajectory from start to goal, each 00184 /// experiment's metrics log or a dataset's results file records the 00185 /// following lists of points: 00186 /// - bump: where the robot bumped into objects 00187 /// - stop: where the emergency_stop behaviour was activated 00188 /// - extr: where the LRF-based extricate behaviour was activated 00189 /// - lgmd: where the LGMD-based extricate behaviour was activated 00190 /// 00191 /// These flags can be used to turn the rendering of each of the above 00192 /// lists on or off. By default, the render_results behaviour renders 00193 /// all the lists. 00194 bool m_bump, m_stop, m_extr, m_lgmd ; 00195 00196 /// These settings specify colors to use for the different point 00197 /// lists. 00198 GLColor m_bump_color, m_stop_color, m_extr_color, m_lgmd_color ; 00199 00200 /// The render_results behaviour supports a slideshow mode, wherein it 00201 /// periodically switches to the next metlog or results file in its 00202 /// list. The behaviour's update_frequency setting controls the rate 00203 /// at which the slideshow progresses. 00204 bool m_slideshow ; 00205 00206 /// This behaviour can visualize the metrics logs for individual 00207 /// experiments as well as the results file containing the analysis 00208 /// for an entire dataset. In slideshow mode, when we get to a dataset 00209 /// results file after a long string of metlogs, we might want to 00210 /// pause the slideshow in order to be able to study the average-case 00211 /// behaviour in greater detail. 00212 /// 00213 /// This flag allows us to do that. By default, it is off. When it is 00214 /// turned on and we reach a dataset's results file, the slideshow 00215 /// will pause. To restart the slideshow, press the 'p' key. 00216 bool m_pause_on_dataset ; 00217 00218 /// The number of milliseconds between successive iterations of this 00219 /// behaviour. 00220 /// 00221 /// NOTE: Whereas the update_delay setting for most behaviours is a 00222 /// crucial parameter that can disrupt proper robot operation, it is 00223 /// only used to control the slideshow rate for the render_results 00224 /// behaviour, which is just an offline, visualization task. 00225 int m_update_delay ; 00226 00227 /// Private constructor because this is a singleton. 00228 RenderResultsParams() ; 00229 friend class singleton<RenderResultsParams> ; 00230 00231 // Shortcuts just make code line up nicely 00232 typedef const std::string& str ; 00233 typedef const GLColor& Color ; 00234 00235 public: 00236 /// Accessing the various parameters 00237 //@{ 00238 static str results_file_name() {return instance().m_results_file_name ;} 00239 static str results_dir(){return instance().m_results_dir;} 00240 static str ss_fmt() {return instance().m_ss_fmt ;} 00241 static Color stop_color() {return instance().m_stop_color ;} 00242 static Color extr_color() {return instance().m_extr_color ;} 00243 static Color lgmd_color() {return instance().m_lgmd_color ;} 00244 static Color bump_color() {return instance().m_bump_color ;} 00245 static bool render_stop() {return instance().m_stop ;} 00246 static bool render_extr() {return instance().m_extr ;} 00247 static bool render_lgmd() {return instance().m_lgmd ;} 00248 static bool render_bump() {return instance().m_bump ;} 00249 static bool slideshow() {return instance().m_slideshow ;} 00250 static bool pause_on_dataset() {return instance().m_pause_on_dataset ;} 00251 static int update_delay() {return instance().m_update_delay ;} 00252 //@} 00253 } ; 00254 00255 // Parameter initialization 00256 RenderResultsParams::RenderResultsParams() 00257 : m_results_dir(conf<std::string>("results_dir", "")), 00258 m_results_file_name(conf<std::string>("results_file_name", 00259 "/(metlog-[[:digit:]]{8}-[[:digit:]]{6}|result)$")), 00260 m_ss_fmt(std::string(".") + 00261 ui_conf<std::string>("screen_capture_fmt", "png")), 00262 m_bump(conf("render_bump_points", true)), 00263 m_stop(conf("render_stop_points", true)), 00264 m_extr(conf("render_lrf_extrication_points", true)), 00265 m_lgmd(conf("render_lgmd_extrication_points", true)), 00266 m_bump_color(conf<int>("bump_points_color", 00267 make_triple(255, 0, 0))), 00268 m_stop_color(conf<int>("stop_points_color", 00269 make_triple(255, 165, 0))), 00270 m_extr_color(conf<int>("lrf_extrication_points_color", 00271 make_triple(0, 0, 255))), 00272 m_lgmd_color(conf<int>("lgmd_extrication_points_color", 00273 make_triple(105, 139, 105))), 00274 m_slideshow(conf("slideshow", false)), 00275 m_pause_on_dataset(conf("pause_on_dataset", false)), 00276 m_update_delay(clamp(conf("update_delay", 2500), 500, 60000)) 00277 { 00278 boost::trim(m_results_file_name) ; 00279 } 00280 00281 // Convenient shortcut 00282 typedef RenderResultsParams Params ; 00283 00284 } // end of local anonymous namespace encapsulating above helpers 00285 00286 //-------------------------- INITIALIZATION ----------------------------- 00287 00288 RenderResults::RenderResults() 00289 : base(Params::update_delay(), LOBE_RENDER_RESULTS), 00290 m_traj(300), m_stop(75), m_extr(25), m_lgmd(50), m_bump(5), 00291 m_results_file_being_visualized(false) 00292 { 00293 start(LOBE_RENDER_RESULTS) ; 00294 } 00295 00296 void RenderResults::pre_run() 00297 { 00298 Map* map = App::map() ; 00299 if (! map) 00300 throw behavior_error(MAPPING_DISABLED) ; 00301 00302 const std::string map_file = SlamParams::map_file() ; 00303 if (map_file.empty()) 00304 throw behavior_error(MAPPING_DISABLED) ; 00305 map->update(OccGrid(map_file)) ; 00306 map->add_hook(RenderHook(render_results, 00307 reinterpret_cast<unsigned long>(this))) ; 00308 00309 std::string goal_conf = get_conf<std::string>(LOBE_GOAL, "goals", "") ; 00310 if (goal_conf.empty()) 00311 throw behavior_error(NO_GOALS) ; 00312 std::istringstream goal(goal_conf) ; 00313 goal >> m_goal ; 00314 00315 const std::string& results_dir = Params::results_dir() ; 00316 const std::string& results_file_name = Params::results_file_name() ; 00317 if (results_dir.empty() || results_file_name.empty()) 00318 throw behavior_error(BAD_RESULTS_SPECS) ; 00319 m_results_files = find_file(results_dir, results_file_name) ; 00320 m_results = m_results_files.end() - 1 ; 00321 next() ; 00322 } 00323 00324 void RenderResults::load(const std::string& file_name) 00325 { 00326 m_results_file_being_visualized = false ; 00327 clear_point_lists() ; 00328 load_metlog(file_name) ; 00329 if (m_traj.empty()) { // maybe not a metlog; see if it's a results file 00330 clear_point_lists() ; // just in case other point lists got some gunk 00331 load_results(file_name) ; 00332 m_results_file_being_visualized = true ; 00333 } 00334 00335 const char* s = file_name.c_str() ; 00336 if (m_traj.empty()) // 2nd time failure ==> neither metlog nor results file 00337 LERROR("%s: not a metlog nor a results file!", s) ; 00338 else 00339 LERROR("loaded %s", s) ; 00340 } 00341 00342 void RenderResults::load_metlog(const std::string& file_name) 00343 { 00344 FILE* file = fopen(file_name.c_str(), "r") ; 00345 if (! file) 00346 return ; 00347 00348 Experiment* experiment = Experiment::create(file_name) ; 00349 00350 yyscan_t parser ; 00351 lomet_parser_lex_init(&parser) ; 00352 lomet_parser_set_extra(experiment, parser) ; 00353 lomet_parser_set_in(file, parser) ; 00354 lomet_parser_lex(parser) ; 00355 lomet_parser_lex_destroy(parser) ; 00356 fclose(file) ; 00357 00358 m_traj = experiment->point_list(TRAJECTORY) ; 00359 m_stop = experiment->point_list(EMERGENCY_STOP) ; 00360 m_extr = experiment->point_list(EXTRICATE) ; 00361 m_lgmd = experiment->point_list(LGMD_EXTRICATE) ; 00362 m_bump = experiment->point_list(BUMP) ; 00363 00364 delete experiment ; 00365 } 00366 00367 void RenderResults::load_results(const std::string& file_name) 00368 { 00369 std::ifstream file(file_name.c_str()) ; 00370 for(;;) 00371 { 00372 std::string line ; 00373 std::getline(file, line) ; 00374 if (! file) 00375 break ; 00376 00377 std::istringstream point_spec(line) ; 00378 std::string point_type ; int x, y ; 00379 point_spec >> point_type >> x >> y ; 00380 if (point_type == "trajectory") 00381 m_traj.add(x, y) ; 00382 else if (point_type == "emergency_stop") 00383 m_stop.add(x, y) ; 00384 else if (point_type == "extricate") 00385 m_extr.add(x, y) ; 00386 else if (point_type == "lgmd_extricate") 00387 m_lgmd.add(x, y) ; 00388 else if (point_type == "bump") 00389 m_bump.add(x, y) ; 00390 } 00391 } 00392 00393 //---------------------- THE BEHAVIOUR'S ACTION ------------------------- 00394 00395 void RenderResults::action() 00396 { 00397 if (Params::slideshow()) { 00398 next() ; 00399 if (Params::pause_on_dataset() && m_results_file_being_visualized) 00400 Pause::toggle() ; 00401 } 00402 } 00403 00404 void RenderResults::keypress(unsigned char key) 00405 { 00406 switch (key) 00407 { 00408 case 'n': // next 00409 case 'N': // next 00410 case 'f': // forward 00411 case 'F': // forward 00412 case '+': 00413 case ' ': 00414 next() ; 00415 break ; 00416 case 'b': // back 00417 case 'B': // back 00418 case '-': 00419 prev() ; 00420 break ; 00421 case 's': // screenshot 00422 case 'S': // screenshot 00423 { 00424 std::string file_name = curr() + Params::ss_fmt() ; 00425 if (exists(file_name)) 00426 LERROR("%s: already exists; will not overwrite", 00427 file_name.c_str()) ; 00428 else 00429 MainWindow::instance().save_screenshot(file_name) ; 00430 } 00431 break ; 00432 } 00433 } 00434 00435 void RenderResults::prev() 00436 { 00437 viz_lock() ; 00438 if (m_results == m_results_files.begin()) 00439 m_results = m_results_files.end() ; 00440 --m_results ; 00441 load(*m_results) ; 00442 viz_unlock() ; 00443 } 00444 00445 void RenderResults::next() 00446 { 00447 viz_lock() ; 00448 ++m_results ; 00449 if (m_results == m_results_files.end()) 00450 m_results = m_results_files.begin() ; 00451 load(*m_results) ; 00452 viz_unlock() ; 00453 } 00454 00455 std::string RenderResults::curr() 00456 { 00457 viz_lock() ; 00458 std::string results_file_name = *m_results ; 00459 viz_unlock() ; 00460 return results_file_name ; 00461 } 00462 00463 //--------------------------- VISUALIZATION ----------------------------- 00464 00465 #ifdef INVT_HAVE_LIBGL 00466 00467 // Quick helper to "emit" GL vertex coordinates from an mPoint. 00468 // 00469 // NOTE: Since the map coordinate system is rotated ccw, we need to swap 00470 // the x and y coordinates. 00471 static void mpoint_to_vertex(const mPoint& p) 00472 { 00473 glVertex2i(p.second, p.first) ; 00474 } 00475 00476 // Helper function to render a point list 00477 static void 00478 render_points(const PointList& L, const GLColor& color) 00479 { 00480 glPushAttrib(GL_POINT_BIT | GL_CURRENT_BIT) ; 00481 glPointSize(5) ; 00482 glColor3fv(color.rgb()) ; 00483 glBegin(GL_POINTS) ; 00484 std::for_each(L.begin(), L.end(), mpoint_to_vertex) ; 00485 glEnd() ; 00486 glPopAttrib() ; 00487 } 00488 00489 // This function renders the different point lists read from the results 00490 // file onto the map. 00491 void RenderResults::render_results(unsigned long client_data) 00492 { 00493 const RenderResults* me = reinterpret_cast<RenderResults*>(client_data) ; 00494 00495 // Setup 2D "view volume" to match real/physical coordinate system 00496 // except that the whole thing is rotated 90 degrees ccw so that our 00497 // notion of "up" matches that of the robot's. 00498 float L, R, B, T ; // map extents 00499 SlamParams::map_extents(&L, &R, &B, &T) ; 00500 me->setup_view_volume(T, B, L, R) ; 00501 00502 // Now we're ready to draw the goal box and the various point lists... 00503 me->render_goal() ; 00504 me->render_traj() ; 00505 if (Params::render_stop()) 00506 render_points(me->m_stop, Params::stop_color()) ; 00507 if (Params::render_extr()) 00508 render_points(me->m_extr, Params::extr_color()) ; 00509 if (Params::render_lgmd()) 00510 render_points(me->m_lgmd, Params::lgmd_color()) ; 00511 if (Params::render_bump()) 00512 render_points(me->m_bump, Params::bump_color()) ; 00513 00514 // Reset GL transformations so next drawable won't get screwed 00515 me->restore_view_volume() ; 00516 } 00517 00518 // Helper function to draw a dashed box signifying the robot's goal 00519 // within the slalom course. 00520 void RenderResults::render_goal() const 00521 { 00522 glPushAttrib(GL_LINE_BIT) ; 00523 glEnable(GL_LINE_STIPPLE) ; 00524 glLineStipple(1, 0xCCCC) ; 00525 glColor3f(1, 0, 0) ; 00526 glBegin(GL_LINE_LOOP) ; 00527 glVertex2f(m_goal.top, m_goal.left) ; 00528 glVertex2f(m_goal.bottom, m_goal.left) ; 00529 glVertex2f(m_goal.bottom, m_goal.right); 00530 glVertex2f(m_goal.top, m_goal.right); 00531 glEnd() ; 00532 glPopAttrib() ; 00533 } 00534 00535 // Helper function to render the robot's trajectory from start to goal 00536 void RenderResults::render_traj() const 00537 { 00538 glColor3f(0, 0, 0) ; 00539 glBegin(GL_LINE_STRIP) ; 00540 std::for_each(m_traj.begin(), m_traj.end(), mpoint_to_vertex) ; 00541 glEnd() ; 00542 } 00543 00544 #endif 00545 00546 //----------------------------- CLEAN-UP -------------------------------- 00547 00548 void RenderResults::clear_point_lists() 00549 { 00550 m_traj.clear() ; 00551 m_stop.clear() ; 00552 m_extr.clear() ; 00553 m_lgmd.clear() ; 00554 m_bump.clear() ; 00555 } 00556 00557 RenderResults::~RenderResults(){} 00558 00559 //----------------------------------------------------------------------- 00560 00561 } // end of namespace encapsulating this file's definitions 00562 00563 /* So things look consistent in everyone's emacs... */ 00564 /* Local Variables: */ 00565 /* indent-tabs-mode: nil */ 00566 /* End: */