00001 /*!@file AppPsycho/psycho-reading.C Psychophysics display of 00002 paragraphs, individtual sentences, and transitions between 00003 sentences with questions. */ 00004 00005 // //////////////////////////////////////////////////////////////////// // 00006 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00007 // University of Southern California (USC) and the iLab at USC. // 00008 // See http://iLab.usc.edu for information about this project. // 00009 // //////////////////////////////////////////////////////////////////// // 00010 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00011 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00012 // in Visual Environments, and Applications'' by Christof Koch and // 00013 // Laurent Itti, California Institute of Technology, 2001 (patent // 00014 // pending; application number 09/912,225 filed July 23, 2001; see // 00015 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00016 // //////////////////////////////////////////////////////////////////// // 00017 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00018 // // 00019 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00020 // redistribute it and/or modify it under the terms of the GNU General // 00021 // Public License as published by the Free Software Foundation; either // 00022 // version 2 of the License, or (at your option) any later version. // 00023 // // 00024 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00025 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00026 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00027 // PURPOSE. See the GNU General Public License for more details. // 00028 // // 00029 // You should have received a copy of the GNU General Public License // 00030 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00031 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00032 // Boston, MA 02111-1307 USA. // 00033 // //////////////////////////////////////////////////////////////////// // 00034 // 00035 // Primary maintainer for this file: David J. Berg <dberg@usc.edu> 00036 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/AppPsycho/psycho-reading-eval.C $ 00037 00038 #include "Component/ModelManager.H" 00039 #include "Image/Image.H" 00040 #include "Image/ShapeOps.H" 00041 #include "Image/DrawOps.H" 00042 #include "Image/Transforms.H" 00043 #include "Image/SimpleFont.H" 00044 #include "Raster/Raster.H" 00045 #include "Psycho/PsychoDisplay.H" 00046 #include "Psycho/EyeTrackerConfigurator.H" 00047 #include "Psycho/EyeTracker.H" 00048 #include "Psycho/PsychoOpts.H" 00049 #include "Component/EventLog.H" 00050 #include "Component/ComponentOpts.H" 00051 #include "Util/StringUtil.H" 00052 #include "Util/sformat.H" 00053 00054 #include <iostream> 00055 #include <fstream> 00056 00057 #define TEXTSCREEN 1920 00058 #define TEXTYPOS 25 00059 // ###################################################################### 00060 // Load our image files 00061 // ###################################################################### 00062 std::vector<Image<PixRGB<byte> > > readParagraphs(const std::string& filename, 00063 std::vector<std::string>& names) 00064 { 00065 names.clear(); 00066 00067 std::vector<std::string> parts; 00068 split(filename, ".", back_inserter(parts)); 00069 const std::string base = parts[0]; 00070 const std::string ext = parts[1]; 00071 00072 std::vector<Image<PixRGB<byte> > > vec; 00073 int c = 0; 00074 bool add = false; 00075 std::string fn = filename; 00076 while (Raster::fileExists(fn)) 00077 { 00078 //good file, so add it 00079 LINFO("adding file : %s:", fn.c_str()); 00080 vec.push_back(Raster::ReadRGB(fn)); 00081 names.push_back(fn); 00082 00083 //next filename 00084 const int cp = (add) ? 1 : 0; 00085 fn = base + "_" + toStr<int>(c) + toStr<int>(c + cp) + "." + ext; 00086 if (add) 00087 ++c; 00088 add = !add; 00089 } 00090 return vec; 00091 } 00092 00093 // ###################################################################### 00094 // count the number of summaries for a document 00095 // ###################################################################### 00096 const uint countSummaries(const std::string& base) 00097 { 00098 int ii = 1; 00099 std::string fn = base + "_" + toStr<int>(ii) + ".png"; 00100 00101 while (Raster::fileExists(fn)) 00102 fn = base + "_" + toStr<int>(++ii) + ".png"; 00103 00104 return ii - 1; 00105 } 00106 00107 00108 // ###################################################################### 00109 extern "C" int main(const int argc, char** argv) 00110 { 00111 MYLOGVERB = LOG_INFO; // suppress debug messages 00112 00113 // Instantiate a ModelManager: 00114 ModelManager manager("Psycho Reading"); 00115 00116 // Instantiate our various ModelComponents: 00117 nub::soft_ref<PsychoDisplay> d(new PsychoDisplay(manager)); 00118 manager.addSubComponent(d); 00119 00120 nub::soft_ref<EyeTrackerConfigurator> 00121 etc(new EyeTrackerConfigurator(manager)); 00122 manager.addSubComponent(etc); 00123 00124 nub::soft_ref<EventLog> el(new EventLog(manager)); 00125 manager.addSubComponent(el); 00126 00127 manager.setOptionValString(&OPT_EventLogFileName, "psychodata.psy"); 00128 manager.setOptionValString(&OPT_EyeTrackerType, "EL"); 00129 00130 // Parse command-line: 00131 if (manager.parseCommandLine(argc, argv, 00132 "<questions file> <doc base1> <doc base2> ", 00133 2, -1)==false) 00134 return(1); 00135 00136 // hook our various babies up and do post-command-line configs: 00137 nub::soft_ref<EyeTracker> et = etc->getET(); 00138 d->setEyeTracker(et); 00139 d->setEventLog(el); 00140 et->setEventLog(el); 00141 00142 // let's get all our ModelComponent instances started: 00143 manager.start(); 00144 00145 // setup array of document indices: 00146 uint nbimgs = manager.numExtraArgs()-1; int index[nbimgs]; 00147 for (uint ii = 0; ii < nbimgs; ++ii) index[ii] = ii+1; 00148 randShuffle(index, nbimgs); 00149 00150 SimpleFont fnt = SimpleFont::FIXED(20); 00151 00152 /*read in the questions */ 00153 std::vector<std::string > questions; 00154 std::ifstream file; 00155 file.open(manager.getExtraArg(0).c_str()); 00156 if (file.is_open()) 00157 { 00158 while (!file.eof()) 00159 { 00160 std::string temp; 00161 getline(file, temp); 00162 LINFO("%s", temp.c_str()); 00163 questions.push_back(temp); 00164 } 00165 questions.pop_back(); 00166 file.close(); 00167 } 00168 else LFATAL("Cannot open questions file. "); 00169 00170 try 00171 { 00172 // main loop: 00173 for (uint i = 0; i < nbimgs; ++i) 00174 { 00175 // let's do an eye tracker calibration: 00176 LINFO("Have the subject read summary: %s", manager.getExtraArg(index[i]).c_str()); 00177 d->displayText("Please read a summary <press enter when finished>, then we'll do a quick calibration."); 00178 d->waitForKey(); 00179 00180 LINFO("Performing calibration"); 00181 et->calibrate(d); 00182 00183 //clear the screen of any junk 00184 d->clearScreen(); 00185 00186 //randomize summaries for this document 00187 uint nbsums = countSummaries(manager.getExtraArg(index[i])); 00188 LINFO("%d Summaries found for Document '%s'", nbsums, 00189 manager.getExtraArg(index[i]).c_str()); 00190 int sindex[nbsums]; 00191 for (uint ii = 0; ii < nbsums; ++ii) sindex[ii] = ii + 1; 00192 randShuffle(sindex, nbsums); 00193 00194 for (uint j = 0; j < nbsums; ++j) 00195 { 00196 //write a loading message to the screen 00197 d->clearScreen(); 00198 d->displayText("Loading - please wait....."); 00199 00200 const std::string file = manager.getExtraArg(index[i]) + "_" + 00201 toStr<int>(sindex[j]) + ".png"; 00202 LINFO("Loading summary set '%s'...", file.c_str()); 00203 std::vector<std::string> fnames; 00204 std::vector<Image<PixRGB<byte> > > imageset = 00205 readParagraphs(file, fnames); 00206 00207 //setup our anchors for displaying text 00208 //int w = imageset[0].getWidth();//width and height of SDL surface 00209 const int w = TEXTSCREEN; 00210 std::vector<Point2D<int> > anchors; 00211 std::vector<std::string >::const_iterator 00212 iter(questions.begin()), end(questions.end()); 00213 while (iter != end) 00214 { 00215 int hposq = w/2 - 00216 int(double(fnt.w() * (uint)iter->size()) / 2.0); 00217 anchors.push_back(Point2D<int>(hposq, TEXTYPOS)); 00218 ++iter; 00219 } 00220 00221 d->clearScreen(); 00222 00223 //loop over the images 00224 for (uint k = 0; k < imageset.size(); ++k) 00225 { 00226 //display the first image 00227 Image<PixRGB<byte> > cimg = imageset[k]; 00228 00229 //if our first image display with fixation blink etc 00230 if (k == 0) 00231 { 00232 //create surface 00233 SDL_Surface *surf = d->makeBlittableSurface(cimg, true); 00234 00235 LINFO("ready '%s'...", fnames[k].c_str()); 00236 00237 //do a quick drift correct 00238 et->recalibrate(d,0); 00239 00240 //fixation 00241 //d->displaFixation(); 00242 00243 // ready to go whenever the user is ready: 00244 //d->waitForKey(); 00245 d->waitNextRequestedVsync(false, true); 00246 d->pushEvent(sformat("===== Showing text: %s =====", 00247 fnames[k].c_str())); 00248 00249 // start the eye tracker: 00250 et->track(true); 00251 00252 // blink the fixation: 00253 d->displayFixationBlink(); 00254 00255 // show the image: 00256 d->displaySurface(surf, -2); 00257 00258 // wait for key: 00259 d->waitForKey(); 00260 00261 // free the image: 00262 SDL_FreeSurface(surf); 00263 00264 // stop the eye tracker: 00265 usleep(50000); 00266 et->track(false); 00267 LINFO("recorded eye tracker session"); 00268 } 00269 00270 //ask the user a questions and record a new eye tracker session: 00271 std::vector<Image<PixRGB<byte> > > cimgq(questions.size(), cimg); 00272 for (uint m = 0; m < questions.size(); ++m) 00273 { 00274 //create images with questions 00275 const float factor = (float)d->getHeight() / (float)d->getWidth(); 00276 const int tempv = TEXTSCREEN * factor; 00277 Image<PixRGB<byte> > temp(TEXTSCREEN, tempv, ZEROS); 00278 00279 writeText(temp, anchors[m], questions[m].c_str(), 00280 PixRGB<byte>(255,255,255), PixRGB<byte>(0,0,0),fnt); 00281 00282 //combine question text with our current image 00283 temp = rescaleBilinear(temp, cimgq[m].getDims()); 00284 cimgq[m] = composite(temp, cimgq[m], PixRGB<byte>(0,0,0)); 00285 00286 //create surface 00287 SDL_Surface *surf = d->makeBlittableSurface(cimgq[m], true); 00288 00289 LINFO("ready Q%d '%s'...", m, fnames[k].c_str()); 00290 00291 d->waitNextRequestedVsync(false, true); 00292 d->pushEvent(sformat("===== Showing Q%d \"%s\": %s =====", 00293 m, questions[k].c_str(), fnames[k].c_str())); 00294 00295 // show the image: 00296 d->displaySurface(surf, -2); 00297 d->waitForKey(); 00298 SDL_FreeSurface(surf); 00299 } 00300 } 00301 } 00302 } 00303 00304 d->clearScreen(); 00305 d->displayText("Experiment complete. Thank you! Press any key."); 00306 d->waitForKey(); 00307 00308 } 00309 catch (...) 00310 { 00311 REPORT_CURRENT_EXCEPTION; 00312 }; 00313 00314 // stop all our ModelComponents 00315 manager.stop(); 00316 00317 // all done! 00318 return 0; 00319 } 00320 00321 // ###################################################################### 00322 /* So things look consistent in everyone's emacs... */ 00323 /* Local Variables: */ 00324 /* indent-tabs-mode: nil */ 00325 /* End: */