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