LoRenderResults.C

Go to the documentation of this file.
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: */
Generated on Sun May 8 08:41:23 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3