00001 /*!@file AppPsycho/psycho-compare.C Compare two still images */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00005 // University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: Laurent Itti <itti@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/AppPsycho/psycho-compare.C $ 00035 // $Id: psycho-compare.C 9412 2008-03-10 23:10:15Z farhan $ 00036 // 00037 00038 #include "Component/ModelManager.H" 00039 #include "Component/ComponentOpts.H" 00040 #include "Component/EventLog.H" 00041 #include "Image/Image.H" 00042 #include "Image/CutPaste.H" 00043 #include "Image/DrawOps.H" 00044 #include "Psycho/PsychoDisplay.H" 00045 #include "Psycho/EyeTrackerConfigurator.H" 00046 #include "Psycho/EyeTracker.H" 00047 #include "Psycho/PsychoOpts.H" 00048 #include "GUI/GUIOpts.H" 00049 #include "Raster/Raster.H" 00050 #include "Util/Types.H" 00051 #include "Util/StringUtil.H" 00052 #include "Util/MathFunctions.H" 00053 #include <fstream> 00054 00055 //! Compare two images along a number of criteria 00056 /*! This shows two images and asks the user to emit a judgement along 00057 a number of criteria. The two images are randomly presented on the 00058 left/right of fixation. After 5 seconds, criteria questions are 00059 displayed at the bottom of the screen. Input file format is: 00060 00061 <num criteria> 00062 <textual description of criterion 1> 00063 ... 00064 <textual description of criterion N> 00065 <image filename 1> <image filename 2> 00066 ... 00067 <image filename 1> <image filename 2> 00068 00069 00070 EXAMPLE: 00071 00072 3 00073 Which image is more beautiful? 00074 Which image contains more animals? 00075 Which image is more geeky? 00076 image001.png image002.png 00077 image003.png image004.png 00078 image005.png image006.png 00079 00080 */ 00081 00082 // ###################################################################### 00083 static int submain(const int argc, char** argv) 00084 { 00085 MYLOGVERB = LOG_INFO; // suppress debug messages 00086 00087 // Instantiate a ModelManager: 00088 ModelManager manager("Psychophysics Comparison"); 00089 00090 // Instantiate our various ModelComponents: 00091 nub::soft_ref<EventLog> el(new EventLog(manager)); 00092 manager.addSubComponent(el); 00093 00094 nub::soft_ref<PsychoDisplay> d(new PsychoDisplay(manager)); 00095 manager.addSubComponent(d); 00096 00097 nub::soft_ref<EyeTrackerConfigurator> 00098 etc(new EyeTrackerConfigurator(manager)); 00099 manager.addSubComponent(etc); 00100 00101 00102 // set a default display size: 00103 manager.exportOptions(MC_RECURSE); 00104 manager.setOptionValString(&OPT_SDLdisplayDims, "1920x1080"); 00105 manager.setOptionValString(&OPT_EventLogFileName, "psychodata.psy"); 00106 manager.setOptionValString(&OPT_EyeTrackerType, "ISCAN"); 00107 00108 // Parse command-line: 00109 if (manager.parseCommandLine(argc, argv, "<fileList>", 1, -1) == false) 00110 return(1); 00111 00112 nub::soft_ref<EyeTracker> et = etc->getET(); 00113 d->setEyeTracker(et); 00114 d->setEventLog(el); 00115 et->setEventLog(el); 00116 00117 // let's get all our ModelComponent instances started: 00118 manager.start(); 00119 00120 // get the list of images 00121 std::ifstream file(manager.getExtraArg(0).c_str()); 00122 if (file == 0) LFATAL("Couldn't open file: '%s'", 00123 manager.getExtraArg(0).c_str()); 00124 00125 initRandomNumbers(); 00126 00127 std::string line; 00128 if (!getline(file, line)) LFATAL("Bogus file format: missing num criteria"); 00129 int numcrit; convertFromString(line, numcrit); 00130 std::vector<std::string> message; 00131 00132 for (int i = 0; i < numcrit; ++ i) 00133 { 00134 if (!getline(file, line)) 00135 LFATAL("Bogus file format: missing text for criterion %d", i+1); 00136 message.push_back(line); 00137 } 00138 00139 // load up all the image pairs: 00140 std::vector<std::string> lines; 00141 while(getline(file, line)) lines.push_back(line); 00142 00143 // create a randomized index: 00144 uint *idx = new uint[lines.size()]; 00145 for (uint i = 0; i < lines.size(); i ++) idx[i] = i; 00146 randShuffle(idx, lines.size()); 00147 LINFO("Randomized %"ZU" pairs of images.", lines.size()); 00148 00149 const uint dw = d->getWidth(), dh = d->getHeight(); 00150 00151 // let's do an eye tracker calibration: 00152 // let's display an ISCAN calibration grid: 00153 d->clearScreen(); 00154 d->displayISCANcalib(); 00155 d->waitForKey(); 00156 00157 // let's do an eye tracker calibration: 00158 d->displayText("<SPACE> to calibrate; other key to skip"); 00159 int c = d->waitForKey(); 00160 if (c == ' ') d->displayEyeTrackerCalibration(); 00161 00162 d->clearScreen(); 00163 d->displayText("<SPACE> To Cotinue Experiment"); 00164 c = d->waitForKey(); 00165 00166 for (uint ii = 0; ii < lines.size(); ++ii) 00167 { 00168 std::vector<std::string> tokens; 00169 split(lines[idx[ii]], " \t", std::back_inserter(tokens)); 00170 if (tokens.size() != 2) 00171 LFATAL("Need two filenames per line: %s", lines[idx[ii]].c_str()); 00172 00173 // pick a random display order: 00174 uint idx1 = 0, idx2 = 1; 00175 if (randomUpToNotIncluding(2) > 0) { idx1 = 1; idx2 = 0; } 00176 00177 // load up the two images and show a fixation cross on a blank screen: 00178 d->clearScreen(); 00179 LINFO("Loading '%s'...", tokens[idx1].c_str()); 00180 Image< PixRGB<byte> > image1 = Raster::ReadRGB(tokens[idx1]); 00181 LINFO("Loading '%s'...", tokens[idx2].c_str()); 00182 Image< PixRGB<byte> > image2 = Raster::ReadRGB(tokens[idx2]); 00183 00184 // Create a composite side-by-side image: 00185 Image< PixRGB<byte> > image(d->getDims(), NO_INIT); 00186 image.clear(d->getGrey()); 00187 00188 const uint m = 20; // margin around the images 00189 const uint mm = 20; // half spacing between the images 00190 inplaceEmbed(image, image1, 00191 Rectangle(Point2D<int>(m, m), Dims(dw/2-mm-m*2, dh-m*2)), 00192 d->getGrey(), true); 00193 inplaceEmbed(image, image2, 00194 Rectangle(Point2D<int>(dw/2+mm+m, m), Dims(dw/2-mm-m*2, dh-m*2)), 00195 d->getGrey(), true); 00196 00197 SDL_Surface *surf = d->makeBlittableSurface(image, true); 00198 00199 LINFO("%s / %s ready.", tokens[idx1].c_str(), tokens[idx2].c_str()); 00200 d->displayFixation(); 00201 00202 // ready to go whenever the user is ready: 00203 d->waitForKey(); 00204 d->pushEvent(std::string("===== Showing images: ") + 00205 tokens[idx1] + " / " + tokens[idx2] + " ====="); 00206 00207 // start the eye tracker: 00208 et->track(true); 00209 00210 // blink the fixation: 00211 d->displayFixationBlink(); 00212 00213 // show the image: 00214 d->displaySurface(surf, -2); 00215 00216 // sleep a bit and stop the tracker: 00217 usleep(5000000); 00218 et->track(false); 00219 00220 const int fh = 20; // font height in pixels 00221 00222 // display the questions and wait for a key each time: 00223 for (int i = 0; i < numcrit; ++i) 00224 { 00225 d->pushEvent(std::string("===== Question: ") + 00226 message[i] + " ====="); 00227 00228 drawFilledRect(image, Rectangle(Point2D<int>(0, d->getHeight() - fh-10), 00229 Dims(d->getWidth(), fh+10)), 00230 d->getGrey()); 00231 00232 writeText(image, 00233 Point2D<int>((d->getWidth() - 10 * message[i].size()) / 2, 00234 d->getHeight() - fh - 5), 00235 message[i].c_str(), PixRGB<byte>(0, 0, 64), 00236 d->getGrey(), SimpleFont::FIXED(10)); 00237 00238 SDL_Surface *s = d->makeBlittableSurface(image, true); 00239 d->displaySurface(s, -2); 00240 00241 // wait for key (will log which key was pressed): 00242 d->waitForKey(); 00243 00244 SDL_FreeSurface(s); 00245 } 00246 00247 // get ready for next one or end: 00248 d->clearScreen(); 00249 SDL_FreeSurface(surf); 00250 } 00251 00252 d->clearScreen(); 00253 d->displayText("Experiment complete. Thank you!"); 00254 d->waitForKey(); 00255 00256 // stop all our ModelComponents 00257 manager.stop(); 00258 00259 delete [] idx; 00260 00261 // all done! 00262 return 0; 00263 } 00264 00265 // ###################################################################### 00266 extern "C" int main(const int argc, char** argv) 00267 { 00268 // simple wrapper around submain() to catch exceptions (because we 00269 // want to allow PsychoDisplay to shut down cleanly; otherwise if we 00270 // abort while SDL is in fullscreen mode, the X server won't return 00271 // to its original resolution) 00272 try 00273 { 00274 return submain(argc, argv); 00275 } 00276 catch (...) 00277 { 00278 REPORT_CURRENT_EXCEPTION; 00279 } 00280 00281 return 1; 00282 } 00283 00284 // ###################################################################### 00285 /* So things look consistent in everyone's emacs... */ 00286 /* Local Variables: */ 00287 /* indent-tabs-mode: nil */ 00288 /* End: */