00001 /*!@file AppMedia/stim-renderLib.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" // for mosaic() 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 "rutz/shared_ptr.h" 00061 #include "Util/MathFunctions.H" 00062 #include "Util/StringUtil.H" 00063 #include "Util/StringConversions.H" 00064 #include "GUI/GUIOpts.H" 00065 00066 #include <sstream> 00067 #include <ctime> 00068 #include <ctype.h> 00069 #include <vector> 00070 #include <string> 00071 #include <fstream> 00072 00073 using namespace std; 00074 00075 static const ModelOptionCateg MOC_RANDGENIMAGE = { 00076 MOC_SORTPRI_2, "Options for random image generation" }; 00077 00078 static const ModelOptionDef OPT_RandImageDims = 00079 { MODOPT_ARG(Dims), "GenImageDims", &MOC_RANDGENIMAGE, OPTEXP_CORE, 00080 "dimensions of the random image", 00081 "rand-image-dims", '\0', "<width>x<height>", "1280x720" }; 00082 00083 // Fill classes for different kinds of textures/colors/gradients 00084 class fillTool { 00085 public: 00086 fillTool() {} 00087 virtual Image<byte> fill(const Dims d) const {return Image<byte>(d,NO_INIT);} 00088 // virtual void whoami() const {LINFO("I am a fillTool...");} 00089 protected: 00090 }; 00091 00092 // brodatz only for now 00093 class fillTexture : public fillTool { 00094 public: 00095 Image<byte> fill(const Dims d) const; 00096 00097 void setSeed(const uint c) {seed = c;} 00098 uint getSeed() const {return seed;} 00099 // void whoami() const {LINFO("I am a fillTexture...");} 00100 protected: 00101 // brodatz seed 00102 uint seed; 00103 }; 00104 00105 // single, grayscale 'color' 00106 class fillColor : public fillTool { 00107 public: 00108 Image<byte> fill(const Dims d) const; 00109 00110 void setColor(const byte c) {bg = c;} 00111 byte getColor() const {return bg;} 00112 // void whoami() const {LINFO("I am a fillColor...");} 00113 00114 protected: 00115 byte bg; 00116 }; 00117 00118 // generic class of objects that can be drawn 00119 class renderObject { 00120 public: 00121 renderObject() {} 00122 virtual void drawOn(Image<byte> &im) const = 0; 00123 virtual bool inBounds(const Dims &d) const = 0; 00124 Point2D<int> getLocation() {return loc;} const 00125 void setLocation(const Point2D<int> &P) {loc = P;} 00126 00127 int getSize() {return siz;} const 00128 void setSize(const int &s) {siz = s;} 00129 00130 float getDirection() {return dir;} const 00131 void setDirection(const float &t) {dir = t;} 00132 00133 protected: 00134 Point2D<int> loc; 00135 float dir; //angle 00136 int siz; 00137 }; 00138 00139 class renderDot : public renderObject { 00140 public: 00141 void drawOn(Image<byte> &im) const; 00142 bool inBounds(const Dims &d) const 00143 {return 0 <= loc.i - dir && 0 <= loc.j - dir && loc.i + dir < d.w() && loc.j + dir < d.h();} 00144 00145 void setFill(rutz::shared_ptr<fillTool> f) {filler = f;} 00146 rutz::shared_ptr<fillTool> getFill() const {return filler;} 00147 private: 00148 rutz::shared_ptr<fillTool> filler; 00149 }; 00150 00151 // Generate a random integer uniformly in (x,y) 00152 int randomInRange(const int x, const int y); 00153 00154 // Generate a random point uniformly in d 00155 //geom::vec2d randomPtIn(const Dims d); 00156 Point2D<int> randomPointIn(const Dims d); 00157 //geom::vec2d randomPtIn(const Rectangle d); 00158 00159 void drawRandomLine(Image<byte> & im, const byte val, const int thickness); 00160 void extrapolateLine(Dims d, Point2D<int> & X, Point2D<int> & Y); 00161 00162 template <class T> 00163 Image<byte> makeNary(const Image<T>& src, const std::vector<T> thresholds, 00164 const std::vector<byte> levels); 00165 00166 00167 Image<byte> texturizeImage(const Image<byte> im, const uint Nlevels); 00168 Image<byte> discretizeImage(const Image<byte> im, const int Nlevels); 00169 Image<byte> getBrodatzTexture(uint seed, const Dims dims); 00170 Image<byte> getStretchedTexture(const std::string filename, const Dims dims); 00171 Image<byte> getTiledTexture(const std::string filename, const Dims dims); 00172 00173 // ###################################################################### 00174 static int submain(const int argc, char** argv) 00175 { 00176 MYLOGVERB = LOG_INFO; // suppress debug messages 00177 00178 // Instantiate a ModelManager: 00179 ModelManager manager("AppMedia: Flyover stimulus"); 00180 00181 // get dimensions of window 00182 if (manager.parseCommandLine(argc, argv,"<out_stem>", 1, 1) == false) 00183 return(1); 00184 00185 // Image<float> myFloatImg(dims.w(), dims.h(), ZEROS); 00186 //Image<double> myDoubleImg(dims.w(), dims.h(), ZEROS); 00187 char filename[255], texfile[255], fillfile[255]; 00188 00189 OModelParam<Dims> dims(&OPT_RandImageDims, &manager); 00190 00191 // get command line parameters for filename 00192 sprintf(filename, "%s.png",manager.getExtraArg(0).c_str()); 00193 sprintf(texfile, "%s-tex.png",manager.getExtraArg(0).c_str()); 00194 sprintf(fillfile, "%s-fill.png",manager.getExtraArg(0).c_str()); 00195 00196 // let's get all our ModelComponent instances started: 00197 manager.start(); 00198 00199 // **************** Experimental settings *************** // 00200 00201 // number of available noise frames in the stimuli folder 00202 // const uint Nnoises = 100; 00203 00204 //tests 00205 Image<byte> myBkgd = Image<byte>(dims.getVal(),ZEROS)+128; //gray bkgd 00206 Image<byte> myMap = myBkgd; 00207 rutz::shared_ptr<fillTexture> style(new fillTexture); 00208 rutz::shared_ptr<fillColor> color(new fillColor); 00209 00210 rutz::shared_ptr<renderDot> dot(new renderDot); 00211 dot->setSize(50); 00212 00213 int rseed, rcol; 00214 for(int i = 0; i < 50; i++) { 00215 rseed = randomUpToIncluding(255); 00216 rcol = randomUpToIncluding(1); 00217 if(rcol==0) { 00218 style->setSeed(rseed); 00219 dot->setFill(style); 00220 } 00221 else { 00222 color->setColor(rseed); 00223 dot->setFill(color); 00224 } 00225 dot->setLocation(randomPointIn(dims.getVal())); 00226 dot->drawOn(myMap); 00227 } 00228 // test texture 00229 LINFO("writing pattern image to %s", filename); 00230 Raster::WriteGray(myMap,filename); 00231 00232 // stop all our ModelComponents 00233 manager.stop(); 00234 00235 // all done! 00236 return 0; 00237 } 00238 00239 // ###################################################################### 00240 00241 extern "C" int main(const int argc, char** argv) 00242 { 00243 // simple wrapper around submain() to catch exceptions (because we 00244 // want to allow PsychoDisplay to shut down cleanly; otherwise if we 00245 // abort while SDL is in fullscreen mode, the X server won't return 00246 // to its original resolution) 00247 try 00248 { 00249 return submain(argc, argv); 00250 } 00251 catch (...) 00252 { 00253 REPORT_CURRENT_EXCEPTION; 00254 } 00255 00256 return 1; 00257 } 00258 00259 00260 // ###################################################################### 00261 Image<byte> fillTexture::fill(const Dims d) const { 00262 return getBrodatzTexture(seed, d); 00263 } 00264 00265 // ###################################################################### 00266 Image<byte> fillColor::fill(const Dims d) const { 00267 return Image<byte>(d,ZEROS) + bg; 00268 } 00269 00270 // ###################################################################### 00271 void renderDot::drawOn(Image<byte> & im) const 00272 { 00273 const byte mask_col = 255; 00274 00275 Image<byte> mask(im.getDims(), ZEROS); 00276 drawDisk(mask, loc, siz, mask_col); 00277 00278 Image<byte> myPrint = filler->fill(im.getDims()); 00279 std::vector<Image<byte> > bgs; 00280 bgs.push_back(im); bgs.push_back(myPrint); 00281 byte bg_assigns[2] = {0, mask_col}; 00282 im = mosaic(mask, &bgs[0], bg_assigns, 2); 00283 } 00284 00285 // ###################################################################### 00286 // Generate a random integer uniformly in (x,y) (open interval) 00287 int randomInRange(const int x, const int y) 00288 { 00289 return randomUpToNotIncluding(y-x-1)+(x+1); 00290 } 00291 00292 // ###################################################################### 00293 00294 // draw a random line 00295 void drawRandomLine(Image<byte> & im, const byte val, const int thickness) 00296 { 00297 Point2D<int> P = randomPointIn(im.getDims()); 00298 Point2D<int> Q = randomPointIn(im.getDims()); 00299 00300 extrapolateLine(im.getDims(),P,Q); 00301 drawLine(im, P, Q, val, thickness); 00302 } 00303 00304 // ###################################################################### 00305 00306 // extend a line segment to boundaries 00307 void extrapolateLine(Dims d, Point2D<int> & X, Point2D<int> & Y) 00308 { 00309 // check if X and Y are in d 00310 Image<byte> foo(d, NO_INIT); 00311 if(!(foo.coordsOk(X) && foo.coordsOk(Y)) || X == Y) return; 00312 00313 if (X.i == Y.i) {X.j = 0; Y.j = d.h(); return;} 00314 else if(X.j == Y.j) {X.i = 0; Y.j = d.w(); return;} 00315 else {float y_0 = (X.j*Y.i-X.i*Y.j)/(Y.i-X.i); 00316 float x_0 = (X.i*Y.j-X.j*Y.i)/(Y.j-X.j); 00317 float slope = (Y.j-X.j)/(Y.i-X.i); 00318 00319 std::vector<Point2D<int> > bounds; 00320 bounds.push_back(Point2D<int>(0,y_0)); 00321 bounds.push_back(Point2D<int>(x_0,0)); 00322 bounds.push_back(Point2D<int>(d.w()-1,y_0+(d.w()-1)*slope)); 00323 bounds.push_back(Point2D<int>(x_0+(d.h()-1)/slope,d.h()-1)); 00324 00325 bool Xdone = 0; 00326 for(int i = 0; i < 4; i++) 00327 if(foo.coordsOk(bounds[i])) { 00328 if(!Xdone) { 00329 X = bounds[i]; 00330 Xdone = true; 00331 } 00332 else { 00333 Y = bounds[i]; 00334 break; 00335 } 00336 } 00337 } 00338 } 00339 00340 // ###################################################################### 00341 00342 // Generate a random point uniformly in d 00343 Point2D<int> randomPointIn(const Dims d) 00344 { 00345 return Point2D<int>(randomInRange(0,d.w()), 00346 randomInRange(0,d.h())); 00347 } 00348 00349 // ###################################################################### 00350 00351 // inspired from makeBinary in Transforms.C 00352 template <class T> 00353 Image<byte> makeNary(const Image<T>& src, const std::vector<T> thresholds, 00354 const std::vector<byte> levels) 00355 { 00356 ASSERT(thresholds.size() == levels.size() - 1); 00357 Image<byte> acc(src.getDims(),ZEROS); 00358 byte floor; 00359 for(uint i = 0; i < thresholds.size(); i++) 00360 { 00361 if(i == 0) 00362 { 00363 floor = levels[0]; 00364 } 00365 else 00366 { 00367 floor = 0; 00368 } 00369 acc += makeBinary(src, thresholds[i],floor,levels[1]); 00370 } 00371 00372 return acc; 00373 } 00374 00375 // ###################################################################### 00376 // maps discretized image to textured image 00377 Image<byte> texturizeImage(const Image<byte> im, const uint Nlevels) 00378 { 00379 uint i, seed; 00380 // Image<byte> levelImage = discretizeImage(im, Nlevels); 00381 std::vector<Image<byte> > texBkgds; 00382 for(i = 0; i < Nlevels; i++) 00383 { 00384 seed = randomInRange(0,112); // num brodatz images 00385 texBkgds.push_back(getBrodatzTexture(seed, im.getDims())); 00386 } 00387 00388 byte tiers[Nlevels]; 00389 for(uint i = 0; i < Nlevels; i++) 00390 tiers[i] = i*(255/(Nlevels-1)); 00391 00392 return mosaic(im, &texBkgds[0], tiers, Nlevels); 00393 //return mosaic(levelImage, &texBkgds[0], tiers, Nlevels); 00394 } 00395 00396 // ###################################################################### 00397 // discretizes image 00398 Image<byte> discretizeImage(const Image<byte> im, const int Nlevels) 00399 { 00400 byte imMin, imMax, i; 00401 getMinMax(im, imMin, imMax); 00402 00403 const byte Ncuts = Nlevels - 1; 00404 00405 // the ratios that partition the image 00406 float coeffs[Ncuts]; 00407 for(i = 0; i < Ncuts; i++) 00408 coeffs[i] = (i+1.0)/(Ncuts+1.0); 00409 00410 // the values in the noise image that partition the image 00411 std::vector<byte> cuts; 00412 for(i = 0; i < Ncuts; i++) 00413 cuts.push_back(imMax*coeffs[i]+imMin*(1-coeffs[i])); 00414 00415 // the mapped values of the outside image 00416 std::vector<byte> tiers; 00417 for(i = 0; i <= cuts.size(); i++) 00418 tiers.push_back(i*(255/cuts.size())); 00419 00420 // use makeNary to cut the image 00421 Image<byte> pattern = makeNary(im,cuts,tiers); 00422 00423 // draw a random line cutting across the image 00424 drawRandomLine(pattern, tiers[2], 50); 00425 00426 return pattern; 00427 00428 } 00429 00430 // ###################################################################### 00431 Image<byte> getBrodatzTexture(uint seed, const Dims dims) 00432 { 00433 char texPath[255]; 00434 00435 // there are only 111 images in the brodatz database 00436 const uint Nimages = 111; 00437 seed = seed % (Nimages - 1) + 1; 00438 00439 sprintf(texPath, "/lab/jshen/projects/eye-cuing/stimuli/textures/brodatz/D%u.png",seed); 00440 return getStretchedTexture(texPath, dims); 00441 } 00442 00443 // ###################################################################### 00444 Image<byte> getStretchedTexture(const std::string filename, const Dims dims) 00445 { 00446 Image<byte> pat = Raster::ReadGray(filename,RASFMT_PNG); 00447 return rescale(pat, dims); 00448 } 00449 00450 // ###################################################################### 00451 Image<byte> getTiledTexture(const std::string filename, const Dims dims) 00452 { 00453 //filename refers to a simple texture, black and white, PNG 00454 Image<byte> pat = Raster::ReadGray(filename,RASFMT_PNG); 00455 00456 const size_t nX = dims.w()/pat.getWidth()+1; 00457 const size_t nY = dims.h()/pat.getHeight()+1; 00458 00459 std::vector<Image<byte> > tiles(nX,pat); 00460 Layout<byte> horiztile(&tiles[0],nX,Layout<byte>::H); 00461 00462 std::vector<Layout<byte> > rows(nY,horiztile); 00463 Layout<byte> whole(&rows[0],nY,Layout<byte>::V); 00464 00465 return crop(whole.render(),Point2D<int>(0,0),dims); 00466 } 00467 00468 // ###################################################################### 00469 /* So things look consistent in everyone's emacs... */ 00470 /* Local Variables: */ 00471 /* indent-tabs-mode: nil */ 00472 /* End: */