stim-flyover.C

00001 /*!@file AppPsycho/psycho-noisecuing.C Psychophysics display for a search for a
00002   target that is presented in various repeated noise backgrounds */
00003 
00004 // //////////////////////////////////////////////////////////////////// //
00005 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the //
00006 // University of Southern California (USC) and the iLab at USC.         //
00007 // See http://iLab.usc.edu for information about this project.          //
00008 // //////////////////////////////////////////////////////////////////// //
00009 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00010 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00011 // in Visual Environments, and Applications'' by Christof Koch and      //
00012 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00013 // pending; application number 09/912,225 filed July 23, 2001; see      //
00014 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00015 // //////////////////////////////////////////////////////////////////// //
00016 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00017 //                                                                      //
00018 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00019 // redistribute it and/or modify it under the terms of the GNU General  //
00020 // Public License as published by the Free Software Foundation; either  //
00021 // version 2 of the License, or (at your option) any later version.     //
00022 //                                                                      //
00023 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00024 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00025 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00026 // PURPOSE.  See the GNU General Public License for more details.       //
00027 //                                                                      //
00028 // You should have received a copy of the GNU General Public License    //
00029 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00030 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00031 // Boston, MA 02111-1307 USA.                                           //
00032 // //////////////////////////////////////////////////////////////////// //
00033 //
00034 // Primary maintainer for this file: Laurent Itti <itti@usc.edu>
00035 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/AppPsycho/psycho-searchGabor.C $
00036 // $Id: psycho-searchGabor.C 10794 2009-02-08 06:21:09Z itti $
00037 //
00038 
00039 #include "Component/ModelManager.H"
00040 #include "Component/ModelOptionDef.H"
00041 #include "Image/ColorOps.H" // for makeRGB()
00042 #include "Image/CutPaste.H" // for inplacePaste()
00043 #include "Image/DrawOps.H" // for drawLine()
00044 #include "Image/Image.H"
00045 #include "Image/MathOps.H"  // for inplaceSpeckleNoise()
00046 #include "Image/LowPass.H" // for LowPass5x, LowPass5y
00047 #include "Image/ShapeOps.H" // for rescale()
00048 #include "Image/Transforms.H"
00049 #include "Image/Layout.H"
00050 #include "Psycho/PsychoDisplay.H"
00051 #include "Psycho/EyeTrackerConfigurator.H"
00052 #include "Psycho/EyeTracker.H"
00053 #include "Psycho/PsychoOpts.H"
00054 #include "Image/geom.h"
00055 #include "Psycho/ClassicSearchItem.H"
00056 #include "Psycho/SearchArray.H"
00057 #include "Component/EventLog.H"
00058 #include "Component/ComponentOpts.H"
00059 #include "Raster/Raster.H"
00060 #include "Util/MathFunctions.H"
00061 #include "Util/StringUtil.H"
00062 #include "Util/StringConversions.H"
00063 #include "GUI/GUIOpts.H"
00064 
00065 #include <sstream>
00066 #include <ctime>
00067 #include <ctype.h>
00068 #include <vector>
00069 #include <string>
00070 #include <fstream>
00071 
00072 using namespace std;
00073 
00074 static const ModelOptionCateg MOC_RANDGENIMAGE = {
00075   MOC_SORTPRI_2, "Options for random image generation" };
00076 
00077 static const ModelOptionDef OPT_RandImageDims =
00078   { MODOPT_ARG(Dims), "GenImageDims", &MOC_RANDGENIMAGE, OPTEXP_CORE,
00079     "dimensions of the random image",
00080     "rand-image-dims", '\0', "<width>x<height>", "1280x720" };
00081 /*
00082 static const ModelOptionDef OPT_TextureLibrary =
00083   { MODOPT_FLAG, "RemoveMagnitude", &MOC_RANDIMAGE, OPTEXP_CORE,
00084     "remove the phase component of an image",
00085     "remove-magnitude", '\0', "--[no]remove-phase", "false" };
00086 */
00087 static const ModelOptionDef OPT_NoiseMagnitude =
00088   { MODOPT_ARG(float), "NoiseMagnitude", &MOC_RANDGENIMAGE, OPTEXP_CORE,
00089     "adjust the magnitude of the overlaid noise, from 0.0 to 1.0",
00090     "noise-magnitude", '\0', "<float>", "0.0"};
00091 
00092 static const ModelOptionDef OPT_NoiseColor =
00093   { MODOPT_ARG(std::string), "NoiseColor", &MOC_RANDGENIMAGE, OPTEXP_CORE,
00094     "give the color of the overlaid noise, in white, pink, or brown",
00095     "noise-color", '\0', "[white,pink,brown]", "white"};
00096 
00097 //! number of frames in the mask
00098 //#define NMASK 10
00099 
00100 // Trial design for contextual cueing experiments.  May class this up soon. 
00101 // Also may make Trial/Experiment classes as ModelComponents of this style.
00102 // Easier to encapsulate experimental designs as separate from trial content.
00103 
00104 // But for now it's easier to keep everything public.
00105 enum NoiseColor { WHITE, PINK, BROWN };
00106 struct trialAgenda
00107 {
00108   bool repeated;
00109   NoiseColor color;
00110   geom::vec2d targetloc;
00111   uint noiseSeed;
00112   trialAgenda(const bool r, const NoiseColor c, 
00113               const geom::vec2d t, const uint n)
00114   {
00115     repeated = r;
00116     color = c;
00117     targetloc = t;
00118     noiseSeed = n;
00119   }
00120 
00121   std::string colname() const
00122   {
00123     switch(color) {
00124     case WHITE: return "white";
00125     case PINK:  return "pink";
00126     case BROWN: return "brown";
00127     }
00128     return "";
00129   }
00130 
00131   void randomizeNoise(const uint Nbkgds)
00132   {
00133     noiseSeed = randomUpToNotIncluding(Nbkgds)+1;
00134   }
00135 
00136   std::string backgroundFile() const
00137   {
00138     std::string stimdir = "/lab/jshen/projects/eye-cuing/stimuli/noiseseeds";
00139     return sformat("%s/%s%03d.png",stimdir.c_str(),colname().c_str(),noiseSeed);
00140   }
00141 };
00142 
00143 std::string convertToString(const trialAgenda& val)
00144 {
00145   std::stringstream s; 
00146   s << val.colname() << " noise, ";
00147   if (val.repeated)
00148     s << "repeated, seed " << val.noiseSeed;
00149   else
00150     s << "random";
00151 
00152   s << ", target @ (" << val.targetloc.x() << "," << val.targetloc.y() << ")";
00153   return s.str();
00154 }
00155 
00156 // Generate a random integer uniformly in (x,y);
00157 int randomInRange(const int x, const int y)
00158 {
00159   return randomUpToNotIncluding(y-x-1)+(x+1);
00160 }
00161 
00162 // Generate a random point uniformly in d
00163 geom::vec2d randomPtIn(const Dims d);
00164 // Generate a random point uniformly in d
00165 Point2D<int> randomPointIn(const Dims d);
00166 
00167 // Generate a random point uniformly in d
00168 geom::vec2d randomPtIn(const Rectangle d)
00169 {
00170   return geom::vec2d(randomInRange(d.left(),d.rightO()),
00171                      randomInRange(d.top(),d.bottomO()));
00172 }
00173 
00174 Image<byte> plainBkgd(const trialAgenda A)
00175 {
00176   //skips file validation step
00177   //  return getPixelComponentImage(Raster::ReadRGB(A.backgroundFile()),0); 
00178   return Raster::ReadGray(A.backgroundFile(),RASFMT_PNG);
00179 //the second arg can be 0,1,2 since the image is B&W
00180 }
00181 
00182 Image<PixRGB<byte> > colorizeBkgd(const trialAgenda A,const uint Nbkgds)
00183 {
00184   trialAgenda B = A;
00185 
00186   std::vector<Image<byte> > comp;
00187   for (uint i = 0; i < 3; i++) {
00188     if(!B.repeated) //randomize noise
00189       B.randomizeNoise(Nbkgds);  
00190     else //systematically step
00191       B.noiseSeed = (B.noiseSeed)%Nbkgds+1;
00192 
00193     //skips file validation step
00194     comp.push_back(plainBkgd(B));
00195   }
00196   return makeRGB(comp[0],comp[1],comp[2]);
00197 }
00198 
00199 void drawRandomLine(Image<byte> & im, const byte val, const int thickness);
00200 void extrapolateLine(Dims d, Point2D<int> & X, Point2D<int> & Y);
00201 
00202 template <class T>
00203 Image<byte> makeNary(const Image<T>& src, const std::vector<T> thresholds,
00204                      const std::vector<byte> levels);
00205 
00206 
00207 Image<byte> texturizeImage(const Image<byte> im, const uint Nlevels);
00208 Image<byte> discretizeImage(const Image<byte> im, const int Nlevels);
00209 Image<byte> getBrodatzTexture(uint seed, const Dims dims);
00210 Image<byte> getStretchedTexture(const std::string filename, const Dims dims);
00211 Image<byte> getTiledTexture(const std::string filename, const Dims dims);
00212 
00213 // ######################################################################
00214 static int submain(const int argc, char** argv)
00215 {
00216   MYLOGVERB = LOG_INFO;  // suppress debug messages
00217 
00218   // Instantiate a ModelManager:
00219   ModelManager manager("AppMedia: Flyover stimulus");
00220 
00221   // get dimensions of window
00222   if (manager.parseCommandLine(argc, argv,"<out_stem>", 1, 1) == false)
00223     return(1);
00224 
00225   //  Image<float> myFloatImg(dims.w(), dims.h(), ZEROS);
00226   //Image<double> myDoubleImg(dims.w(), dims.h(), ZEROS);
00227   char filename[255], texfile[255];
00228 
00229   OModelParam<Dims> dims(&OPT_RandImageDims, &manager);
00230   OModelParam<float> noiseMag(&OPT_NoiseMagnitude, &manager);
00231   OModelParam<std::string> noiseColor(&OPT_NoiseColor, &manager);
00232 
00233   // get command line parameters for filename
00234   sprintf(filename, "%s.png",manager.getExtraArg(0).c_str());
00235   sprintf(texfile, "%s-1.png",manager.getExtraArg(0).c_str());
00236 
00237   /*
00238   nub::soft_ref<OutputFrameSeries> ofs(new OutputFrameSeries(manager));
00239   manager.addSubComponent(ofs);
00240   */
00241 
00242   // let's get all our ModelComponent instances started:
00243   manager.start();
00244 
00245   // **************** Experimental settings *************** //
00246 
00247   // number of available noise frames in the stimuli folder
00248   const uint Nnoises = 100;
00249 
00250   /*
00251   // size of screen - should be no bigger than 1920x1080
00252   const Dims screenDims = 
00253     fromStr<Dims>(manager.getOptionValString(&OPT_SDLdisplayDims));
00254   
00255   // target/distractor type: we have choice of c o q - t l +
00256   const std::string Ttype = "T", Dtype = "L";
00257   const double phiMax = M_PI*5/8, phiMin = M_PI*3/8; 
00258   
00259   ClassicSearchItemFactory 
00260     targetsLeft(SearchItem::FOREGROUND, Ttype, 
00261                 itemsize,
00262                 Range<double>(-phiMax,-phiMin)),
00263     targetsRight(SearchItem::FOREGROUND, Ttype, 
00264                  itemsize,
00265                  Range<double>(phiMin,phiMax)),
00266     distractors(SearchItem::BACKGROUND, Dtype, 
00267                 itemsize,
00268                 Range<double>(-M_PI/2,M_PI/2));
00269   */
00270   // ******************** Trial Design ************************* //
00271 
00272   std::vector<rutz::shared_ptr<trialAgenda> > trials;
00273   const uint Ntrials = 1;
00274   const uint Nrepeats = 1;
00275   NoiseColor colors[Ntrials];
00276   bool rep[Ntrials];
00277 
00278   //  SearchArray sarray(dims, grid_spacing, min_spacing, itemsize);
00279   const PixRGB<byte> gray(128,128,128);
00280 
00281   // Design and shuffle trials
00282   initRandomNumbers();
00283   for (uint i = 0; i < Ntrials; i++) 
00284     {
00285       colors[i] = BROWN; //NoiseColor(i%3); 
00286       rep[i] = (i < Nrepeats);
00287     }
00288 
00289   for (uint i = 0; i < Ntrials; i++)
00290     {
00291       // a random location for each target
00292       const geom::vec2d pos = randomPtIn(dims.getVal());
00293       // a random seed for each trial
00294       const uint seed = randomInRange(1,Nnoises);
00295       trials.push_back
00296             (rutz::shared_ptr<trialAgenda>
00297              (new trialAgenda(rep[i],colors[i],pos,seed)));
00298     }
00299 
00300   //tests
00301   Image<byte> myMap = discretizeImage(plainBkgd(*(trials[0])),4);
00302   Image<byte> myBkgd = texturizeImage(myMap,4);
00303 
00304   LINFO("writing texture image to %s", filename);
00305   Raster::WriteGray(myBkgd,filename);
00306   
00307   // test texture
00308   LINFO("writing pattern image to %s", texfile);
00309   Raster::WriteGray(myMap,texfile);
00310 
00311   // stop all our ModelComponents
00312   manager.stop();
00313 
00314   // all done!
00315   return 0;
00316 }
00317 
00318 // ######################################################################
00319 
00320 extern "C" int main(const int argc, char** argv)
00321 {
00322   // simple wrapper around submain() to catch exceptions (because we
00323   // want to allow PsychoDisplay to shut down cleanly; otherwise if we
00324   // abort while SDL is in fullscreen mode, the X server won't return
00325   // to its original resolution)
00326   try
00327     {
00328       return submain(argc, argv);
00329     }
00330   catch (...)
00331     {
00332       REPORT_CURRENT_EXCEPTION;
00333     }
00334 
00335   return 1;
00336 }
00337 
00338 // ######################################################################
00339 
00340 // draw a random line 
00341 void drawRandomLine(Image<byte> & im, const byte val, const int thickness)
00342 {
00343   Point2D<int> P = randomPointIn(im.getDims());
00344   Point2D<int> Q = randomPointIn(im.getDims());
00345 
00346   extrapolateLine(im.getDims(),P,Q);
00347   drawLine(im, P, Q, val, thickness);
00348 }
00349 
00350 // ######################################################################
00351 
00352 // extend a line segment to boundaries
00353 void extrapolateLine(Dims d, Point2D<int> & X, Point2D<int> & Y)
00354 {
00355   // check if X and Y are in d
00356   Image<byte> foo(d, NO_INIT);
00357   if(!(foo.coordsOk(X) && foo.coordsOk(Y)) || X == Y) return;
00358 
00359   if (X.i == Y.i) {X.j = 0; Y.j = d.h(); return;}
00360   else if(X.j == Y.j) {X.i = 0; Y.j = d.w(); return;}
00361   else {float y_0 = (X.j*Y.i-X.i*Y.j)/(Y.i-X.i);
00362     float x_0 = (X.i*Y.j-X.j*Y.i)/(Y.j-X.j);
00363     float slope = (Y.j-X.j)/(Y.i-X.i);
00364     
00365     std::vector<Point2D<int> > bounds; 
00366     bounds.push_back(Point2D<int>(0,y_0));
00367     bounds.push_back(Point2D<int>(x_0,0));
00368     bounds.push_back(Point2D<int>(d.w()-1,y_0+(d.w()-1)*slope));
00369     bounds.push_back(Point2D<int>(x_0+(d.h()-1)/slope,d.h()-1));
00370 
00371     bool Xdone = 0;
00372     for(int i = 0; i < 4; i++)
00373       if(foo.coordsOk(bounds[i])) { 
00374         if(!Xdone) {
00375           X = bounds[i]; 
00376           Xdone = true;
00377         }
00378         else {
00379             Y = bounds[i]; 
00380             break;
00381         }
00382       }
00383   }
00384 }
00385 
00386 // ######################################################################
00387 
00388 // Generate a random point uniformly in d
00389 geom::vec2d randomPtIn(const Dims d)
00390 {
00391   return geom::vec2d(randomInRange(0,d.w()),
00392                      randomInRange(0,d.h()));
00393 }
00394 
00395 // Generate a random point uniformly in d
00396 Point2D<int> randomPointIn(const Dims d)
00397 {
00398   return Point2D<int>(randomInRange(0,d.w()),
00399                       randomInRange(0,d.h()));
00400 }
00401 
00402 // ######################################################################
00403 
00404 // inspired from makeBinary in Transforms.C
00405 template <class T>
00406 Image<byte> makeNary(const Image<T>& src, const std::vector<T> thresholds,
00407                   const std::vector<byte> levels)
00408 {
00409   ASSERT(thresholds.size() == levels.size() - 1);
00410   Image<byte> acc(src.getDims(),ZEROS);
00411   byte floor;
00412   for(uint i = 0; i < thresholds.size(); i++)
00413     {
00414       if(i == 0) 
00415         {
00416           floor = levels[0];
00417         }
00418     else 
00419       {
00420         floor = 0;
00421       }
00422     acc += makeBinary(src, thresholds[i],floor,levels[1]);
00423     }
00424 
00425   return acc;
00426 }
00427 
00428 // ######################################################################
00429 // maps discretized image to textured image
00430 Image<byte> texturizeImage(const Image<byte> im, const uint Nlevels)
00431 {
00432   uint i, seed;
00433   //  Image<byte> levelImage = discretizeImage(im, Nlevels);
00434   std::vector<Image<byte> > texBkgds;
00435   for(i = 0; i < Nlevels; i++)
00436     {
00437       seed = randomInRange(0,112); // num brodatz images
00438       texBkgds.push_back(getBrodatzTexture(seed, im.getDims()));
00439     }
00440 
00441   byte tiers[Nlevels];
00442   for(uint i = 0; i < Nlevels; i++) 
00443       tiers[i] = i*(255/(Nlevels-1));
00444 
00445   return mosaic(im, &texBkgds[0], tiers, Nlevels);
00446   //return mosaic(levelImage, &texBkgds[0], tiers, Nlevels);
00447 }
00448 
00449 // ######################################################################
00450 // discretizes image
00451 Image<byte> discretizeImage(const Image<byte> im, const int Nlevels)
00452 {
00453   byte imMin, imMax, i;
00454   getMinMax(im, imMin, imMax);
00455 
00456   const byte Ncuts = Nlevels - 1;
00457   
00458   // the ratios that partition the image
00459   float coeffs[Ncuts];
00460   for(i = 0; i < Ncuts; i++)
00461       coeffs[i] = (i+1.0)/(Ncuts+1.0);
00462 
00463   // the values in the noise image that partition the image
00464   std::vector<byte> cuts;
00465   for(i = 0; i < Ncuts; i++)
00466       cuts.push_back(imMax*coeffs[i]+imMin*(1-coeffs[i])); 
00467 
00468   // the mapped values of the outside image
00469   std::vector<byte> tiers;
00470   for(i = 0; i <= cuts.size(); i++) 
00471       tiers.push_back(i*(255/cuts.size()));
00472 
00473   // use makeNary to cut the image
00474   Image<byte> pattern = makeNary(im,cuts,tiers);
00475 
00476   // draw a random line cutting across the image
00477   drawRandomLine(pattern, tiers[2], 50);
00478 
00479   return pattern;
00480   
00481 } 
00482 
00483 // ######################################################################
00484 Image<byte> getBrodatzTexture(uint seed, const Dims dims)
00485 {
00486   char texPath[255];
00487   
00488   // there are only 111 images in the brodatz database
00489   const uint Nimages = 111;
00490   seed = seed % (Nimages - 1) + 1;
00491 
00492   sprintf(texPath, "/lab/jshen/projects/eye-cuing/stimuli/textures/brodatz/D%u.png",seed);
00493   return getStretchedTexture(texPath, dims);
00494 }
00495 
00496 // ######################################################################
00497 Image<byte> getStretchedTexture(const std::string filename, const Dims dims)
00498 {
00499   Image<byte> pat = Raster::ReadGray(filename,RASFMT_PNG);
00500   return rescale(pat, dims);
00501 }
00502 
00503 // ######################################################################
00504 Image<byte> getTiledTexture(const std::string filename, const Dims dims)
00505 {
00506   //filename refers to a simple texture, black and white, PNG
00507   Image<byte> pat = Raster::ReadGray(filename,RASFMT_PNG);
00508 
00509   const size_t nX = dims.w()/pat.getWidth()+1;
00510   const size_t nY = dims.h()/pat.getHeight()+1;
00511 
00512   std::vector<Image<byte> > tiles(nX,pat);
00513   Layout<byte> horiztile(&tiles[0],nX,Layout<byte>::H);
00514 
00515   std::vector<Layout<byte> > rows(nY,horiztile);
00516   Layout<byte> whole(&rows[0],nY,Layout<byte>::V);
00517 
00518   return crop(whole.render(),Point2D<int>(0,0),dims);
00519 }
00520 
00521 // ######################################################################
00522 /* So things look consistent in everyone's emacs... */
00523 /* Local Variables: */
00524 /* indent-tabs-mode: nil */
00525 /* End: */
Generated on Sun May 8 08:04:11 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3