00001 /*!@file Psycho/PsychoDisplay.C Display psychophysics stimuli */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2002 // 00005 // by the 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/Psycho/PsychoDisplay.C $ 00035 // $Id: PsychoDisplay.C 14376 2011-01-11 02:44:34Z pez $ 00036 // 00037 00038 #ifdef HAVE_SDL_SDL_H 00039 00040 #include "Psycho/PsychoDisplay.H" 00041 00042 #include "Component/ModelOptionDef.H" 00043 #include "Component/OptionManager.H" 00044 #include "GUI/SDLdisplay.H" 00045 #include "Image/ColorOps.H" 00046 #include "Image/DrawOps.H" 00047 #include "Psycho/EyeTracker.H" 00048 #include "Psycho/PsychoOpts.H" 00049 #include "Util/sformat.H" 00050 #include <vector> 00051 00052 // Used by: PsychoDisplay 00053 const ModelOptionDef OPT_PsychoDisplayBackgroundColor = 00054 { MODOPT_ARG(PixRGB<byte>), "PsychoDisplayBackgroundColor", &MOC_PSYCHODISP, OPTEXP_CORE, 00055 "Background grey color for PsychoDisplay", 00056 "psycho-background-color", '\0', "<r,g,b>", "128,128,128" }; 00057 00058 // Used by: PsychoDisplay 00059 const ModelOptionDef OPT_PsychoDisplayTextColor = 00060 { MODOPT_ARG(PixRGB<byte>), "PsychoDisplayTextColor", &MOC_PSYCHODISP, OPTEXP_CORE, 00061 "Foreground text color for PsychoDisplay", 00062 "psycho-text-color", '\0', "<r,g,b>", "0,0,0" }; 00063 00064 // Used by: PsychoDisplay 00065 const ModelOptionDef OPT_PsychoDisplayBlack = 00066 { MODOPT_ARG(PixRGB<byte>), "PsychoDisplayBlack", &MOC_PSYCHODISP, OPTEXP_CORE, 00067 "'Black' color for PsychoDisplay", 00068 "psycho-black", '\0', "<r,g,b>", "0,0,0" }; 00069 00070 // Used by: PsychoDisplay 00071 const ModelOptionDef OPT_PsychoDisplayWhite = 00072 { MODOPT_ARG(PixRGB<byte>), "PsychoDisplayWhite", &MOC_PSYCHODISP, OPTEXP_CORE, 00073 "'White' color for PsychoDisplay", 00074 "psycho-white", '\0', "<r,g,b>", "255,255,255" }; 00075 00076 // Used by: PsychoDisplay 00077 const ModelOptionDef OPT_PsychoDisplayFixationIcon = 00078 { MODOPT_ARG_STRING, "PsychoDisplayFixationIcon", &MOC_PSYCHODISP, OPTEXP_CORE, 00079 "Filename of fixation image icon, instead of displaying fixation cross", 00080 "psycho-fixation-icon", '\0', "<filename.jpg>", "" }; 00081 00082 00083 // ###################################################################### 00084 PsychoDisplay::PsychoDisplay(OptionManager& mgr, const std::string& descrName, 00085 const std::string& tagName) : 00086 SDLdisplay(mgr, descrName, tagName), 00087 itsBackgroundColor(&OPT_PsychoDisplayBackgroundColor, this), 00088 itsTextColor(&OPT_PsychoDisplayTextColor, this), 00089 itsBlack(&OPT_PsychoDisplayBlack, this), 00090 itsWhite(&OPT_PsychoDisplayWhite, this), 00091 itsFixationIcon(&OPT_PsychoDisplayFixationIcon, this), 00092 itsFixSiz("PsychoDisplayFixSiz", this, 11), 00093 itsFixThick("PsychoDisplayFixThick", this, 1), 00094 itsEyeTracker() 00095 { } 00096 00097 // ###################################################################### 00098 PsychoDisplay::~PsychoDisplay() 00099 { } 00100 00101 // ###################################################################### 00102 void PsychoDisplay::setEyeTracker(nub::soft_ref<EyeTracker> e) 00103 { itsEyeTracker = e; } 00104 00105 // ###################################################################### 00106 void PsychoDisplay::setFixationSize(const int size) 00107 { 00108 itsFixSiz.setVal(size); 00109 } 00110 00111 // ###################################################################### 00112 void PsychoDisplay::clearScreen(const bool vsync) 00113 { SDLdisplay::clearScreen(itsBackgroundColor.getVal(), vsync); } 00114 00115 // ###################################################################### 00116 void PsychoDisplay::displayFixationIcon(const Image< PixRGB<byte> >& image, 00117 const int x, const int y, const bool vsync) 00118 { 00119 const int siz = image.getWidth(); 00120 int i, j; // position of center of cross 00121 int siz2 = (siz - 1) / 2; // half cross size 00122 int w = itsDims.getVal().w(), h = itsDims.getVal().h(); 00123 00124 if (x == -1 || y == -1) { 00125 i = itsDims.getVal().w() / 2 - 1; 00126 j = itsDims.getVal().h() / 2 - 1; 00127 } else { 00128 i = x; j = y; 00129 if (i < siz2) i = siz2; else if (i >= w - siz2) i = w - siz2 - 1; 00130 if (j < siz2) j = siz2; else if (j >= h - siz2) j = h - siz2 - 1; 00131 } 00132 pushEventBegin(sformat("displayFixationIcon (%d, %d)", i, j)); 00133 00134 Point2D<int> xy(i-siz2, j-siz2); 00135 displayImagePatch(image, xy, -2, true, true); 00136 00137 syncScreen(vsync, false, true); 00138 pushEventEnd("displayFixationIcon"); 00139 } 00140 00141 // ###################################################################### 00142 void PsychoDisplay::displayFixation(const int x, const int y, const bool vsync) 00143 { 00144 SDL_Rect rect; 00145 const int thick = itsFixThick.getVal(), siz = itsFixSiz.getVal(); 00146 int i, j; // position of center of cross 00147 int siz2 = (siz - 1) / 2; // half cross size 00148 int w = itsDims.getVal().w(), h = itsDims.getVal().h(); 00149 00150 if (x == -1 || y == -1) { 00151 i = itsDims.getVal().w() / 2 - 1; 00152 j = itsDims.getVal().h() / 2 - 1; 00153 } else { 00154 i = x; j = y; 00155 if (i < siz2) i = siz2; else if (i >= w - siz2) i = w - siz2 - 1; 00156 if (j < siz2) j = siz2; else if (j >= h - siz2) j = h - siz2 - 1; 00157 } 00158 pushEventBegin(sformat("displayFixation (%d, %d)", i, j)); 00159 00160 // horizontal bar 00161 rect.x = i - siz2; 00162 rect.y = j - thick / 2; 00163 rect.w = siz; 00164 rect.h = thick; 00165 SDL_FillRect(itsScreen, &rect, getBlackUint32()); 00166 00167 // vertical bar 00168 rect.x = i - thick / 2; 00169 rect.y = j - siz2; 00170 rect.w = thick; 00171 rect.h = siz; 00172 SDL_FillRect(itsScreen, &rect, getBlackUint32()); 00173 00174 syncScreen(vsync, false, true); 00175 pushEventEnd("displayFixation"); 00176 } 00177 00178 // ###################################################################### 00179 void PsychoDisplay::displayCircle(const int x, const int y, 00180 const int radius, 00181 const PixRGB<byte> color, const bool vsync) 00182 { 00183 int i, j; // position of center of circle 00184 00185 // center of circle 00186 if (x == -1 || y == -1) { 00187 i = itsDims.getVal().w() / 2 - 1; 00188 j = itsDims.getVal().h() / 2 - 1; 00189 } else { 00190 i = x; j = y; 00191 } 00192 00193 // draw circle 00194 pushEventBegin(sformat("displayCircle (%d, %d)", i, j)); 00195 aacircleRGBA(itsScreen, i, j, radius, color.red(), color.green(), color.blue(), 0XFF); 00196 syncScreen(vsync, false, true); 00197 pushEventEnd(sformat("displayCircle (%d, %d)", i, j)); 00198 } 00199 00200 // ###################################################################### 00201 void PsychoDisplay::displayFilledCircle(const int x, const int y, 00202 const int radius, 00203 const PixRGB<byte> color, const bool vsync) 00204 { 00205 int i, j; // position of center of circle 00206 00207 // center of circle 00208 if (x == -1 || y == -1) { 00209 i = itsDims.getVal().w() / 2 - 1; 00210 j = itsDims.getVal().h() / 2 - 1; 00211 } else { 00212 i = x; j = y; 00213 } 00214 00215 // draw circle 00216 pushEventBegin(sformat("displayCircle (%d, %d)", i, j)); 00217 filledCircleRGBA(itsScreen, i, j, radius, color.red(), color.green(), color.blue(), 0XFF); 00218 syncScreen(vsync, false, true); 00219 pushEventEnd(sformat("displayCircle (%d, %d)", i, j)); 00220 } 00221 00222 // ###################################################################### 00223 void PsychoDisplay::displayFilledCircleBlink(const int x, const int y, 00224 const int radius, 00225 const PixRGB<byte> color, 00226 const int iter, const int delay) 00227 { 00228 pushEventBegin("displayFilledCircleBlink"); 00229 for (int i = 0; i < iter; i ++) 00230 { 00231 displayFilledCircle(x, y, radius, color, true); 00232 for (int i = 0; i < delay; ++i) waitNextRequestedVsync(false, true); 00233 displayFilledCircle(x, y, radius, itsBackgroundColor.getVal(), true); 00234 for (int i = 0; i < delay; ++i) waitNextRequestedVsync(false, true); 00235 } 00236 pushEventEnd("displayFilledCircleBlink"); 00237 } 00238 00239 // ###################################################################### 00240 void PsychoDisplay::displayFixationIconBlink(const Image< PixRGB<byte> >& image, 00241 const int x, const int y, 00242 const int iter, const int delay) 00243 { 00244 pushEventBegin("displayFixationBlink"); 00245 for (int i = 0; i < iter; i ++) 00246 { 00247 displayFixationIcon(image, x, y, true); 00248 for (int i = 0; i < delay; ++i) waitNextRequestedVsync(false, true); 00249 clearScreen(true); 00250 for (int i = 0; i < delay; ++i) waitNextRequestedVsync(false, true); 00251 } 00252 pushEventEnd("displayFixationBlink"); 00253 } 00254 00255 // ###################################################################### 00256 void PsychoDisplay::displayFixationBlink(const int x, const int y, 00257 const int iter, const int delay) 00258 { 00259 pushEventBegin("displayFixationBlink"); 00260 for (int i = 0; i < iter; i ++) 00261 { 00262 displayFixation(x, y, true); 00263 for (int i = 0; i < delay; ++i) waitNextRequestedVsync(false, true); 00264 clearScreen(true); 00265 for (int i = 0; i < delay; ++i) waitNextRequestedVsync(false, true); 00266 } 00267 pushEventEnd("displayFixationBlink"); 00268 } 00269 00270 // ###################################################################### 00271 void PsychoDisplay::displayColorDotFixationBlink(const int x, const int y, 00272 const int iter, const int delay, 00273 const PixRGB<byte> color) 00274 { 00275 pushEventBegin("displayColorDotFixationBlink"); 00276 for (int i = 0; i < iter; i ++) 00277 { 00278 displayColorDotFixation(x, y, color, true); 00279 for (int i = 0; i < delay; ++i) waitNextRequestedVsync(false, true); 00280 //clearScreen(true); 00281 displayColorDotFixation(x, y, itsBackgroundColor.getVal(), true); 00282 for (int i = 0; i < delay; ++i) waitNextRequestedVsync(false, true); 00283 } 00284 pushEventEnd("displayColorDotFixationBlink"); 00285 } 00286 00287 // ###################################################################### 00288 void PsychoDisplay::displayColorDotFixation(const int x, const int y, 00289 const PixRGB<byte> color, const bool vsync) 00290 { 00291 SDL_Rect rect; 00292 const int siz = itsFixSiz.getVal() / 2; 00293 int i, j; // position of center of cross 00294 int siz2 = (siz - 1) / 2; // half cross size 00295 int w = itsDims.getVal().w(), h = itsDims.getVal().h(); 00296 00297 if (x == -1 || y == -1) { 00298 i = itsDims.getVal().w() / 2 - 1; 00299 j = itsDims.getVal().h() / 2 - 1; 00300 } else { 00301 i = x; j = y; 00302 if (i < siz2) i = siz2; else if (i >= w - siz2) i = w - siz2 - 1; 00303 if (j < siz2) j = siz2; else if (j >= h - siz2) j = h - siz2 - 1; 00304 } 00305 00306 rect.x = i - siz2; rect.y = j - siz2; rect.w = siz; rect.h = siz; 00307 SDL_FillRect(itsScreen, &rect, getUint32color(color)); 00308 00309 syncScreen(vsync, false, true); 00310 } 00311 00312 // ###################################################################### 00313 void PsychoDisplay::displayRedDotFixation(const int x, const int y, 00314 const bool vsync) 00315 { 00316 SDL_Rect rect; 00317 const int siz = itsFixSiz.getVal() / 2; 00318 int i, j; // position of center of cross 00319 int siz2 = (siz - 1) / 2; // half cross size 00320 int w = itsDims.getVal().w(), h = itsDims.getVal().h(); 00321 00322 if (x == -1 || y == -1) { 00323 i = itsDims.getVal().w() / 2 - 1; 00324 j = itsDims.getVal().h() / 2 - 1; 00325 } else { 00326 i = x; j = y; 00327 if (i < siz2) i = siz2; else if (i >= w - siz2) i = w - siz2 - 1; 00328 if (j < siz2) j = siz2; else if (j >= h - siz2) j = h - siz2 - 1; 00329 } 00330 pushEventBegin(sformat("displayRedDotFixation (%d, %d)", i, j)); 00331 00332 rect.x = i - siz2; rect.y = j - siz2; rect.w = siz; rect.h = siz; 00333 SDL_FillRect(itsScreen, &rect, getUint32color(PixRGB<byte>(255, 0, 0))); 00334 00335 syncScreen(vsync, false, true); 00336 pushEventEnd("displayRedDotFixation"); 00337 } 00338 00339 // ###################################################################### 00340 void PsychoDisplay::displayWhiteDotFixation(const int x, const int y, 00341 const bool vsync) 00342 { 00343 SDL_Rect rect; 00344 const int siz = itsFixSiz.getVal() / 2; 00345 int i, j; // position of center of cross 00346 int siz2 = (siz - 1) / 2; // half cross size 00347 int w = itsDims.getVal().w(), h = itsDims.getVal().h(); 00348 00349 if (x == -1 || y == -1) { 00350 i = itsDims.getVal().w() / 2 - 1; 00351 j = itsDims.getVal().h() / 2 - 1; 00352 } else { 00353 i = x; j = y; 00354 if (i < siz2) i = siz2; else if (i >= w - siz2) i = w - siz2 - 1; 00355 if (j < siz2) j = siz2; else if (j >= h - siz2) j = h - siz2 - 1; 00356 } 00357 pushEventBegin(sformat("displayWhiteDotFixation (%d, %d)", i, j)); 00358 00359 rect.x = i - siz2; rect.y = j - siz2; rect.w = siz; rect.h = siz; 00360 SDL_FillRect(itsScreen, &rect, getUint32color(PixRGB<byte>(255, 255, 255))); 00361 00362 syncScreen(vsync, false, true); 00363 pushEventEnd("displayWhiteDotFixation"); 00364 } 00365 00366 // ###################################################################### 00367 void PsychoDisplay::displayRedDotFixationBlink(const int x, const int y, 00368 const int iter, const int delay) 00369 { 00370 pushEventBegin("displayRedDotFixationBlink"); 00371 for (int i = 0; i < iter; i ++) 00372 { 00373 displayRedDotFixation(x, y, true); 00374 for (int i = 0; i < delay; ++i) waitNextRequestedVsync(false, true); 00375 clearScreen(true); 00376 for (int i = 0; i < delay; ++i) waitNextRequestedVsync(false, true); 00377 } 00378 pushEventEnd("displayRedDotFixationBlink"); 00379 } 00380 00381 // ###################################################################### 00382 void PsychoDisplay::displayRedDotFixationBlink(Image< PixRGB<byte> > img, const int x, const int y, 00383 const int iter, const int delay) 00384 { 00385 //make a copy of our original surface 00386 SDL_Surface *imgs = makeBlittableSurface(img); 00387 SDL_Rect screct;screct.w = 640; screct.h = 480; 00388 pushEventBegin("displayRedDotFixationBlink"); 00389 for (int i = 0; i < iter; i ++) 00390 { 00391 displayImage(img); 00392 displayRedDotFixation(x, y, true); 00393 for (int i = 0; i < delay*4; ++i) waitNextRequestedVsync(false, true); 00394 SDL_BlitSurface(imgs, NULL, itsScreen, &screct); 00395 SDL_UpdateRect(itsScreen,screct.x,screct.y,screct.w,screct.h); 00396 for (int i = 0; i < delay*4; ++i) waitNextRequestedVsync(false, true); 00397 } 00398 pushEventEnd("displayRedDotFixationBlink"); 00399 } 00400 00401 // ###################################################################### 00402 void PsychoDisplay::displayISCANcalib() 00403 { 00404 pushEventBegin("ISCANcalibration"); 00405 int w = itsDims.getVal().w(), h = itsDims.getVal().h(); 00406 00407 // get the coords of the fixation points in a 640x480 image: 00408 const int npts = 5; 00409 int px[npts] = { 320, 100, 540, 100, 540 }; 00410 int py[npts] = { 240, 80, 80, 380, 380 }; 00411 00412 // rescale the coords of the fixation points if image size not 640x480: 00413 if (w != 640) 00414 for (int i = 0; i < npts; i ++) 00415 px[i] = int(round(float(px[i]) * float(w) / 640.0f)); 00416 if (h != 480) 00417 for (int i = 0; i < npts; i ++) 00418 py[i] = int(round(float(py[i]) * float(h) / 480.0f)); 00419 00420 // let's loop over the points and display them all at once: 00421 SDL_Rect rect; rect.w = 5; rect.h = 5; 00422 SDL_Rect rect2; rect2.w = 3; rect2.h = 3; 00423 for (int i = 0; i < npts; i ++) 00424 { 00425 rect.x = px[i] - 1; rect.y = py[i] - 1; rect2.x = px[i]; rect2.y = py[i]; 00426 00427 SDL_FillRect(itsScreen, &rect, getBlackUint32()); 00428 SDL_FillRect(itsScreen, &rect2, getWhiteUint32()); 00429 } 00430 syncScreen(true, false); 00431 pushEventEnd("ISCANcalibration"); 00432 } 00433 00434 //####################################################################### 00435 void PsychoDisplay::displayMovingDotBackground(SDL_Surface *img, const int startX, const int startY, 00436 const int endX, const int endY, const float speed, const PixRGB<byte> color) 00437 { 00438 00439 if (itsEyeTracker.isValid() == false) 00440 LFATAL("You need to set an EyeTracker using setEyeTracker() first"); 00441 00442 00443 00444 //get color in RGB space 00445 Uint32 col = getUint32color(color); 00446 00447 // calulate number of frames (the unit of speed is pixels/frame 00448 float frameNum = sqrt(pow(endX-startX,2)+pow(endY-startY,2))/speed; 00449 std::vector<Point2D<int> > pts; 00450 float unitX = (endX-startX)/frameNum, unitY = (endY-startY)/frameNum; 00451 for(int i=0; i<frameNum; i++){ 00452 int x = static_cast<int>(startX + i * unitX); 00453 int y = static_cast<int>(startY + i * unitY); 00454 pts.push_back(Point2D<int>(x, y)); 00455 } 00456 00457 //lets create a rectangle for the whole screem 00458 SDL_Rect screct;screct.w = 640; screct.h = 480; 00459 screct.x = 0; screct.y = 0; 00460 00461 //let's loop over the points and display them 00462 SDL_Rect rect; rect.w = 7; rect.h = 7; 00463 for(unsigned int i=0; i<pts.size(); i++){ 00464 Point2D<int> p = pts[i]; 00465 rect.x = p.i-3; rect.y = p.j-3; 00466 00467 //make a copy of our original surface 00468 SDL_BlitSurface(img, NULL, itsScreen, &screct); 00469 //display new square 00470 SDL_FillRect(itsScreen, &rect, col); 00471 //update screen here 00472 waitNextRequestedVsync(false, true); 00473 SDL_UpdateRect(itsScreen,screct.x,screct.y,screct.w,screct.h); 00474 00475 // log moving square position: 00476 pushEvent(sformat("Dot_Position at (%d, %d)", p.i, p.j)); 00477 } 00478 } 00479 00480 //###################################################################### 00481 void PsychoDisplay::displayMovingDotTrain(const Image< PixRGB<byte> >& img, int location[][2], int num_loc, 00482 float speed[], int stay[], const PixRGB<byte> color) 00483 { 00484 SDL_Surface *sdlimg = makeBlittableSurface(img); 00485 pushEventBegin("Display Dot Movement Train"); 00486 //clearScreen(); 00487 00488 // foreach transition location 00489 for(int i=0; i<num_loc-1; i++){ 00490 // stay 00491 pushEventBegin(sformat("Stay at location (%d, %d)", location[i][0], location[i][1])); 00492 00493 for(int j=0; j<stay[i]; j++) {waitNextRequestedVsync(false, true);} 00494 00495 pushEventEnd(sformat("Stay at location (%d, %d)", location[i][0], location[i][1])); 00496 00497 // move 00498 pushEventBegin(sformat("Move from (%d, %d) to (%d, %d) at %f pixel/frame", 00499 location[i][0], location[i][1], location[i+1][0], 00500 location[i+1][1], speed[i])); 00501 displayMovingDotBackground(sdlimg,location[i][0], location[i][1], 00502 location[i+1][0], location[i+1][1], speed[i],color); 00503 pushEventEnd(sformat("Move from (%d, %d) to (%d, %d) at %f pixel/frame", 00504 location[i][0], location[i][1], location[i+1][0], 00505 location[i+1][1], speed[i])); 00506 } 00507 00508 // stay for the last location 00509 int l = num_loc-1; 00510 pushEventBegin(sformat("Stay at location (%d, %d)", location[l][0], location[l][1])); 00511 for(int j=0; j<stay[l]; j++){ waitNextRequestedVsync(false, true);} 00512 pushEventEnd(sformat("Stay at location (%d, %d)", location[l][0], location[l][1])); 00513 pushEventEnd("Display Dot Movement Train"); 00514 00515 } 00516 00517 00518 00519 00520 00521 // ###################################################################### 00522 void PsychoDisplay::displaySmoothPursuitGroupCalibration(int location[][2], int num_loc, float speed[], int num_speed) 00523 00524 { 00525 pushEventBegin("Eye Tracker Calibration by Smooth Pursuit"); 00526 00527 clearScreen(); 00528 00529 // start the eye tracker 00530 itsEyeTracker->track(true); 00531 00532 // for each speed 00533 for(int i=0; i<num_speed; i++){ 00534 00535 // display a fixation point at the beginning of the path with different speed 00536 displayFixation(location[0][0], location[0][1]); 00537 waitForKey(); 00538 clearScreen(); 00539 00540 // for each start/end pair 00541 for(int j=0; j<num_loc-1; j++){ 00542 displaySmoothPursuitCalibration(location[j][0], location[j][1], 00543 location[j+1][0], location[j+1][1], speed[i]); 00544 } 00545 } 00546 00547 // stop the eye tracker 00548 itsEyeTracker->track(false); 00549 00550 clearScreen(); 00551 00552 pushEventEnd("Eye Tracker Calibration by Smooth Pursuit"); 00553 } 00554 00555 // ###################################################################### 00556 void PsychoDisplay::displaySmoothPursuitCalibration( 00557 const int startX, const int startY, 00558 const int endX, const int endY, const float speed, uint color) 00559 { 00560 00561 if (itsEyeTracker.isValid() == false) 00562 LFATAL("You need to set an EyeTracker using setEyeTracker() first"); 00563 00564 // calulate number of frames (the unit of speed is pixels/frame 00565 float frameNum = sqrt(pow(endX-startX,2)+pow(endY-startY,2))/speed; 00566 std::vector<Point2D<int> > pts; 00567 float unitX = (endX-startX)/frameNum, unitY = (endY-startY)/frameNum; 00568 for(int i=0; i<frameNum; i++){ 00569 int x = static_cast<int>(startX + i * unitX); 00570 int y = static_cast<int>(startY + i * unitY); 00571 pts.push_back(Point2D<int>(x, y)); 00572 } 00573 00574 //let's loop over the points and display them 00575 SDL_Rect rect; rect.w = 7; rect.h = 7; 00576 SDL_Rect preRect; preRect.w = rect.w; preRect.h = rect.h; preRect.x = 0; preRect.y = 0; 00577 for(unsigned int i=0; i<pts.size(); i++){ 00578 Point2D<int> p = pts[i]; 00579 rect.x = p.i-3; rect.y = p.j-3; 00580 00581 //erase the square displayed in previous frame 00582 SDL_FillRect(itsScreen, &preRect, getGreyUint32()); 00583 00584 //display new square 00585 SDL_FillRect(itsScreen, &rect, color); 00586 00587 waitNextRequestedVsync(false, true); 00588 SDL_UpdateRect(itsScreen, preRect.x, preRect.y, preRect.w, preRect.h); 00589 SDL_UpdateRect(itsScreen, rect.x, rect.y, rect.w, rect.h); 00590 00591 // update previous rectangle location to the current one 00592 preRect.x = rect.x; preRect.y = rect.y; 00593 00594 // log moving square position: 00595 pushEvent(sformat("smoothPursuit_squarePosition at (%d, %d)", p.i, p.j)); 00596 } 00597 00598 // erase the lastest square displayed 00599 SDL_FillRect(itsScreen, &preRect, 00600 getUint32color(itsBackgroundColor.getVal())); 00601 00602 waitNextRequestedVsync(false, true); 00603 SDL_UpdateRect(itsScreen, preRect.x, preRect.y, preRect.w, preRect.h); 00604 } 00605 00606 // ###################################################################### 00607 void PsychoDisplay::displaySmoothPursuitFancyTrace(int location[][2], int num_loc, 00608 float speed[], int stay[], uint color) 00609 { 00610 pushEventBegin("Display Fancy Trace for Smooth Pursuit"); 00611 clearScreen(); 00612 00613 // start the eye-tracker 00614 // itsEyeTracker->track(true); 00615 00616 // foreach transition location 00617 for(int i=0; i<num_loc-1; i++){ 00618 // stay 00619 pushEventBegin(sformat("Stay at location (%d, %d)", location[i][0], location[i][1])); 00620 SDL_Rect rect; rect.w = 7; rect.h = 7; 00621 rect.x=location[i][0]-3; rect.y=location[i][1]-3; 00622 SDL_FillRect(itsScreen, &rect, color); 00623 SDL_UpdateRect(itsScreen, rect.x, rect.y, rect.w, rect.h); 00624 for(int j=0; j<stay[i]; j++){ 00625 waitNextRequestedVsync(false, true); 00626 } 00627 SDL_FillRect(itsScreen, &rect, getGreyUint32()); 00628 SDL_UpdateRect(itsScreen, rect.x, rect.y, rect.w, rect.h); 00629 pushEventEnd(sformat("Stay at location (%d, %d)", location[i][0], location[i][1])); 00630 00631 // move 00632 pushEventBegin(sformat("Move from (%d, %d) to (%d, %d) at %f pixel/frame", 00633 location[i][0], location[i][1], location[i+1][0], 00634 location[i+1][1], speed[i])); 00635 displaySmoothPursuitCalibration(location[i][0], location[i][1], 00636 location[i+1][0], location[i+1][1], speed[i],color); 00637 pushEventEnd(sformat("Move from (%d, %d) to (%d, %d) at %f pixel/frame", 00638 location[i][0], location[i][1], location[i+1][0], 00639 location[i+1][1], speed[i])); 00640 } 00641 00642 // stay for the last location 00643 int l = num_loc-1; 00644 pushEventBegin(sformat("Stay at location (%d, %d)", location[l][0], location[l][1])); 00645 SDL_Rect rect; rect.w = 7; rect.h = 7; 00646 rect.x=location[l][0]-3; rect.y=location[l][1]-3; 00647 SDL_FillRect(itsScreen, &rect, color); 00648 SDL_UpdateRect(itsScreen, rect.x, rect.y, rect.w, rect.h); 00649 for(int j=0; j<stay[l]; j++){ 00650 waitNextRequestedVsync(false, true); 00651 } 00652 SDL_FillRect(itsScreen, &rect, getGreyUint32()); 00653 SDL_UpdateRect(itsScreen, rect.x, rect.y, rect.w, rect.h); 00654 pushEventEnd(sformat("Stay at location (%d, %d)", location[l][0], location[l][1])); 00655 00656 // stop the eye-tracker 00657 // itsEyeTracker->track(false); 00658 clearScreen(); 00659 00660 pushEventEnd("Display Fancy Trace for Smooth Pursuit"); 00661 } 00662 00663 00664 00665 00666 // ###################################################################### 00667 void PsychoDisplay::displayEyeTrackerCalibration(const int nptshoriz, 00668 const int nptsvertic, 00669 const int timefactor, 00670 const bool mouserespond ) 00671 { 00672 if (itsEyeTracker.isValid() == false) 00673 LFATAL("You need to set an EyeTracker using setEyeTracker() first"); 00674 00675 pushEventBegin("EyeTrackerCalibration"); 00676 int w = itsDims.getVal().w(), h = itsDims.getVal().h(); 00677 int deltax = w / (nptshoriz + 1), deltay = h / (nptsvertic + 1); 00678 00679 // list all the points we want: 00680 std::vector<Point2D<int> > pts; 00681 for (int j = deltay-1; j < h - deltay; j += deltay) 00682 for (int i = deltax-1; i < w - deltax; i += deltax) 00683 pts.push_back(Point2D<int>(i, j)); 00684 00685 // randomize these babies: 00686 for (uint i = 0; i < pts.size(); i ++) 00687 { 00688 uint j = i + randomUpToNotIncluding(pts.size() - i); 00689 Point2D<int> tmp = pts[i]; pts[i] = pts[j]; pts[j] = tmp; 00690 } 00691 00692 // let's loop over the points and display them: 00693 SDL_Rect rect; rect.w = 3; rect.h = 3; 00694 SDL_Rect rect2; rect2.w = 1; rect2.h = 1; 00695 while (pts.size()) { 00696 // get current point: 00697 Point2D<int> p = pts.back(); pts.pop_back(); 00698 rect.x = p.i - 1; rect.y = p.j - 1; rect2.x = p.i; rect2.y = p.j; 00699 00700 // show fixation and wait for key: 00701 clearScreen(); 00702 displayFixation(); 00703 if(!mouserespond){ 00704 waitForKey(); 00705 }else{ 00706 waitForMouseClick(); 00707 } 00708 00709 waitNextRequestedVsync(false, true); 00710 00711 // start the eye tracker: 00712 itsEyeTracker->track(true); 00713 00714 // blink the fixation: 00715 displayFixationBlink(-1, -1, 5, 2*timefactor); 00716 00717 // log fixation position: 00718 pushEventBegin(sformat("eyeTrackerCalibration at (%d, %d)", p.i, p.j)); 00719 00720 // let's flash a marker at the desired position: 00721 for (int k = 0; k < 9; k ++) { 00722 SDL_FillRect(itsScreen, &rect, getBlackUint32()); 00723 SDL_FillRect(itsScreen, &rect2, getWhiteUint32()); 00724 syncScreen(true, false, true); 00725 for (int i = 0; i < 2*timefactor; ++i) 00726 waitNextRequestedVsync(false, true); // sleep a bit 00727 00728 SDL_FillRect(itsScreen, &rect, getWhiteUint32()); 00729 SDL_FillRect(itsScreen, &rect2, getBlackUint32()); 00730 syncScreen(true, false, true); 00731 for (int i = 0; i < 2*timefactor; ++i) 00732 waitNextRequestedVsync(false, true); // sleep a bit 00733 } 00734 pushEventEnd(sformat("eyeTrackerCalibration at (%d, %d)", p.i, p.j)); 00735 00736 // stop the eye tracker: 00737 itsEyeTracker->track(false); 00738 } 00739 00740 // done! 00741 clearScreen(); 00742 pushEventEnd("EyeTrackerCalibration"); 00743 } 00744 00745 // ###################################################################### 00746 void PsychoDisplay::displayRandomText(const int stringlength, const int fontsize, const bool vsync , int ind ) 00747 { 00748 // generating a random string 00749 char random_str[stringlength]; 00750 for(int i=0; i<stringlength; i++){ 00751 random_str[i] = 65 + (int)(26 * (rand() / (RAND_MAX + 1.0))); 00752 } 00753 std::string msg (random_str); 00754 LINFO("random text: %s", msg.c_str()); 00755 00756 00757 // display the string 00758 SDLdisplay::displayText(msg, vsync, itsTextColor.getVal(), 00759 itsBackgroundColor.getVal(), ind, fontsize ); 00760 } 00761 00762 // ###################################################################### 00763 void PsychoDisplay::displayText(const std::string& msg, const bool vsync , int ind, const int fontsize ) 00764 { 00765 SDLdisplay::displayText(msg, vsync, itsTextColor.getVal(), 00766 itsBackgroundColor.getVal(), ind, fontsize ); 00767 } 00768 00769 // ###################################################################### 00770 void PsychoDisplay::displayText(const std::string& msg, const Point2D<int>& p , const PixRGB<byte> txtcol ,const PixRGB<byte> bgcol , const bool vsync) 00771 { 00772 SDLdisplay::displayText(msg, p,txtcol ,bgcol,vsync) ; 00773 } 00774 00775 // ###################################################################### 00776 PixRGB<byte> PsychoDisplay::getGrey() const 00777 { return itsBackgroundColor.getVal(); } 00778 00779 // ###################################################################### 00780 void PsychoDisplay::drawCloud(DOT *clouds, const int numDots, 00781 const Uint8 r, const Uint8 g, const Uint8 b, 00782 const bool vsync) 00783 { 00784 // Map the color white to this display (R=0xff, G=0xFF, B=0xFF) 00785 // Note: If the display is palettized, you must set the palette first. 00786 Uint32 color = getUint32color(PixRGB<byte>(r, g, b)); 00787 00788 // Lock the screen for direct access to the pixels 00789 if ( SDL_MUSTLOCK(itsScreen) ) { 00790 if ( SDL_LockSurface(itsScreen) < 0 ) { 00791 LINFO("Can't lock screen: %s", SDL_GetError()); 00792 return; 00793 } 00794 } 00795 00796 for (int j = 0; j < 25; j ++){ 00797 for (int i = 0; i < numDots; i++){ 00798 DOT* dot = clouds + j * numDots + i; 00799 if (dot != NULL) putPixel32(dot->x, dot->y, color); 00800 } 00801 } 00802 00803 if ( SDL_MUSTLOCK(itsScreen) ) 00804 SDL_UnlockSurface(itsScreen); 00805 00806 if (vsync) syncScreen(vsync, true, false); 00807 } 00808 00809 // ###################################################################### 00810 void PsychoDisplay::drawClouds(DOT *newClouds, DOT *oldClouds, 00811 const int numDots, 00812 const Uint8 r, const Uint8 g, const Uint8 b, 00813 const bool vsync) 00814 { 00815 // Map the color white to this display (R=0xff, G=0xFF, B=0xFF) 00816 // Note: If the display is palettized, you must set the palette first. 00817 Uint32 color = getUint32color(PixRGB<byte>(r, g, b)); 00818 Uint32 black = getUint32color(PixRGB<byte>(0, 0, 0)); 00819 00820 // Lock the screen for direct access to the pixels 00821 if ( SDL_MUSTLOCK(itsScreen) ) { 00822 if ( SDL_LockSurface(itsScreen) < 0 ) { 00823 LINFO("Can't lock screen: %s", SDL_GetError()); 00824 return; 00825 } 00826 } 00827 00828 for (int j = 0; j < 25; j ++){ 00829 for (int i = 0; i < numDots; i++){ 00830 DOT* newdot = newClouds + j * numDots + i; 00831 putPixel32(newdot->x, newdot->y, color); 00832 DOT* olddot = oldClouds + j * numDots + i; 00833 putPixel32(olddot->x, olddot->y, black); 00834 } 00835 } 00836 if ( SDL_MUSTLOCK(itsScreen) ) 00837 SDL_UnlockSurface(itsScreen); 00838 00839 if (vsync) syncScreen(vsync, true, false); 00840 } 00841 00842 // ###################################################################### 00843 int PsychoDisplay::displayNumbers(const int targetRow, 00844 const int targetCol, const bool vsync) 00845 { 00846 // let's get a white image: 00847 Image<PixRGB<byte> > cimg(itsDims.getVal(), NO_INIT); 00848 cimg.clear(itsBackgroundColor.getVal()); 00849 00850 // randomize the cell indices 00851 int index[25]; 00852 for (int i = 0; i < 25; i++) 00853 index[i] = i; 00854 randShuffle (index, 25); 00855 00856 int targetNum = -1; 00857 // for each random cell, display a number 00858 for (int i = 0; i < 5; i++) 00859 for (int j = 0; j < 5; j++){ 00860 int row = index[i*5+j]/5, col = index[i*5+j]%5; 00861 Point2D<int> p; p.i = col * 128 + 64; p.j = row * 96 + 48; 00862 if (p.i < 0) LERROR("Text does not fit on screen!"); 00863 char msg[5]; 00864 snprintf(msg, sizeof(msg), "%d%d", i+1, j+1); 00865 writeText(cimg, p, msg, 00866 itsTextColor.getVal(), 00867 itsBackgroundColor.getVal()); 00868 if (row == targetRow && col == targetCol) 00869 targetNum = (i+1)*10 + (j+1); 00870 } 00871 // let's convert it to something we can blit: 00872 SDL_Surface *surf = 00873 SDL_CreateRGBSurfaceFrom(cimg.getArrayPtr(), cimg.getWidth(), 00874 cimg.getHeight(), 24, 3 * cimg.getWidth(), 00875 0x0000ff, 0x00ff00, 0xff0000, 0x0); 00876 SDL_Surface *surf2 = SDL_DisplayFormat(surf); 00877 SDL_FreeSurface(surf); 00878 displaySurface(surf2, -1, vsync); 00879 SDL_FreeSurface(surf2); 00880 return targetNum; 00881 } 00882 00883 00884 // ###################################################################### 00885 int PsychoDisplay::drawPoint(const Point2D<int> thePoint) 00886 { 00887 if (itsEyeTracker.isValid() == false) 00888 LFATAL("You need to set an EyeTracker using setEyeTracker() first"); 00889 00890 //int w = itsDims.getVal().w(), h = itsDims.getVal().h(); 00891 00892 00893 // let's loop over the points and display them: 00894 SDL_Rect rect; rect.w = 10; rect.h = 10; rect.x=thePoint.i; rect.y = thePoint.j; 00895 00896 // log fixation position: 00897 pushEventBegin(sformat("eye tracker instantaneous eye position %d, %d", thePoint.i, thePoint.j)); 00898 00899 SDL_FillRect(itsScreen, &rect, getUint32color(PixRGB<byte>(255, 0, 0))); 00900 00901 syncScreen(true, false, true); 00902 SDL_UpdateRect(itsScreen, rect.x, rect.y, rect.w, rect.h); 00903 waitNextRequestedVsync(false, true); // sleep a bit 00904 return 1; 00905 } 00906 00907 00908 //###################################################################### 00909 00910 int PsychoDisplay::drawPointColor(const Point2D<int> thePoint, PixRGB<byte> color) 00911 { 00912 if (itsEyeTracker.isValid() == false) 00913 LFATAL("You need to set an EyeTracker using setEyeTracker() first"); 00914 00915 //int w = itsDims.getVal().w(), h = itsDims.getVal().h(); 00916 00917 00918 // let's loop over the points and display them: 00919 SDL_Rect rect; rect.w = 10; rect.h = 10; rect.x=thePoint.i; rect.y = thePoint.j; 00920 00921 // log fixation position: 00922 pushEventBegin(sformat("eye tracker instantaneous eye position %d, %d", thePoint.i, thePoint.j)); 00923 00924 SDL_FillRect(itsScreen, &rect, getUint32color(color)); 00925 00926 syncScreen(true, false, true); 00927 SDL_UpdateRect(itsScreen, rect.x, rect.y, rect.w, rect.h); 00928 waitNextRequestedVsync(false, true); // sleep a bit 00929 return 1; 00930 } 00931 00932 //############################################################################# 00933 int PsychoDisplay::drawCalibPoint(const Point2D<int> thePoint) 00934 { 00935 SDL_Rect rect; rect.w = 5; rect.h = 5; 00936 SDL_Rect rect2; rect2.w = 3; rect2.h = 3; 00937 rect.x = thePoint.i - 1; rect.y = thePoint.j - 1; rect2.x = thePoint.i; rect2.y = thePoint.j; 00938 SDL_FillRect(itsScreen, &rect, getBlackUint32()); 00939 SDL_FillRect(itsScreen, &rect2, getWhiteUint32()); 00940 return 1; 00941 } 00942 00943 // ###################################################################### 00944 Uint32 PsychoDisplay::getGreyUint32() const 00945 { return getUint32color(itsBackgroundColor.getVal()); } 00946 00947 // ###################################################################### 00948 Uint32 PsychoDisplay::getBlackUint32() const 00949 { return getUint32color(itsBlack.getVal()); } 00950 00951 // ###################################################################### 00952 Uint32 PsychoDisplay::getWhiteUint32() const 00953 { return getUint32color(PixRGB<byte>(itsWhite.getVal())); } 00954 00955 // ###################################################################### 00956 void PsychoDisplay::changeBackgroundColor(PixRGB<byte> c) 00957 { itsBackgroundColor.setVal(c); } 00958 00959 #endif // HAVE_SDL_SDL_H 00960 00961 // ###################################################################### 00962 /* So things look consistent in everyone's emacs... */ 00963 /* Local Variables: */ 00964 /* indent-tabs-mode: nil */ 00965 /* End: */