HandTrace.C

Go to the documentation of this file.
00001 /*!@file Psycho/HandTrace.C */
00002 // //////////////////////////////////////////////////////////////////// //
00003 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005   //
00004 // by the University of Southern California (USC) and the iLab at USC.  //
00005 // See http://iLab.usc.edu for information about this project.          //
00006 // //////////////////////////////////////////////////////////////////// //
00007 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00008 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00009 // in Visual Environments, and Applications'' by Christof Koch and      //
00010 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00011 // pending; application number 09/912,225 filed July 23, 2001; see      //
00012 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00013 // //////////////////////////////////////////////////////////////////// //
00014 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00015 //                                                                      //
00016 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00017 // redistribute it and/or modify it under the terms of the GNU General  //
00018 // Public License as published by the Free Software Foundation; either  //
00019 // version 2 of the License, or (at your option) any later version.     //
00020 //                                                                      //
00021 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00022 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00023 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00024 // PURPOSE.  See the GNU General Public License for more details.       //
00025 //                                                                      //
00026 // You should have received a copy of the GNU General Public License    //
00027 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00028 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00029 // Boston, MA 02111-1307 USA.                                           //
00030 // //////////////////////////////////////////////////////////////////// //
00031 //
00032 // Primary maintainer for this file: Dicky Nauli Sihite <sihite@usc.edu>
00033 // $HeadURL: 
00034 // $Id: 
00035 
00036 
00037 #ifndef PSYCHO_HANDTRACE_C_DEFINED
00038 #define PSYCHO_HANDTRACE_C_DEFINED
00039 
00040 #include "Psycho/HandTrace.H"
00041 #include "Util/StringConversions.H"
00042 #include "Util/StringUtil.H"
00043 #include "Util/log.H"
00044 #include "rutz/compat_cmath.h" // for isnan()
00045 
00046 #include <fstream>
00047 
00048 // ######################################################################
00049 // ## namespace
00050 // ######################################################################
00051 namespace
00052 {
00053   /* Look for auxiliary data files associated with the main .hanD
00054    * file, and read data from them if present. Specifically, if
00055    * itsFilename is e.g. "foo.hanD", then we will look these files:
00056    *
00057    * foo.hanD.ntrash: to contain a value for itsTrash
00058    * foo.hanD.rate: to contain a value for itsPeriod
00059    *
00060    * Note that the .rate file should contain a suffix to indicate what
00061    * units the value is in, e.g. "240.19Hz" or "4.16337ms".
00062    *
00063    * Returns true if all pieces of metadata were succesfully read. */
00064   bool getMetadataFromAuxFiles(const std::string& mainfilename,
00065                                size_t& ntrashOut, SimTime& periodOut)
00066   {
00067     bool gotperiod = false, gottrash = false;
00068     
00069     // ######################################################################
00070     { // These brackets are for making local variable
00071       std::ifstream f((mainfilename + ".ntrash").c_str());
00072       if (f.is_open()) {
00073         size_t ntrash = 0;
00074         f >> ntrash;
00075         if (f.fail()) LFATAL("couldn't read ntrash value from %s.ntrash",
00076                              mainfilename.c_str());
00077         
00078         ntrashOut = ntrash;
00079         gottrash = true;
00080         
00081         LINFO("read ntrash=%"ZU" from %s.ntrash",
00082               ntrashOut, mainfilename.c_str());
00083       }
00084     }
00085     
00086     // ######################################################################
00087     {
00088       std::ifstream f((mainfilename + ".rate").c_str());
00089       if (f.is_open()) {
00090         std::string rate;
00091         f >> rate;
00092         if (f.fail())
00093           LFATAL("couldn't read period/rate value from %s.rate",
00094                  mainfilename.c_str());
00095         
00096         periodOut = SimTime::fromString(rate);
00097         gotperiod = true;
00098         
00099         LINFO("read rate=%fHz from %s.rate",
00100               periodOut.hertz(), mainfilename.c_str());
00101       }   
00102     }
00103     
00104     return gottrash && gotperiod;
00105   }
00106 }
00107 
00108 // ######################################################################
00109 // ##  HandTrace Class
00110 // ######################################################################
00111 HandTrace::HandTrace(const std::string& filename, const PixRGB<byte>& color) :
00112   itsFilename(filename), itsColor(color), itsPeriod(SimTime::ZERO()),
00113   itsTrash(0), itsNumEvents(0), nativeX(-1), nativeY(-1), itsData()
00114 {
00115   // open the file
00116   const char *fn = filename.c_str();
00117   std::ifstream fil(fn);
00118   if (fil.is_open() == false) PLFATAL("Cannot open '%s'", fn);
00119 
00120   // text parsing variables
00121   std::string line; int linenum = -1;
00122   const std::string delim(" \t");
00123   bool gotperiod = false, gottrash = false, gotcols = false;
00124   uint samp_count = 0, trashed = 0;
00125 
00126   /* We need to get metadata, look for the following:
00127    * 1. Auxiliary files
00128    *    Extra files contains metadata values. If none found,
00129    *    continue to look for:
00130    * 2. Inside the main file
00131    *    Look for 'key=value' metadata lines
00132    */
00133 
00134   // 1. Aux files
00135   //    Read additional files for metadata
00136   const bool got_all_metadata_from_aux_files =
00137     getMetadataFromAuxFiles(itsFilename, itsTrash, itsPeriod);
00138 
00139   // 2. Inside main file
00140   //    If no aux files was found, read the main file and grab only metadata
00141   while (!got_all_metadata_from_aux_files && getline(fil, line)) {
00142     // one more line that we have read
00143     ++linenum;
00144     
00145     // skip initial whitespace and tab
00146     //std::string::size_type pos = line.find_first_not_of(delim, 0);
00147     
00148     //if (pos == line.npos) continue; // line was all whitespace/empty
00149     //if (line[pos] == '#') continue; // line is a comment
00150     
00151     // is it some metadata: "key = value"?
00152     if (line.find('=') != line.npos) {
00153       // let's tokenize it:
00154       std::vector<std::string> tok;
00155       split(line, "= \t", std::back_inserter(tok));
00156       
00157       // do we know that key?
00158       if (tok[0].compare("period") == 0) {
00159         itsPeriod = SimTime::fromString(tok[1]);
00160         gotperiod = true; }
00161       else if (tok[0].compare("trash") == 0) { 
00162         itsTrash = fromStr<int>(tok[1]);
00163         gottrash = true; }
00164       else if (tok[0].compare("cols") == 0) { 
00165         tok.erase(tok.begin()); // get rid of "cols"
00166         itsFields = tok;
00167         gotcols = true;}
00168       else if (tok[0].compare("res") == 0) {
00169         std::vector<std::string> res;
00170         split(tok[1], "x", std::back_inserter(res));
00171         nativeX = fromStr<int>(res[0]);
00172         nativeY = fromStr<int>(res[1]);
00173         }
00174       else {
00175         LFATAL("Unknown parameter '%s', file '%s' line %d",
00176                tok[0].c_str(), fn, linenum); }
00177       
00178       // done with this line, let's keep going:
00179       continue;
00180     }
00181 
00182     // check columns
00183     if (!gotcols) {
00184       std::string flds = "x y b";
00185       split(flds, " ", std::back_inserter(itsFields));
00186       gotcols = true;
00187     }
00188 
00189     // We got everything and since we already read this line, just proc it
00190     if (gotperiod && gottrash) {
00191       if (trashed < itsTrash) { ++trashed;  continue; }
00192       else {pushData(line);}
00193       samp_count++;
00194       break;
00195     }
00196 
00197     // if we reach this point, either we have hit some junk, or a
00198     // data line but we are missing some parameters. Not good:
00199     LFATAL("I need to have period and trash information before "
00200            "data starts, file '%s' line %d", fn, linenum);
00201   }
00202   
00203   // We got all the necessary metadata
00204   LINFO("%s: period = %.3fms, trash = %"ZU" samples.",
00205         fn, itsPeriod.msecs(), itsTrash);
00206 
00207   // Reset the variables back to default to prevent unwanted error
00208   line = ""; linenum = -1;
00209   
00210   // Now we have all the metadata. Let's get the data next.
00211   // Note that there is a bit of redundancy between the loop here and the
00212   // previous one, but it runs faster this way because from now on we
00213   // are sure that all the metadata is available:
00214   while (getline(fil, line)) {
00215     // one more line that we have read:
00216     ++linenum;
00217     
00218     // skip initial whitespace:
00219     //std::string::size_type pos = line.find_first_not_of(delim, 0);
00220 
00221     //if (pos == line.npos) continue; // line was all whitespace
00222     //if (line[pos] == '#') continue; // line is a comment
00223 
00224     // maybe we want to trash it right here:
00225     if (trashed < itsTrash) { ++trashed;  continue; }
00226     
00227     // else let's read the data
00228     else {pushData(line);}
00229     
00230     // update our sample count
00231     samp_count++;
00232   }
00233   
00234   LINFO("%s: %zu samples, %u events.", fn, itsData.size(), itsNumEvents);
00235 }
00236 
00237 // ######################################################################
00238 HandTrace::~HandTrace()
00239 { }
00240 
00241 // ######################################################################
00242 bool HandTrace::pushData(const std::string line)
00243 {
00244   /* Parses the line from a .hanD file and decides whether
00245    * to store extra data or not.
00246    * There are few types of fields(?):
00247    * 1. the standard (key) fields (x,y,buttons)
00248    * 2. event related fields: these fields receive a '*' designation
00249    *    and will be treated as "events"
00250    * 3. extra (non-key), non-event related fields 
00251    * 
00252    * Note: this code depends on some defaults that are set in .hanD
00253    */
00254 
00255   // Parse values from the line
00256   std::vector<std::string> strVals;
00257   split(line, " ", std::back_inserter(strVals));
00258 
00259   /* We expect the log file to have the following:
00260    * x y b0 b1 b2 b3 ...  *Event
00261    * the number of buttons available is not static/constant,
00262    * but they are expected to remain constant during the whole parsing
00263    * x and y is expected to be an integer, meanwhile buttons are expected
00264    * to be number 0 for false or any other for true condition
00265    * an event should be after the stuffs and always started with star
00266    */
00267 
00268   // Is the line an event field ?
00269   bool hasEventData = false;  uint i, iExtraData=0; //iExtraData shows where the extra data begins
00270   for (i = 0; i < strVals.size(); i++){
00271     // field should be indicative of an event and value should be valid
00272     if (isEventField(strVals[i]) && (fromStr<double>(strVals[i])==0.0)) {
00273       hasEventData = true; iExtraData=i; break;
00274     }
00275   }
00276 
00277   //! parse the line now for normal data
00278   //Create empty data
00279   int x_ = -1, y_ = -1;
00280   std::vector<bool> b_;
00281   int mx_ = -1, my_ = -1, nmx_ = nativeX, nmy_ = nativeY;
00282   bool mlb_ = false, mmb_ = false, mrb_ = false;
00283   //char * kbch_ = (char*)("");
00284 
00285   int no_btn=0; uint incr = 0; std::string kbData_("");
00286 
00287 
00288   // Doing the fields checking
00289   for (uint fldno = 0; fldno < itsFields.size(); fldno++) {
00290     //LINFO("BLAH %d %d %s %d",fldno,incr,itsFields[fldno].c_str(),
00291     //      int(strVals.size()));
00292     switch (itsFields[fldno][0]) {
00293     case 'x': //Joystick's x axis
00294       x_ = fromStr<int>(strVals[fldno+incr]); break;
00295     case 'y': //Joystick's y axis
00296       y_ = fromStr<int>(strVals[fldno+incr]); break;
00297     case 'b': //Joystick's buttons
00298       if (itsFields[fldno].size() > 1) { // we specify number of buttons
00299         std::string tmpline=itsFields[fldno];
00300         no_btn=fromStr<int>(tmpline.erase(0,1));
00301         incr += no_btn-1; // minus 1 coz we processed 1st data
00302         for (i = fldno; i < fldno+no_btn; i++) {
00303           b_.push_back(strVals[i].compare("0") != 0); }        
00304       } else { // none specified, we take the rest of data as buttons
00305         for (i = fldno; i < strVals.size(); i++) {
00306           b_.push_back(strVals[i].compare("0") != 0); }        
00307       } break;
00308     case 'm': // Mouse items
00309       switch (itsFields[fldno][1]) {
00310       case 'x': mx_ = fromStr<int>(strVals[fldno+incr]); break;
00311       case 'y': my_ = fromStr<int>(strVals[fldno+incr]); break;
00312       case 'l': mlb_ = (strVals[fldno+incr].compare("0") != 0); break;
00313       case 'm': mmb_ = (strVals[fldno+incr].compare("0") != 0); break;
00314       case 'r': mrb_ = (strVals[fldno+incr].compare("0") != 0); break;
00315       default: LFATAL("unknown field %s", itsFields[fldno+incr].c_str()); break;
00316       }
00317       break;
00318     case 'n': // Native resolution
00319       LFATAL("put native resolution in 'res=123x456'");
00320       break;
00321     case 'k': // Keyboard, treat the rest of data as string
00322       for (i = fldno+incr; i < strVals.size(); i++) {
00323         kbData_ += strVals[i]; kbData_ += " ";}
00324       break;
00325     default:
00326       LFATAL("unknown field %s", itsFields[fldno+incr].c_str());
00327       break;
00328     }
00329   }
00330 
00331   // parse the line for extra data
00332   rutz::shared_ptr<ParamMap> dataBuffer(new ParamMap);
00333   std::vector<std::string> temp;
00334   if (hasEventData){
00335     for (i = iExtraData; i < strVals.size(); i++){
00336       split(strVals[i], "=", std::back_inserter(temp));
00337       dataBuffer->putDoubleParam(unspecial(temp[0]),fromStr<double>(temp[1]));
00338     }
00339   }
00340   
00341   // if we have event-related data
00342   if(hasEventData) {
00343     // add to list of events
00344     itsEvents.push_back(dataBuffer);
00345     itsNumEvents++;
00346   }
00347 
00348   // Put all the data we gathered
00349   RawHandData ed = {x_, y_, b_, mx_, my_, nmx_, nmy_,
00350                     //mlb_, mmb_, mrb_, kbch_, dataBuffer};
00351                     mlb_, mmb_, mrb_, kbData_, dataBuffer};
00352   itsData.push_back(ed);
00353   
00354   return true;
00355 }
00356 
00357 // ######################################################################
00358 bool HandTrace::isEventField(const std::string &field) const {
00359   return (field[0] == '*'); }
00360 
00361 // ######################################################################
00362 std::string HandTrace::special(std::string field) const {
00363   if(field[0]=='*') return field;
00364   else return "*"+field; }
00365 
00366 // ######################################################################
00367 std::string HandTrace::unspecial(std::string field) const {
00368   if(field[0]=='*') return field.erase(0,1);
00369   else return field; }
00370 
00371 // ######################################################################
00372 /* So things look consistent in everyone's emacs... */
00373 /* Local Variables: */
00374 /* mode: c++ */
00375 /* indent-tabs-mode: nil */
00376 /* End: */
00377 
00378 #endif // PSYCHO_HANDTRACE_C_DEFINED
Generated on Sun May 8 08:41:13 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3